Java面向对象-一文搞懂java中的注解(Annotation)

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!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值