标签:dash 注解 private 创建对象 抽象方法 计算 imp cep 检测
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
简单来说,就是说明程序的。给计算机看的。这里要说一下注释:用文字描述程序的。给程序员看的。
代码分析:通过代码里标识的注解对代码进行分析【使用反射】
编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
检测被该注解标注的方法是否是继承自父类(接口)的
上例中,该类继承了父类Object类,重写了toString()方法,用@Override注解进行标示。而toString1()方法不是重写其继承的父类、超类或接口中的方法,用@Override注解进行标示,会出现编译错误(Method does not override method from its superclass)。
该注解标注的内容,表示已过时
创建了method1方法,不过后来发现有更好的方式,可以实现method1方法的功能,便创建了method2方法。可是为了method1方法还能使用,便用@Deprecated注解进行标示,标示该方法已经过时了,如果我们继续调用method1方法,如上图所示,方法中出现一条横线。
压制警告
表示在注释元素(以及注释元素中包含的所有程序元素)中应该抑制命名的编译器警告。 请注意,给定元素中抑制的一组警告是所有包含元素中抑制的警告的超集。 例如,如果您注释一个类来抑制一个警告并注释方法来抑制另一个警告,则两个警告将在该方法中被抑制。一般传递参数all —— @SuppressWarnings("all")
元注解 public @interface 注解名称{ 属性列表; }
定义一个注解:
public @interface MyAnnotation { /* 属性:接口中的抽象方法 */ public abstract String method(); }
注解本质上就是一个接口,MyAnnotation接口默认继承Annotation接口。接口中可以定义的成员方法是接口的属性。
public interface MyAnnotation extends java.lang.annotation.Annotation {}
public interface Annotation 接口,是所有注释类型扩展的公共接口。
接口中的抽象方法。
属性的返回值类型有下列取值
public enum Person {
P1, P2 }
public @interface MyAnnotation2 { }
定义了属性,在使用时需要给属性赋值
如:定义一个注解
在使用的时候:
如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
如:定义一个注解
在使用的时候:
1、使用默认值:"LeeHua"
2、不使用默认值:
如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
如:定义一个注解
使用的时候:可以省略value,也可以不省略value,如下
数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
如:定义一个注解
使用的时候,{}可以省略,也可以不省略:
简单来说,元注解就是描述注解的注解。如@Override
这里的@Target和@Retention就是元注解。
@Target // 描述注解能够作用的位置 @Retention // 描述注解被保留的阶段 @Documented // 描述注解是否被抽取到api文档中 @Inherited // 描述注解是否被子类继承
public @interface Target { /** * 返回注释类型可应用于的元素种类的数组。 */ ElementType[] value(); }
ElementType[] 是一个枚举数组:
/** * 这个枚举类型的常量提供了在Java程序中可能出现注释的句法位置的简单分类。 * 在java.lang.annotation.Target元注释中使用这些常量来指定写入给定类型的 * 注释的合法位置。 */ public enum ElementType { /** 类,接口(包括注释类型)或枚举声明 */ TYPE, /** 字段声明(包括枚举常数) */ FIELD, /** 方法声明 */ METHOD, /** 形式参数声明 */ PARAMETER, /** 构造函数声明 */ CONSTRUCTOR, /** 局部变量声明 */ LOCAL_VARIABLE, /** 注释类型声明 */ ANNOTATION_TYPE, /** 包声明 */ PACKAGE, /** * 类型参数声明 * * @since 1.8 */ TYPE_PARAMETER, /** * 使用类型 * * @since 1.8 */ TYPE_USE }
自定义一个注解:
import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target(ElementType.TYPE) // 表示MyAnnotation7注解只能作用于类上 public @interface MyAnnotation7 { }
该注解的使用:
在这里,MyAnnotation7注解作用在类上是可以的。不过,作用在方法上,或成员变量上、方法上、... ... ,是不可以的。
假如想要作用于成员变量上、方法上、... ... ,可以根据public enum ElementType { ... ... } 在调用元注解的注解上进行添参,从而实现更多的作用域,如:
import java.lang.annotation.ElementType; @Target(ElementType.TYPE, , ElementType.METHOD, ElementType.FIELD) // 表示MyAnnotation7注解能作用于类、方法、成员变量上 public @interface MyAnnotation7 { }
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { /** * @return 保留策略。 */ RetentionPolicy value(); }
RetentionPolicy[] 是一个枚举数组:
/** * 注释保留策略。 此枚举类型的常量描述了用于保留注释的各种策略。 它们 * 与@Retention元注释类型一起使用,以指定将保留注释多长时间。 */ public enum RetentionPolicy { /** 注释将被编译器丢弃。 */ SOURCE, /** 注释将由编译器记录在类文件中,但JVM不需要在运行时保留。 */ CLASS, /** * 注释将由编译器记录在类文件中,并由JVM在运行时保留,因此可以反射读取。 * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME)
// 当前被描述的注解(MyAnnotation8),会保留到class字节码文件中,并被JVM读取到 public @interface MyAnnotation8 { }
/** * 表示具有类型的注释默认情况下由javadoc和类似工具记录。 * 即:描述注解是否被抽取到api文档中 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
自定义一个注解:
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Documented;
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// 表示MyAnnotation7注解能作用于类、方法、成员变量上
@Retention(RetentionPolicy.RUNTIME)
// 当前被描述的注解(MyAnnotation8),会保留到class字节码文件中,并被JVM读取到
@Documented
// 描述注解会被抽取到api文档中 public @interface MyAnnotation9 { }
使用该注解:
@MyAnnotation9 public class Demo09Annotation { private String name = "LeeHua"; @MyAnnotation9 public void method() { } }
在终端,进行编译:
cd /Users/liyihua/IdeaProjects/Study/src/view/study/demo47/
javadoc Demo09Annotation.java
javadoc编译完成后,生成一大堆文件:
Google浏览器打开index-all.html文件,可以查看被抽取到API文档中的描述:
/** 描述注解是否被子类继承 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
自定义一个注解:
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.annotation.ElementType; import java.lang.annotation.Documented; import java.lang.annotation.Inherited; @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
// 表示MyAnnotation7注解能作用于类、方法、成员变量上
@Retention(RetentionPolicy.RUNTIME)
// 当前被描述的注解(MyAnnotation8),会保留到class字节码文件中,并被JVM读取到
@Documented
// 描述注解会被抽取到api文档中
@Inherited
// 描述注解是否被子类继承 public @interface MyAnnotation10 { }
父类使用这个注解:
@MyAnnotation10 public class Demo09Annotation { }
子类继承父类:
public class Demo10Annotation extends Demo09Annotation { }
在@Inherited注解描述的情况下,父类使用了@MyAnnotation10注解,子类继承父类,也会使用@MyAnnotation10注解。
写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现之前,定义Person.java、Student.java:
package view.study.demo48; public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name=‘" + name + ‘\‘‘ + ", age=" + age + ‘}‘; } public void personMethod() { System.out.println("我是Person中的方法!!!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
package view.study.demo48; public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{" + "name=‘" + name + ‘\‘‘ + ", age=" + age + ‘}‘; } public void studentMethod() { System.out.println("我是Student中的方法!!!"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
定义一个注解:
package view.study.demo48; 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) // 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到 public @interface pro { public abstract String className(); public abstract String methodName(); }
定义实现类:
package view.study.demo48; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * 写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法 * 利用:反射、注解实现 */ @pro(className = "view.study.demo48.Person", methodName = "personMethod") public class Demo01Reflection { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { // 1. 解析注解 // 1.1 获取Demo01Reflection类的字节码文件对象 Class<Demo01Reflection> drClass = Demo01Reflection.class; // 2. 获取上边的注解对象:@pro(className = "view.study.demo48.Person", methodName = "personMethod") pro annotation = drClass.getAnnotation(pro.class); // 3. 调用注解中定义的抽象方法,获取返回值 // 3.1 获取className String className = annotation.className(); // 3.2 获取methodName String methodName = annotation.methodName(); // 4. 加载该类(获取到的类)进内存 Class<?> aClass = Class.forName(className); // 5. 创建对象 Object object = aClass.newInstance(); // 6. 获取方法对象 Method method = aClass.getMethod(methodName); // 7. 执行方法 method.invoke(object); } }
第二步:
// 第二步其实就是在内存中生成了一个该注解接口的子类实现对象 public class proImpl implements pro { // 实现pro接口中的className()方法和methodName()方法 public String className() { return className; } public String methodName() { return methodName; } }
运行程序,控制台输出:
我是Person中的方法!!!
修改注解中的className、methodName:
@pro(className = "view.study.demo48.Student", methodName = "studentMethod")
再次运行程序,控制台输出:
我是Student中的方法!!!
利用反射、注解,写一个简单的测试框架,当主方法执行后,会自动加载被检测的所有方法,判断是否有异常,并记录到文件中。注解名称是@Check
被测试的方法如下:
package view.study.demo48; public class Calculator { /** 加法 */ @Check public int add(int a, int b) { return a + b; } /** 减法 */ @Check public int sub(int a, int b) { return a - b; } /** 乘法 */ @Check public int mul(int a, int b) { return a * b; } /** 除法 */ @Check public int div(int a, int b) { return a / b; } public void method() { System.out.println("永无bug!!!"); } }
自定义一个注解:
package view.study.demo48; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) // 当前被描述的注解,会保留到class字节码文件中,并被JVM读取到 @Target(ElementType.METHOD) // 注解能作用于方法 public @interface Check { }
主方法实现类:
package view.study.demo48; import java.io.IOException; import java.io.FileWriter; import java.io.BufferedWriter; import java.lang.reflect.Method; public class Demo01Calculator { public static void main(String[] args) throws IOException { // 1. 创建计算器对象 Calculator calculator = new Calculator(); // 2. 通过计算器对象,获取字节码文件 Class<? extends Calculator> aClass = calculator.getClass(); // 3. 通过字节码文件,获取calculator中所有的方法 Method[] methods = aClass.getDeclaredMethods(); // 4. 定义异常出现的次数、创建字符缓冲输入流 int count = 0; BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("bug.txt", true)); // 5. 判断方法上是否有"@Check"注解 for (Method method : methods) { if (method.isAnnotationPresent(Check.class)) { try { // 6. 如果方法上有"@Check"注解,那么执行该方法 method.invoke(calculator, 1, 0); } catch (Exception e) { // 7. 捕获异常,记录到文件中 count ++; bufferedWriter.newLine(); bufferedWriter.write("被测试的方法:" + method.getName() + ",该方法出现异常。"); bufferedWriter.newLine(); bufferedWriter.write("异常的名称:" + e.getCause().getClass().getSimpleName()); bufferedWriter.newLine(); bufferedWriter.write("异常的原因:" + e.getCause().getMessage()); bufferedWriter.newLine(); } } } bufferedWriter.write("本次测试一共出现了" + count + "次异常。"); bufferedWriter.newLine(); bufferedWriter.write("===================================="); // 8. 释放BufferedWriter资源 bufferedWriter.flush(); bufferedWriter.close(); } }
运行主方法,bug.txt文件中会写入一定的内容,内容如下:
被测试的方法:div,该方法出现异常。 异常的名称:ArithmeticException 异常的原因:/ by zero 本次测试一共出现了1次异常。 ====================================
分析:
要被检测的方法有四个,分别是:加法、减法、乘法、除法。
主方法中传入的参数a、b是:1和0
很明显,1除0,是无意义的,所以div方法出现异常,异常名称是:ArithmeticException
其他方法没有出现异常。所以出现的异常次数是:1
标签:dash 注解 private 创建对象 抽象方法 计算 imp cep 检测
原文地址:https://www.cnblogs.com/liyihua/p/12305369.html