博客 |
---|
Java-(高级) |
注释和注解概述
注解:注解就是符合一定格式的语法 @xxxx,说明程序的,给计算机看的。
注释:用文字描述程序的。给程序员看的
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
概念描述:
JDK1.5之后的新特性
说明程序的
使用注解:@注解名称
作用分类:
①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
jdk5提供的注解
@Override:告知编译器此方法是覆盖父类的
@Deprecated:标注过时
@SuppressWarnings:压制警告
自定义注解
格式:
元注解
public @interface 注解名称{
属性列表;
}
本质:注解本质上就是一个接口,该接口默认继承Annotation接口
自定义一个接口
public @interface MyAnno {
}
使用反编译工具查看
import java.lang.annotation.Annotation;
public interface MyAnno extends Annotation{
}
属性:接口中的抽象方法要满足
通过注解使用注解中的抽象方法,好像是给属性赋值,所以把抽象方法描述成属性。
1. 属性的返回值类型有下列取值
基本数据类型
String
枚举
注解
以上类型的数组
2. 定义了属性,在使用时需要给属性赋值
1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
- 定义注解属性代码演示
public @interface MyAnno {
//基本数据类型
int show();
//String
String show2();
//枚举
Person per();
//注解
MyAnno2 myAnno();
//以及上述类型的数组
String[] strs();
}
Mynno2注解给Mynno做属性
public @interface MyAnno2 {
}
Person给Mynno做枚举类类型
public enum Person {
p1,p2,p3
}
- 使用注解属性代码演示
@MyAnno(value = 1,per = Person.p1,anno = @MyAnno2,name = "李四",strs = {"aaa","bbb"})
public class Work {
}
自定义注解MyAoon
public @interface MyAnno {
//基本类型
//int show();
//如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
int value();
//枚举类型
Person per();
//注解类型
MyAnno2 anno();
//如果我不想给注解的该属性赋值,就是用default给一个默认值。
String name() default "张三";
//如果数组中只有一个值,可以省略大括号{}。
String[] strs();
}
Mynno2注解给Mynno做属性
public @interface MyAnno2 {
}
Person给Mynno做枚举类类型
public enum Person {
p1,p2,p3
}
元注解
元注解:用于描述注解的注解
@Target:描述注解能够作用的位置
ElementType取值:
TYPE:可以作用于类(接口)上
METHOD:可以作用于方法上
FIELD:可以作用于成员变量上
PARAMETER:可以作于方法参数上
CONSTRUCTOR:可以作用于构造方法上
LOCAL_VARIABLE:可以作用与成员变量上
@Retention:描述注解被保留的阶段
@Retention(RetentionPolicy.RUNTIME):注解在整个运行阶段都可见(自定义一般都是取该值)
@Retention(RetentionPolicy.CLASS):注解在字节码文件级别可见
@Retention(RetentionPolicy.SOURCE):注解在源码级别可见
@Documented:描述注解是否被抽取到api文档中(加上该注解,表示子注解可以在生成的文档中看到)
@Inherited:描述注解是否被子类继承(加上该注解,表是子注解标记的类的子类也可以继承子注解)
@Target:描述注解能够作用的位置
使用自定义注解
@MyAnno
public class Student {
//@MyAnno 该注解只能定义在类上,所以报错
public void getStudent(){}
}
自定义注解
@Target(ElementType.TYPE)//表示下面的注解只能定义在类上
public @interface MyAnno {
}
@Retention:描述注解被保留的阶段
SOURCE、CLASS、RUNTIME注解的保留阶段的图解。
@Documented:描述注解是否被抽取到api文档中
我自定义了两个注解,和一个实体类来测试
自定义MyAnno注解,加上@Documented注解,就可以在生成的API中查看到@MyAnno
@Target(ElementType.METHOD)
@Documented //表示下面的注解在生成API中可以看到
public @interface MyAnno {
}
自定义MyAnno2注解
@Target(ElementType.METHOD)
public @interface MyAnno2 {
}
实体类
public class Student {
@MyAnno
public void getStudent(){}
@MyAnno2
public void getStudent2(){}
}
上述三个方法一个文件中然后使用Javadoc 执行Student.java文件就可以看到生成的API文档
加上@Documented注解,就可以在生成的API中查看到@MyAnno
@Inherited:描述注解是否被子类继承
自定义注解
@Target(ElementType.TYPE)
@Inherited//加上该注解,表是子注解标记的类的子类也可以继承子注解
public @interface MyAnno {
}
子类Student
//因为子注解被@Inherited标记,就表示Person的注解@MyAnno被继承了过来
//就相当于@MyAnno
public class Student extends Person{
}
父类Person
@MyAnno
public class Person {
}
在程序使用(解析)注解:获取注解中定义的属性值
获取注解中定义的属性值测试
@MyAnno(className = "com.ginger.demo01.Student", methodName = "show")
public class MyAnnoTest {
@Test
public void testMyAnno() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取MyAnno注解对象
//其实就是在内存中生成了一个该注解接口的子类实现对象
/*public class MyAnnoImpl implements MyAnno{
public String className(){
return "cn.itcast.annotation.Demo1";
}
public String methodName(){
return "show";
}
}
*/
MyAnno myAnno = MyAnnoTest.class.getAnnotation(MyAnno.class);
//根据MyAnno注解对象获取类名和方法名称
String className = myAnno.className();
String methodName = myAnno.methodName();
//System.out.println(className);
//System.out.println(methodName);
//获取该类的字节码文件对象
Class c = Class.forName(className);
//通过无参创建学生对象
Constructor cons = c.getConstructor();
//创建对象
Object obj = cons.newInstance();
//获取方法对象
Method mehtod = c.getMethod(methodName);
//执行该方法
mehtod.invoke(obj);
}
}
自定义注解
@Target(ElementType.TYPE)//子注解只能作用在类上
@Retention(RetentionPolicy.RUNTIME)//注解在整个运行阶段都可见
public @interface MyAnno {
String className();
String methodName();
}
实体类
public class Student {
public void show(){
System.out.println("show....");
}
}
结果:
className:com.ginger.demo01.Student
methodName:show
show方法执行了.....
案例:简单的测试框架
写一个框架,就是在计算机类中的方法只要被@Check标记,被标记的方法就要被测试,并给出测试异常名称和异常原因,以及异常次数并记录bug.txt文件中。
代码演示
public class CalculatorTest {
@Test
public void testCalculator() throws Exception {
//通过字节码文件对象,获取该字节码文件对象中的所有的方法对象。
Class c1 = CalCulator.class;
Method[] methods = c1.getMethods();
//通过无参构造创建对象
Object obj = c1.getConstructor().newInstance();
//定义一个统计变量,用于统计异常次数
int number = 0;
BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
//遍历方法对象数组
for (Method method : methods) {
//判断方法上的注解是否是@Check
if (method.isAnnotationPresent(Check.class)) {
try {
//有@Check注解方法,就执行该方法
method.invoke(obj);//执行的每一个计算类中的方法,可能会抛出异常
} catch (Exception e) {
//System.out.println(e); reflect.InvocationTargetException 运行期异常
//System.out.println(e.getCause()); ArithmeticException: / by zero 编译器异常
/*
这里不是很懂,InvocationTargetException对象调用getCause()方法就变成ArithmeticException对象了
通过查看InvocationTargetException类的getCause()源码。
private Throwable target;
public Throwable getCause() {
return target;
}
public InvocationTargetException(Throwable target, String s) {
super(s, null);
this.target = target;
}
通过查看源码,知道返回的是一个Throwable对象,Throwable是Error和Exception父类,通过我自己分析
构造传来的对象应该就是实际真正出错的原因,即ArithmeticException。
*/
//捕获异常
//抛出异常后统计变量做++
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();
}
}
自定义注解
@Target(ElementType.METHOD)//注解只能作用在方法上
@Retention(RetentionPolicy.RUNTIME)//在整个运行阶段都可见
public @interface Check {
}
计算机类
/**
* 计算器类
*/
public class CalCulator {
//加法
@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("永无bug...");
}
}
结果:bug.txt文件内容
add 方法出现异常了
异常名称:NullPointerException
异常原因:null
------------------------
div 方法出现异常了
异常名称:ArithmeticException
异常原因:/ by zero
------------------------
本次测试一共出现 2 次异常