您当前的位置: 首页 >  Java

Kevin-Dev

暂无认证

  • 0浏览

    0关注

    544博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Java -- 基础】深入探索 -- 注解(Annotation)

Kevin-Dev 发布时间:2018-09-26 20:18:23 ,浏览量:0

一、前言

在 Java 中,注解(Annotation)非常重要,但对于很多开发者来说,这并不容易理解,甚至觉得有点神秘。

二、类型

注解的类型包括:元注解 、 Java 内置注解 & 自定义注解。

1. 元注解

在这里插入图片描述

  • @Retention 定义:保留注解 作用:解释 / 说明了注解的生命周期 具体使用:
// 元注解@Retention(RetentionPolicy.RUNTIME)的作用:说明 注解Carson_Annotation的生命周期 = 保留到程序运行时 & 被加载到 JVM 中
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation {
}


// RetentionPolicy.RUNTIME:注解保留到程序运行时 & 会被加载进入到 JVM 中,所以在程序运行时可以获取到它们
// RetentionPolicy.CLASS:注解只被保留到编译进行时 & 不会被加载到 JVM 
// RetentionPolicy.SOURCE:注解只在源码阶段保留 & 在编译器进行编译时将被丢弃忽视。

  • @Documented 定义:Java文档注解 作用:将注解中的元素包含到 Javadoc 文档中 具体使用:
// 元注解@Documented作用:说明 注解Carson_Annotation的元素包含到 Javadoc 文档中
@Documented
public @interface Carson_Annotation {
}
  • @Target 定义:目标注解 作用:限定了注解作用的目标范围,包括类、方法等等 具体使用:
// 元注解@Target作用:限定了注解Carson_Annotation作用的目标范围 = 方法
// 即注解Carson_Annotation只能用于解释说明 某个方法
@Target(ElementType.METHOD)
public @interface Carson_Annotation {
}


// ElementType.PACKAGE:可以给一个包进行注解
// ElementType.ANNOTATION_TYPE:可以给一个注解进行注解
// ElementType.TYPE:可以给一个类型进行注解,如类、接口、枚举
// ElementType.CONSTRUCTOR:可以给构造方法进行注解
// ElementType.METHOD:可以给方法进行注解
// ElementType.PARAMETER 可以给一个方法内的参数进行注解
// ElementType.FIELD:可以给属性进行注解
// ElementType.LOCAL_VARIABLE:可以给局部变量进行注解
  • @Inherited 定义:继承注解 作用:使得一个被 @Inherited 注解的注解 作用的类的子类可以继承该类的注解 具体使用:
// 元注解@Inherited 作用于 注解Carson_Annotation
@Inherited
public @interface Carson_Annotation {
}


// 注解Carson_Annotation 作用于A类
@Carson_Annotation
public class A {
  }

// B类继承了A类,即B类 = A类的子类,且B类没被任何注解应用
// 那么B类继承了A类的注解 Carson_Annotation
public class B extends A {}
  • @Repeatable 定义:可重复注解 作用:使得作用的注解可以取多个值 具体使用:
// 1. 定义 容器注解 @ 职业
public @interface Job {
    Person[]  value();
}

// 定义:本身也是一个注解
// 作用:存放其它注解
// 具体使用:必须有一个 value 属性;类型 = 被 @Repeatable 注解的注解数组
// 如本例中,被 @Repeatable 作用 = @Person ,所以value属性 = Person []数组

// 2. 定义@Person 
// 3. 使用@Repeatable 注解 @Person
// 注:@Repeatable 括号中的类 = 容器注解
@Repeatable(Job.class)
public @interface Person{
    String role default "";
}

// 在使用@Person(被@Repeatable 注解 )时,可以取多个值来解释Java代码
// 下面注解表示:Carson类即是产品经理,又是程序猿
@Person(role="coder")
@Person(role="PM")
public class Carson{

}

注:一个注解可以被多个元注解作用

@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Carson_Annotation {
}
2. Java 内置的注解

在这里插入图片描述

  • @Deprecated 定义:过时注解 作用:标记已过时 & 被抛弃的元素(类、方法等) 具体使用:
// 用 注解@Deprecated 标记类中已过时的 方法Hello()
public class Buyer2   {

    @Deprecated
    public void Hello(){
        System.out.println("Hello 2015!");
    }
}

使用该类中被 @Deprecated 作用的方法时,IDE 会提示该方法已过时 / 抛弃 在这里插入图片描述

  • @Override 定义:复写注解 作用:标记该方法需要被子类复写 具体使用:该方法大家很熟悉了,此处不作过多描述

  • @SuppressWarnings 定义:阻止警告注解 作用:标记的元素会阻止编译器发出警告提醒 具体使用:

// 在括号内传入需要忽略警告的属性
@SuppressWarnings("deprecation")
public void test(){
    Buyer2 mBuyer2 = new Buyer2();
    mBuyer2.hello();
    // IDE将不会发出警告(即不会在hello()出现划线)
}
  • @SafeVarargs 定义:参数安全类型注解 作用:提醒开发者不要用参数做不安全的操作 & 阻止编译器产生 unchecked警告 具体使用:
