Java枚举类

简介: Java枚举类


一个简单的例子

class Enum1  {
    public static final int RED = 0;
    public static final int YELLOW = 1;
    public static final int BLUE = 2;
    public static final int PINK = 3;
    public static final int WHITE = 4;
    public static final int BLACK = 5;
}

上述这种定义常量的方法被称为int的枚举模式, 这种方法虽然可以正常使用, 但是还存在着许多不足之处, 例如同时定义了int值相同的两个变量, 就非常容易混淆.

所以在Java中提供了一个枚举enum类来用来表示这种情况.

public enum test {
    RED,YELLOW,BLUE,PINK,WHITE,BLACK;
}

这种枚举的方法, 将一些常用的常量集合在一个类当中

一些应用场景: 例如错误状态码, 消息类型等等.

本质上, enum类是java.lang.Enum的子类, 也就是说, 自己写的枚举类, 就算没有显示继承Enum, 但是也默认继承了Enum这个类.

枚举类型比较

下面有如下例子:

public enum test {RED,YELLOW,BLUE,PINK,WHITE,BLACK;}

实际上这个声明定义的test他是一个类,它里面有6个实例, 不可能再去构造新的对象. 因此我们在比较枚举类型的时候并不需要去使用equals()方法, 直接使用"=="来比较. 因为枚举值是单例的, 相同枚举值, 一定是相同的对象.

同样的使用.equals方法, 它里面也是使用"=="实现的:

public enum test {
    RED,BLACK,GREEN,WHITE;
 
    public static void main(String[] args) {
        test value1 = test.BLACK;
        test value2 = test.RED;
        test value3 = test.RED;
        // 使用 运算符 '=='
        System.out.println(value1 == value2);
        // 使用equals()方法
        System.out.println(value1.equals(value2));
 
        System.out.println(value3 == value2);
    }
}

运行结果为:

使用场景

  1. switch-case语句:
public enum TestEnum {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        TestEnum testEnum2 = TestEnum.BLACK;
 
        switch (testEnum2) {
            case RED:
                System.out.println("red");
                break;
            case BLACK:
                System.out.println("black");
                break;
            case WHITE:
                System.out.println("WHITE");
                break;
            case GREEN:
                System.out.println("black");
                break;
            default:
                break;
        }
    }
}
  1. 拿到某一单个的枚举实例成员并输出
public enum test {
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        test tem = test.BLACK;
        System.out.println(tem);
    }
 
}

输出:

Enum类常用的方法

虽然enum类没有显示继承类, 但是其实底层默认继承了Enum类, Enum类中提供了一些方法,用来对这个enum类来进行一些便捷操作:

方法

作用

values()

以数组形式返回枚举类型的所有成员

ordinal()

获取枚举成员的索引位置

valueOf()

将普通字符串转换为枚举实例

compareTo()

比较两个枚举成员在定义时的顺序

toString()

以字符串的形式返回枚举常量名

1.values()

以数组形式返回枚举类型的所有成员

  • 直接fori循环遍历
public enum test {
    RED,BLACK,GREEN,WHITE;
 
    public static void main(String[] args) {
        test[] tem = test.values();
 
        for(int i = 0; i < tem.length; i++) {
            System.out.println(tem[i]);
        }
    }
 
}
  • for-each遍历
public enum test {
    RED,BLACK,GREEN,WHITE;
 
    public static void main(String[] args) {
        test[] tem = test.values();
        for(test x : tem) {
            System.out.println(x);
        }
    }
 
}

2. ordinal()

int ordinak()

获取枚举成员的索引位置

一个枚举类型: public enum test {RED,BLACK,GREEN,WHITE;}

他的实例的索引是从0开始的, 例如:

RED -> 0

BLACK -> 1

GREEN -> 2

WHITE -> 3

public enum test {
    RED,BLACK,GREEN,WHITE;
 
    public static void main(String[] args) {
        test value = test.WHITE;
        int ret = value.ordinal();
        System.out.println(ret);
    }
}

使用ordinal方法返回这个枚举实例的索引

3. valueOf()

static Enum valueOf(Class enumClass, String name)

返回一个字符串对应的枚举实例

public enum test {
    RED,BLACK,GREEN,WHITE;
 
    public static void main(String[] args) {
        String tem = "RED";
        test ret = Enum.valueOf(test.class,"RED");
        System.out.println(ret);
    }
}

其中 test.class是返回一个test类的类型的实例

4.compareTo()

int compareTo(E other)

如果枚举常量出现在other之前, 则返回一个负整数, 如果this == other则返回0, 否则, 返回一个正整数, 枚举常量的出现次序在enum声明中输出.也就是比较两个枚举成员在定义时的顺序

例如有枚举类:

public enum test {
    RED,BLACK,GREEN,WHITE;
 
    public static void main(String[] args) {
        test value1 = test.BLACK;
        test value2 = test.WHITE;
        System.out.println(value1.compareTo(value2));
    }
}

结果输出 -2, 也就是BLACK索引为1, WHITE索引为3. 因为BLACK常量在WHITE之前, 所以返回一个辅助, 也就是-2.

5. toString()

