day14 14 解释 注解五“问”

14、注解小结

14.1 什么是注解?

概念
从 Java 5 版本之后可以在源代码中嵌入一些补充信息,这种补充信息称为注解(Annotation),是 Java 平台中非常重要的一部分。注解都是 @ 符号开头的,例如我们在学习方法重写时使用过的 @Override 注解。同 Class 和 Interface 一样,注解也属于一种类型。

理解

Annotation 可以翻译为“注解”或“注释”,一般翻译为“注解”
因为“注释”一词已经用于说明“//”、“/**...*/”和“/*...*/”等注释符号了
这里的“注释”是英文 Comment 翻译。

注解并不能改变程序的运行结果,也不会影响程序运行的性能。有些注解可以在编译时给用户提示或警告,有的注解可以在运行时读写字节码文件信息。

注解就是一种描述数据的数据。所以可以说注解就是源代码的元数据。
分类
根据注解是否包含成员变量,可以分为如下两类。

  • 标记注解没有定义成员变量的注解类型被称为标记注解。这种注解仅利用自身的存在与否来提供信息,如 @Override就是标记注解。
  • 元数据注解包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。

注解的实例代码

@Override
public String toString() {//@Override注解说明重写继承类或者接口方法。
    return "string";
}

上面的代码重写了 Object 类的 toString() 方法并使用了 @Override 注解。如果不使用 @Override 注解标记代码,程序也能够正常执行。那么这么写有什么好处吗?

事实上,使用 @Override 注解就相当于告诉编译器这个方法是一个重写方法,如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。这样可以防止不小心拼写错误造成麻烦。

例如,在没有使用 @Override 注解的情况下,将 toString() 写成了 toStrring(),这时程序依然能编译运行,但运行结果会和所期望的结果大不相同。

14.2 注解的作用有哪些?

注解常见的作用有以下几种:

  • 生成帮助文档。这是最常见的,也是 Java 最早提供的注解。常用的有 @see、@param 和 @return 等;
  • 跟踪代码依赖性,实现替代配置文件功能。比较常见的是 Spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
  • 在编译时进行格式检查。如把 @Override 注解放在方法前,如果这个方法并不是重写了父类方法,则编译时就能检查出。

无论是哪一种注解,本质上都一种数据类型,是一种接口类型

14.3 我们常见的注解,如何使用?

14.3.1 常见注解

到 Java 8 为止 Java SE 提供了 11 个内置注解。其中有 5 个是基本注解,它们来自于 java.lang 包。有 6 个是元注解,它们来自于 java.lang.annotation 包,自定义注解会用到元注解。

元注解

元注解就是负责注解其他的注解。如@Target用来限制注解使用的范围
Java 5 定义了 4 个注解,分别是 @Documented@Target@Retention@Inherited。
Java 8 又增加了 @Repeatable@Native 两个注解。这些注解可以在 java.lang.annotation 包中找到。
名字作用
Documented@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。
Inherited@Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。
Native使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。
Repeatable@Repeatable 注解是 Java 8 新增加的,它允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。
Retention@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。
Target@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标

基本注解包括:

  • 1)@Override
    用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。
  • 2)@Deprecated
    可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。Java 9 为 @Deprecated 注解增加了以下两个属性:forRemoval该 boolean 类型的属性指定该 API 在将来是否会被删除。since该 String 类型的属性指定该 API 从哪个版本被标记为过时。
  • 3)@SuppressWarnings
    指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。例如,使用 @SuppressWarnings 修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。
  • 4)@SafeVarargs
    可用 @SafeVarargs 注解抑制编译器警告,@SafeVarargs注解不适用于非 static 或非 final 声明的方法,对于未声明为 static 或 final 的方法,如果要抑制 unchecked 警告,可以使用 @SuppressWarnings 注解。
  • 5)@FunctionalInterface
    用来指定某个接口必须是函数式接口,所以 @FunInterface 只能修饰接口,不能修饰其它程序元素。

14.3.2 如何使用(基本注解使用)

1)@Override
用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。

package Annotation_Learning;

public class Override_test {
    @Override
    public String toString(){
        System.out.println("这是重写Object类的toString()方法,使用Override注解告知编译器。\n" +
                "如果没有重写,则 Error:(4, 5) java: 方法不会覆盖或实现超类型的方法");
        return "返回值为String类型";
    }
    public static void main(String[]args){
        Override_test test = new Override_test();
        test.toString();
    }
}

