一、注解介绍
注解(Annotation)是JAVA5 引入的一种代码辅助工具,其核心作用是对类、方法、变量、参数和包进行标注,通过反射来访问这些标注信息,以此在运行时改变所注解对象的行为。JAVA中的注解由内置注解和元注解组成
1. 注解与注释
- JAVA注解又称为JAVA标注,是JAVA语言5.0版本开始支持加入源代码的特殊语法元数据。
- 普通的注释在编译后的class文件中是不存在的,而注解附加的信息则根据需要可以保存到class文件中,甚至运行期加载的Class对象中。
2. 元注解介绍
- 创建注解:
public @interface [AnnotationName] {}
- 元注解(描述注解的一种方式)
- @Retention 定义注解的声明周期,主要有三种
- source
- class
- runtime (常用)
- @documented 文档注解,会被Javadoc工具文档化
- @Inherited 是否让子类继承该注解
- @Traget 描述了注解的应用范围,其可选的内容如以下所示:
- TYPE:表示可以用来修饰类、接口、注解类型或枚举类型;
- PACKAGE:可以用来修饰包;
- PARAMETER:可以用来修饰参数;
- ANNOTATION_TYPE:可以用来修饰注解类型;
- METHOD:可以用来修饰属性;
- FIELD:可以用来修饰属性(包括枚举常量);
- CONSTRUCTOR:可以用来修饰构造器;
- LOCAL_VARIABLE:可以用来修饰局部变量;
- 创建和使用注解的一个简单例子
- 注解的创建方式:
1. 配置元注解,由元注解来声明当前注解的作为范围和声明周期。
2. 注解中如果需要添加信息,可以用以上方式添加。
3. 注解信息支持java的基本数据结构。
- 创建注解 @Study
package cn.lazyfennec.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.TYPE}) // 元注解,定义注解的修饰范围,可以设置多个
@Retention(RetentionPolicy.RUNTIME) // 元注解,定义注解的声明周期
public @interface Study { // 注解内容可以设置值,也可以不设置值
// 其中的属性是支持JAVA的八大属性的 byte、short、int、long、float、double、boolean、char
// 如果属性为value,那么使用时,赋值可以不写 "value ="
String name() default "Neco Deng"; // 表示定义了一个name属性,并且设置了默认值为Neco Deng
String[] mores(); // 表示定义了一个名字为mores的字符串数组属性,并且没有默认值,即该属性需要显示定义
}
- 在一个类上使用注解 Person
package cn.lazyfennec.model;
import cn.lazyfennec.annotation.Study;
@Study(mores = {"first", "second"}) // 在类上使用注解,这里必须定义mores, 不然会报错
public class Person {
private int id;
@Study(mores = {"first", "second"}) // 在属性上使用注解,这里必须定义mores, 不然会报错
private String name;
}
二、反射
反射(Reflection):在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射。
1. 反射的优缺点
- 通过反射可以使程序代码访问装载到JVM中的类的内部信息,获取已装载类的属性信息,获取已装载类的方法,获取已装载类的构造方法信息。
- 反射提供了JAVA程序的灵活性和扩展性,降低耦合性,提高自适应能力。
- 反射会对性能造成一定的影响,同时让代码的可读性变低。
2.常用的反射API
方法名 | 返回值 | 参数描述 |
---|---|---|
Class.forName(String) | 获取类的元信息 | 当前类文件的具体位置 |
clazz.getClass() | 获取类的元信息 | 无 |
clazz.getDeclaredFields() | 获取当前类中的所有属性 | 无 |
setAccessible(true) | 设置当前属性为可见 | true或false |
getMethods() | 获取类所有方法 | 无 |
invoke(obj) | 通过反射执行方法 | 类的元信息 |
getAnnotation(class) | 获取注解 | 需要获取到额注解的Class |
- 更多方法和内容,请查看官网
3. 一个涵盖很多示例的例子
package cn.lazyfennec.reflection;
import cn.lazyfennec.annotation.Study;
import cn.lazyfennec.model.Person;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException,
InstantiationException, NoSuchMethodException, InvocationTargetException {
// 实例1:通过反射获取到Class元信息
Person person = new Person();
Class<? extends Person> aClass1 = person.getClass(); // 通过getClass获取元信息
Class<?> aClass2 = Class.forName("cn.lazyfennec.model.Person"); // 通过forName获取元信息
// 实例2:通过反射获取类名,包名
String name = aClass1.getName(); // 全路径类名 > cn.lazyfennec.model.Person
String simpleName = aClass1.getSimpleName(); // 不包含路径 > Person
Package aPackage = aClass1.getPackage(); // 包名 > package cn.lazyfennec.model
System.out.println(name);
System.out.println(simpleName);
System.out.println(aPackage);
System.out.println("===============================");
// 实例3:获取类属性
Field[] declaredFields = aClass1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
// 实例4:获取类属性的具体的值
person.setId(1);
person.setName("Neco");
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true); // 设置属性可见可访问,处理私有属性无法访问的问题
System.out.println(declaredField.get(person));
}
// 实例4的另一种写法,只是简单的写法,可以进行优化
Object obj = aClass1.newInstance(); // 实例化一个新的对象, 相当于反射中的实例化
declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
if (declaredField.getName().equals("name")) {
declaredField.set(obj, "Neco");
} else {
declaredField.set(obj, 1);
}
System.out.println(declaredField.get(obj));
}
// 实例5:反射获取当前类的方法
Method[] methods = aClass1.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
Method method = aClass1.getMethod("getString");
Object invoke = method.invoke(obj);
System.out.println(invoke);
// 实例6:反射获得注解
Study study = aClass1.getAnnotation(Study.class); // 从类中获取注解
System.out.println(study);
String[] mores = study.mores();
String name1 = study.name();
System.out.println("name: " + name1 + " mores: " + mores);
// 从方法上获取注解
methods = aClass1.getDeclaredMethods();
for (Method method1 : methods) {
Study annotation = method1.getAnnotation(Study.class);
if (annotation == null) continue;
String name2 = annotation.name();
String[] mores1 = annotation.mores();
System.out.println("name: " + name2 + " mores: " + mores1);
}
// 从属性上获取注解
declaredFields = aClass1.getDeclaredFields();
for (Field declaredField : declaredFields) {
Study annotation = declaredField.getAnnotation(Study.class);
if (annotation == null) continue;
String annotationName = annotation.name();
String[] annotationMores = annotation.mores();
System.out.println("name: " + annotationName + " mores: " + annotationMores);
}
}
}