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》