2)@Deprecated
可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。Java 9 为 @Deprecated 注解增加了以下两个属性:forRemoval该 boolean 类型的属性指定该 API 在将来是否会被删除。since该 String 类型的属性指定该 API 从哪个版本被标记为过时。

package Annotation_Learning;

/**
 * 当其他程序使用已过时的元素时,编译器将会给出警告。同时Ide会给该行代码画上横线表示过时
 */

public class Deprecated_Test {
    @Deprecated(since="9",forRemoval = true)
    public String toString1(){
        System.out.println("这是@Deprecated,表示过时的注解,可以用来注解类、接口、成员方法等");
        return "@Deprecated";
    }
    public static void main(String[]args){
        Deprecated_Test test = new Deprecated_Test();
        test.toString1();
   }

}

发现toString方法上画了线,表示过时。
在这里插入图片描述
3)@SuppressWarnings
指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。例如,使用 @SuppressWarnings 修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。
注解的使用有以下三种:

  • 抑制单类型的警告:@SuppressWarnings(“unchecked”)
  • 抑制多类型的警告:@SuppressWarnings(“unchecked”,“rawtypes”)
  • 抑制所有类型的警告:@SuppressWarnings(“unchecked”)

抑制警告的关键字如下表所示。

关键字用途
all抑制所有警告
boxing抑制装箱、拆箱操作时候的警告
cast抑制映射相关的警告
dep-ann抑制启用注释的警告
deprecation抑制过期方法警告
fallthrough抑制在 switch 中缺失 breaks 的警告
finally抑制 finally 模块没有返回的警告
hiding抑制相对于隐藏变量的局部变量的警告
incomplete-switch忽略不完整的 switch 语句
nls忽略非 nls 格式的字符
null忽略对 null 的操作
rawtypes使用 generics 时忽略没有指定相应的类型
restriction抑制禁止使用劝阻或禁止引用的警告
serial忽略在 serializable 类中没有声明 serialVersionUID 变量
static-access抑制不正确的静态访问方式警告
synthetic-access抑制子类没有按最优方法访问内部类的警告
unchecked抑制没有进行类型检查操作的警告
unqualified-field-access抑制没有权限访问的域的警告
unused抑制没被使用过的代码的警告
package Annotation_Learning;

public class SuppressWarnings_Test {
    @Deprecated(since="9",forRemoval = true)
    public String  toString1(){
        System.out.println("这是过时的方法");
        return "被@SuppressWarnings注解抑制了。";
    }
    @SuppressWarnings("deprecation")
    public static void main(String[]args){
        SuppressWarnings_Test test = new SuppressWarnings_Test();
        System.out.println(test.toString1());
    }
}

4)@FunctionalInterface

在学习 Lambda 表达式时,我们提到如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),那么该接口就是函数式接口

  • @FunctionalInterface 就是用来指定某个接口必须是函数式接口,所以 @FunInterface 只能修饰接口,不能修饰其它程序元素。
  • 函数式接口就是为 Java 8 的 Lambda 表达式准备的,Java 8 允许使用 Lambda 表达式创建函数式接口的实例,
  • 因此 Java 8 专门增加了 @FunctionalInterface。
  • @FunctionalInterface 注解主要是帮助程序员避免一些低级错误,例如,在下面的 FunInterface 接口中再增加一个抽象方法 abc(),编译程序时将出现错误提示。
package Annotation_Learning;

@FunctionalInterface
interface FunctionInterface_Lambda{
    void display();//默认为抽象方法,函数式接口只能有一个抽象方法,可以有静态方法和普通方法
    default void learn(){
        System.out.println("这是普通方法");
    }
    static void learn_static(){
        System.out.println("这是静态方法");
    }
}
public  class FunctionalInterface_Test implements FunctionInterface_Lambda {
    @Override
     public void display(){//使用注解表示为重写接口的抽象方法。
        System.out.println("继承函数式接口,必须在相应的类里边实现该方法");
    }


    public static void main(String[]args){
        FunctionInterface_Lambda test = new FunctionalInterface_Test();
        test.display();
        test.learn();
        FunctionInterface_Lambda.learn_static();
    }
}

只有一个抽象方法,编译通过
在这里插入图片描述

加多一个抽象方法,就会报错
在这里插入图片描述
5)@SageVarargas

可用 @SafeVarargs 注解抑制编译器警告,@SafeVarargs注解不适用于非 static 或非 final 声明的方法,对于未声明为 static 或 final 的方法,如果要抑制 unchecked 警告,可以使用 @SuppressWarnings 注解。

