前言
写java经常会遇见@ano这样的东西,这个就叫做注解,注解就像是一个标记,我们在一个类,一个方法或者一个成员变量上面写上注解时就
表示这个东西被这个注解标记了,假设有一个类Stu有public String name属性,我们在这个属性上写@ano("张三"),就表示name被
ano标记了,我们标记时还记录了一个参数,是"张三",那么我们只要再去写一个解析类,去分析Stu这个类的name属性是否被ano标记了,
如果被标记了,是否有参数,这样我们就能去通过注解去执行一些东西了,例如将参数作为name的默认值之类的操作,接下来就可以了解
下我们如何标记,如何判断是否被标记,标记是否存在参数。
1、注解和元注解
javac:编译class文件 java:运行文件 javadoc:生成文档 javap:class反编译为java文件
注解:标记java类、变量、方法等可通反射获取这些被标记的内容。java注解本身没有意义,需要对应的注解解释类去使用这些注解。
元注解:注解的注解,用于创建新的注解类时使用。
注解作用分类:
(1)编写文档:通过代码里标识的注解生成文档(javadoc命令)。如:@document
(2)代码分析:通过代码里标识的注解对代码进行分析。
(3)编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查。如:@override
常见注解:
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
常用元注解(可以比对下面的代码更容易理解如何使用):
@Retention(RetentionPoicy.xxx) - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。xxx的值有:
SOURCE:在源文件中有效(即源文件保留)
CLASS:在class文件中有效(即class保留)
RUNTIME:在运行时有效(即运行时保留)
@Documented - 标记这些注解是否包含在用户文档中。
@Target(ElementType.xxx) - 标记这个注解应该是哪种 Java 成员。xxx的值有:
CONSTRUCTOR:用于描述构造器
FIELD:用于描述域
LOCAL_VARIABLE:用于描述局部变量
METHOD:用于描述方法
PACKAGE:用于描述包
PARAMETER:用于描述参数
TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类),即如果一个类被标记了该注解,那么其子类也会继承该注解。
2、自定义注解
建立格式:
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface name()throw Exception{
public abstract int method1(); //在注解类中,这些抽象方法叫属性
public abstract String method2() default "张三";
}
//注解参数的可支持数据类型(即注解属性的返回值类型):
//所有基本数据类型(int,float,boolean,byte,double,char,long,short)
//String类型、Class类型、enum类型、Annotation类型
//以上所有类型的数组
本质为:
public interface name implements java.lang.annotation.Annotation(){
public abstract int method1(); //在注解类中,这些抽象方法叫属性
public abstract String method2() default "张三";
} //因此定义注解类实际是定义了一个接口,接口能做的,,注解类就能做。
3、注解的使用
假设现有注解ano接口,接口代码使用上文定义的格式,接口可适用于方法和类,有使用注解的stu类
@ano(method1 = 100,method2 = "com.org.stu")
class stu{
@ano(method1 = 12,method2 = "李四")
public void eat(){}
}
//从图可知为什么注解接口的抽象方法会被叫做属性,它可以像属性一样被赋值。
//赋值类型应和注解接口属性返回值一样,具体原因看下文解析注解。
/*如果注解接口只有一个属性,且这个属性名字叫做value,那么使用时可直接向注解赋值*/
/*例如@ano("李四")*/
4、解析注解
注解通过java反射使用:假设现有注解ano接口,接口代码沿用上文,有使用注解的stu类,stu类代码沿用上文。现有一针对stu设计的注解解释类analy
1. 获取stu类的Class对象,名字定为stuc。如何获取在反射有讲的三种获取方式。
2. 通过stuc.isAnnotationPresent(ano.class)判断该类上是否有@ano注解
3. 通过ano res = stuc.getAnnotation(ano.class) 获取在stu上的注解接口实例对象,能获取注解接口对象是因为此时在内存中系统已经自动生成注解接口的子类的实例对象。具体如下:
class ano子类 implements ano(){
public String method1(){
return 100;
}
public String method2(){
return "com.org.stu";
}
} //所有方法体都是将参数返回
4. 通过res.method1()或res.method2()可获取在stu类上注解@ano的配置参数从而做出一些
5. 一到四解析了在类上的注解,那么属性与方法上的注解如何使用?过程和解析类上的注解一样。Class对象有isAnnotationPresent(ano.class)和getAnnotation(ano.class)方法,Field对象和Method对象上同样具备,以此通过反射获取Field对象和Method对象后就能够知道哪些方法或属性被标记了,标记里的参数是什么。
6. 具体实现代码如下:
class analy{
Class stuc = Class.forName("stu");
public void analy_class(){
if(stuc.isAnnotationPresent(ano.class)){
ano res = stuc.getAnnotation(ano.class);
int a = res.method1();
String b = res.method2();
dosomeing......;
}
}
public void analy_method(){
Method eatt stuc.getMethod("eat");
if(eatt.isAnnotationPresent(ano.class)){
ano res = eatt.getAnnotation(ano.class);
int a = res.method1();
String b = res.method2();
dosomeing......;
}
}
}