将对应枚举常量转化为字符串的形式.

public enum test {
    RED,BLACK,GREEN,WHITE;
 
    public static void main(String[] args) {
        test value = test.BLACK;
        String ret = value.toString();
        System.out.println(ret);
    }
}

结果为:

枚举的自定义构造

在java中的枚举就是一个类, 所以我们在定义类的时候还可以为其提供相应的构造方法:

public enum TestEnum {
    RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
    private String name;
    private int key;
    /**
     * 1、当枚举对象有参数后,需要提供相应的构造函数
     * 2、枚举的构造函数默认是私有的 这个一定要记住
     * @param name
     * @param key
     */
    private TestEnum (String name,int key) {
        this.name = name;
        this.key = key;
    }
    public static TestEnum getEnumKey (int key) {
 
        for (TestEnum t: TestEnum.values()) {
            if(t.key == key) {
                return t;
            }
        }
        return null;
    }
    public static void main(String[] args) {
        System.out.println(getEnumKey(2));
    }
}

RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);

创建了自己的枚举构造方法之后, 其索引值还是根据默认来设置:

枚举实例

索引值

RED

0

BLACK

1

WHITE

2

GREEN

3

当枚举实例需要参数的时候, 就需要提供有参数的构造方法

比如说我们的一个枚举实例:RED, 它里面有参数name和一个key值, 进行构造方法的时候,需要传入相关参数.

那这种属性有什么用呢? 其实就是方便存储数据, 例如你设计了一个图片表, 需要存储图片里面拥有什么主颜色, 这个是有一般人就会想到:

Image image = new Image();
image.setColor(1);

但是我们会忘记这里的1是指的什么颜色, 也就容易造成混淆. 所以有了枚举只有, 就方便多了. 于是我们可以利用枚举来进行修改:

Image image = new Image();
image.setColor(TestEnum.RED.getEnumKey() );

其中RED.getEnumKey()方法就是获得RED的key值.

枚举的反射

我们说, 枚举的构造器总是私有的,可以省略private修饰符, 因为他默认就是private修饰的构造器. 如果将一个构造方法声明为public 或者protected就会出现语法错误

但是,我们刚刚在反射里边看到了,任何一个类,哪怕其构造方法是私有的,我们也可以通过反射拿到他的实例对象,那么枚举的构造方法也是私有的,我们是否可以拿到呢?接下来,我们来实验一下:

import java.lang.reflect.Constructor;
 
