首页 > 编程语言 >Java 注解

Java 注解

时间:2023-06-18 16:36:28浏览次数:47  
标签:Java String public 注解 警告 class

一、Java注解(Annotation)简介

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

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

注解即一种描述数据的数据。所以可以说注解就是源代码的元数据。例如以下代码:

@Override
public String toString() {
    return "helloworld";
}

上面的代码重写了 Object 类的 toString() 方法并使用了 @Override 注解。如果不使用 @Override 注解标记代码,程序也能够正常执行。那么这么写有什么好处吗?事实上,使用 @Override 注解就相当于告诉编译器这个方法是一个重写方法,如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。这样可以防止不小心拼写错误造成麻烦。

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

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

无论是哪一种注解,本质上都一种数据类型,是一种接口类型。到 Java 8 为止 Java SE 提供了 11 个内置注解。其中有 5 个是基本注解,它们来自于 java.lang 包。有 6 个是元注解,它们来自于 java.lang.annotation 包,自定义注解会用到元注解。(元注解就是负责注解其他的注解)
基本注解包括:@Override、@Deprecated、@SuppressWarnings、@SafeVarargs 和 @FunctionalInterface。

二、Java中常见注解

2.1.Java @Override注解

Java 中 @Override 注解是用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。使用 @Override 注解示例代码如下:

public class User implements Serializable{
    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

说明:

  • @Override 的作用是告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错。
  • 如果代码中方法前面不加 @Override 注解,即便是方法编辑错误了,编译器也不会有提示(有@Override注解,如果有错误,编译的时候不会通过,报异常)。这时 Object 父类的 toString() 方法并没有被重写,将会引起程序出现 Bug(缺陷)。

2.2.Java @Deprecated注解 

Java 中 @Deprecated 可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。

@Deprecated
public class User implements Serializable{
    @Deprecated
    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    @Deprecated
    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

上述 User类、成员变量 username 和 setName方法都被 @Deprecated 注解。在 idea 中这些被注解的 API 都会被画上删除线。示例代码如下。 

Java 注解_成员变量

2.3.Java @SuppressWarnings:抑制编译器警告

Java 中的 @SuppressWarnings 注解指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。例如,使用 @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

抑制没被使用过的代码的警告


使用 @SuppressWarnings 注解示例代码如下:

public class Test02 {
    //抑制 过时方法或者类
    @SuppressWarnings({ "deprecation" })
    public static void main(String[] args) {
        User user = new User("admin","123456");
        user.setUsername("root");
        System.out.println(user.toString());

        //抑制 创建集合没有指定泛型的异常,all表示所有警告
        @SuppressWarnings({"all"})
        ArrayList objects = new ArrayList();

    }
}

在显示如下图所示:

Java 注解_Java_02

上述代码使用 @SuppressWarnings({ "deprecation" }) 注解了 main 方法。在之前案例中的 User 代码中,这些 API 已经过时了,所以代码有编译警告,但是在使用了 @SuppressWarnings 注解之后会发现程序代码的警告没有了。

2.4.Java @SafeVarargs注解

@SafeVarargs在JDK 7中引入,主要目的是处理可变长参数中的泛型,此注解告诉编译器:在可变参数中的泛型是类型安全的。可变参数是使用数组存储的,而数组和泛型不能很好的混合使用。

public class HelloWorld {
    public static void main(String[] args) {
        //传递可变参数。参数是泛型集合
        display(10,23,12);

        // 传递可变参数,参数是非泛型集合
        display("hello",230,12);//会有编译警告
    }

        
    public static <T> void display(T... arrays){
        for (T array : arrays) {
            System.out.println(array.getClass().getName()+":"+array);
        }
    }
}

上面代码声明了一种可变参数方法 display,display 方法参数个数可以变化,它可以接受不确定数量的相同类型的参数。可以通过在参数类型名后面加入...的方式来表示这是可变参数。可变参数方法中的参数类型相同,为此声明参数是需要指定泛型。
但是调用可变参数方法时,应该提供相同类型的参数,代码第一次调用时没有警告,而代码第二行调用时则会发生警告,这个警告是 unchecked(未检查不安全代码,参数化变量类型可能造成堆污染),就是因为将非泛型变量赋值给泛型变量所发生的。

可用 @SafeVarargs 注解抑制编译器警告,修改代码如下:

public class HelloWorld {
    public static void main(String[] args) {
        // 传递可变参数,参数是泛型集合
        display(10, 20, 30);
        // 传递可变参数,参数是非泛型集合
        display("10", 20, 30); // 会有编译警告
    }

