1.什么是注解
* 从jdk1.5 开始,java增加了对元数据(MetaData)的支持,即注解(Annotation);
* 注解(Annotation)就是代码里的特殊标记,这种标记可以在 编译,类加载,运行 时被读取,并执行相应的处理;
* 注解(Annotation)可以像修饰符一样被使用,可用于修饰 包,类,属性,方法,构造器,参数,局部变量的声明;
2.java编译时的三个常见注解
下面先来了解一下 java中常见的三个注解,依次来加深对注解的印象:
【以下三个注解都是在编译时起作用的】
1.@Override : 修饰被重写的方法,子类重写父类的方法、接口的实现重现接口的方法声明,
【只可以修饰方法】
2.@Deprecated :用于表示修饰的类、方法等已经过时,不推荐使用,
【通常是因为该结构存在危险或存在更好的选择】
3.@SuppressWarnings : 抑制编译时的警告,比如 变量声明而未使用、集合泛型未指明等
1.@Override : 方法重写的注解
/**
* 1.一个父类中有一个方法 walk()
*/
class Father {
public void walk(){
System.out.println("Father walk");
}
}
/**
* 2.一个接口中声明一个方法 sing()
*/
interface action{
void sing();
}
/**
* 3.一个类,继承父类 + 实现接口
* 即重写父类walk()方法,也实现接口sing()方法
* 两个方法都使用 @Override进行标记
*/
class Son extends Father implements action{
@Override
public void walk() {
System.out.println("Son walk");
}
@Override
public void sing() {
System.out.println("Sing a song");
}
}
2.@Deprecated : 已过时的
【在开发工具中,已过时的会用 横线标出来】
下面是 Date 构造器的源码内容:
1.使用 @Deprecated 注解进行标记了
2.并且注释中已经提示 :使用 Calendar.set(year + 1900, month, date)
或者使用 GregorianCalendar(year + 1900, month, date) 进行替代。
即有了更好的解决方案。
/**
* Allocates a <code>Date</code> object and initializes it so that
* it represents midnight, local time, at the beginning of the day
* specified by the <code>year</code>, <code>month</code>, and
* <code>date</code> arguments.
*
* @param year the year minus 1900.
* @param month the month between 0-11.
* @param date the day of the month between 1-31.
* @see java.util.Calendar
* @deprecated As of JDK version 1.1,
* replaced by <code>Calendar.set(year + 1900, month, date)</code>
* or <code>GregorianCalendar(year + 1900, month, date)</code>.
*/
@Deprecated
public Date(int year, int month, int date) {
this(year, month, date, 0, 0, 0);
}
3.@SuppressWarnings : 抑制警告
3.元注解(meta-annotation)
1.元注解就是 来修饰注解的注解(meta-annotation);
2.jdk1.5中提供了四个标准的元注解:
@Retention
@Target
@Document
@Inherited
下面对上面四个元注解进行详细的介绍
3.1 @Retention(重点)
1.只能修饰注解的定义,即在定义注解的时候使用:用于指定 被修饰注解 的生命周期;
2.@Retention注解的参数是一个 RetentionPolicy 枚举类的对象,且该参数必填;
2.1 RetentionPolicy.SOURCE : 在源文件中有效,即会被编译器抛弃;
2.2 RetentionPolicy.CLASS : 在class文件中有效,即会参与编译,但执行时会被jvm忽略(默认值);
2.3 RetentionPolicy.RUNTIME : 在运行时有效,即在运行时 jvm会保留注解,且可以被反射获取到!
3.2 @Target(重点)
1.只能修饰注解的定义,即在定义注解的时候使用:用于指定 被修饰注解 可以用在哪些地方;
2.@Target注解的参数是一个ElementType类型的数组,表明被修饰注解可以修饰多种程序元素,该参数必填;
3.ElementType 也是一个枚举类:
3.1 ElementType.TYPT : 修饰 类、接口(包括注解的定义)、枚举类型;
3.2 ElementType.FIELD : 修饰 类的属性,(包括枚举类的常量);
3.3 ElementType.METHOD : 修饰类的方法;
3.4 ElementType.PARAMETER : 修饰形参的声明;
3.5 ElementType.CONSTRUCTOR : 修饰构造器;
3.6 ElementType.LOCAL_VARIABLE : 修饰局部变量;
3.7 ElementType.ANNOTATION_TYPE : 修饰注解类型;
3.8 ElementType.PACKAGE : 修饰包;
3.9 ElementType.TYPE_PARAMETER : 修饰类型参数(如泛型等)【jdk1.8新特性】
3.10 ElementType.TYPE_UES : 修饰被使用的类型(有类型的任何地方)【jdk1.8新特性】
3.3 @Document(了解)
1.只能修饰注解的定义,即在定义注解的时候使用:用于指定 被修饰注解 可以被javadoc提取成文档;
2.默认情况下,javadoc在生成文档的时候是不会把注解放进来的;
3.使用@Document修饰的注解,@Retention的参数必须是 RententionPolicy.RUNTIME : 即运行时
3.4 @Inherited(了解)
1.只能修饰注解的定义,即在定义注解的时候使用:用于指定 被修饰注解 可以被子类继承
即 : 一个类如果使用了该注解,则此类的子类默认也会包含该注解;
2.问题:如何验证呢?
答 : 此处需要配合 反射 来进行验证。代码如下:
【注意:如果想在运行时获取到注解,则@Retentiion必须指定参数为 RetentionPolicy.RUNTIME】
1.自定义一个注解@MyAnnotation,并用@Inherited进行修饰
(具体的自定义注解的操作在 第4小节 介绍)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
public @interface MyAnnotation {
}
2.声明一个类ClassWithMyAnnotation,用@MyAnnotation修饰
@MyAnnotation
class ClassWithMyAnnotation{
}
3.写一个子类Son2,继承ClassWithMyAnnotation
class Son2 extends ClassWithMyAnnotation{
}
4.编写测试类,反射获取对应的注解
class ApplicaionUseMyAnnotation{
public static void main(String[] args) {
Class<Son2> son2Class = Son2.class; // 获取Son2的class
Annotation[] annotations = son2Class.getAnnotations(); // 反射获取类上的注解
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
5.运行结果
@com.xx.xx.xx.MyAnnotation() : 这就是那个自定义的可以被继承的注解
4.自定义注解
自定义注解的概述:
1.自定义注解使用 @interface 关键字 修饰;
2.自定义注解自动继承了 java.lang.annotation.Annotation 接口;
3.自定义注解中的可以存在成员变量:
3.1 成员变量以 无参数方法的形式来声明;
3.2 方法名即成员变量的名称,返回值类型即成员变量的类型;
3.3 成员变量的类型只能是 八种基本数据类型、String、Class、enum、Annotation及以上所有类型的数组类型;
3.4 成员变量可以指定初始值,并要求使用 default 关键字来指定;
3.5 成员变量只有一个的时候,建议使用名称为 value;
3.6 成员变量 也被称为 : 配置参数。
4.自定义注解的使用:
4.1 如果存在配置参数且没有配置默认值,那么在使用的时候必须指定参数值,格式为 “参数名=参数值”;
4.2 如果存在配置参数且有默认值,则可以不指定参数值而使用默认值;
4.3 如果只有一个配置参数,且配置参数名是 “value”的,使用时可以省略 “value=”,直接写参数值即可;
5.没有成员变量的注解称为“标记”,存在成员变量的注解称为“元数据注解”;
6.自定义注解可以使用元注解来修饰。
【注意】自定义注解必须配合注解的信息处理流程才有意义。
【所谓的注解信息的出流程可以理解为反射获取注解信息,后面反射中详细介绍】
1.自定义注解的代码:
// 表示生命周期在运行时
@Retention(RetentionPolicy.RUNTIME)
//表示可以用来修饰:类、接口、枚举类、方法
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation2 {
//普通String类型
String name();
//普通int类型+指定默认值
int age() default 0;
//String数组类型+指定默认值
String[] hobbies() default {};
//Class数组类型
Class[] className();
// 定义一个枚举类型,并且在 注解中直接使用
enum enumName{ SPRING, SUMMER, AUTUMA, WINTER;};
//使用枚举类中的值
enumName season() default enumName.WINTER; // 指定默认值
enumName season2(); // 不指定默认值
}
2.使用自定义注解的代码
@MyAnnotation2(
name = "class1", // 手动赋值
className = {class1.class}, // 数组类型的值
season2 = MyAnnotation2.enumName.SUMMER // 可以使用注解中的枚举类型进行赋值
)
class class1{
// 类中的其他内容正常写
}
5.java8注解新特性1-可复用注解
可复用注解就是一个注解在同一个地方使用两次及以上。
jdk1.8 之前的思路:
再定义一个注解,配置参数是注解的数组即可
//1.定义一个注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@interface MyAnnotation003{
String value();
}
//2.定义一个包含注解数组参数的注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@interface MyAnnotationArray003{
MyAnnotation003[] value();
}
//3.注解复用:
@MyAnnotationArray003(value = {
@MyAnnotation003(value = "Hello"),
@MyAnnotation003(value = "World")
})
class ClassUseAnnotation{
}
jdk1.8新特性-可复用注解:
思路 :
1.声明一个注解MyAnnotation004,此注解为要可重复使用的注解;
2.声明一个注解MyAnnotationArray004,此注解的配置参数为 MyAnnotation004类型的数组;
3.给MyAnnotation004注解添加注解 @Repeatable,并指定配置参数为 MyAnnotationArray004;
4. 特别注意:MyAnnotation004和MyAnnotationArray004使用的元注解必须都一致,@Retention,@Target等。
5.经过上述步骤,可复用注解即可复用了!
//使用@Repeatable 注解指定可复用注解
@Repeatable(MyAnnotationArray004.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@interface MyAnnotation004{
String value();
}
//定义一个注解,配置参数类型是 要重复使用的注解类型
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@interface MyAnnotationArray004{
MyAnnotation004[] value();
}
//使用可重复注解
@MyAnnotation004("Hello")
@MyAnnotation004("World")
class ClassUseAnnotation02{
}
6.java8注解新特性2-类型注解
1.jdk1.8之后,元注解@Target的参数类型ElementType枚举值多了两个:TYPE_PARAMETER,TYPE_USE.
2.jdk1.8之前,注解只能在声明的地方使用,从jdk1.8开始,注解可以在任何地方使用:
2.1 ElementType.TYPE_PARAMTER : 表示该注解可以写在类型变量的声明语句中,比如:泛型的声明;
2.2 ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中。
下面简单理解一下这两个注解的使用,不过对于ElementType.TYPE_PARAETER还没有搞明白作用。期待后续吧。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.TYPE_USE})
@interface MyAnnotation006{
}
@MyAnnotation006
class classC{
// ElementType.TYPE_USE : 写在类型变量的声明语句中
public static void sayHello(@MyAnnotation006 String name) throws @MyAnnotation006 Exception{
System.out.println("Hello "+name);
ArrayList<@MyAnnotation006 String> list = new ArrayList<>();
}
}
7.完成
Congratulations!
You are one step closer to success!