1、枚举类
JDK5.0之后,引入的专门的关键字 enum 定义枚举类
什么时候用枚举,同类事物可以有限穷举时。
比如小于 7的正整数: 1、2、3、4、5、6
人的时期划分:婴儿期、幼年期、青年期、中年期、老年期
订单的过程:新建订单、待付款、已付款、已发货、已确认、售后中、售后完成。
像上面这些情况就比较适合用枚举来定义。
定义一组常量,也适合使用枚举类。
1.1 JDK 5.0 之前定义枚举类
5.0之前都是自定义枚举类:
其实也是一个普通类,只不过对属性、构造器等做了要求。
public class Season {
// 1、声明属性 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
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、提供获取枚举类对象的属性的方法
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
第一步定义 为final 是因为属性 枚举本身就是定义确定的,不可变的
第二步 私有化构造器,不希望外界来创建对象。这个枚举对象应该是我们造好的
第三步 定义多个公共的静态属性,值为该枚举类对象,使用私有化构造器初始化对象。
第四步 提供公共的属性获取方法,因为是属性是final的,没有setter方法。
1.2 JDK 5.0以后使用枚举类
1.2.1 使用enum定义枚举类
public enum SeasonEnum {
// 1、提供当前枚举类常量,接收枚举类的对象,多个对象之间用对象隔开,最后一个使用分号结束。
SPRING ("春天", "春天春天"),
SUMMER ("夏天", "夏天夏天"),
AUTUMN ("秋天", "秋天秋天"),
WINTER ("冬天", "冬天冬天");
// 2、声明属性 final 定义常量不可变
private final String seasonName;
private final String seasonDesc;
// 3、私有化的构造器 并初始化对象属性
SeasonEnum(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
// 4、提供获取枚举类对象的属性的方法
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
使用enum 定义枚举类与 之前的定义方式一样,这4步都要有。但是有一些区别。
首先是一开始就需要定义 公共的静态的不可变属性 接收初始化的对象。这里的修饰符在 Jvm 已经定义好了,我们不需要写上,也不能写上。
然后就是构造器的 私有化修饰符 也不需要写了。默认就是私有化的。可以写,但是不需要写。
其他的声明枚举的属性、获取属性 都需要提供,没什么区别。
1.2.2 enum 类的主要方法
我们使用enum定义的枚举类,默认继承Enum类,会有如下的常用方法
-
values 方法 ,这是一个类方法,返回枚举类型数组。
SeasonEnum[] values = SeasonEnum.values();
for (SeasonEnum value : values) {
System.out.println(value);
}
// 输出 所有的枚举常量
SPRING
SUMMER
AUTUMN
WINTER
-
valueOf (String str) 方法 返回枚举对象
SeasonEnum seasonEnum = SeasonEnum.valueOf(SeasonEnum.AUTUMN.toString()); System.out.println(seasonEnum); // AUTUMN SeasonEnum spring = SeasonEnum.valueOf("SPRING"); System.out.println(spring); // SPRING SeasonEnum spring = SeasonEnum.valueOf("SP"); System.out.println(spring); // 报错:java.lang.IllegalArgumentException: No enum constant
通过传入 枚举类名称字符串,找到对应的枚举对象。如果找不到会报错。
-
toString() 方法 返回当前枚举类对象常量的名称
String s = SeasonEnum.SPRING.toString();
System.out.println(s);
toString是枚举类对象的方法,返回改对象常量的名称
1.2.3 实现接口
1、枚举类也可以像普通类一样,实现接口。然后在枚举类中实现这个接口中的抽象方法。
public interface Info {
/**
* show 方法
* @return void
**/
void show();
}
有一个Info接口,只有一个show方法。
public enum SeasonEnum implements Info {
// 1、提供当前枚举类对象,多个对象之间用对象隔开,最后一个使用分号结束。
SPRING ("春天", "春天春天"),
SUMMER ("夏天", "夏天夏天"),
AUTUMN ("秋天", "秋天秋天"),
WINTER ("冬天", "冬天冬天");
// 2、声明属性 final 定义常量不可变
private final String seasonName;
private final String seasonDesc;
// 3、私有化的构造器 并初始化对象属性
SeasonEnum(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
// 4、提供获取枚举类对象的属性的方法
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
// 实现 show方法
@Override
public void show() {
System.out.println("这是一个季节枚举类");
}
}
上面的代码是直接在枚举类中实现了show方法。这种实现方式,每一个对象都会调用同一个show方法。
枚举类还支持在对象里面实现show方法,达到每一个对象调用自己的show方法。
2、枚举类也可以像普通类一样,在枚举对象中实现接口抽象方法。
public enum SeasonEnum implements Info {
// 1、提供当前枚举类对象,多个对象之间用对象隔开,最后一个使用分号结束。
SPRING ("春天", "春天春天"){
@Override
public void show() {
System.out.println("这是一个春天枚举对象");
}
},
SUMMER ("夏天", "夏天夏天") {
@Override
public void show() {
System.out.println("夏天枚举对象");
}
},
AUTUMN ("秋天", "秋天秋天"){
@Override
public void show() {
System.out.println("秋天枚举对象");
}
},
WINTER ("冬天", "冬天冬天"){
@Override
public void show() {
System.out.println("冬天枚举对象");
}
};
// 2、声明属性 final 定义常量不可变
private final String seasonName;
private final String seasonDesc;
// 3、私有化的构造器 并初始化对象属性
SeasonEnum(String seasonName, String seasonDesc) {
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
// 4、提供获取枚举类对象的属性的方法
public String getSeasonName() {
return seasonName;
}
public String getSeasonDesc() {
return seasonDesc;
}
}
在 每一个枚举类对象后面加上一对大括号,里面实现抽象方法show 即可。
2、注解
JDK 5.0 开始,java增加了对元数据MetaData 的支持,就是Annotation 注解
注解其实就是代码中的特殊标记。这些标记可以在编译、类加载、运行时被读取。根据标记的不同做相应的处理。
重点:注解本身是没有任何作用的,真正起作用的是 读取注解后的操作。
通过使用注解,就可以在不改变原有代码的基础上,进行功能拓展。
注解可以像修饰符一样被使用,用于修饰 包、类、构造器、方法、成员变量、参数、局部变量声明等等。
这些信息被保存到Annotation的name=value对中。
常见的注解:
@Override
这个注解是加在方法上面,表示这个方法是重写了父类、抽象类、接口等的方法。
编译的时候,当获取到这个注解时,就会对这个方法进行检查,看是否正确的重写的父类或抽象类或接口中的方法。
加上注解:标记这是一个重写方法。
注解真正起作用:在编译时获取这个@Override 注解,执行检查代码。
@Deprecated
用于修饰 类、方法,表示已过时。
加上注解:标记这是一个过时方法,建议不要使用。
起作用:
运行时获取到这个注解,弹出提示这是过时方法,建议用新的替代方法。
@SuppressWarnings
抑制编译器警告
以上三个注解是JDK内置的注解。直接用即可。
2.1 注解的定义
注解的类型是 @interface
@interface 与 interface 没有什么关系。
public @interface MyAnnotation {
// 定义属性 有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
String[] value();
}
使用:
@MyAnnotation(value = "你好,注解")
public class TestMain {}
如果只传一个值,建议使用value作为属性名称。这样就可以省略value不写。
@MyAnnotation("你好,注解")
public class TestMain {}
如果需要传多个值,前提是定义的属性是一个数组。可以使用大括号的方式定义。
@MyAnnotation({"你好,注解", "自定义注解"})
public class TestMain {}
定义 带默认值的注解。
public @interface MyAnnotation {
// 定义属性 有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
String[] value() default "哈哈哈";
}
有默认值的注解,这个属性就不是必须写了,如果写了,就会替换默认值。
注解并不是一定要有属性,因为注解本身就是一个标记,有名字就可以了,属性根据需要添加。
如果定义了属性,使用时一定要传入。使用时不传入 就一定要在注解定义属性时 声明默认值。
2.2 注解的使用
上一节的使用只是把注解加载某个地方,类或者方法。即在一个地方打上标记。
真正使用时,得利用反射获取到这个注解。然后再干一点事情。
2.3 元注解
用于修饰注解的注解
JDK5.0 提供了4个标准的元注解。
-
Retention 保留: 声明注解的生命周期
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
Retention 注解只有一个 value属性,属性类型是 RetentionPolicy枚举类。枚举对象有
public enum RetentionPolicy {
/**
* 源代码时期,编译器将丢弃注解
*/
SOURCE,
/**
class字节码文件时期,运行时会被抛弃。这是默认设置行为。
*/
CLASS,
/**
注释将由编译器记录在类文件中,并且运行时也会保留。可以使用反射获取注解。
*/
RUNTIME
}
-
Target 目标:声明注解可以修饰的对象,即可以放置的位置
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
Target 注解只有一个value属性,是一个ElementType类型的数组。ElementType是一个枚举类。
public enum ElementType {
/** 类, 接口 (包括注解), 或者枚举 */
TYPE,
/** 属性 (包括枚举的常量) */
FIELD,
/** 方法 */
METHOD,
/** 形式参数 */
PARAMETER,
/** 构造器 */
CONSTRUCTOR,
/** 局部变量 */
LOCAL_VARIABLE,
/** 注解类型 */
ANNOTATION_TYPE,
/** 包 */
PACKAGE,
/**
* 参数类型
*/
TYPE_PARAMETER,
/**
* Use of a type
*/
TYPE_USE
}
如何不写 Target 注解修饰,默认是哪里都可以修饰。一般都会写上,指定可以修饰哪些结构。
-
Documented 文档
用于指定被该注解修饰的注解将被javadoc工具提取成文档。javadoc默认是不包括注解的
当使用 Documented 注解时,Retention 注解的生命周期必须为 RUNTIME
-
Inherited 继承
被继承注解修饰的注解具有继承性。父类加的注解如果被Inherited 注解修饰,那么子类也会有这个注解。
2.4 JDK8.0 注解新特性
1、支持可重复注解
jdk8之前的写法:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// 定义属性 有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
String[] value() default "哈哈哈";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
// 属性类型是 MyAnnotation 注解数组
MyAnnotation[] value();
}
// 使用
@MyAnnotations(value = {@MyAnnotation(value = "1"), @MyAnnotation(value = "2")})
public class TestMain {}
JDK8.0 之后的写法:
使用 @Repeatable 注解
@Repeatable(value = MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
// 定义属性 有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
String[] value() default "哈哈哈";
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
// 属性类型是 MyAnnotation 注解数组
MyAnnotation[] value();
}
// 使用
@MyAnnotation(value = "1")
@MyAnnotation(value = "2")
public class TestMain {}
这种写法还是需要定义 MyAnnotations 这个注解的,需要在 MyAnnotation注解上加上 Repeatable注解,值为MyAnnotations .class
注意MyAnnotations 与 MyAnnotation 的生命周期(@Retention)和目标位置(@Target)以及 继承性(@Inherited )要一致。
JDK8之前与jdk8之后只是写法不一致,但是意思是一样的。
2、类型注解
就是 @Target 注解值里面 ElementType 枚举类新增的 2个枚举对象 TYPE_PARAMETER、TYPE_USE
public enum ElementType {
。。。。省略其他
/**
* 参数类型
*/
TYPE_PARAMETER,
/**
* Use of a type
*/
TYPE_USE
}
1、使用 TYPE_PARAMETER 就可以让注解修饰 参数,泛型等结构。看例子
@Repeatable(value = MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.TYPE_PARAMETER}) // @Target 增加了 ElementType.TYPE_PARAMETER
public @interface MyAnnotation {
// 定义属性 有点像方法,实际是属性。 返回值是属性类型, 如果需要接收多个属性值,就可以定义为数组
String[] value() default "哈哈哈";
}
// 使用:@MyAnnotation T
public class Generic<@MyAnnotation T> {
}
2、使用 TYPE_USE 修饰 泛型参数、强制、异常
@Repeatable(value = MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) // 又增加了ElementType.TYPE_USE
public @interface MyAnnotation {
String[] value() default "哈哈哈";
}
public class Generic<@MyAnnotation T> {
// 1、修饰异常
public void show() throws @MyAnnotation RuntimeException{
// 2、修饰强制类型
int num = (@MyAnnotation int) 10L;
}
// 3、修饰泛型参数
ArrayList<@MyAnnotation String> list = new ArrayList<>();
}