    @SafeVarargs
    public static <T> void display(T... array) {
        for (T arg : array) {
            System.out.println(arg.getClass().getName() + ":" + arg);
        }
    }
}

上述代码在可变参数 display 前添加了 @SafeVarargs 注解,当然也可以使用 @SuppressWarnings("unchecked") 注解,但是两者相比较来说 @SafeVarargs 注解更适合。
注意:@SafeVarargs注解不适用于非 static 或非 final 声明的方法,对于未声明为 static 或 final 的方法,如果要抑制 unchecked 警告,可以使用 @SuppressWarnings 注解。

2.5.Java @FunctionalInterface注解

@FunctionalInterface是Java 8中引入的注解,用于标识一个接口是否为函数式接口。函数式接口指的是只有一个抽象方法的接口,它可以被Lambda表达式或方法引用所使用。

@FunctionalInterface可以帮助我们在编译期发现不符合函数式接口规范的接口,并避免在后续使用Lambda表达式时出现错误。如果一个接口被标记为@FunctionalInterface,但其中有多个抽象方法,编译器会提示错误。

@FunctionalInterface
interface MyFunctionalInterface {
    void doSomething();
}

上面的代码中,MyFunctionalInterface是一个函数式接口,因为它只有一个抽象方法doSomething()。如果我们在该接口中添加另外一个抽象方法,编译器会提示错误。

需要:

  • @FunctionalInterface注解不是必需的,只是用于帮助开发者识别函数式接口的工具之一。
  • 如果一个接口只有一个抽象方法,即使没有使用@FunctionalInterface注解,它仍然是一个函数式接口。

三、Java自定义注解

Java 的基本注解和元注解如果不能满足你的需求,可以自定义注解。声明自定义注解使用 @interface 关键字(interface 关键字前加 @ 符号)实现。定义注解与定义接口非常像,如下代码可定义一个简单形式的注解类型。

public @interface Test11 {
    
}

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

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

例如上面声明的 Test11 注解以及基本注解中的 @Override 注解都属于标记注解。根据需要,注解中可以定义成员变量,成员变量以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。代码如下所示:

public @interface MyTag {
    // 定义带两个成员变量的注解
    // 注解中的成员变量以方法的形式来定义
    String name();
    int age();
}

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

public class Test12 {
    //使用带成员变量的注解时,需要为成员变量赋值
    @MyTag(name = "liming", age=12)
    public void info(){
        
    }
}

注解中的成员变量也可以有默认值,可使用 default 关键字。如下代码定义了 @MyTag 注解,该注解里包含了 name 和 age 两个成员变量。

public @interface MyTag {
    // 定义了两个成员变量的注解
    // 使用default为两个成员变量指定初始值
    String name() default "李媛";
    int age() default 34;
}

如果为注解的成员变量指定了默认值,那么使用该注解时就可以不为这些成员变量赋值,而是直接使用默认值。

public class Test12 {
    // 使用带成员变量的注解
    // MyTag注释的成员变量有默认值,所以可以不为它的成员变量赋值
    @MyTag
    public void info(){
        
    }
}

当然也可以在使用 MyTag 注解时为成员变量指定值,如果为 MyTag 的成员变量指定了值,则默认值不会起作用。
根据注解是否包含成员变量,可以分为如下两类。

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

现在只学习注解的大致技能点,具体怎么应用  后面慢慢学习。

四、Java元注解作用及使用

元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。Java 5 定义了 4 个注解,分别是 @Documented、@Target、@Retention 和 @Inherited。Java 8 又增加了 @Repeatable 和 @Native 两个注解。这些注解都可以在 java.lang.annotation 包中找到。下面主要介绍每个元注解的作用及使用。

4.1.@Documented

@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。

@Documented
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface MyDocumented {
    public String value() default "这是@Documented注解";
}

测试类代码:

@MyDocumented
public class DocumentedTest {

