注解

注解类型

1.@Retention(RetentionPolicy.RUNTIME)元注解表明,Test注解应该在运行时保留。如果没有保留,测试工具就无法知道Test注解。public enum RetentionPolicy,此枚举类型的常量描述了用于保留注释的各种策略。它们与Retention元注释类型结合使用,以指定要保留注释的时间。

枚举常量和描述
CLASS注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。这是默认行为。
RUNTIME注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。因此可以反射性地读取它们。
SOURCE注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。

2.@Target(ElementType.METHOD)元注解表明,Test注解只在方法声明中才合法:它不能运用到类声明、域声明或者其他程序元素上。

枚举常量和描述
ANNOTATION_TYPE可以给一个注解进行注解
CONSTRUCTOR构造函数注解
FIELD字段属性注解(包括枚举常量)
LOCAL_VARIABLE局部变量注解
METHOD方法注解
PACKAGE可以给一个包进行注解
PARAMETER正式参数注解
TYPE类,接口(包括注释类型)或枚举注解
TYPE_PARAMETER可以给一个方法内的参数进行注解
TYPE_USE

使用一种类型

3.@Documented顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。

4.@Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。 写个例子讲解一下:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}

@Test
public class A {}

public class B extends A {}

可以这么理解,注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。

5.@Repeatable 自然是可重复的意思。@Repeatable 是 Java 1.8 才加进来的,所以算是一个新的特性。什么时候注解会多次使用呢?这里我们看一个列子,一个人他既是程序员又是产品经理,同时他还是个画家。

@interface Persons {
    Person[]  value();
}


@Repeatable(Persons.class)
@interface Person{
    String role default "";
}


@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan{

}

注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。我们再看看代码中的相关容器注解。

@interface Persons {
    Person[]  value();
}

按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。如果不好理解的话,可以这样理解。Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。我们可能对于 @Person(role=”PM”) 括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM ,大家不明白正常,马上就讲到注解的属性这一块。

6.@Native,指示可以从本机代码引用定义常量值的字段。注释可以用作生成本机头文件的工具的提示,以确定是否需要头文件,如果需要,它应该包含哪些声明。

注解的属性

1.注解的属性也叫做成员变量。注解只有成员变量,没有方法。注解的成员变量在注解的定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。

2.需要注意的是,在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组。

3.注解中属性可以有默认值,默认值需要用 default 关键值指定

下面来看一个列子:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Test {
    public int age() default 25;
    public String name() default "zhangsan";
}

Test 中 age 属性默认值为 25,name 属性默认值为 zhangsan,所以在使用注释的时候可以省略掉括号的值。

@Test()
public class Sample {}

java预置的注解

1.@Deprecated这个元素是用来标记过时的元素,想必大家在日常开发中经常碰到。编译器在编译阶段遇到这个注解时会发出提醒警告,告诉开发者正在调用一个过时的元素比如过时的方法、过时的类、过时的成员变量。

2.@Override这个大家应该很熟悉了,提示子类要复写父类中被 @Override 修饰的方法

3.@SuppressWarnings阻止警告的意思。之前说过调用被 @Deprecated 注解的方法后,编译器会警告提醒,而有时候开发者会忽略这种警告,他们可以在调用的地方通过 @SuppressWarnings 达到目的。

4.@SafeVarargs参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告。它是在 Java 1.7 的版本中加入的。

5.@FunctionalInterface函数式接口注解,这个是 Java 1.8 版本引入的新特性。函数式编程很火,所以 Java 8 也及时添加了这个特性。想了解更多的同学可以自行查找资料。

为了更好的理解注解,首先来看一个注解的例子:

/**
 * 只用于无参的静态方法
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}

Test注解类型的声明就是它自身通过Retention和Target注解进行了注解。注解类型声明中的这种注解被称为元注解(meta-annotation)。

上面例子中的这个注解只能用于无参的静态方法。如果编译器能够强制这一限制最好,但是它做不到。编译器可以替你完成多少错误检查,这个是有限制的,即使利用注解。下面就是现实应用中的Test注解,称作标记注解,因为它没有参数,只是“标注”被注解的元素。

public class Sample {
    @Test public static void m1(){};
    public static void m2(){};
    @Test public  static void m3(){
        throw new RuntimeException("Boom");
    };
    public static void m4(){};
    @Test public void m5(){};
    public static void m6(){};
    @Test public static void m7(){
        throw new RuntimeException("crash");
    };
    public static void m8(){};
}

Test注解对Sample类的语义没有直接的影响。他们只负责提供信息供相关的程序使用。更一般来讲,注解永远不会改变被注解代码的语义。下面来看一下简单的测试运行类:

public class RunTests {
    public static void main(String[] args) throws ClassNotFoundException {
        int test = 0;
        int passed = 0;
        Class<?> aClass = Class.forName("cn.modesty.effective.annotation.Sample");
        for (Method m : aClass.getDeclaredMethods()) {
            if (m.isAnnotationPresent(Test.class)){
                test++;
                try {
                    m.invoke(null);
                    passed++;
                }catch (InvocationTargetException wrappedExc){
                    Throwable exc = wrappedExc.getCause();
                    System.out.println(m+" failed "+exc);
                }catch (Exception exc){
                    System.out.println("INVALID @Test:"+ m);
                }
            }
        }
        System.out.printf("passed : %d,Failed :%d%n",passed,test -passed);
    }
}

来看一下打印的结果:

public static void cn.modesty.effective.annotation.Sample.m7() failed java.lang.RuntimeException: crash
public static void cn.modesty.effective.annotation.Sample.m3() failed java.lang.RuntimeException: Boom
INVALID @Test:public void cn.modesty.effective.annotation.Sample.m5()
passed : 1,Failed :3

Method.invoke反射式地运行类中所有标注了Test的方法。isAnnotationPresent方法告知该工具要运行哪些方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值