java注解(通俗易懂)
java注解
-
概念:说明程序的,是给计算机看的。
-
注释:用文字描述程序的,给程序员看的。
-
定义:注解(Annotation),也叫做元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次【因为其声明是public @interface xxx{}】。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面用来对这些元素进行说明、注释。
-
概念描述:
- JDK1.5以后的新特性
- 用来说明程序的
- 使用注解的格式:@注解名称
-
作用分类
-
编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
-
代码分析:通过代码里标识的注解对代码进行分析【使用反射】
-
编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【例:@Override】
-
-
JDK中预定义的一些注解
-
@Override:检测被该注解标注的方法是否是继承父类(接口)的
-
@Deprecated:该注解标注的内容,表示已经过时
-
@SuppressWarnings:压制警告
- 一般传递参数all : @SuppressWarnings(“all”)代表压制.java文件中所有警告信息
- idea中java文件警告展现方式:
-
package annocation;
@SuppressWarnings("all")
//压制所有警告,主要是右侧的黄色警告全部消失
public class AnnoDemo2 {
@Override
public String toString() {
return super.toString();
}
@Deprecated
public void show1(){
//有缺陷
}
public void show2(){
//替换show1方法
}
public void demo(){
show1();//虽然过时但是仍然可以使用
}
}
-
自定义注解
-
格式:
元注解
public @interface 注解名称{}
-
本质:本质上是一个接口,这个接口继承下面的Annocation接口,故接口中可以定义什么,注解中就可以定义什么。
public interface MyAnno extends java.lang.annotation.Annotation { }
-
将一个自定义注解编译成字节码文件(.class文件)。
javac MyAnno.java
-
将字节码文件反编译成.java文件。
javap MyAnno.class
-
-
属性:接口中的抽象方法
-
属性的返回值类型有下列取值:
- 基本数据类型
- String
- 枚举
- 注解
- 以上数据类型的数组
package annocation; public @interface MyAnno { int age(); String name() default "皮皮虾"; Person per(); MyAnno2 anno2(); String[] str(); }
-
定义了属性,在使用注解的时候要为注解中的属性赋值。
- 如果在定义属性的时候,使用default关键字给属性默认初始化值,则使用注解的时候,可以不进行属性的赋值。
- 如果只有一个属性的时候,并且属性的名称为value则value可以省了,直接定义值即可。
- 数组赋值的时候,值使用{}包裹,如果数组中的值只有一项则可以省了{}。
package annocation; @MyAnno(age = 12,name = "张三",per = Person.MAN,anno2 = @MyAnno2,str = {"abc","sdsd"}) public class Worker { }
-
-
元注解:用于描述注解的注解
-
@Target:描述注解能够作用的位置【常用】
- ElementType(@Target注解的枚举属性)取值
- TYPE:可以作用于类上
- METHOD:可以作用于方法上
- FIELD:可以作用于成员变量上
# 自定义注解@MyAnno3 package annocation; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(value = {ElementType.TYPE,ElementType.METHOD}) public @interface MyAnno3 { } #测试自定义注解 package annocation; @MyAnno3 public class Worker { @MyAnno3//下划线警告:因为该注解中未描述其可以作用于成员变量上 public String name = "aaa"; @MyAnno3 public void show(){ } }
- ElementType(@Target注解的枚举属性)取值
-
@Retention:描述注解被保留的阶段(java代码的三个阶段)【常用】
- RetentionPolicy(@Retention注解的枚举属性)取值
- **SOURCE:**当前被描述的注解不会保留到class字节码文件中
- **CLASS:**当前被描述的注解会被保留到class字节码文件中不会被JVM读取到
- RUNTIME:当前被描述的注解会被保留到class字节码文件中,并被JVM读取到【常用】
- RetentionPolicy(@Retention注解的枚举属性)取值
-
@Documented:描述注解是否被抽取到api文档中
# 该注解没有属性 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
将java文件生成为javadoc文档:由于Worker.java文件中的成员变量未带有自定义的注解,所以在javadoc文档中不会保留注解样式。
- @Inherited:描述注解是否被子类继承
-
-
-
在程序中使用(解析)注解:获取注解中定义的属性值
-
案例01:创建框架类,要求不能改变该类的任何代码,可以创建任意类的对象,可以执行任意方法。
- 获取注解定义位置的对象 (Class、Method、Field)
- 获取指定的注解
- getAnnotation(Class)
- 调用注解中抽象方法获取配置的属性值
# 框架类java文件 package annocation; import java.io.InputStream; import java.lang.reflect.Method; import java.sql.Ref; import java.util.Properties; /** * @ClassName ReflectUtil * @Description TODO 创建任意类的对象,可以执行任意方法 * @Author ROG * @Date 2019/10/24 16:00 * @Version 1.0 */ @Pro(ClassName = "annocation.Demo",MethodName = "test") public class ReflectUtil { public static void main(String[] args) throws Exception { // 1. 解析注解 // 1.1获取该类的字节码文件对象 Class<ReflectUtil> reflectUtilClass = ReflectUtil.class; // 2.获取上边Pro的注解对象,实际上就是在内存中生成了该注解接口的子类实现对象(类似于内部类的用法) Pro annotation = reflectUtilClass.getAnnotation(Pro.class); // 调用注解对象中定义的抽象方法,获得返回值 String className = annotation.ClassName(); String methodName = annotation.MethodName(); System.out.println(className + " " + methodName); // 根据全类名获得字节码文件对象 Class aClass = Class.forName(className); // 创建实际Runtime时对象 Object o = aClass.newInstance(); System.out.println(o); // 获取字节码文件对象中的Method对象 Method method = aClass.getMethod(methodName); // 执行方法 method.invoke(o); } } # 注解@Pro文件 package annocation; //描述需要执行的类名、方法名 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Pro { String ClassName(); String MethodName(); } # 实体类Demo.java文件 package annocation; public class Demo { public String name = "我是个好人"; @Override public String toString() { return "Demo{" + "name='" + name + '\'' + '}'; } public void test(){ System.out.println("我爱你"); } }
-
案例02:检测一个封装类中的部分方法是否出现异常,如果出现异常将这些异常信息记录到bug.txt文件中。
-
思路:
- 创建受检测封装类的对象。
- 获取封装类的字节码对象。
- 根据字节码对象获取封装对象的方法对象列表(使用反射)。
- 检测方法是否带有注解。
- 如果方法带有注解,执行方法。
- 如果方法执行之后出现异常,进行异常捕获,并把异常信息记录到bug.txt文件中。
-
代码:
# @Check注解 package annocation.demo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Check { } # 封装类 package annocation.demo; /** * @ClassName Calculateor * @Description TODO 自定义的计算器 * @Author ROG * @Date 2019/10/25 13:57 * @Version 1.0 */ public class Calculateor { @Check public void add(){ String str = null; str.toString(); 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("永不报错"); } } # 测试类 package annocation.demo; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @ClassName CheckTest * @Description TODO * @Author ROG * @Date 2019/10/25 14:02 * @Version 1.0 */ public class CheckTest { public static void main(String[] args) throws IOException { //1.创建Calcuator对象 Calculateor calculateor = new Calculateor(); //2.获取其字节码文件对象 Class calculateorClass = Calculateor.class; //3.获取其所有方法 Method[] methods = calculateorClass.getMethods(); int number = 0; BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt")); //4.对方法进行检测,查看方法是否被@Check注解修饰 for (Method method : methods) { //5.如果方法被注解修饰,执行 if(method.isAnnotationPresent(Check.class)){ try { method.invoke(calculateor); } catch (Exception e) { //6.方法报错,捕获异常,将异常信息打印在txt文件中 number ++; 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("本次测试一共出现" + number + "次异常"); bw.flush(); bw.close(); } } # bug.txt内容 add方法出现异常了 异常的名称是:NullPointerException 异常的原因是:null ------------------ div方法出现异常了 异常的名称是:ArithmeticException 异常的原因是:/ by zero ------------------ 本次测试一共出现2次异常
-
-
小结:
- 以后大多数的时候我们会使用注解,很少自定义注解。
- 注解给谁用?
- 给编译器用:编译器识别注解,检测编译看有没有问题,有问题就在代码中会有提示,一般为红色下划曲线。
- 给解析程序用(上述两个案例中的ReflectUtil.java 、CheckTest.java)
- 注解不是程序的一部分,可以理解为注解是一个标签,它不会影响程序代码的执行逻辑。
-
上一篇: 通俗易懂——Vuex教程
下一篇: mybatis****