// 以下是官方例子
// 虽然编译阶段不报错,但运行时会抛出 ClassCastException 异常
// 所以该注解只是作提示作用,但是实际上还是要开发者自己处理问题
@SafeVarargs // Not actually safe!
    static void m(List... stringLists) {
    Object[] array = stringLists;
    List tmpList = Arrays.asList(42);
    array[0] = tmpList; // Semantically invalid, but compiles without warnings
    String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
}
  • @FunctionalInterface 定义:函数式接口注解 作用:表示该接口 = 函数式接口 具体使用:
// 多线程开发中常用的 Runnable 就是一个典型的函数式接口(被 @FunctionalInterface 注解)
@FunctionalInterface
public interface Runnable {
   
    public abstract void run();
}


// 原因:函数式接口很容易转换为 Lambda 表达式
// 这是另外一个很大话题,此处不作过多讲解,感兴趣的同学可自行了解
3. 自定义注解

3.1. 定义

// 通过 @interface 关键字进行定义
// 形式类似于接口,区别在于多了一个 @ 符号

public @interface Kevin_Annotation {

}

// 上面的代码创建了一个名为 Kevin_Annotaion 的注解

3.2. 属性


// 注解的属性 在定义该注解本身时 进行定义
public @interface Kevin_Annotation {
// 注解的属性 = 成员变量
// 注解只有成员变量,没有方法

    // 注解@Kevin_Annotation中有2个属性:id 和 msg  
    int id();
    String msg() default "Hi" ;

    // 说明:
      // 注解的属性以 “无形参的方法” 形式来声明
      // 方法名 = 属性名
      // 方法返回值 = 属性类型 = 8 种基本数据类型 + 类、接口、注解及对应数组类型
      // 用 default 关键值指定 属性的默认值,如上面的msg的默认值 = ”Hi“
}


// 注解的属性在使用时进行赋值
// 注解属性的赋值方式 = 注解括号内以 “value=”xx” “ 形式;用 ”,“隔开多个属性

// 注解Kevin_Annotation 作用于A类
// 在作用 / 使用时对注解属性进行赋值
@Kevin_Annotation(id=1,msg="hello,i am Carson")
public class A {
  }

// 特别说明:若注解只有一个属性,则赋值时”value“可以省略

// 假设注解@Kevin_Annotation只有1个msg属性
// 赋值时直接赋值即可
@Kevin_Annotation("hello,i am Carson")
public class A {
  }

3.3. 应用

// 在类 / 成员变量 / 方法 定义前 加上 “@注解名” 就可以使用该注解
@Kevin_Annotation
public class Kevin {

    @Kevin_Annotation
    int a;
    
    @Kevin_Annotation
    public void bMethod(){}

}

// 即注解@Kevin_Annotation 用于标识解释 Kevin 类 / a变量 / b方法
三、实例

1. 需求:通过注解,检查一个类中的方法是否存在异常

2. 具体步骤

  • 步骤1:自定义测试注解
// 因为注解@Kevin_Test需要在程序运行时使用
// 所以必须采用元注解Retention(RetentionPolicy.RUNTIME)
@Retention(RetentionPolicy.RUNTIME)
public @interface Kevin_Test {

}
  • 步骤2:定义需要测试的类
public class Test {

    @Kevin_Test
    public void method1(){
        System.out.println("method1正常运行 = " + (1+1));
    }
    @Kevin_Test
    public void method2(){
        System.out.println("method2正常运行 = " + (2*3));
    }

    @Kevin_Test
    public void method3(){
        System.out.println("method3正常运行 = " + (2/0));
    }
    
}
  • 步骤3:采用注解 测试 类的方法 是否存在 Bug
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1. 获取测试类Test对象
        Test mTest = new Test();
        Class mTest_Class = mTest.getClass();

        // 2. 获取测试类Test的所有方法(通过数组存放)
        Method[] method = mTest_Class.getDeclaredMethods();
        
        // 3. 通过注解@Kevin_Test 测试类中的方法
        
        // a. 遍历类中所有方法
        for ( Method m: method ) {
            // b. 只有被 @Kevin_Test 标注过的方法才允许进行测试
            if ( m.isAnnotationPresent( Kevin_Test.class )) {
                try {
                    m.setAccessible(true);
                    // c. 通过反射调用测试类中的方法
                    m.invoke(mTest);
                    // d. 捕捉方法出现的异常 & 输出异常信息
                } catch (Exception e) {
                    System.out.println( "Test类出现Bug了!!!");
                    System.out.println( "出现Bug的方法:" + m.getName());
                    System.out.println( "Bug类型:" + e.getCause().getClass().getSimpleName());
                    System.out.println( "Bug信息:" + e.getCause().getMessage());
                }
            }
        }
}

测试结果: 在这里插入图片描述

关注
打赏
1658837700
查看更多评论
立即登录/注册

微信扫码登录

0.1446s