Java修饰注解的注解(元注解)
- @Target 设置目标范围
- @Retention 设置保持性
- @Documented 文档
- @Inherited 注解继承
- @Repeatable 此注解可以重复修饰
Java自带注解
- @SuppressWarning 压制警告,修饰变量 / 方法 / 构造函数 / 类等
- @SuppressWarning(“unchecked”) 忽略unchecked警告信息
- @SuppressWarning(“deprecated”) 忽略过时方法的警告信息
- @SuppressWarning({“unchecked”, “deprecated”}) 忽略unchecked和过时方法的警告信息(JDK自规定了这两种)
- @Deprecated 描述某个方法或代码已过时,修饰类 / 类的元素 / 包
- @Override 重写方法标记,修饰方法
- @SafeVarargs 不会对不定项参数做危险操作
- @FunctionInterface 声明功能性接口
public static void main(String[] args) {
int a = 5;
Date d = new Date();
// 警告
System.out.println(d.getYear());
}
// Date类中的getYear()函数
@Deprecated
public int getYear() {
return normalize().getYear() - 1900;
}
使用@SupperssWarning后
@SuppressWarnings("all")
public static void main(String[] args) {
int a = 5;
Date d = new Date();
System.out.println(d.getYear());
}
无任何警告
Java自定义注解
- 从JDK1.5引入,注解定义:扩展java.lang.annotation.Annotation注解接口
- 注解可以包括的类型
- 基本类型(int / short / long / float / double / byte / char / boolean)
- String
- Class
- enum类型
- 注解类型
- 有前面类型组成的数组
注解使用的位置
- @Target 可以限定位置
- 允许的位置
- 包 / 类 / 接口 / 方法 / 构造器 / 成员变量 / 局部变量 / 形参变量 / 类型参数
注解无参数
/**
* @ClassName: Foo
* @Author: Tang
* @Date: 2021/6/4 15:01
*/
public class Foo {
@Test
public static void m1() {
}
@Test
public static void m2() {
throw new RuntimeException("m2 function error");
}
@Test
public static void m3() {
}
@Test
public static void m4() {
throw new RuntimeException("m4 function error");
}
@Test
public static void m5() {
}
}
注解测试
public class Demo1 {
public static void main(String[] args)throws Exception {
// 记录通过以及不同的个数
int pass = 0, failed = 0;
String className = "exmple.com.demo8.Foo";
// 获取Foo类中的所有方法
for (Method m : Class.forName(className).getMethods()) {
// 判断是否有@Test注解
if (m.isAnnotationPresent(Test.class)) {
try {
m.invoke(null);
pass++;
} catch (Throwable ex) {
System.out.printf("测试: %s 失败: %s %n", m, ex.getCause());
}
}
}
System.out.printf("通过: %d, 失败 %d%n", pass, failed);
}
}
// 表示该注解会保留在class文件中
@Retention(RetentionPolicy.RUNTIME)
// 表示该注解只会作用于方法上
@Target(ElementType.METHOD)
@interface Test {
}
运行结果:
测试: public static void exmple.com.demo8.Foo.m4() 失败: java.lang.RuntimeException: m4 function error
测试: public static void exmple.com.demo8.Foo.m2() 失败: java.lang.RuntimeException: m2 function error
通过: 3, 失败 0
注解带参数
public class Foo {
// 给a赋值
@SingleTest(1)
public static void m1(int a) {
if (a < 0) {
throw new RuntimeException("m1 function parameter error");
}
}
// 等价于@SingleTest(-2)
@SingleTest(value = -2)
public static void m2(int b) {
if (b < 0) {
throw new RuntimeException("m2 function parameter error");
}
}
}
注解测试
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException {
int pass = 0, failed = 0;
String className = "exmple.com.demo8.Foo";
for (Method m : Class.forName(className).getMethods()) {
// 判断是否有SingleTest注解
if (m.isAnnotationPresent(SingleTest.class)) {
// 输出函数名
System.out.println(m.getName());
// 获取方法上的注解
SingleTest st = m.getAnnotation(SingleTest.class);
try {
// 将获取到的值赋值给方法的形参
m.invoke(null, st.value()); // 等价SingleTest.value()
pass++;
} catch (Throwable e) {
System.out.printf("测试: %s 失败: %s %n", m, e.getCause());
failed++;
}
}
}
System.out.printf("通过: %d, 失败: %d%n", pass, failed);
}
}
// 表示该注解会保留在class文件中
@Retention(RetentionPolicy.RUNTIME)
// 表示该注解只能用于方法中
@Target(ElementType.METHOD)
@interface SingleTest {
// 定义一个int类型变量,默认值为0
// 与int a() default 0;等价
int value() default 0;
}
注解带多个参数时
// 表示该注解会保留在class文件中
@Retention(RetentionPolicy.RUNTIME)
// 表示该注解只能用于方法中
@Target(ElementType.METHOD)
@interface SingleTest {
// 定义一个int类型变量,默认值为0
int a() default 0;
int b() default 0;
}
// 使用注解时
@SingleTest(a = 1)
public static void m1(int a) {
if (a < 0) {
throw new RuntimeException("m1 function parameter error");
}
}
@SingleTest(a = -2, b = 2)
public static void m2(int a, int b) {
if (b < 0) {
throw new RuntimeException("m2 function parameter error");
}
}
// 反射机制给方法形参赋值时
m.invoke(null, st.a(), st.b()); // 等价于SingleTest.a()
Java元注解
- @Target 设置目标范围
- @Retention 设置保持性
- @Inherited 注解继承
- @Repeatable 此注解可以重复修饰
- @Documented 文档
@Rentention注解
- 用来修饰其他注解的存在范围
- RetentionPolicy.SOURCE 注解仅存在源码,不在class文件中
- RetentionPolicy.CLASS 默认的保留策略。注解存在于.class文件中,当但不能被JVM加载,即不能使用反射获取到
- RetentionPolicy.RUNTIME 注解可以被JVM运行时访问到。通常情况下,可以结合反射来实现一些功能
@Target注解
- 限定注解作用于什么位置
- ElementType.ANNOTAION_TYPE (修饰注解)
- ElementType.CONSTRUCTOR
- ElementType.FIELD
- ElementType.LOCAL_VARIABLE (局部变量)
- ElementType.METHOD
- ElementType.PACKAGE
- ElementType.RARAMETER (参数)
- ElementType.TYPE (任何类型,即上面的类型都可以修饰)
@Inherited注解
- 让一个类和它的子类都包括某个注解
- 普通注解没有继承功能
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("exmple.com.demo8.MySupperClass1");
Class c2 = Class.forName("exmple.com.demo8.MySubClass1");
System.out.println("使用了@Inherited: ");
System.out.println("父类: " + c1.getAnnotations().length);
System.out.println("子类:" + c2.getAnnotations().length);
System.out.println("没有使用@Inherited: ");
Class c3 = Class.forName("exmple.com.demo8.MySupperClass2");
Class c4 = Class.forName("exmple.com.demo8.MySubClass2");
System.out.println("父类:" + c3.getAnnotations().length);
System.out.println("子类:" + c4.getAnnotations().length);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface MyAnnotation1 {}
@MyAnnotation1
class MySupperClass1 {}
class MySubClass1 extends MySupperClass1 {}
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2 {}
@MyAnnotation2
class MySupperClass2 {}
class MySubClass2 extends MySupperClass2 {}
运行结果
使用了@Inherited:
父类: 1
子类:1
没有使用@Inherited:
父类:1
子类:0
可以看出使用@Inherited可以使子类包含父类的注解
@Repleatable注解
- 自JDK1.8引入
- 表示被修饰的注解可以重复应用标注
- 需要定义注解和容易注解
public class Demo4 {
public static void main(String[] args) throws ClassNotFoundException {
String className = "exmple.com.demo8.Student";
// 获取目标对象的所有方法
for (Method m : Class.forName(className).getMethods()) {
// 判断是否有容器注解
if (m.isAnnotationPresent(RepeatableAnnotations.class)) {
RepeatableAnnotation[] annos = m.getAnnotationsByType(RepeatableAnnotation.class);
for (RepeatableAnnotation anno : annos) {
System.out.println(anno.a() + "," + anno.b() + "," + anno.c());
try {
// 将获取到的值传递给方法形参
m.invoke(null, anno.a(), anno.b(), anno.c());
} catch (Throwable ex) {
System.out.printf("测试: %s 失败: %s %n", m, ex.getCause());
}
}
}
}
}
}
class Student {
@RepeatableAnnotation(a = 1, b = 2, c = 3)
@RepeatableAnnotation(a = 1, b = 2, c = 4)
public static void add(int a, int b, int c) {
if (c != a + b) {
throw new RuntimeException("add function error");
}
}
}
@Retention(RetentionPolicy.RUNTIME)
// 运行该注解可以重复标注
@Repeatable(RepeatableAnnotations.class)
@interface RepeatableAnnotation {
int a() default 0;
int b() default 0;
int c() default 0;
}
// 容器注解
@Retention(RetentionPolicy.RUNTIME)
@interface RepeatableAnnotations {
RepeatableAnnotation[] value();
}
运行结果
1,2,3
1,2,4
测试: public static void exmple.com.demo8.Student.add(int,int,int) 失败: java.lang.RuntimeException: add function error
@Documented注解
指明这个注解可以被JavaDoc工具解析,形成帮助文档