    /**
     * 测试注解
     * @return
     */
    @MyDocumented
    public String Test(){
        return "这是Java教程";
    }
}

打开 Java 文件所在的目录,分别输入如下两条命令行:

javac MyDocumented.java DocumentedTest.java
javadoc -d doc MyDocumented.java DocumentedTest.java

如下图:

Java 注解_成员变量_03

4.2.@Target

@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组,下表为 ElementType 常用的枚举常量。

名称

说明

CONSTRUCTOR

用于构造方法

FIELD

用于成员变量(包括枚举常量)

LOCAL_VARIABLE

用于局部变量

METHOD

用于方法

PACKAGE

用于包

PARAMETER

用于类型参数(JDK 1.8新增)

TYPE

用于类、接口(包括注解类型)或 enum 声明

自定义一个 MyTarget 注解,使用范围为方法如下:

@Target({ElementType.METHOD})
public @interface MyTarget {
}

class Test13{
    @MyTarget
    String name;
}

会编译错误,错误信息为:

'@MyTarget' not applicable to field

提示此位置不允许使用注解 @MyDocumented,@MyTarget 不能修饰成员变量,只能修饰方法。

4.3.@Retention

@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型,RetentionPolicy 有 3 个枚举常量,如下所示。

  1. SOURCE:在源文件中有效(即源文件保留)
  2. CLASS:在 class 文件中有效(即 class 保留)
  3. RUNTIME:在运行时有效(即运行时保留)

生命周期大小排序为 SOURCE < CLASS < RUNTIME,前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS 注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。

4.4.@Inherited

@Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。

@Target({ElementType.TYPE})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInherited {
    
}

@MyInherited
class Test14{

    public static void main(String[] args) {
        System.out.println(Test14.class.getAnnotation(MyInherited.class));
        System.out.println(Test14.class.getAnnotation(MyInherited.class));
        System.out.println(Test14.class.getAnnotation(MyInherited.class));
    }
}

class testA extends Test14{

}

class testB extends Test14{

}

运行结果为:

@com.assemble.MyInherited()
@com.assemble.MyInherited()
@com.assemble.MyInherited()

4.5.@Repeatable

@Repeatable 注解是 Java 8 新增加的,允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。案例如下:

在生活中一个人往往是具有多种身份,例如我是一家公司的老板,同时我还是我妻子的丈夫,更是我父母的孩子,如果希望借助注解的方式来表达该如何呢?

  • 首先定义一个Persons类来表示我所有的身份:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public   @interface Persons {
    Person[] value();
}

这里@Target是声明Persons注解的作用范围,参数ElementType.Type代表可以给一个类型进行注解,比如类,接口,枚举。@Retention是注解的有效时间,RetentionPolicy.RUNTIME是指程序运行的时候。

  • 接下来我们就定义一个注解,这里用到了@Repeatable注解,来真正表达我们的身份:
@Repeatable(Persons.class)
public  @interface Person{
    String role() default "";
}

@Repeatable括号内的就相当于用来保存该注解内容的容器。

  • 然后,为“我”来创建一个实体类:
@Person(role="CEO")
@Person(role="husband")
@Person(role="father")
@Person(role="son")
public  class Man{
    String name="";

    public static void main(String[] args) {

        //Java中的getAnnotations()方法可以获取一个类或者方法等程序元素上所有的注解,同时也可以获取注解上的元数据信息。
        //该方法返回的是一个Annotation[]数组,它包含了指定元素上所有的注解对象,但是并不包括继承的注解。
        Annotation[] annotations = Man.class.getAnnotations();

        System.out.println(annotations.length);//输出长度 1
        Persons p1=(Persons) annotations[0];
        for(Person t:p1.value()){
            System.out.println(t.role());
        }
    }
}
  • 最后测试一下,获取所有的身份信息并输出:
@Person(role="CEO")
@Person(role="husband")
@Person(role="father")
@Person(role="son")
public  class Man{
    String name="";

