下面来说一下注解形式:
既然注解是接口,那么注解里面就有抽象方法,也就是我们常说的属性,那接口里面的方法能返回些啥啊?好奇不?
基本数据类型?ok
String类型?ok
枚举也可以?ok
注解?没想到吧,注解里面还可以返回注解,没想到吧,兄弟
还有一种类型void?这个可不行,没有具体定义
总结一下:
那么下面我先来说一下预定义注解:
预定义注解:就是jdk已经给我们设计好的注解接口,都有啥?
如下,也就是我们写程序常常见到的一些东西:
上面就是判断是不是重载的父类的方法,是不是过时,还可以屏蔽无端的警告信息
下面说一下元注解:
定义注解的注解:
这玩意,就是告诉编译器,你这个自定义注解可以在什么地方用,可以保留到程序什么阶段
大致就是上面这些东西,给注解用的,随便看一下,点进去它的源程序看一下:
里面是一个变量名为value的数组,这个返回类型也是给我们设计好的,但是你要传入的是啥值不是也是设计好的嘛,我们只需要去调用设计好的属性就ok.这里还需要说明一点就是,属性名如果是value,可以省略不写
下面重点说说,注解的用途:
这玩意,首先一看,我们用系统给我们设计的用的倒是挺好哈,所以,首先我们是要会用,就是会用别人给我们设计的,而不是我们自己设计的。
完了之后,我们可以用注解来替换我们的配置文件,这样倒是也常用,方便嘛,你想啊,配置文件也是属性值,注解也是属性,值,而配置文件我们每次去引用是不是挺费劲,又要找到Class对象,又要得到资源位置,再加载到Properties对象中
之前做反射的时候,我想利用配置文件中的全类名,与方法名,来构造一个对象,然后执行一个方法,我现在不用配置文件了,用注解来做,于是我设计一个Pro注解类:
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)//保留到字节码文件中,并被jvm读取到
public @interface Pro {
String className();
String methodName();
}
然后在看下一下主体代码:
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
@Pro(className="domain.Person",methodName = "eat")
public class ReflectTest {
public static void main(String[] args) throws IOException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
//主要是去找到注解里面的属性内容
//获取该类的字节码对象
Class<ReflectTest> reflectTestClass = ReflectTest.class;
//获取注解对象,内部是传入一个注解的字节码文件类
Pro an = reflectTestClass.getAnnotation(Pro.class);
//调用注解对象中抽象方法,获取返回值
String className = an.className();
String methodName = an.methodName();
//先得到对象的字节码类文件
Class obj = Class.forName(className);
//创建对象去调用方法
Constructor cons = obj.getConstructor();
//然后实例化一个对象
Object obj1 = cons.newInstance();
//获取方法
Method method = obj.getMethod("eat",String.class);
method.invoke(obj1,"pingguo");
}
}
上面调用了Class类中下面的方法:
这里简单来说一下,为什么,Class类,Field类,Method类实现的对象都可以调用getAnnotation()方法,直接上图:
很明显就是他们都实现了AnnotatedElement这个接口 ,而这里面有一个得到注解类对象的方法,传入注解类的字节码对象就行
最后,我们可以用注解来标注成员方法,然后根据是否有注解信息,来验证程序数据是否合理
话不说直接看代码:
一个计算器工具类:
package demo;
/**
* 计算器类
*/
public class Calculator {
//加法
@Check
public void add(){
String str = null;
int len = str.length();
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("no bug...");
}
}
写一个注解类:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
上面这个注解类注意它的保留位置,如果只是在源代码阶段,那么加载到内存我们就获取不了,所以让它停留的时间尽可能长一点啊。
下面看我们测试代码:
package demo;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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();
for(Method method : methods) {
System.out.println(method);
}
int number = 0;//出现异常的次数
BufferedWriter bw = new BufferedWriter(new FileWriter("bug2.txt"));
for (Method method : methods) {
Check check = method.getAnnotation(Check.class);
//4.判断方法上是否有Check注解
if(check != null){
System.out.println("进来了几次");
try {
method.invoke(c);
//这个位置你知道出现什么异常?其实不知道,也就是会出现多种可能的异常
//不知道啥异常,我用Exception来捕获
} catch (Exception e) {
//把这个异常信息写入一个日志文件
number++;
//真实的异常类名(带包)getCause()会得到一个真实的异常对象(当我们不知道会出现什么异常的时候可以使用)
bw.write("异常类名:" + e.getCause().getClass().getName());
bw.newLine();
bw.write("异常原因:" + e.getCause().getMessage());
bw.newLine();
bw.write("------------");
bw.newLine();
}
} else {
System.out.println("方法没有注解");
}
}
bw.write("本次测试一共出现 "+number+" 次异常");
bw.flush();
bw.close();
}
}
多说两句,如果是这种方式判断是否注解,这种方法要学会去找:
下面看一下这个异常问题: