JDK的元注解
种类
- @Retention : 指定注解的作用范围,三种SOURCE,CLASS,RUNTIME
- @Target : 指定注解可以在哪些地方使用
- Documented : 指定注解是否会在javadoc中体现
- Inherited : 子类会继承父类的注解
@Retention
只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留多长时间, [@Rentention ](/Rentention ) 包含一个 RetentionPolicy 类型的成员变量, 使用 [@Rentention ](/Rentention ) 时必须为该 value 成员变量指定值: [@Retention ](/Retention ) 的三种值
RetentionPolicy.SOURCE: 编译器使用后,直接丢弃这种策略的注释
RetentionPolicy.CLASS: 编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注解。 这是默认
RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注解. 程序可以 通过反射获取该注解
结论:保留时间递增
@Target
注解 : 作用范围
@Documented
用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档,即在生成文档时,可以看到该注解.
说明 : 定义为Documented的注解必须设置Rentention值为RUNTIME
源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@Inherited
被它修饰的 Annotation 将具有继承性,如果某个类使用了被 @Inherited 修饰的 Annotation ,则其子类将自动具有该注释
实际应用中,使用较少
annotation
- @Override:限定某个方法,是重写父类方法,该注解只能用于方法
- @Deprecated: 用于表示某个程序元素已过时
- @SuppressWarnings:抑制编译器警告
@Override
@Override 注解放在fly方法上,表示子类的fly方法时重写了父类的fly
这里如果没有写 @Override 还是重写了父类fly
如果你写了@Override注解,编译器就会去检查该方法是否真的重写了父类的方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误
看看 @Override的定义
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
解读:
- 如果发现 @interface 不是 interface 表示一个 注解类,jdk1.5之后加入
- @Target表示注解可以放在哪些元素上(只能在方法上):修饰注解的注解,叫做元注解
@Deprecated
@Deprecated 修饰某个元素, 表示该元素已经过时
即不在推荐使用,但是仍然可以使用
查看 @Deprecated 注解类的源码
Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
String since() default "";
boolean forRemoval() default false;
}
可以修饰方法,类,字段, 包, 参数 等等
@Deprecated 可以做版本升级过渡使用
@SuppressWarnings
- 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings注解来抑制警告信息
- 在{“”} 中,可以写入你希望抑制(不显示)警告信息
- 可以指定的警告类型有 | 填入信息 | 作用 | | —- | —- | | all | 抑制所有警告 | | boxing | 抑制与封装/拆装作业相关的警告 | | cast | 抑制与强制转型作业相关的警告 | | dep-ann | 抑制与淘汰注释相关的警告 | | deprecation | 抑制与淘汰的相关警告 | | fallthrough | 抑制与switch陈述式中遗漏break相关的警告 | | finally | 抑制与未传回finally区块相关的警告 | | hiding | 抑制与隐藏变数的区域变数相关的警告 | | incomplete-switch | 抑制与switch陈述式(enum case)中遗漏项目相关的警告 | | javadoc | 抑制与javadoc相关的警告 | | nls | 抑制与非nls字串文字相关的警告 | | null | 抑制与空值分析相关的警告 | | rawtypes | 抑制与使用没有指定泛型的警告(传参时没有指定泛型的警告错误) | | resource | 抑制与使用Closeable类型的资源相关的警告 | | restriction | 抑制与使用不建议或禁止参照相关的警告 | | serial | 抑制与可序列化的类别遗漏serialVersionUID栏位相关的警告 | | static-access | 抑制与静态存取不正确相关的警告 | | static-method | 抑制与可能宣告为static的方法相关的警告 | | super | 抑制与置换方法相关但不含super呼叫的警告 | | synthetic-access | 抑制与内部类别的存取未最佳化相关的警告 | | sync-override | 抑制因为置换同步方法而遗漏同步化的警告 | | unchecked | 抑制与未检查的作业相关的警告 | | unqualified-field-access | 抑制与栏位存取不合格相关的警告 | | unused | 忽略没有使用的某个变量的警告错误 |
关于SuppressWarnings 作用范围是和你放置的位置相关通常我们可以放置具体的语句, 方法, 类上
比如 @SuppressWarnings放置在 main方法,那么抑制警告的范围就是 main查看源码
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
1. 放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE
2. 该注解类有数组 String[] values() 设置一个数组比如 {"rawtypes", "unchecked", "unused"}
枚举类
枚举第一种实现方式:
1. 枚举对应的英文
2. 枚举是一种常量的集合
3. 枚举属于一种特殊的类,里面只包含一组有限的特定的对象。
枚举第二种实现方式
- 自定义类实现枚举
- 使用 enum 关键字实现枚举
自定义类实现枚举
- 构造器私有化
- 本类内部创建一组对象[四个 春夏秋冬]
- 对外暴露对象(通过为对象添加 public final static 修饰符)
- 可以提供 get 方法,但是不要提供 set
public static final Season SPRING = new Season("春天", "温暖");
public static final Season WINTER = new Season("冬天", "寒冷");
public static final Season AUTUMN = new Season("秋天", "凉爽");
public static final Season SUMMER = new Season("夏天", "炎热");
enum 关键字实现枚举
//演示使用 enum 关键字来实现枚举类
enum Season2{
SPRING("春天", "温暖"), WINTER("冬天", "寒冷"),AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热")/*, What()*/;
private String name;
private String desc;//描述
private Season2() {//无参构造器
}
private Season2(String name, String desc) {
this.name = name;
this.desc = desc;
}
}
使用关键字 enum 替代 class
当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类, 而且是一个 final 类[如何证明],老师使用 javap 工 具来演示
public static final Season SPRING = new Season(“春天”, “温暖”) 简化成
SPRING("春天", "温暖") 解读 常量名(实参列表)
当有多个枚举对象时,使用,间隔,最后有一个分号结尾
如果使用 enum 来实现枚举,要求将定义常量对象,写在前面: 枚举对象必须放在枚举类的行首.
如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略
enum常用方法
name()
输出枚举对象的名称
public final String name() {
return name;
}
ordinal()
输出的是该枚举对象的次序/编号,从0开始编号
public final int ordinal() {
return ordinal;
}
values
源码隐藏起来了
返回 Season2[] (含有定义的所有枚举对象)
Season2[] values = Season2.values();
System.out.println("===遍历取出枚举对象(增强for)====");
for (Season2 season: values) {//增强for循环
System.out.println(season);
}
valueOf
将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
- 根据你输入的 “AUTUMN” 到 Season2 的枚举对象去查找
- 如果找到了,就返回,如果没有找到,就报错
源码:
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
compareTo
比较两个枚举常量,比较的就是编号(编号相减)
源码:
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
Season2.AUTUMN的编号[2] - Season2.SUMMER的编号[3]
细节
1. 使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而Java是单继承机制
2. enum实现的枚举类,仍然是一个类,所以还是可以实现接口的.s调用构造方法需要通过枚举对象调用.
实例:
public class EnumMethod {
public static void main(String[] args) {
//使用Season2 枚举类,来演示各种方法
Season2 autumn = Season2.AUTUMN;
//输出枚举对象的名字
System.out.println(autumn.name());
//ordinal() 输出的是该枚举对象的次序/编号,从0开始编号
//AUTUMN 枚举对象是第三个,因此输出 2
System.out.println(autumn.ordinal());
//从反编译可以看出 values方法,返回 Season2[]
//含有定义的所有枚举对象
Season2[] values = Season2.values();
System.out.println("===遍历取出枚举对象(增强for)====");
for (Season2 season: values) {//增强for循环
System.out.println(season);
}
//valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
//执行流程
//1. 根据你输入的 "AUTUMN" 到 Season2的枚举对象去查找
//2. 如果找到了,就返回,如果没有找到,就报错
Season2 autumn1 = Season2.valueOf("AUTUMN");
System.out.println("autumn1=" + autumn1);
System.out.println(autumn == autumn1);
//compareTo:比较两个枚举常量,比较的就是编号
//老韩解读
//1. 就是把 Season2.AUTUMN 枚举对象的编号 和 Season2.SUMMER枚举对象的编号比较
//2. 看看结果
/*
public final int compareTo(E o) {
return self.ordinal - other.ordinal;
}
Season2.AUTUMN的编号[2] - Season2.SUMMER的编号[3]
*/
System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER));
//补充了一个增强for
// int[] nums = {1, 2, 9};
// //普通的for循环
// System.out.println("=====普通的for=====");
// for (int i = 0; i < nums.length; i++) {
// System.out.println(nums[i]);
// }
// System.out.println("=====增强的for=====");
// //执行流程是 依次从nums数组中取出数据,赋给i, 如果取出完毕,则退出for
// for(int i : nums) {
// System.out.println("i=" + i);
// }
}
}