    public static void main(String[] args) {

        //Java中的getAnnotations()方法可以获取一个类或者方法等程序元素上所有的注解,同时也可以获取注解上的元数据信息。
        //该方法返回的是一个Annotation[]数组,它包含了指定元素上所有的注解对象,但是并不包括继承的注解。
        Annotation[] annotations = Man.class.getAnnotations();

        System.out.println(annotations.length);//输出长度 1
        Persons p1=(Persons) annotations[0];
        for(Person t:p1.value()){
            System.out.println(t.role());
        }
    }
}

4.6.@Native

使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可



标签:Java,String,public,注解,警告,class
From: https://blog.51cto.com/u_13661275/6508752

相关文章

  • Java反射机制
    一、Java反射机制是什么?Java 反射机制是Java语言的一个重要特性。在学习Java反射机制前,大家应该先了解编译期和运行期两个概念:编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在Java中也就是把Java代码编成class文件的过程。编译期只是做了一些翻译功能,......
  • java操作redis之jedis
    我们之前对Redis的学习都是在命令行窗口,那么如何使用Java来对Redis进行操作呢?对于Java连接Redis的开发工具有很多,这里先介绍通过Jedis实现对Redis的各种操作。(前提是你的redis已经配置了远程访问)1.创建一个maven工程,并且添加以下依赖<dependencies><!--jedis--><......
  • java web模板学习+1
    今天找到一个模板很厉害,代码开源的,拔下来就能跑。下面是他的博客地址和他的源代码地址。博客地址:开源一套简单通用的后台管理系统-huanzi-qch-博客园(cnblogs.com)开源地址:https://gitee.com/huanzi-qch/base-admin......
  • /usr/bin/java -> /etc/alternatives/java
    [root@localhosteclipse]#whichjava/usr/bin/java[root@localhosteclipse]#ls-l/usr/bin/javalrwxrwxrwx1rootroot22Aug12012/usr/bin/java->/etc/alternatives/java[root@localhosteclipse]#ls/etc/alternatives/antlr......
  • 【Java学习】 Spring的基础理解 IOC、AOP以及事务
    一、简介  官网: https://spring.io/projects/spring-framework#overview   官方下载工具: https://repo.spring.io/release/org/springframework/spring/  github下载: https://github.com/spring-projects/spring-framework   maven依赖:<dependency>......
  • java 聚合项目--pom.xml配置文件
    java聚合项目创建聚合项目的2种方式:分层项目开发:1.DAO:java工程项目;(mavenquickstart)2.Service:java工程项目;(mavenquickstart)3.模型:java工程项目;(mavenquickstart)4.共工模块:java工程项目;(mavenquickstart)5.controller+view:webapp:web工程项目(mavenwebapp)工程类型:packing......
  • java 创建聚合项目的2种方式及 部署Nacos
    创建聚合项目的2种方式byIDEA20231.创建module,erp-parent2.创建子module,设置每个module的pom.xml属性pom及父模块;3.配置erp-parent模块里的pom.xml1.创建module,erp-parent2.创建子module--mavenarchet部署nacosype(archetype:quickstart(java工程),webapp(web工程),设置每......
  • lilo java 快速 graphql stitching 包
    lilo是一个快速的graphqlstitching包,可以实现合并多个graphql服务的合并(schema,以及调用)比较适合的业务场景是gateway说明同时在springone官方中也有介绍到,内部使用到了graphql-java进行处理参考资料https://github.com/friatech/lilohttps://bitbucket.org/atlassian/graphql......
  • 3、【java程序运行监控byteman】Byteman脚本详解及javaagent和byteman结合使用详细示
    java程序运行监控byteman系列文章1、【java程序运行监控byteman】之安装部署和入门示例2、【java程序运行监控byteman】使用示例(运行中方法耗时监控、javaagent监控、jvm监控、规则的检查、是否运行以及查看等)3、【java程序运行监控byteman】Byteman脚本详解及javaagent和bytem......
  • java创建List时候 初始化赋值
    List<String>list=newArrayList<>(){//这个大括号就相当于我们new接口{//这个大括号就是构造代码块会在构造函数前调用System.out.println("我是构造代码块");this.add("one");//this可以省略这里加上只是为了让......