public enum TestEnum {
    RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
    private String name;
    private int key;
        // 默认为private修饰的构造方法
    private TestEnum (String name,int key) {
        this.name = name;
        this.key = key;
    }
    public static TestEnum getEnumKey (int key) {
        for (TestEnum t: TestEnum.values()) {
            if(t.key == key) {
                return t;
            }
        }
        return null;
    }
    public static void reflectPrivateConstructor() {
        try {
            Class<?> classStudent = Class.forName("TestEnum");
//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。
            Constructor<?> declaredConstructorStudent = classStudent.getDeclaredConstructor(String.class,int.class);
//设置为true后可修改访问权限
            declaredConstructorStudent.setAccessible(true);
            Object objectStudent = declaredConstructorStudent.newInstance("绿色",666);
            TestEnum testEnum = (TestEnum) objectStudent;
            System.out.println("获得枚举的私有构造函数:"+testEnum);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    public static void main(String[] args) {
        reflectPrivateConstructor();
    }
}

输出结果:

异常信息提示: NoSuchMethodException, 也就是没有找到对应的构造方法. 问题出在哪呢?

我们前面所提到的所有的枚举enum类都是继承Enum类的, 我们除了需要帮助子类构造, 还需要先对其父类进行构造, 所以我们需要修改这个反射的代码:

将获取构造方法的代码改为:

Constructor<?> declaredConstructorStudent =

classStudent.getDeclaredConstructor(String.class,int.class,String.class,int.class);

同时修改新建对象的参数

import java.lang.reflect.Constructor;
 
public enum TestEnum {
    RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
    private String name;
    private int key;
        // 默认为private修饰的构造方法
    private TestEnum (String name,int key) {
        this.name = name;
        this.key = key;
    }
    public static TestEnum getEnumKey (int key) {
        for (TestEnum t: TestEnum.values()) {
            if(t.key == key) {
                return t;
            }
        }
        return null;
    }
    public static void reflectPrivateConstructor() {
        try {
            Class<?> classStudent = Class.forName("TestEnum");
//注意传入对应的参数,获得对应的构造方法来构造对象,当前枚举类是提供了两个参数分别是String和int。
           Constructor<?> declaredConstructorStudent =
classStudent.getDeclaredConstructor(String.class,int.class,String.class,int.class);
//设置为true后可修改访问权限
            declaredConstructorStudent.setAccessible(true);
            Object objectStudent = declaredConstructorStudent.newInstance("绿色",666);
            TestEnum testEnum = (TestEnum) objectStudent;
            System.out.println("获得枚举的私有构造函数:"+testEnum);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    public static void main(String[] args) {
        reflectPrivateConstructor();
    }
}

结果输出:

异常提示: IlleagalArgumentException, Cannot reflectively create enum objects;

为什么会抛出异常呢? 我们进入到newInstance的方法源码中看, 如下:

@CallerSensitive
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

其中:

也就是在这里, 枚举类型被过滤掉了, 所以并不能反射枚举类型实例


目录
相关文章
|
1天前
|
Java 开发者
Java 面向对象编程实战:从类定义到对象应用,让你成为高手!
【6月更文挑战第15天】在Java中,掌握面向对象编程至关重要。通过创建`Book`类,展示了属性如`title`和`author`,以及构造方法和getter方法。实例化对象如`book1`和`book2`,并访问其属性。进一步扩展类,添加`pages`和`calculateReadingTime`方法,显示了类的可扩展性。在更大规模的项目中,如电商系统,可以定义`Product`、`User`和`Order`类,利用对象表示实体和它们的交互。实践是精通Java OOP的关键,不断学习和应用以提升技能。
|
1天前
|
Java 开发者
为什么 Java 的类和对象如此重要?看完这篇你就懂了!
【6月更文挑战第15天】在Java中,类和对象是核心概念。类作为蓝图定义数据(如角色的名称和健康值)和行为(如`takeDamage`方法),而对象是类的实例,允许交互(如战士受伤)。通过继承,如`Circle`和`Rectangle`扩展`Shape`类,实现多态性,增强代码灵活性。类和对象促进代码组织、复用和可维护性,是高效软件开发的基础。
|
1天前
|
设计模式 Java
一文掌握 Java 面向对象精髓:从类定义到对象实战
【6月更文挑战第15天】Java面向对象编程初学者指南:类是对象模板,如`Person`类含`name`和`age`属性。创建对象用`new`,如`Person person = new Person()`。访问属性如`person.name=&quot;Alice&quot;`,调用方法如`person.sayHello()`。类能继承,如`Student extends Person`。对象间共享数据可传参或共用引用。多态性允许父类引用调用子类方法。注意对象生命周期和内存管理,避免内存泄漏。通过实践和理解这些基础,提升编程技能。
|
1天前
|
Java
深入 Java 面向对象:类的定义,竟然藏着这么多门道!
【6月更文挑战第15天】Java中的类定义是OOP的基础,它封装属性(如Student的name和age)和行为(如study())。简单的类仅触及表面,而复杂的类可模拟真实世界对象的多样性和交互。类还可通过继承扩展,如Student从Person派生,增加特有属性和行为。接口和抽象类等概念进一步增强了灵活性和可扩展性。类定义的深度和广度是构建高效、可维护代码的关键。
|
1天前
|
Java
别再被 Java 类和对象绕晕了!一篇文章让你秒懂!
【6月更文挑战第15天】Java中的类是对象的模板,定义属性(如姓名、年龄)和行为(如走路、说话)。对象是类的实例,有自己的属性值。例如,`Person`类可创建`Person`对象,赋予属性值并调用方法。同样,`Car`类可创建不同品牌的汽车对象,每个对象能加速或刹车。理解类与对象的关系是Java面向对象编程的基础。
|
1天前
|
Java
Java 小白也能看懂!类和对象详解,轻松入门面向对象编程
【6月更文挑战第15天】面向对象编程对新手可能抽象,但理解Java中的类和对象是关键。类是事物模板,如“汽车”类包含属性(颜色、型号、速度)和行为(启动、加速、刹车)。对象是类的实例,像具体的汽车。通过创建对象并调用其方法,可以模拟现实世界的情景。例如,`Car myCar = new Car();`创建了一个汽车对象,之后可设置属性和调用方法。多练习有助于掌握这一概念。
|
1天前
|
Java
Java 面向对象编程:你真的懂类和对象吗?
【6月更文挑战第15天】Java的面向对象编程聚焦于类与对象。类是共享属性和行为的抽象,如`Person`类含名字和年龄。对象是类的具体实例,如`person1`和`person2`各有独特的属性值。设计类时要考虑扩展性,以便适应变化,如添加“性别”属性。对象管理涉及保持其状态和行为的正确性。继承允许类如`Student`扩展`Person`,增加新特性。理解面向对象不仅是创建和使用,更是关于设计和架构的智慧。
|
1天前
|
Java
震惊!原来 Java 的类是这样定义的,你还在等什么?
【6月更文挑战第15天】Java 中的类是对象的蓝图,定义了其属性(如 `name`, `age`)和行为(构造器、方法)。例如,`Student` 类展示了如何通过构造器初始化属性,并通过方法(`getName`, `getAge`)访问这些属性。类的使用促进代码模块化、可维护性和扩展性,允许创建具有相同特征的多个对象,适应不断变化的需求。掌握类定义是提升Java编程能力的基础。
|
2天前
|
安全 Java 数据安全/隐私保护
深入理解java中Unsafe类及其实现原理
深入理解java中Unsafe类及其实现原理
5 0
|
2天前
|
存储 安全 Java
滚雪球学Java(60):深入解析Java中的Vector集合类!
【6月更文挑战第14天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
137 59
滚雪球学Java(60):深入解析Java中的Vector集合类!