Java反射
一、简介
Java程序中的对象有两种类型:编译时类型和运行时类型(Animal cat = new Cat();),通过反射机制可以在程序运行时获取对象和类的真实信息。
二、Class对象
1、获取Class对象
每个类被加载后,在JVM中会生成一个对应的Class对象,通过该对象就可以访问到JVM中的这个类。可以通过以下三种方式获取:
-
使用
java.lang.Class.forName(String)参数为类的全路径名称,例如:
Class.forName("com.strikeback.model.Animal") -
使用类的
class属性例如:
Animal.class。此种方法与前一种相比,有以下优势:-
在编译阶段就能检查用到的Class是否存在,代码更健壮。
-
无需调用方法,性能更好。
-
-
调用(对象的)
java.lang.Object.getClass()方法例如:
Animal animal = new Animal(); animal.getClass();
2、Class对象常用方法
样例使用的Animal类:
package com.strikeback.model;
public class Animal implements Cloneable, Comparable<Animal>{
private int age;
private String name;
public float weight;
public Animal(){}
private Animal(int age){
this.age = age;
}
public Animal(String name){
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void bark(){}
public void bark(String voice){
System.out.println("Voice:" + voice);
}
private void eat(Animal food){}
private void introduce(){
System.out.println("My name is " + name);
}
@Override
public int compareTo(Animal o) {
return 0;
}
public class Cat extends Animal{
}
}
-
getName()&getSimpleName()getName():返回Class对象所对应类的名称getSimpleName():返回Class对象所对应类的简称Class<?> clazz = Animal.class; System.out.println(clazz.getName()); System.out.println(clazz.getSimpleName());输出:
com.strikeback.model.Animal Animal -
String getCanonicalName()返回Java语言规范定义的基础类的规范名称。 如果基础类没有规范名称(即,如果它是本地或匿名类或其组件类型不具有规范名称的数组),则返回null。
Class<?> clazz = Animal.class; System.out.println(clazz.getCanonicalName()); Class<Cat> catClass = Animal.Cat.class; System.out.println(catClass.getName()); System.out.println(catClass.getSimpleName()); System.out.println(catClass.getCanonicalName()); Class<? extends Object[]> arrClass = new Object[0].getClass(); System.out.println(arrClass.getName()); System.out.println(arrClass.getSimpleName()); System.out.println(arrClass.getCanonicalName());输出:
com.strikeback.model.Animal com.strikeback.model.Animal$Cat Cat com.strikeback.model.Animal.Cat [Ljava.lang.Object; Object[] java.lang.Object[] -
Package getPackage()获取Class对象所对应类的包名
Class<?> clazz = Animal.class; System.out.println(clazz.getPackage());//package com.strikeback.model -
Constructor<T> getConstructor(Class<?>... parameterTypes)获取Class对象所表示类的指定参数的public构器器。
Class<?> clazz = Animal.class; //无参构造器 System.out.println(clazz.getConstructor()); //有参构造器 System.out.println(clazz.getConstructor(String.class)); //获取私有构造器 System.out.println(clazz.getConstructor(Integer.class));输出:
public com.strikeback.model.Animal() public com.strikeback.model.Animal(java.lang.String) java.lang.NoSuchMethodException: com.strikeback.model.Animal.<init>(int) -
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)获取指定的构造器,不受访问级别限制
Class<?> clazz = Animal.class; //获取私有构造器 System.out.println(clazz.getDeclaredConstructor(int.class));输出:
private com.strikeback.model.Animal(int) -
Method getMethod(String name, Class<?>... parameterTypes)获取指定的public方法
Class<?> clazz = Animal.class; //无参方法 System.out.println(clazz.getMethod("bark")); //有参方法 System.out.println(clazz.getMethod("bark", String.class)); //获取私有方法 System.out.println(clazz.getMethod("eat", Animal.class));输出:
public void com.strikeback.model.Animal.bark() public void com.strikeback.model.Animal.bark(java.lang.String) java.lang.NoSuchMethodException: com.strikeback.model.Animal.eat(com.strikeback.model.Animal) -
Method getDeclaredMethod(String name, Class<?>... parameterTypes)获取指定的方法,不受访问级别的限制
Class<?> clazz = Animal.class; //获取私有方法 System.out.println(clazz.getDeclaredMethod("eat", Animal.class)); -
Field getField(String name)获取指定的public属性
Class<?> clazz = Animal.class; //获取public属性 System.out.println(clazz.getField("weight")); //获取private属性 System.out.println(clazz.getField("name"));输出:
public float com.strikeback.model.Animal.weight java.lang.NoSuchFieldException: name -
Field getDeclaredField(String name)获取指定的属性,不受访问级别限制
Class<?> clazz = Animal.class; //获取private属性 System.out.println(clazz.getDeclaredField("name"));输出:
private java.lang.String com.strikeback.model.Animal.name -
Class<?>[] getInterfaces()获取该Class对象所对应的类实现的所有接口
Class<?> clazz = Animal.class; for(Class<?> itf : clazz.getInterfaces()){ System.out.println(itf.getName()); }输出:
java.lang.Cloneable java.lang.Comparable -
Class<?>[] getDeclaredClasses()返回该Class对象所对应的类中包含的所有内部类
Class<?> clazz = Animal.class; for(Class<?> cls : clazz.getDeclaredClasses()){ System.out.println(cls.getName()); }输出:
com.strikeback.model.Animal$Cat -
Class<?> getDeclaringClass()返回该Class对象所对应的类所在的外部类
Class<Cat> catClass = Animal.Cat.class; System.out.println(catClass.getDeclaringClass().getName());输出:
com.strikeback.model.Animal -
boolean isInstance(Object obj)判断
obj是否是此Class对象的实例,与instanceof操作符功能类似 -
boolean isAnnotation()判断该Class对象所对应的类是否是接口
-
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)判断该Class对象上是否存在
annotationClass的注解 -
boolean isAnonymousClass()判断该Class对象是否是一个匿名类
-
boolean isArray()判断该Class对象是否是数组
-
boolean isEnum()判断该Class对象是否是枚举
-
boolean isInterface()判断该Class对象是否是接口
……
三、使用反射
1、创建对象
-
使用
Class对象的newInstance()方法此种方式要求要创建的类必须有默认的构造器,执行此方法时实际上是使用默认构造器来创建实例的。
Class<?> clazz = Class.forName("com.strikeback.model.Animal"); Animal animal = (Animal) clazz.newInstance(); -
使用
Constructor对象的newInstance()方法先使用
Class对象获取指定的Constructor对象,然后调用Constructor对象的newInstance()方法创建实例。使用这种方式可以调用指定构造器。//获取Class对象 Class<?> clazz = Class.forName("com.strikeback.model.Animal"); //获取指定构造器 Constructor<?> constructor = clazz.getDeclaredConstructor(String.class); //创建对象 Animal animal = (Animal) constructor.newInstance("Coco"); System.out.println(animal.getName());//Coco
一般只有当程序中需要动态创建某个类的对象时才会使用反射,因为通过反射创建对象时性能会低一点;通常在开发一些通用性广的框架、平台时会大量使用反射。
2、调用方法
可以通过Class对象获取指定的Method对象,之后就可以通过Method对象的invoke方法来调用该方法。
Object invoke(Object obj, Object... args)
Class<?> clazz = Class.forName("com.strikeback.model.Animal");
Animal animal = (Animal) clazz.newInstance();
Method method = clazz.getMethod("bark", String.class);
method.invoke(animal, "miemie");
输出:Voice:miemie
通过Method对象的invoke()方法来调用方法时必须要有访问该方法的权限;如果需要调用某个对象的private方法,可以先调用setAccessible(true)方法取消Java对访问权限的检查。
Class<?> clazz = Class.forName("com.strikeback.model.Animal");
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
Animal animal = (Animal) constructor.newInstance("Coco");
Method method = clazz.getDeclaredMethod("introduce");
method.setAccessible(true);
method.invoke(animal);
输出:My name is Coco
3、访问属性
可以通过Class对象获取指定的Field对象,之后可以通过set或get方法设置或获取对象的该属性值:
-
set(Object obj, Object value)或setXxx(Object obj, Xxx value)给
obj对象设置此属性的值为value -
get(Object obj)或getXxx(Object obj)获取
obj对象中该属性的值
Class<?> clazz = Class.forName("com.strikeback.model.Animal");
Animal animal = (Animal) clazz.newInstance();
Field field = clazz.getDeclaredField("name");
//取消访问权限的检查
field.setAccessible(true);
field.set(animal, "Tom");
System.out.println(field.get(animal));//Tom
field = clazz.getDeclaredField("age");
//取消访问权限的检查
field.setAccessible(true);
field.setInt(animal, 5);
System.out.println(field.getInt(animal));//5
4、处理数组
可以使用Array类来动态创建数组、操作数组元素。
-
创建数组
-
static Object newInstance(Class<?> componentType, int length)创建指定类型和长度的数组。
//创建一维数组 Object array = Array.newInstance(String.class, 3); System.out.println(Array.getLength(array));//3 -
static Object newInstance(Class<?> componentType, int... dimensions)创建指定类型和维度的数组。
//创建二维数组 Object array = Array.newInstance(int.class, 2, 3); System.out.println(Array.getLength(array));//2 System.out.println(Array.getLength(Array.get(array, 0)));//3
-
-
操作数组元素
-
static native Object get(Object array, int index)或Xxx getXxx(Object array, int index)获取数组array中指定位置的元素。
-
static native void set(Object array, int index, Object value)或void setXxx(Object array, int index, Xxx value)设置数组array中index位置的值为value。
Object array = Array.newInstance(int.class, 2, 3); Array.set(array, 0, new int[]{1, 2, 3}); Array.set(array, 1, new int[]{4, 5, 6}); Object arr = Array.get(array, 0); System.out.println(Array.getInt(arr, 0));//1 System.out.println(Array.getInt(arr, 1));//2 System.out.println(Array.getInt(arr, 2));//3 -
参考资料:
-
《疯狂Java》