前言
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能、抑制警告等等。在JavaEE中,注解占据了更重要的角色,现在流行框架
Spring... Mybatis
等都支持注解开发
1、注解使用示例
Java中有很多注解,我们举例三个常见的注解
1.1、@Override
作用域:只能写在方法上
含义:表示这个方法是重写的父类的方法
重写了父类的方法,如果添加上这个注解,那么重写的方法如果方法名称错误或者返回值类型错误,编译就会报错提示,反之没加注解,则不会提示
案例(观察sayHi3、sayHi4
方法,都想要重写sayHi2
方法,但是方法名都写错了,查看结果)
package com.tcc.entity;
import java.io.Serializable;
/**
* @author 宇辰
* @date 2022/9/26-11:27
**/
public class User extends Person {
public static void main(String[] args) {
User user = new User();
user.sayHi(); // 我是用户
}
@Override
public void sayHi() {
System.out.println("我是用户");
}
@Override
public void sayHi3() {
System.out.println("想要重写父类方法sayHi2");
}
public void sayHi4() {
System.out.println("想要重写父类方法sayHi2");
}
}
class Person {
private String name;
private String sex;
public void sayHi(){
System.out.println("我是人");
}
public void sayHi2(){
System.out.println("被重写的方法");
}
}
结果(可以看到带注解的会报错,因为在父类中没有找到被重写的方法sayHi3
)
1.2、@Deprecated
作用域:只能写在方法上
含义:表示这个方法已经过时,不建议使用,但仍可以使用
被这个注解描述过的方法,调用的时候会在上面会显示被划掉的特殊样式
案例(Date日期类型很多方法都过时了)
调用结果
1.3、@SuppressWarnings
作用域:可以写在任何位置,类、方法、属性…等
含义:抑制编译器的警告,不提示警告消息
参数
- 抑制单类型警告@SuppressWarnings(“unchecked”)、
抑制多类型警告@SuppressWarnings(“unchecked”,“rawtypes)、
抑制所有类型警告:@SuppressWarnings(“all”)- 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: 抑制没被使用过的代码的警告
使用Eclipse的话,如果参数定义了没使用,会有个黄色警告。
使用Idea的话,则是参数名称颜色变灰,都表示一个意思,警告
没有被使用,并且没有添加注解
没有被使用,但是使用了注解
2、自定义注解
注解一般在反射机制中用的多
- 定义新的Annotation类型使用
@interface
关键字- 自定义注解自动继承了
java.lang.annotation.Annotation
接口- Annotation的成员变量在Annotation定义中以无参数方法的形式来生命,其方法名和返回值定义了该成员的名字和类型。。我们成为配置参数。类型只能是八种基本数据类型:
String、Class、Enum、Annotation
以上所有类型的数组。- 可以在定义Annotation的成员变量是为其指定初始值,指定成员变量的初始值可使用
default
关键字- 如果只有一个参数成员,建议使用参数名为
value
- 如果定义的注解含有配置参数,那么使用时必须指定参数值,除非有默认的值,格式是:
参数名=参数值
,如果只有一个参数成员,且名称为value,则可以省略value=
,直接写值- 没有成员定义的Annotation成为标记,包含成员变量的注解称为
元数据注解
自定义注解必须配上注解的含义才有意义(在注解上写文档注解表名该注解含义)
无参注解
定义
package com.tcc.anno;
/**
* @author 宇辰
* @date 2022/9/26-11:27
**/
public @interface MyAnno {
}
使用
有参注解
定义
package com.tcc.anno;
/**
* @author 宇辰
* @date 2022/9/26-11:27
**/
public @interface MyAnno {
String value();
// 有默认值的注解,有默认值的话,使用注解可以不写该参数的值
//String value() default "demo";
}
使用
3、元注解
元注解是定义在注解上的注解,用于修饰其他注解
JDK5.0提供了4个标准的
meta-annotation
类型,分别是:
- Retention:指定该注解的生命周期
- Target:指定该注解使用的地方
- Documented:指定是否在
JavaDoc
文档中显示- Inherited:指定子类是否继承父类的这个注解
3.1、Retention注解
只能用于修饰一个
Annotation
定义,用于指定该Annotation
的生命周期,@Retention
包含一个@RetentionPolicy
类型的成员变量,使用@Rentention
时,必须为该value
成员变量指定值
- RetentionPolicy.SOURCE:在源文件中有效(即源文件中保留),编译器直接丢掉这种策略的注解
- RetentionPolicy.CLASS(默认):在class文件中保留(即编译文件中保留),当运行Java程序时,JVM不会保留注解
- RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行Java程序时,JVM会保留注解,程序可以通过反射来获取该注解
下面分别举例
SOURCE
-
在自定义注解上,添加该注解,并且参数为
RetentionPolicy.SOURCE
package com.tcc.anno; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * @author 宇辰 * @date 2022/9/26-11:27 **/ @Retention(RetentionPolicy.SOURCE) public @interface MyAnno { }
-
把注解写到main方法上
-
运行
main
方法,查看编译后的文件是否保留了该注解(并没有保留)
CLASS
-
在自定义注解上,添加该注解,并且参数为
RetentionPolicy.CLASS
package com.tcc.anno; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * @author 宇辰 * @date 2022/9/26-11:27 **/ @Retention(RetentionPolicy.CLASS) public @interface MyAnno { }
-
把注解写到main方法上
-
运行
main
方法,查看编译后的文件是否保留了该注解(保留了)
RUNTIME
-
在自定义注解上,添加该注解,并且参数为
RetentionPolicy.RUNTIME
-
把注解写到自定义的实体类上
package com.tcc.entity; import com.tcc.anno.MyAnno; /** * @author 宇辰 * @date 2022/10/22-14:09 **/ @MyAnno public class User { private String name; private String sex; }
-
在
Main
方法中,使用反射获得该注解(只有值为RUNTIME
的才可以使用反射获取注解)package com.tcc.test; import com.tcc.anno.MyAnno; import com.tcc.entity.User; /** * @author 宇辰 * @date 2022/8/31-8:53 **/ public class Test { public static void main(String[] args){ Class<User> userClass = User.class; MyAnno annotation = userClass.getAnnotation(MyAnno.class); System.out.println(annotation.toString()); // @com.tcc.anno.MyAnno() } }
3.2、Target注解
用于修饰
Annotation
定义,用于指定被修饰的Annotation
能用于修饰那些程序元素@Target也包含一个名为value的成员变量,参数类型为
ElementType
的枚举类
ElementType
参数有很多,这里就不一一讲解, 这里只演示ElementType.METHOD
,作用在方法上
package com.tcc.test;
import com.tcc.anno.MyAnno;
import com.tcc.entity.User;
/**
* @author 宇辰
* @date 2022/8/31-8:53
**/
// @MyAnno 写在类上,报错
public class Test {
@MyAnno // 只能写在方法上面
public static void main(String[] args){
Class<User> userClass = User.class;
MyAnno annotation = userClass.getAnnotation(MyAnno.class);
System.out.println(annotation.toString()); // @com.tcc.anno.MyAnno()
}
}
3.3、Documented注解
表明该注解是否显示在生成的
JavaDoc
文档中,没有参数,用作标识默认情况下JavaDoc是不包括注解的
举例
如何生成JavaDoc文档
3.4、Inherited注解
被它修饰的
Annotation
将具有继承性,如果某个类使用了被@Inherited
修饰的Annotation
,则其子类自动具有该注解
举例
-
在自定义注解上添加该注解
package com.tcc.anno; import java.lang.annotation.*; /** * @author 宇辰 * @date 2022/9/26-11:27 **/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) @Documented @Inherited public @interface MyAnno { }
-
在实体类(User)中使用该注解
package com.tcc.entity; import com.tcc.anno.MyAnno; /** * @author 宇辰 * @date 2022/10/22-14:09 **/ @MyAnno public class User { private String name; private String sex; }
-
在Main方法所在的类(Test)中继承
User
实体类,然后使用反射获取(Test)类上的注解package com.tcc.test; import com.tcc.anno.MyAnno; import com.tcc.entity.User; /** * @author 宇辰 * @date 2022/8/31-8:53 **/ public class Test extends User { public static void main(String[] args){ Class<Test> testClass = Test.class; MyAnno annotation = testClass.getAnnotation(MyAnno.class); System.out.println(annotation.toString()); // @com.tcc.anno.MyAnno() 因为继承了User类,这个类上有MyAnno注解,所以它自己也有 } }
4、JDK8新特性(可重复注解)
@Repeatable(MyAnnos.class)里面参数使用自己定义的注解
准备
在一个地方重复使用两次注解,查看结果(会报错)
4.1、JDK8之前解决办法
思路:
创建一个注解,参数为想重复写的注解数组,然后在想要写的地方使用该注解,参数填入注解数组即可实现写多个重复注解
-
创建一个
MyAnnos
注解,定义类型为MyAnno
数组的参数package com.tcc.anno; /** * @author 宇辰 * @date 2022/10/22-15:34 **/ public interface MyAnnos { MyAnno[] value(); }
-
在
User
类上使用MyAnnos
注解,参数传入两个相同的MyAnno
注解package com.tcc.entity; import com.tcc.anno.MyAnno; import com.tcc.anno.MyAnnos; /** * @author 宇辰 * @date 2022/10/22-14:09 **/ /*@MyAnno("test1") @MyAnno("test2")*/ @MyAnnos({@MyAnno("test1"),@MyAnno("test2")}) public class User { private String name; private String sex; }
4.2、JDK8解决办法
使用注解:@Repeatable(MyAnnos.class)
注意:
- 参数里面自定义的注解的元注解,要与里面的参数里面的注解的元要保持一致,即子有,父也要有
- 作用域可以不保持一致,但是一定要比参数注解的生命周期长
-
使用我们上面定义的
MyAnnos注解
package com.tcc.anno; /** * @author 宇辰 * @date 2022/10/22-15:34 **/ public @interface MyAnnos { MyAnno[] value(); }
-
在需要重复使用的注解上面添加:
@Repeatable(MyAnnos.class)
注解即可package com.tcc.anno; import java.lang.annotation.*; /** * @author 宇辰 * @date 2022/10/22-15:34 **/ // 因为参数注解也有这些属性,所以父注解也需要有 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) @Documented @Inherited public @interface MyAnnos { MyAnno[] value(); }
-
使用
package com.tcc.entity; import com.tcc.anno.MyAnno; /** * @author 宇辰 * @date 2022/10/22-14:09 **/ @MyAnno("test1") @MyAnno("test2") // 不报错了 //@MyAnnos({@MyAnno("test1"),@MyAnno("test2")}) public class User { private String name; private String sex; }
5、JDK8新特性(类型注解)
在Java8中,
ElementType
新增了两个值
- TYPE_PARAMETER:作用在类型参数上( 比如:泛型)
- TYPE_USE:作用在类型上(包含上面那个)
举例
-
修改
MyAnno
注解上的@Target
元注解参数package com.tcc.anno; import java.lang.annotation.*; /** * @author 宇辰 * @date 2022/9/26-11:27 **/ @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE_USE) @Documented @Inherited public @interface MyAnno { }
-
使用