1 枚举
1.1 概述
-
类的对象只有有限个,确定的。
例如: -
- 星期:Monday(星期一)、......、Sunday(星期天)。
-
- 性别:Man(男)、Woman(女)。
-
- 季节:Spring(春节)......Winter(冬天)。
-
- 支付方式:Cash(现金)、WeChatPay(微信)、Alipay(支付宝)、BankCard(银 行卡)、CreditCard(信用卡)。
-
- 就职状态:Busy、Free、Vocation、Dimission。
-
- 订单状态:Nonpayment(未付款)、Paid(已付款)、Delivered(已发货)、 Return(退货)、Checked(已确认)Fulfilled(已配货)。
-
- 线程状态:创建、就绪、运行、阻塞、死亡。
-
枚举类型本质也是也是一种类,只不多是这个类的对象是固定的几个,而不能随意让用户创建。
-
在JDK 5之前,需要程序员自定义枚举类。
-
在JDK 5之后,Java支持通过
enum
关键字来快速的定义枚举类型。
1.2 JDK 5之前
-
在JDK 5之前如何声明枚举类?
-
- ① 私有化构造器,保证在类的外部不能创建其对象。
-
- ② 在类的内部创建枚举类的实例,通过
public static final
修饰。
- ② 在类的内部创建枚举类的实例,通过
-
- ③ 对象如果有实例变量,通过
private final
修饰,并在构造器中进行初始化。
- ③ 对象如果有实例变量,通过
-
示例:
package top.open1024.demo18;
/**
* @author open1024
* @version 1.0
* @since 2021-09-10 16:58
*/
public class Gender {
// 在类的内部创建枚举类的实例,通过`public static final`修饰。
public static final Gender MAN = new Gender("男");
public static final Gender WOMAN = new Gender("女");
// 对象如果有实例变量,通过`private final`修饰,并在构造器中进行初始化。
private final String gender;
// 私有化构造器,保证在类的外部不能创建其对象。
private Gender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Gender{" + "gender='" + this.gender + '\'' + '}';
}
}
package top.open1024.demo18;
/**
* @author open1024
* @version 1.0
* @since 2021-09-10 17:03
*/
public class Person {
private Gender gender;
private String name;
private int age;
public Person(Gender gender, String name, int age) {
this.gender = gender;
this.name = name;
this.age = age;
}
public Gender getGender() {
return this.gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "gender=" + this.gender + ", name='" + this.name + '\'' + ", age=" + this.age + '}';
}
}
package top.open1024.demo18;
/**
* @author open1024
* @version 1.0
* @since 2021-09-10 17:09
*/
public class Test {
public static void main(String[] args) {
Person person = new Person(Gender.MAN, "张三", 18);
System.out.println(person);
}
}
1.3 JDK 5之后
- 语法:
权限修饰符 enmu 枚举类名 {
// 常量对象列表
}
权限修饰符 enmu 枚举类名 {
// 常量对象列表
// 其他成员列表
}
-
解释:
-
- ① 枚举类的常量列表必须在枚举类的首行,因为是常量,所以建议大写。
-
- ② 如果常量列表后面没有其他代码,那么
;
可以省略,否则;
不可以省略。
- ② 如果常量列表后面没有其他代码,那么
-
- ③ 常量对象之间必须用
,
隔开;
结尾。
- ③ 常量对象之间必须用
-
- ④ 编译器给枚举类默认提供的是private修饰的无参构造器,如果枚举类需要的是无参构造器,则不需要声明,写常量对象列表的时候也不需要加参数。
-
- ⑤ 如果枚举类需要的是有参构造器,需要手动定义private的有参构造,调用有参构造的方法就是在常量对象名后面加
(实参列表)
即可。
- ⑤ 如果枚举类需要的是有参构造器,需要手动定义private的有参构造,调用有参构造的方法就是在常量对象名后面加
-
- ⑥ 枚举类默认继承的是
java.lang.Enum
类,所以不能再继承其他的类。
- ⑥ 枚举类默认继承的是
-
- ⑦ JDK 5之后的switch支持枚举,即case后面可以写枚举常量对象名。
-
- ⑧ 枚举类如果有其他属性,建议也声明为final的,因为常量对象在逻辑上应该不可变。
-
示例:
package top.open1024.demo19;
/**
* @author open1024
* @version 1.0
* @since 2021-09-13 09:05
*/
public enum Color {
RED, GREEN, BLUE
}
- 示例:
package top.open1024.demo19;
/**
* 性别
*
* @author open1024
* @version 1.0
* @since 2021-09-13 08:56
*/
public enum Gender {
MAN("男"), WOMAN("女");
private final String name;
Gender(String name) {
this.name = name;
}
}
1.4 枚举常用方法
- 返回的是常量名(对象名),可以继续手动重写该方法!
public String toString()
- 返回枚举类型的对象数组。
public static T[] values()
- 将字符串转换为对应的枚举对象,要求字符串必须是枚举类对象名,否则将报异常。
public Enum valueOf(String str)
- 返回的是常量名(对象名),很少用
public final String name()
- 返回的是常量的次序号,默认从0开始
public final int ordinal()
- 示例:
package top.open1024.demo19;
/**
* 性别
*
* @author open1024
* @version 1.0
* @since 2021-09-13 08:56
*/
public enum Gender {
MAN("男"), WOMAN("女");
private final String name;
Gender(String name) {
this.name = name;
}
}
package top.open1024.demo19;
/**
* @author open1024
* @version 1.0
* @since 2021-09-13 09:28
*/
public class Test {
public static void main(String[] args) {
Gender[] values = Gender.values();
for (Gender value : values) {
System.out.println(value);
}
Gender man = Gender.valueOf("MAN");
System.out.println(man);
}
}
- 示例:
package top.open1024.demo20;
/**
* 天气
*
* @author open1024
* @version 1.0
* @since 2021-09-13 08:58
*/
public enum Season {
SPRING("春天", "春暖花开"), SUMMER("夏天", "夏日炎炎"), AUTUMN("秋天", "秋高气爽"), WINTER("冬天", "白雪皑皑");
// 名称
private final String name;
// 描述
private final String desc;
Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
package top.open1024.demo20;
/**
* @author open1024
* @version 1.0
* @since 2021-09-13 09:40
*/
public class Test {
public static void main(String[] args) {
Season[] values = Season.values();
for (Season value : values) {
switch (value) {
case SPRING:
System.out.println(Season.SPRING.getName());
break;
case SUMMER:
System.out.println(Season.SUMMER.getName());
break;
case AUTUMN:
System.out.println(Season.AUTUMN.getName());
break;
case WINTER:
System.out.println(Season.WINTER.getName());
break;
}
}
}
}
1.5 枚举实现接口(了解)
-
和普通Java类一样,枚举类可以实现一个或多个接口。
-
如果每个枚举值在调用实现的接口方法呈现的是相同的行为方法,则只需要统一实现该方法即可。
-
如果需要每个枚举值在调用实现的接口方法呈现不同的行为方式,则可以让每个枚举值分别来实现该方法。
-
示例:
package top.open1024.demo21;
/**
* @author open1024
* @version 1.0
* @since 2021-09-13 09:50
*/
public interface Run {
void run();
}
package top.open1024.demo21;
/**
*
* @author open1024
* @version 1.0
* @since 2021-09-13 08:56
*/
public enum Gender implements Run {
MAN, WOMAN;
@Override
public void run() {
System.out.println("走路啊");
}
}
package top.open1024.demo21;
/**
* @author open1024
* @version 1.0
* @since 2021-09-13 09:50
*/
public class Test {
public static void main(String[] args) {
Gender man = Gender.MAN;
man.run(); // 走路啊
Gender woman = Gender.WOMAN;
woman.run(); // 走路啊
}
}
- 示例:
package top.open1024.demo22;
/**
* @author open1024
* @version 1.0
* @since 2021-09-13 09:50
*/
public interface Run {
void run();
}
package top.open1024.demo22;
/**
*
* @author open1024
* @version 1.0
* @since 2021-09-13 08:56
*/
public enum Gender implements Run {
MAN {
@Override
public void run() {
System.out.println("男生大步流星的走路");
}
},
WOMAN {
@Override
public void run() {
System.out.println("女生婀娜多姿的走路");
}
}
}
package top.open1024.demo22;
/**
* @author open1024
* @version 1.0
* @since 2021-09-13 09:56
*/
public class Test {
public static void main(String[] args) {
Gender man = Gender.MAN;
man.run(); // 男生大步流星的走路
Gender woman = Gender.WOMAN;
woman.run(); // 女生婀娜多姿的走路
}
}
2 注解
2.1 概述
2.1.1 什么是注解?
-
从JDK 5开始,Java增加了对元数据(MetaData)的支持,也就是Annotation(注解)。
-
注解其实就是代码里的
特殊标记
,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证和部署。 -
注解可以像修饰符一样被使用,可以用于
修饰包、类、构造器、方法、成员变量、参数、局部变量
的声明,这些信息被保存在注解的name=value
中。
2.1.2 注解的作用
-
执行编译器的检查,例如:
@Override
注解。 -
分析代码:主要的用途,用来替代配置文件。
-
用在框架里面,注解开发。
2.2 JDK内置的三种基本注解
-
@Override
:限定重写父类方法,该注解只能用于方法。 -
@Deprecated
:用于表示所修饰的元素(类、方法等)已经过时。通常是因为所修饰的结构危险或存在更好的选择。 -
@SuppressWarnings
:抑制编译器警告。
2.3 自定义注解
2.3.1 注解的定义
-
自定义注解必须使用
@interface
关键字。 -
自定义注解自动继承了
java.lang.annotation.Annotation
接口,换言之,注解是一种特殊的接口。 -
语法:
修饰符 @interface 注解名 {
属性类型 属性名() default 默认值;
}
-
注解的属性类型只能是:
-
- ① String。
-
- ② 8种基本数据类型。
-
- ③ 枚举类型。
-
- ④ 注解类型。
-
- ⑤ Class类型。
-
- ⑥ 以上类型的一维数组类型。
-
可以在定义注解的属性的时候使用default关键字指定初始化值。
-
如果注解只有一个属性,建议属性的名称为value。
-
如果自定义的注解含有属性,那么使用的时候必须给注解的属性赋值,除非它有默认值。格式是
属性=值
,如果只有一个属性,且名称是value
,怎可以省略value=
。 -
没有属性的注解称为
标记
,有属性的注解称为元数据注解
。 -
示例:
package top.open1024.demo11;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 17:47
*/
public enum Color {
RED, GREEN, BLUE
}
package top.open1024.demo11;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 17:46
*/
public @interface MyAnnotation {
// 基本数据类型
int num();
// String类型
String value();
// 枚举类型
Color color();
// Class类型
Class clazz();
// 注解类型
Override override();
// 以上类型的数组类型
String[] values();
}
2.3.2 注解的使用
- 语法:
@注解名(属性名=值,属性名2=值,...)
-
特殊情况:
-
- ① 注解属性有默认值。
属性类型 属性名() default 默认值;
-
- ② 如果注解的属性类型是一维数组,当数组的值只有一个的时候,可以省略
{}
。
- ② 如果注解的属性类型是一维数组,当数组的值只有一个的时候,可以省略
@MyAnnotation(ss={"aa","bb"})
@MyAnnotation(ss="aa")
-
- ③ 如果只有一个属性要赋值的时候,且属性名为value,赋值的时候可以省略
value=
。
- ③ 如果只有一个属性要赋值的时候,且属性名为value,赋值的时候可以省略
-
示例:
package top.open1024.demo11;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 17:47
*/
public enum Color {
RED, GREEN, BLUE
}
package top.open1024.demo11;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 17:46
*/
public @interface MyAnnotation {
// 基本数据类型
int num();
// String类型
String value();
// 枚举类型
Color color();
// Class类型
Class clazz();
// 注解类型
Override override();
// 以上类型的数组类型
String[] values();
}
package top.open1024.demo11;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 18:03
*/
public class Person {
@MyAnnotation(num = 0, value = "你好,open1024", color = Color.RED, clazz = Person.class, override = @Override, values = {"aa","bb"})
private String name;
}
2.4 元注解
2.4.1 概述
- 元注解是用于修饰自定义注解的。
2.4.2 常用的元注解
-
JDK 5提供了四个标准的元注解,分别是:
-
- @Retention注解:
-
-
- 定义该注解可以保留在哪个代码阶段,值为RetentionPolicy类型。
-
-
-
- RetentionPolicy.SOURCE:只在源码阶段保留。
-
-
-
- RetentionPolicy.CLASS:在源码和字节码上保留,默认。
-
-
-
RetentionPolicy.RUNTIME
:在源码、字节码和运行阶段保留。
-
-
- @Target注解:
-
-
- 定义该注解可以作用在什么范围,默认注解可以在任何位置,值为ElementType的枚举值。
-
-
-
- ElementType.TYPE:作用在类、接口(包括注解类型)或enum上。
-
-
-
- ElementType.FIELD:作用在属性上。
-
-
-
- ElementType.METHOD:作用在方法上。
-
-
-
- ElementType.PARAMETER:作用在参数上。
-
-
-
- ElementType.CONSTRUCTOR:作用在构造器上。
-
-
-
- ElementType.LOCAL_VARIABLE:作用在局部变量上。
-
-
-
- ElementType.ANNOTATION_TYPE:作用在注解上。
-
-
-
- ElementType.PACKAGE:作用在包上。
-
-
-
- ElementType.TYPE_PARAMETER:作用在类型声明上,如泛型声明(JDK8新增)。
-
-
-
- ElementType.TYPE_USE:作用在使用类型的任何语句中(JDK8新增)。
-
-
- @Document注解:
-
-
- 定义该注解可以被javadoc工具提取成文档。默认情况下,javadoc是不包括注解的。
-
-
- @Inherit注解:
-
-
- 定义该注解具有继承性。如果某个类使用被@Inherit注解修饰的注解,则该子类将自动具有该注解。
-
-
JDK 8 提供了新的注解,@Repeatable注解,可重复注解,值是
Class<? extends Annotation>
类型。 -
示例:
package top.open1024.demo11;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 17:47
*/
public enum Color {
RED, GREEN, BLUE
}
package top.open1024.demo11;
import java.lang.annotation.*;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 17:46
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface MyAnnotation {
// 基本数据类型
int num();
// String类型
String value();
// 枚举类型
Color color();
// Class类型
Class clazz();
// 注解类型
Override override();
// 以上类型的数组类型
String[] values();
}
package top.open1024.demo11;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 18:03
*/
public class Person {
@MyAnnotation(num = 0, value = "你好,open1024", color = Color.RED, clazz = Person.class, override = @Override,
values = {"aa", "bb"})
private String name;
}
2.4.3 反射获取注解信息
-
注解解析的目的:
-
- ① 获取类、成员变量、成员方法、方法参数、构造函数等上面的注解对象。
-
- ② 获取注解对象的属性。
-
- ③ 判断某个类、成员变量、成员方法等上面是否有某个注解。
-
JDK 5在java.lang.reflect包下新增了AnnotatedElement接口,该接口代表程序中可以接受注解的程序元素。
-
当一个注解被定义为运行时注解后,该注解才是运行时可见,当class文件被加载时,保存在class文件中的注解才会被虚拟机读取。
-
Class、Method、Package、Field、Constructor、Package、Parameter等实现了AnnotatedElement接口。
-
程序员可以调用AnnotatedElement对象的方法获取注解信息。
-
判断指定的注解是否存在:
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass){}
- 获取指定类型的注解,如果没有,返回null:
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
- 获取自己的所有注解:
Annotation[] getDeclaredAnnotations();
- 获取所有的注解,包括从父类继承下来的注解:
Annotation[] getAnnotations();
- 根据指定的类型获取所有的注解,包括从父类继承下来的注解:
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {}
- 获取指定类型的注解,如果没有,返回null:
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {}
- 根据指定的类型获取自己的所有注解:
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {}
- 示例:
package top.open1024.demo12;
import java.lang.annotation.*;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 20:20
*/
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {}
package top.open1024.demo12;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 20:21
*/
@MyAnnotation
public class Demo {
@MyAnnotation
public void run01() {
}
public void run02() {
}
}
package top.open1024.demo12;
import java.lang.reflect.Method;
/**
* @author open1024
* @version 1.0
* @since 2021-10-07 20:21
*/
public class Test {
public static void main(String[] args) throws NoSuchMethodException {
Class<?> clazz = Demo.class;
boolean annotationPresent = clazz.isAnnotationPresent(MyAnnotation.class);
System.out.println("annotationPresent = " + annotationPresent);
Method run01 = clazz.getDeclaredMethod("run01");
boolean annotationPresent1 = run01.isAnnotationPresent(MyAnnotation.class);
System.out.println("annotationPresent1 = " + annotationPresent1);
}
}
评论区