注解概述
注解也就是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();
}
}
测试结果:
评论区