注解和反射
注解
什么是注解
这里你可以描述知识空间建设的目标和意义,有利于团队成员理解目标并朝着目标努力。
如何自定义一个注解
这里你可以介绍知识空间的主要内容和面向的成员群体。
什么是元注解
对注解类进行注解的注解类
自定义注解时需要用到的两个元注解
@Target 限制可以应用该注解的元素类型
@Rentention 限制注解的存储方式
使用注解时传递参数
注解的应用场景
按照@Rentention限制的三种存储方式,有三种应用场景
RetentionPolicy.SOURCE
作用于源码级别的注解,可以用于语法检查、APT注解处理器等场景。
RetentionPolicy.CLASS
作用于AspectJ、热修复Roubust等场景
RetentionPolicy.RUNTIME
结合反射使用
反射
反射定义
反射是在运行时而非编译时,才知道要操作的类是什么,动态的获取类型的信息,比如接口信息、成员信息、方法信息、构造方法等,根据这些信息去创建对象、访问方法、访问或者修改成员变量。
Class类
每个已加载的类在内部都有一份类信息,每个对象都有指向他所属的类信息的引用。类信息对应的类就是java.lang.Class。Class是一个泛型类。
可通过三种途径获取Class对象。
-
通过类名获取
Class<Date> cls = Date.getClass;
-
通过类名全路径直接加载获取
Class<?> cls = Class.forName("java.util.HashMap");
-
通过对象获取,getClass并不知道具体的类型, 所以返回的是Class<?>
Date date = new Date();
Class<?> clz = date.getClass();
使用Class对象操作字段信息
-
获取所有的public字段,包括父类的
getFields()
-
获取本类中所有的字段,包括非public的,但不包括父类的
getDeclaredFields()
-
获取本类或者父类中指定名称的public字段
getField("字段名称")
-
获取本类中声明的指定名臣的字段,包括非public的
getDeclaredField("字段名称")
-
获取字段的名称
field.getName()
-
获取字段的值
field.get(对象)
-
修改public字段的值
field.set(对象名称,新的值)
-
修改非public字段的值 需要设置
field.setAccessible(true)
public class Student {
private String name;
public String location;
public int age;
public Student(String name,String location,int age){
this.name = name;
this.location = location;
this.age = age;
}
private void run(){
System.out.println("this ist the private run method");
}
public String getName() {
System.out.println("this ist the public getName method");
return name;
}
public void setName(String name) {
System.out.println("this ist the public getName method");
this.name = name;
}
public String getLocation() {
System.out.println("this ist the public getLocation method");
return location;
}
public void setLocation(String location) {
System.out.println("this ist the public setLocation method. location = " + location);
this.location = location;
}
public int getAge() {
System.out.println("this ist the public getAge method");
return age;
}
public void setAge(int age) {
System.out.println("this ist the public setAge method");
this.age = age;
}
}
private static void getField() throws IllegalAccessException, InstantiationException {
Class<Student> clz = Student.class;
Student student = (Student) clz.newInstance();
Field[] fields = clz.getFields();
System.out.println("获取所有的public的字段,包括父类的");
for(Field field : fields){
System.out.println(field.getName());
}
System.out.println("*********************************************");
System.out.println("获取本类中所有的的字段,包括非public的,但不包括父类的");
fields = clz.getDeclaredFields();
for(Field field : fields){
System.out.println(field.getName());
}
System.out.println("*********************************************");
System.out.println("获取本类或者父类中指定名称的public字段,");
Field field = null;
try {
field = clz.getField("age");
System.out.println("字段名称 : " + field.getName());
System.out.println("获取字段的值");
System.out.println(field.get(student));
System.out.println("修改字段的值");
field.set(student,43565);
System.out.println(field.get(student));
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("*********************************************");
System.out.println("获取本类中声明的指定名称的字段");
try {
field = clz.getDeclaredField("name");
field.setAccessible(true);
System.out.println("字段名称 : " + field.getName());
System.out.println("修改私有字段的值");
field.set(student,"修改成功");
System.out.println(field.get(student));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
输出为:
获取所有的public的字段,包括父类的
location
age
*********************************************
获取本类中所有的的字段,包括非public的,但不包括父类的
name
location
age
*********************************************
获取本类或者父类中指定名称的public字段,
字段名称 : age
获取字段的值
0
修改字段的值
43565
*********************************************
获取本类中声明的指定名称的字段
字段名称 : name
修改私有字段的值
修改成功
使用Class对象操作方法
-
获取本类和父类中所有的public方法
getMethods()
-
获取本类中声明的所有方法,包括非public的,但不包括父类的
getDeclaredMethods()
-
获取本类和父类中指定名称的public方法
getMethod("setLocation",String.class)
String.class表示入参为String类型 -
获取本类中已声明的指定名称的方法,包括非public的,但是不包括父类的
getDeclaredMethod("run")
-
public方法的调用
method1.invoke(对象,这是入参)
-
非public方法的调用 需要执行
method1.setAccessible(true)
Class<Student> clz = Student.class;
Student student = null;
try {
student = (Student) clz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("************************");
System.out.println("返回所有的public方法,包括父类的");
Method[] methods = clz.getMethods();
for(Method method:methods){
System.out.println(method.getName());
}
System.out.println("************************");
System.out.println("返回本类中声明的所有方法,包括非public的,但不包括父类的");
methods = clz.getDeclaredMethods();
for(Method method:methods){
System.out.println(method.getName());
}
System.out.println("************************");
System.out.println("返回本类和父类中指定名称和参数类型的public方法");
try {
Method method1 = clz.getMethod("setLocation",String.class);
System.out.println(method1.getName());
method1.invoke(student,"这是地址");
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("************************");
System.out.println("返回本类中声明的指定名称和参数类型的方法,包括非public方法");
try {
Method method1 = clz.getDeclaredMethod("run");
System.out.println(method1.getName());
method1.setAccessible(true);
method1.invoke(student);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
运行结果为:
************************
返回所有的public方法,包括父类的
getName
getLocation
setName
setLocation
getAge
setAge
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
************************
返回本类中声明的所有方法,包括非public的,但不包括父类的
getName
run
getLocation
setName
setLocation
getAge
setAge
************************
返回本类和父类中指定名称和参数类型的public方法
setLocation
this ist the public setLocation method. location = 这是地址
************************
返回本类中声明的指定名称和参数类型的方法,包括非public方法
run
this ist the private run method
创建对象和构造方法
-
获取所有的public构造方法
-
获取所有的构造方法,包括非public的
-
获取指定参数类型的public构造方法
-
获取指定参数类型的构造方法,包括非public 的
-
通过public构造方法创建对象
-
通过非public方法创建对象
Class<Student> clz = Student.class;
System.out.println("*********************************************");
System.out.println("获取所有的public构造方法");
Constructor<Student>[] constructors = (Constructor<Student>[]) clz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("*********************************************");
System.out.println("获取所有的构造方法,包括非public的");
constructors = (Constructor<Student>[]) clz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("*********************************************");
System.out.println("获取指定参数类型的public构造方法");
Constructor constructor = null;
try {
constructor = clz.getConstructor(String.class, String.class, int.class);
System.out.println(constructor);
Student s = (Student) constructor.newInstance("this is name", "this is location", 18);
System.out.println(s);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("*********************************************");
System.out.println("获取指定参数类型的构造方法,包括非public的");
constructor = null;
try {
constructor = clz.getDeclaredConstructor(String.class);
System.out.println(constructor);
constructor.setAccessible(true);
Student s = (Student) constructor.newInstance("this is name");
System.out.println(s);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
运行结果为:
*********************************************
获取所有的public构造方法
public reflect.Student(java.lang.String,java.lang.String,int)
*********************************************
获取所有的构造方法,包括非public的
private reflect.Student(java.lang.String)
public reflect.Student(java.lang.String,java.lang.String,int)
*********************************************
获取指定参数类型的public构造方法
public reflect.Student(java.lang.String,java.lang.String,int)
Student{name='this is name', location='this is location', age=18}
*********************************************
获取指定参数类型的构造方法,包括非public的
private reflect.Student(java.lang.String)
Student{name='this is name', location='null', age=0}
- 通过入口类Class可以访问类的各种信息,比如字段、方法、构造方法、父类、接口、泛型信息等,也可以创建和操作对象、调用方法等。
- 借助反射,可以边写通用的,灵活的程序,但是一般不建议使用,一是因为反射更容易出现运行时错误,二是反射的性能相对低一些,在访问字段、调用方法前,反射先要查找对应的Field/Method,所以要慢一些。
反射获取真实类型
TypeVariable 泛型类型变量,可以得到泛型上下限等信息
public class TestType<K extends Comparable & Serializable, V> {
K key;
V value;
public static void main(String[] args) throws NoSuchFieldException {
// 获取字段类型
Field fieldK = TestType.class.getDeclaredField("key");
Field fieldV = TestType.class.getDeclaredField("value");
TypeVariable typeVariableK = (TypeVariable) fieldK.getGenericType();
TypeVariable typeVariableV = (TypeVariable) fieldV.getGenericType();
System.out.println(typeVariableK.getName()); // K
System.out.println(typeVariableV.getName()); // V
System.out.println(typeVariableK.getGenericDeclaration()); // class TestType
System.out.println(typeVariableV.getGenericDeclaration()); // class TestType
for (Type type : typeVariableK.getBounds()) {
System.out.println(type);
// interface java.lang.Comparable
// interface java.io.Serializable
}
for (Type type : typeVariableV.getBounds()) {
System.out.println(type); // class java.lang.Object
}
}
}
ParameterizedType
具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
public class TestType {
Map<String,Integer> map;
public static void main(String[] args) throws NoSuchFieldException {
Field field = TestType.class.getDeclaredField("map");
System.out.println(field.getGenericType()); //java.util.Map<java.lang.String, java.lang.Integer>
ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
System.out.println(parameterizedType.getRawType()); // interface java.util.Map
for (Type type : parameterizedType.getActualTypeArguments()){
System.out.println(type);
//class java.lang.String
//class java.lang.Integer
}
}
}
GenericArrayType
当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
public class TestType {
List<String> [] lists;
public static void main(String[] args) throws NoSuchFieldException {
Field f = TestType.class.getDeclaredField("lists");
GenericArrayType genericType = (GenericArrayType) f.getGenericType();
System.out.println(genericType.getGenericComponentType());
//java.util.List<java.lang.String>
}
}
WildcardType
通配符泛型,获得上下限信息;
public class TestType {
private List<? extends Integer> a; // 上限
private List<? super String> b; //下限
public static void main(String[] args) throws Exception {
Field fieldA = TestType.class.getDeclaredField("a");
Field fieldB = TestType.class.getDeclaredField("b");
ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();
ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();
WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];
WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];
System.out.println(wTypeA.getUpperBounds()[0]); // class java.lang.Integer
System.out.println(wTypeB.getLowerBounds()[0]); // class java.lang.String
System.out.println(wTypeA); // ? extends java.lang.Integer
}
}