侧边栏壁纸
博主头像
coydone博主等级

记录学习,分享生活的个人站点

  • 累计撰写 306 篇文章
  • 累计创建 51 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

注解

coydone
2021-03-10 / 0 评论 / 0 点赞 / 340 阅读 / 6,648 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-01,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

注解概述

注解也就是Annotation,也叫元数据(描述数据属性的信息),是代码里的特殊标志。这些标志可以在编译、类加载、运行时被读取,并执行相应的处理,以便于其他工具补充信息或者进行部署。它是一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

注解:说明程序。给计算机看的。

注释:用文字描述程序,给程序员看的。

作用分类

1、编写文档:通过代码里标识的元数据生成文档【生成doc文档】

2、代码分析:通过代码里标识的元数据对代码进行分析【使用反射】

3、编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】

使用分类

1、内建注解:JDK中预定义的一些注解。在JDK1.5版本的java.lang包下提供了3种标准的注解类型,分别是@Override、@Deprecated、@Suppress Warnings。

2、元注解:java.lang.annotation包下提供了4个元注解,它们用来修饰其它的注解定义。这4个元注解分别是@Target、@Retention、@Documented、@Inherited。

3、自定义注解:自己定义的注解。

编写文档

文档注释:

/**
 * 注解javadoc演示
 * @author coydone
 * @since 1.5
 * @version 1.0
 */
public class AnnoDemo{
    /**
     * 计算两数的和
     * @param a 整数
     * @param b 整数
     * @return  两数的和
     */
    public int add(int a,int b){
        return a+b;
    }
}

将代码保存在桌面的一个文件夹下,将格式选为记事本的默认编码。

在空白位置按shift键+鼠标右键,选择在此处打开Powershell窗口。

输入命令:javadoc+java文件名。

在生成的文件中打开index.html文件,可以看到如下结果

注解的语法

@Annotation(参数)

其中Annotation为注解类型,注解参数可以没有,也可以有一个或多个。如:

@Override
@SuppressWarnings(value="unused")
@MyTag(name="Jack",age=20)

内建注解

即JDK中预定义的一些注解

@Override:检测被该注解标注的方法是否是继承自父类(接口)的。

@Deprecated:该注解标注的内容,表示已过时。

@Suppress Warnings:压制警告。

  • 一般传递参数all,@SuppressWarnings(“all”),表示所有情况。

  • deprecation:使用了过时的程序元素。

  • unchecked:执行了未检查的转换。

  • unused:有程序元素未被使用。

  • fallthrough:switch程序块直接通往下一种情况而没有break。

  • path:在类路径、源文件路径等中有不存在的路径。

  • serial:在可序列化的类上缺少serialVersionUID定义。

  • finally:任何finally子句不能正常完成。

元注解

用于描述注解的注解。

@Target:描述注解能够作用的位置。它有唯一的value值作为成员变量。这个成员变量是java.lang.annotation.ElementType类型。

ElementType取值:

  • TYPE:可以作用于类上

  • METHOD:可以作用于方法上

  • FIELD:可以作用于成员变量上

  • CONSTRUCTOR:构造方法上

  • LOCAL_VARIABLE:局部变量上

  • PACKAGE:包上

@Target(value={ElementType.TYPE})
//表示该MyAnno注解只能作用于类上 
public @interface MyAnno{}

@Retention:描述注解被保留的阶段,自定义注解一般是RUNTIME。

Retention包含一个RetentionPolicy类型的value成员变量,其取值来自java.lang.annotation.RetentionPolicy的枚举类型值,有3个取值。CLASS、RUNTIME、SOURCE。

  • @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到。

  • @Retention(RetentionPolicy.CLASS):当前被描述的注解,会记录在class文件中,当运行java程序时,JVM会保留注解程序可以通过反射获取该注解。

  • @Retention(RetentionPolicy.SOURCE):编译器会直接丢弃被修饰的注解。

  • @Documented:描述注解是否被抽取到API文档中,它没有成员变量。

  • @Inherited:描述注解是否被子类继承。

自定义注解

注解原理

Java中@Override的源代码:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

格式:

//元注解
public  @interface  注解名{    
    属性列表;
}

属性:接口中的抽象方法。

要求:属性的返回值类型:基本数据类型、String、枚举、注解、以上类型的数组。

定义了属性,在使用时需要给属性赋值

  • 如果定义属性时使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。

  • 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。

  • 数组赋值时,值使用{}包裹,如果数组中只有一个值,则{}可以省略。

package com.coydone.annotation;
public @interface MyAnno {
    int age();
    String name() default "张三";
    int id();
}

