注解

注解

注解是一个接口

基本注解

  • @Override 指定方法覆载的,强制一个子类必须覆盖父类的方法。

如果负载的名字不对,则会报错:
Method does not override method from its superclass

  • @Deprecated:表示某个元素或者某个方法已经过时,当其他程序引用该注解注释的
    类或方法时,会展示编译告警'getXXX()' is deprecated

  • @SuppressWarnings:抑制编译告警

  • @SafeVarargs:

    public void test5() {
        List ints = new ArrayList<>();
        ints.add(20);
        List<String> ss = ints;
        System.out.println(ss.get(0));
    }

代码由于泛型擦除,会导致运行时异常。引发这种错误的原因为“堆污染”,档把一个不带泛型
的对象赋值给一个带泛型的变量是,往往会发生这种“堆污染”。

元注解

  • @Retention:只用于修饰注解定义,用于指定被修饰的注解可以保留多长时间,他包含一个RetentionPolicy类型的变量
    使用该注解的时候必须为变量赋值

    RetentionPolicy.CLASS 在class文件中 默认值 JVM无法获取注解信息
    RetentionPolicy.RUNTIME 在class文件中 JVM运行时也可以获取注解信息,可以通过烦死获取该注解信息
    RetentionPolicy.SOURCE 在源文件中,编译器直接丢弃这种注解

  • @Targer 只能修饰注解 用于指定被修饰的注解能用于修饰那些程序单元。

    ElementType.ANNOTATION_TYPE:指定该策略的注解只能修饰注解
    ElementType.CONSTRUCTOR: 修饰构造器
    ElementType.FIELD: 修饰成员变量
    ElementType.LOCAL_VARIABLE: 修饰局部变量
    ElementType.METHOD: 修饰方法
    ElementType.PACKAGE 修饰包定义
    ElementType.TYPE: 修饰类,接口或枚举定义

  • @Documented 用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。它是一个标记注解,没有成员。

  • @Inherited 用于表示某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

自定义注解

1 使用@interfacce关键字

public @interface Crystal {
}

注解还可以带成员变量

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

一旦注解定义了成员变量以后,在使用注解的时候,要为成员变量赋值

@Crystal(name = "car", age = 10)
public class Car {
    
}

修饰方法

public class Car {
    int wheel = 4;

    static int door = 4;
    @Crystal(name = "car", age = 10)
    String name() {
        return "Car";
    }

可以为注解指定初始值

public @interface Crystal {
    // 定义带成员变量的注解
    // 注解中的成员变量以方法的形式定义
    String name() default "crystal";
    int age() default 18;
}

可以根据注解是否包含成员变量,将其分为两类

  • 标记注解,没有成员变量,仅利用自身的存在与否提供信息
  • 元数据注解:包含成员变量

提取注解信息

使用注解注释类,方法,成员变量等成员后,这些注解不会自己生效,必须由开发者提供相应的工具来
提取并处理注解信息。
仅仅使用注解来标记程序元素,对程序是没有任何影响的,为了让注解起作用,我们要为注解提供一个
注解处理工具。
java5在java.lang.reflect包下新增了AnnotatedElement接口,该接口表示程序中可以
接受注解的程序元素。该接口主要有一下几个实现类

  • Class:类定义
  • Constructor:构造器定义
  • Field 类的成员变量定义
  • Method 类的方法定义
  • Package:包定义

反射包提供了取读运行时注解的能力,只有当定义注解时使用了
@Retention(RetentionPolicy.RUNTIME)修饰,该注解才会在运行时可见
JVM才会在装在.class文件时取读保存在class文件中注解信息

程序通过反射获取了某个类的AnnotatedElement对象(Class、Method、Constructor)之后,就可以
通过调用对象的如下几个方法来访问注解信息

<A extends Annotation> A getAnnotation(Class<A> annotationClass)
返回该程序元素上存在的、指定类型的注解。如果该类型的注解不存在,则返回null
<A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)
返回直接修饰该程序元素指定类型的注解。如果该类型的注解不存在,则返回null
Annotation[] getAnnotations()
返回该程序元素上所有注解
Annotation[] getDeclaredAnnotations()
返回直接修饰该程序元素上所有注解
boolean isAnnotationPresend(Class<? extends Annotation> annotationClass)
判定改程序元素上是否存在指定类型的注解,如果存在则返回true,否则返回false
<A extends Annotation>A[] getAnnotationByType(Class<A> annotationClass)
与getAnnotations()方法基本类似
<A extends Annotation>A[] getDeclaredAnnotationByType(Class<A> annotationClass)
与getDeclaredAnnotations()方法基本类似

例子

public class MyTest {
    public static void m1() {
    }

    // 使用@Testable注解表示该方法可测试
    @Testable
    public static void m2() {
    }

    public static void m3() {
    }

    @Testable
    public static void m4() {
        throw new IllegalArgumentException("参数异常");
    }

    public static void m5() {
    }

    @Testable
    public static void m6() {
    }

    public static void m7() {
    }

    @Testable
    public static void m8() {
        throw new RuntimeException("业务异常");
    }
}
public class ProcessorTest {
    public static void process(String clazz) throws ClassNotFoundException {
        int passed = 0;
        int failed = 0;
        // 遍历clazz类里面对应的所有方法
        for (Method m : Class.forName(clazz).getMethods()) {
            if (m.isAnnotationPresent(Testable.class)) {
                try {
                    m.invoke(null);
                    passed++;
                } catch (Exception e) {
                    System.out.println("方法" + m + "运行失败,异常:" + e.getMessage());
                    failed++;

                }
            }
        }
        System.out.println("共运行了" + (passed + failed) + "个方法,其中:\n" + "失败了:" + failed + "个,\n" + "成功了:" + passed + "个");
    }
}
public class RunTests {
    public static void main(String[] args) throws Exception {
        ProcessorTest.process("com.huawei.kemuer.MyTest");
    }
}

输出

方法public static void com.huawei.kemuer.MyTest.m4()运行失败,异常:null
方法public static void com.huawei.kemuer.MyTest.m8()运行失败,异常:null
共运行了4个方法,其中:
失败了:2个,
成功了:2个
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容