本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-06-25
当类的对象是有限个、确定的,需要定义一组常量时,则需要使用枚举(enumeration)类。如果枚举类只有一个对象,则可以作为一种单例模式的实现方式。
枚举类的属性:
private final
修饰private final
修饰的属性应该在构造器中为其赋值/**
* 方式一、jak5.0之前,自定义枚举类
* 方式二、jak5.0后,使用enum关键字定义枚举类
*/
public class SeasonTest {
public static void main(String[] args) {
Season spring = Season.SPRING;
System.out.println(spring); // Season{seasonName='春天', seasonDesc='春暖花开'}
System.out.println(spring.getSeasonName()); // 春天
Season1 summer = Season1.SUMMER;
System.out.println(summer); // SUMMER
// 定义的枚举类默认继承于java.lang.Enum类
System.out.println(Season1.class.getSuperclass()); // class java.lang.Enum
}
}
// 使用enum关键字
enum Season1 {
// 1、提供当前枚举类的对象,对个对象之间用 , 隔开,末尾对象 ; 结束
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "冰天雪地");
// 2、声明类对象的属性,需要private final修饰。
private final String seasonName;
private final String seasonDesc;
// 3、私有化类的构造器,并给对象属性赋值
private Season1(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
// 4、其他诉求1:获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
// 自定义枚举类
class Season {
// 1、声明类对象的属性,需要private final修饰。
private final String seasonName;
private final String seasonDesc;
// 2、私有化类的构造器,并给对象属性赋值
private Season(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
// 3、提供当前枚举类的多个对象
public static final Season SPRING = new Season("春天", "春暖花开");
public static final Season SUMMER = new Season("夏天", "夏日炎炎");
public static final Season AUTUMN = new Season("秋天", "秋高气爽");
public static final Season WINTER = new Season("冬天", "冰天雪地");
// 4、其他诉求1:获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
// 5、其他诉求2:提供toString()方法
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
public class EnumTest {
public static void main(String[] args) {
// values()方法
Season2[] values = Season2.values();
for (int i = 0; i < values.length; i++) {
System.out.println(values[i]); // SPRING SUMMER AUTUMN WINTER 不是字符串,是枚举类
}
TestEnum[] values1 = TestEnum.values();
for (int i = 0; i < values1.length; i++) {
System.out.println(values1[i]); // LOADING SUCCESS FAILURE 不是字符串,是枚举类
}
// valueOf(String objName):返回枚举类中对象名是objName的对象
Season2 winter = Season2.valueOf("WINTER");
System.out.println(winter); // WINTER 不是字符串,是枚举类
TestEnum loading = TestEnum.valueOf("LOADING");
System.out.println(loading); // LOADING 不是字符串,是枚举类
// Error
// TestEnum loading1 = TestEnum.valueOf("LOADING1");
// System.out.println(loading1);
// toString()
TestEnum success = TestEnum.SUCCESS;
System.out.println(success.toString()); // "SUCCESS" 是字符串
}
}
enum TestEnum {
LOADING,
SUCCESS,
FAILURE;
}
enum Season2 {
// 1、提供当前枚举类的对象,对个对象之间用 , 隔开,末尾对象 ; 结束
SPRING("春天", "春暖花开"),
SUMMER("夏天", "夏日炎炎"),
AUTUMN("秋天", "秋高气爽"),
WINTER("冬天", "冰天雪地");
// 2、声明类对象的属性,需要private final修饰。
private final String seasonName;
private final String seasonDesc;
// 3、私有化类的构造器,并给对象属性赋值
private Season2(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
// 4、其他诉求1:获取枚举类对象的属性
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
public class EnumTest2 {
public static void main(String[] args) {
TestEnum2 loading = TestEnum2.LOADING;
loading.show(); // "这是一个 测试枚举类"
loading.say(); // "加载中"
TestEnum2[] values = TestEnum2.values();
for (int i = 0; i < values.length; i++) {
values[i].say(); // 加载中 成功了 失败了
}
if (EnumTest2.getEnum("LOADING") == TestEnum2.LOADING) {
System.out.println("true");
}
}
public static TestEnum2 getEnum(String str) {
return TestEnum2.valueOf(str);
}
}
interface Info {
void show();
void say();
}
enum TestEnum2 implements Info {
// 在枚举类的对象中分别实现接口中的抽象方法
LOADING {
@Override
public void say() {
System.out.println("加载中");
}
},
SUCCESS {
@Override
public void say() {
System.out.println("成功了");
}
},
FAILURE {
@Override
public void say() {
System.out.println("失败了");
}
};
@Override
public void show() {
System.out.println("这是一个测试枚举类");
}
}
从JDK5.0开始,Java增加对元数据(MetaData)的支持,也就是注解(Annotation)。
注解其实就是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充工具。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或进行部署。
注解可以向修饰符一样被使用,可以修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在注解的键值对中。
1、定义新的Annotation
类使用 @interface
关键字
2、自定义注解自动继承了java.lang.annotation.Annotation
接口
3、注解的成员变量在定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型,我们称为配置参数,类型只能是八种基本数据类型,以及String
、Class
、Enum
、Annotation
类型。
4、可以在定义注解的成员变量时指定初始值,指定成员变量的初始值可使用default
关键字。
5、如果只有一个参数成员,建议使用参数名为value
6、如果定义的注解含有配置参数,那么使用时必须指定参数值,除非有默认值。格式是参数名=参数值
,如果只有一个参数成员,则名称为value
,可以省略value=
。
7、没有成员变量的注解称为标记,包含成员变量的注解称为元数据。
8、自定义注解必须配上注解的信息处理流程才有意义。
定义:
// MyAnnotation.java
/**
* 自定义注解
*/
public @interface MyAnnotation {
String value();
String name() default "anno";
}
使用:
package com.cz.java;
public class AnnotationTest {
}
// @MyAnnotation("hello") // 将hello传给value,name有默认值可以不穿
@MyAnnotation(name = "jack", value = "hello") // 显示指定
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
上面示例,仅仅只是展示注解的定义和使用,没有任何信息处理流程,而这一部分需要使用反射,将会在后面提到。
理解与元数据类似,即对现有数据进行修饰的数据,元注解即对现有注解进行解释说明的注解。以下为java中的四种元注解:
1、Retention
用于指定该注解的声明周期,它包含了一个RetentionPolicy
类型的成员变量:
RetentionPolicy.SOURCE
: 在源文件中有效,编译器直接丢弃这种策略的注释RetentionPolicy.CLASS
: 在class文件中有效,当运行java程序时,JVM不会保留注释,是该成员变量的默认值RetentionPolicy.RUNTIME
: 在运行时有效,当运行java程序时,JVM会保留注释,程序可以通过反射获取该注释2、Target
用于指定被修饰的注解能用于修饰哪些程序元素,也包含一个名为value的成员变量。
CONSTUCTOR
: 用于描述构造器FIELD
: 用于描述域LOCAL_VARIABLE
: 用于描述局部变量METHOD
: 用于描述方法PACKAGE
: 用于描述包PARAMETER
: 用于描述参数TYPE
: 用于描述类、接口或enum声明3、Documented
用于指定该注解修饰的类将被javadoc工具提取成文档,默认情况下,javadoc是不包括注解的。定义Documented
注解必须设置Retention
值为 RUNTIME
。
4、Inherited
被它修饰的注解将具有继承性,如果某个类使用了被 @Inherited
修饰的注解,则其子类将自动具有该注解。
如果把标有@Inherited
注解的自定义的注释标注在类级别上,子类可以继承父类类级别的注解,实际应用中,很少使用。
自定义注解通常都会指明两个元注解,Retention
与Target
,另外两个使用频率较低。
在jdk8.0之前使用重复注解的话,需要扩展一个注解容器。这个注解容器与可重复注解的@Retention
和@Target
要一致。
// MyAnnotations.java
// 注解容器
public @interface MyAnnotations {
MyAnnotation[] value();
}
对应注解:
public @interface MyAnnotation {
String value();
String name() default "anno";
}
使用:
@MyAnnotations({ @MyAnnotation("hello"), @MyAnnotation(name="jack", value = "hello")})
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
在jdk8.0之后,提供了 @Repeatable()
注解来简化重复注解,只需要在想要重复注解的注解上加上该元注解并传入注解容器即可。
// MyAnnotation.class
// 注意参数是MyAnnotations注解容器
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
String value();
String name() default "anno";
}
使用:
@MyAnnotation("hello")
@MyAnnotation(name="jack", value = "hello")
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
jdk8之后,关于元注解@Target
的参数类型ElementType
枚举值多了两个,之前注解只能在声明的地方使用,jdk8之后注解可以应用在任何定地方,就是新增的这两个来体现的。
ElementType.TYPE_PARAMETER
:表明该注解能写在类型变量的声明语句中,如泛型声明。ElementType.TYPE_USE
:表明该注解能写在使用类型的任何语句中示例1:
public class TestTypeDefine<@TypeDefine() U> {
private U u;
public <@TypeDefine() T> void test(T t) {}
}
// 声明之后,注解就能修饰泛型了,如上
@Target({ElementType.TYPE_PARAMETER})
@interface TypeDefine {
}
示例2:
// 注解没有成员变量可不写()
// 修饰泛型
class Generic<@TypeDefine T> {
// 修饰异常
public void show() throws @TypeDefine RuntimeException {
// 修饰泛型
ArrayList<@TypeDefine String> list = new ArrayList<>();
// 修饰强转的类型
int num = (@TypeDefine int) 10L;
}
}
// 声明之后,所有声明类型的地方都可以使用了
@Target({ElementType.TYPE_USE})
@interface TypeDefine {
}