package com.coydone.annotation;
@MyAnno(id = 2,age = 12)
public class AnnoDemo02 {
    @MyAnno(age=22,id=9)
    public void demo(){
        show1();
    }
}

将MyAnno.java文件反编译

本质:注解本质上就是接口,该接口默认继承Annotation接口。

public interface MyAnno extends java.lang.annotation.Annotation{}

自定义注解使用

在程序中使用(解析)注解:获取注解中定义的属性值。

1、获取注解定义位置的对象(Field,Method,Class)

2、获取指定的注解

3、调用注解中的抽象方法获取配置的属性值

案例1

我们将之前在Java反射中的案例(写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。)用注解改造一下:

不再通过代码获取配置文件的值,而是将配置文件的值写在注解中,通过代码获取注解里的值。

package com.coydone.annotation;
public class Demo01 {
    public void show(){
        System.out.println("demo1...show...");
    }
}

package com.coydone.annotation;
public class Demo02 {
    public void show(){
        System.out.println("demo2...show...");
    }
}

package com.coydone.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 描述需要执行的类名、方法名
 *
 * */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
    String className();
    String methodName();
}

package com.coydone.annotation;
import java.lang.reflect.Method;
@Pro(className = "com.coydone.annotation.Demo01",methodName = "show")
public class AnnoDemo03 {
    public static void main(String[] args) throws Exception {
        //1、解析注解
        //获取该类的字节码文件对象
        Class<AnnoDemo03> annoDemo03Class = AnnoDemo03.class;
        //获取上面的注解对象
        //其实就是在内存中生成了一个该注解接口的子类实现对象
        /*
            public class ProImpl implements Pro{
                public String className(){
                    return "com.coydone.annotation.Demo01";
                }
                public String methodName(){
                    return "show";
                }
            }
        */
        Pro anno = annoDemo03Class.getAnnotation(Pro.class);
        //调用注解对象中定义的抽象方法,获取返回值
        String className = anno.className();
        String methodName = anno.methodName();
        System.out.println(className);//com.coydone.annotation.Demo01
        System.out.println(methodName);//show

        //加载该类进内存
        Class aClass = Class.forName(className);
        //创建对象
        Object o = aClass.newInstance();

        //获取方法对象
        Method method = aClass.getMethod(methodName);

        //执行方法
        method.invoke(o);
    }
}

在框架中,注解的用处就是简化配置文件的操作,将配置文件中的信息用注解代替。注解可以理解为是程序的标签。

案例2:简单测试框架

自己写一个测试注解,用于测试方法。

  • 当主方法执行后,会自动执行被检测的所有方法。

  • 加了Check注解的方法,判断方法是否有异常,记录到文件中。

package com.coydone.anno;
/*
定义一个计算器类
*/
public class Calculator {
    //加法
    @Check
    public void add(){
        System.out.println("1+0="+(1+0));
    }
    //减法
    @Check
    public void sub(){
        System.out.println("1-0="+(1-0));
    }
    //乘法
    @Check
    public void mul(){
        System.out.println("1*0="+(1*0));
    }
    //除法
    @Check
    public void div(){
        System.out.println("1/0="+(1/0));
    }
    public void show(){
        System.out.println("永无bug...");
    }
}

package com.coydone.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//Check注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}

package com.coydone.anno;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * 简单的测试框架
 * 当主方法执行后,会自动执行被检测的所有方法
 * (加了Check注解的方法,判断方法是否有异常,记录到文件中)
 */
public class TestCheck {
    public static void main(String[] args) throws IOException {
        //1.创建计算器对象
        Calculator c = new Calculator();
        //2.获取字节码文件对象
        Class cls = c.getClass();
        //3.获取所有方法
        Method[] methods = cls.getMethods();
        int sum = 0; //出现异常的次数
        BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
        for (Method method : methods) {
            //4.判断方法上是否有Check注解
            if (method.isAnnotationPresent(Check.class)){
                //5.有,执行
                try {
                    method.invoke(c);
                } catch (Exception e) {
                    //6.捕获异常
                    //记录到文件中
                    sum++;
                    bw.write(method.getName()+"方法出异常了");
                    bw.newLine();
                    bw.write("异常的名称:"+e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因:"+e.getCause().getMessage());
                    bw.newLine();
                    bw.write("------------------");
                    bw.newLine();
                }
            }
        }
        bw.write("本次测试一共出现了"+sum+"次异常");
        bw.flush();
        bw.close();
    }
}

测试结果:

0

评论区