目录
小结:
注解概念以及抽取文档
概念:说明程序的,给计算机看的
1.5之后出现的新特性
作用:
1、编译检查
例如:@Override @FunctionalInterface
2、编写文档:通过代码里标识的注解生成文档(生成javadoc文档)
jdk文档就是从代码中的文档注解抽取生成的
idea可以这里面设置注释模板
注释模板还有很多,需要自己摸索
使用方法:输入/**后回车,快捷键可以自己设置
演示:
/**
* @author Littlehorsebrother
* @version 1.0
* @since 1.5
*/
public class Demo2 {
/**
*
* @param a
* @param b
* @return a+b
*/
public int add(int a,int b){
return a+b;
}
}
在cmd中使用javadoc命令进行文档抽取
javadoc 类名.java
然后生成这么一大坨
点击index.html
就会发现和jdk文档是一样的格式
3、代码分析:通过代码里标识的注解对代码进行分析(使用反射)
JDK中预定的一些注解
@Override
用于检测方法是否继承于父类
@Override
public String toString(){
return toString();
}
@Deprecated
该注解标注的内容,已过时
@Deprecated
public void show1(){
//有缺陷的旧版本
}
public void show2(){
//有缺陷的旧版本
}
public void showDemo(){
show1();
}
@SupperessWarning (一般传递参数all)
压制警告
使用前
使用后(写在类前面)
@SuppressWarnings("all")
public class Demo3 {
再无警告
自定义注解
格式:
元注解
public @interface 注解名称{
属性列表}
自己定义一个注解
public @interface MyAnnotation {
}
实验:
使用javac编译,字符为GBK
然后javap进行反编译
注解的本质:注解本质上就是一个接口,该接口默认继承Annotation接口
注解中的属性:接口中可以定义的抽象方法
要求:
1、属性的返回值类型只能是下面的几种
基本数值类型
String
枚举
注解
以上类型的数组
实验中定义的注解
2、定义了属性,在使用时需要给属性赋值
例如这里属性没有赋值报错了
给show1方法赋值后错误消失
1、若果定义属性时,使用default关键字给属性默认初始化值,使用注解时,可以不进行属性的赋值
这里的default 张三是将name默认赋值为张三
在使用时就可以不用给name赋值了
2、如果只有一个属性需要赋值且属性的名称是value,则value可以忽略,直接定义值就好了
一般属性名都取方便取的名称
3、数组赋值时,值使用{}包裹。如果数组中只有一个值,这{}省略
各个类型属性的赋值方式
元注解
元注解:用于描述注解的注解
@Target:描述注解能够作用的位置
通过查看jdk源码得知
Target的取值是一个value
ElementType是一个枚举
这是其中的取值
@Retention:描述注解被保留的阶段
@Document:描述注解是否被抽取到Api文档中
@Inherited:描述注解是否被子类继承
/*
* @Target:描述注解能够作用的位置
* 常用的有三种取值
* ElementType.METHOD作用于方法
* ElementType.FIELD作用于成员变量
* ElementType.TYPE作用于类
* @Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})//这么写表示以上三种都可以作用
* @Retention:描述注解被保留的阶段
* Retention注解有三个取值
SOURCE表示该注解不会保留到class字节码文件中
CLASS表示会保留到字节码文件,但是不会被JVM读到
RUNTIME表示会保留到class字节码文件且会被JVM读到
* @Document:描述注解是否被抽取到Api文档中,加上此注解后doc生成的api文档中,自定义注解会被加到方法上
* @Inherited:描述注解是否被子类继承,被拥有该元注解的注解作用的类,其子类也会默认被该注解作用
*/
/*
在程序中使用(解析)注解
案例:使用注解代替反射练习中的配置文件
@Pro(className = "注解.Method1",methodName = "show")
public class Practice {
public static void main(String[] args) throws Exception {
//1、解析注解
//1.1获取该类的字节码文件对象
Class<Practice> practiceClass = Practice.class;
//2、获取上边的注解
//注:里面还有个加s的方法是获取上边的所有注解
//这行代码其实就是在内存中生成了一个该注解接口的子类实现对象
/*相当于ProImpl实现类Pro注解,且生成了他的一个对象
public class ProImpl implements Pro{
public String className(){
return "注解.Method1"
}
Public String methodName(){
return "show"
}
}
*/
Pro an = practiceClass.getAnnotation(Pro.class);
//3、调用注解对象中定义的抽象方法,获取返回值
//那么调用方法也是调用上边生成的对象中的方法
String className = an.className();
String methodName = an.methodName();
System.out.println(className);//注解.Method1
System.out.println(methodName);//show
//后面的代码和反射的练习一样了,上面的代码代替了配置文件
//4、加载该类进内存
//加载了Method1类进内存
Class<?> cls = Class.forName(className);
//5、创建对象
//通过反射获取了Method1的构造方法
Constructor<?> constructor = cls.getConstructor();
Object obj = constructor.newInstance();
//6、获取方法对象
//获取到了Method1中的方法
Method method = cls.getMethod(methodName);
//7、执行方法
method.invoke(obj);
}
}
Pro注解:
/*
描述需要执行的类名,和方法名
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Pro {
String className();
String methodName();
}
Method1类
public class Method1 {
public void show(){
System.out.println("method1...show...");
}
}
案例检查bug的框架
Check注解
/*
用于检测代码bug
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}
被用于bug测试的类
/*用于测试的计算器类*/
@SuppressWarnings("all")
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...");
}
}
框架:
/*简单的测试框架
* 当主方法被执行后,会自动检测加了Check注解的所有方法,判断方法是否有异常并记录
* */
public class TestCheck {
public static void main(String[] args) throws IOException {
//1、创建对象
Calculator c=new Calculator();
//2、获取字节码文件对象
Class<? extends Calculator> cls = c.getClass();
//3、获取所有的方法
Method[] methods = cls.getMethods();
int number=0;//出现异常的次数
BufferedWriter bw=new BufferedWriter(new FileWriter("bug.txt"));
for (Method method : methods) {
//4、判断方法上是否有Check注解
//.isAnnotationPresent该方法用于判断有没有指定的注解被加上了
if (method.isAnnotationPresent(Check.class)) {
//5、有,执行
try {
method.invoke(c);
//6、捕获异常
} catch (Exception e) {
//记录到文件中
number++;
bw.write(method.getName()+"方法出异常了");
bw.newLine();
//e.getCause()表示异常的真实原因,通过获取字节码文件对象再使用getSimpleName获取简短名称
bw.write("异常的名称:"+e.getCause().getClass().getSimpleName());
bw.newLine();
bw.write("异常的原因:"+e.getCause().getMessage());
bw.newLine();
bw.write("本次一共出现"+number+"次异常");
bw.newLine();
bw.write("---------------------");
bw.newLine();
}
}
}
bw.flush();
bw.close();
}
}
小结:
1、以后大多数时候,我们会使用注解,而不是自定义注解
2、注解给谁用?
1、编译器
2、给解析程序用(例如查bug程序)
3、注解不是程序的一部分,理解为一个标签