package Annotation_Learning;

public class SafeVarargs_Demo {
    //定义一个可变参数类型的静态成员函数,并使用泛型
    @SafeVarargs
    public static<T> void display(T... array){//得到一个泛型数组
        for(T t:array)
            System.out.println(t.getClass().getName()+":"+t);//打印其类型信息及值的大小
        System.out.println("代码在可变参数 display 前添加了 @SafeVarargs 注解,当然也可以使用 @SuppressWarnings(\\\"unchecked\\\") " +
                "注解,但是两者相比较来说 @SafeVarargs 注解更适合。\n" );
    }
    public static  void main(String[]args){
        display(12,133,14);
        display("12",3,6);
    }
}

不使用@SafeVarargs注解前会提示你
在这里插入图片描述

14.4 如何去创建自定义注解?(跟创建接口类似)

声明自定义注解使用 @interface 关键字(interface 关键字前加 @ 符号)实现。定义注解与定义接口非常像,如下代码可定义一个简单形式的注解类型。

// 定义一个简单的注解类型
public @interface Test {
}

上述代码声明了一个 Test 注解。默认情况下,注解可以在程序的任何地方使用,通常用于修饰类、接口、方法和变量等。

定义注解和定义类相似,注解前面的访问修饰符和类一样有两种,分别是公有访问权限(public)和默认访问权限(默认不写)。一个源程序文件中可以声明多个注解,但只能有一个是公有访问权限的注解。且源程序文件命名和公有访问权限的注解名一致。

不包含任何成员变量的注解称为标记注解,例如上面声明的 Test 注解以及基本注解中的 @Override 注解都属于标记注解。

包含成员变量的注解称为元数据注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。根据需要,注解中可以定义成员变量,成员变量以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。代码如下所示:

public @interface MyTag {
    // 定义带两个成员变量的注解
    // 注解中的成员变量以方法的形式来定义
    String name();
    int age();
    String value() default "abcd";//默认注解里边没有分号!
}

以上代码中声明了一个 MyTag 注解,定义了两个成员变量,分别是 name 和 age。成员变量也可以有访问权限修饰符,但是只能有公有权限和默认权限。

如果在注解里定义了成员变量,那么使用该注解时就应该为它的成员变量指定值,如下代码所示。

public class Test {
    // 使用带成员变量的注解时,需要为成员变量赋值
    @MyTag(name="xx", age=6)//默认注解可以不用赋值
    public void info() {
        ...
    }
    ...
}

14.5 多重注解写?

14.5.1 java8以前定义多重注解的写法(容器、数组实现)

1)使用了注解“容器”,说白点就是注解数组。

package Annotation_Learning;

@interface Continue_Tags{//定义Continue_Tags注解

    Continue_Tag[] value();//定义为Continue_Tag注解的数组
}

@interface Continue_Tag{
    String con();//注解的常量
}
public class Continue_Annotation {
    @Continue_Tags({@Continue_Tag(con="hhh"),@Continue_Tag(con="jjj")})//使用第一个注解
    public void sayHell0(){
        System.out.println("展示多重注解的java7的写法,其实就相当于定义了两个注解,第一个注解其成员为一注解数组,第二个为该注解数组类型的注解");
    }
    @Continue_Tag(con="jj")//使用第二个注解
    public static void main(String[]args){
        new Continue_Annotation().sayHell0();
    }
}

在这里插入图片描述

14.5.2 java8以后使用@Repeatable元注解

2)

package Annotation_Learning;
import java.lang.annotation.Repeatable;

//定义一个注解
@Repeatable(Tags.class)
@interface Tag{
    String value();//都是叫做value
}
//定义一个注解可以多重使用
@interface Tags{
    Tag[] value();//都是叫做value,只不过类型为注解数组
}

public class Repeatable_Test {

    @Tag(value="name2")
    @Tag(value="name1")//连续使用注解
    public void sayHell0(){
        System.out.println("两个注解的成员变量的method要一样!,这里都是value,然后类似于java7的写法,只不过在第二个注解上面加上@Repeatable(第一个注解的class静态量)");
    }
    @Continue_Tag(con="jj")
    public static void main(String[]args){
        new Repeatable_Test().sayHell0();
    }
}

运行结果:
在这里插入图片描述
如果成员变量的“名不同”,就会报错
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨夜※繁华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值