一、概念
Java 反射(Reflection)就是 Java 程序在运行时可以加载一个才知道类名的类,获得类的完整构造方法,并实例化出对象,给对象属性设定值或者调用对象的方法。这种在运行时动态获取类的信息以及动态调用对象的方法的功能称为 Java 的反射机制。
二、Class 类
Class 类继承自 Object 类,是 Java 反射机制的入口,封装了一个类或接口的运行时信息,通过调用 Class 类的方法可以获取到这些信息。怎么理解这个 Class 类呢?如果说普通类是所有对象方法、属性的集合,那就可以把这个 Class 类理解成是所有普通类的集合。
下面列举了获取 Class 类的几种方法:
public class TestClass { public static void main(String[] args) throws ClassNotFoundException { // 1、 Class.forName(); Class<?> aClass0 = Class.forName("java.lang.Object"); // 2、类名.Class Class<Integer> aClass1 = Integer.class; // 3、包装类.TYPE —— 返回基本类型的 Class 引用,基本类型在虚拟机运行时就已经加载了它的Class Class<Integer> aClass2 = Integer.TYPE; // 4、对象名.getClass() String str = "Hello, World"; Class<? extends String> aClass3 = str.getClass(); // 5、Class类.getSuperClass() —— 获得父类的 Class 对象 Class<?> aClass4 = aClass3.getSuperclass(); System.out.println(aClass0.getName()); System.out.println(aClass1.getName()); System.out.println(aClass2.getName()); System.out.println(aClass3.getName()); System.out.println(aClass4.getName()); } }
- aClass.isPrimitive(); //判断 aClass 是否为基本数据类型
- aClass.isAssignableFrom(ArrayList.class); //判断 aClass 是否是 ArrayList 类的父类
- aClass.getGenericType(); //得到泛型类型
三、获取类信息
为了测试 Java 的反射机制,我新建了一对父子类,其中涵盖了四种封装属性,以尽可能的测试多种类信息的获取:
vpublic class Vehicle { private String color; protected Integer seat; int year; public Date createdOn; private String getColor() { return color; } protected Integer getSeat() { return seat; } int getYear() { return year; } public Date getCreatedOn() { return createdOn; } }
public class Car extends Vehicle { private String brand; protected Integer a; int b; public Date updatedOn; public Car(){} private Car(String brand, Integer a, int b, Date updatedOn) { this.brand = brand; this.a = a; this.b = b; this.updatedOn = updatedOn; } private String getBrand() { return brand; } protected Integer getA() { return a; } int getB() { return b; } public Date getUpdatedOn() { return updatedOn; } }
1、获取方法
Class 类对方法的获取主要通过以下两种方式:
Method[] getMethods() 返回该类或接口的所有可访问公共方法(含继承的公共方法)。
Method[] getDeclaredMethods() 返回该类或接口的所有方法(不含继承的方法)。
public class TestMethod { public static void main(String[] args) { Class<Car> carClass = Car.class; Method[] methods = carClass.getMethods(); Method[] declaredMethods = carClass.getDeclaredMethods(); for (Method method : methods) { //for (Method method : declaredMethods) { System.out.println("方法名:" + method.getName()); System.out.println("该方法所在的类或接口:" + method.getDeclaringClass()); System.out.println("该方法的参数列表:" + method.getParameterTypes()); System.out.println("该方法的异常列表:" + method.getExceptionTypes()); System.out.println("该方法的返回值类型:" + method.getReturnType()); } } }
2、获取属性
Class 类对属性的获取主要通过以下两种方式:
Field[] getFields() :存放该类或接口的所有可访问公共属性(含继承的公共属性)。
Field[] getDeclaredFields():存放该类或接口的所有属性(不含继承的属性)。
public class TestField { public static void main(String[] args) { Class<Car> carClass = Car.class; Field[] fields = carClass.getFields(); Field[] declaredFields = carClass.getDeclaredFields(); //for (Field field : fields) { for (Field field : declaredFields) { System.out.println("属性名称是:" + field.getName()); System.out.println("该属性所在的类或接口是:" + field.getDeclaringClass()); System.out.println("该属性的类型是:" + field.getType()); // field.getModifiers() 以整数形式返回由此 Field 对象表示的属性的 Java 访问权限修饰符 System.out.println("该属性的修饰符是:" + Modifier.toString(field.getModifiers())); } } }
3、获取构造函数
Class 类对构造方法的获取主要通过以下两种方式:
Constructor<?>[] getConstructors() :返回该类或接口的所有的公共构造方法
Constructor<?>[] getDeclaredConstructors():返回该类或接口的所有构造方法
public class TestConstructor { public static void main(String[] args) throws NoSuchMethodException { Class<Car> carClass = Car.class; Constructor<?>[] constructors = carClass.getConstructors(); Constructor<?>[] declaredConstructors = carClass.getDeclaredConstructors(); Constructor<Car> carConstructor = carClass.getDeclaredConstructor(String.class, Integer.class, Integer.TYPE, Date.class); //for (Constructor constructor : declaredConstructors) { for (Constructor constructor : constructors) { System.out.println("该构造器的名称是:" + constructor.getName()); System.out.println("该构造器所在的类或接口是:" + constructor.getDeclaringClass()); //返回构造方法的参数类型 constructor.getParameterTypes(); } } }
四、动态调用
到目前为止,我们都是通过 Class 类的方法获取对应类属性、方法和构造函数的详细信息。接下来我们将通过这些信息,来动态创建对象、修改属性和动态调用方法。
public class Test { public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Class<Car> carClass = Car.class; // 1、实例化对象 // 调用 Class 类的newInstance();要求对应类必须有无参构造函数,相当于 Car car = new Car() Car car = carClass.newInstance(); // 调用构造器的newInstance(Object ... initargs); Constructor<Car> declaredConstructor = carClass.getDeclaredConstructor(String.class, Integer.class, Integer.TYPE, Date.class); // 取消访问权限控制,即使是 private 权限也可以访问 declaredConstructor.setAccessible(true); Car car1 = declaredConstructor.newInstance("brand", 21, 21, new Date()); System.out.println(car1.getUpdatedOn()); // 2、修改属性 Field brand = carClass.getDeclaredField("brand"); brand.setAccessible(true); System.out.println("取消访问权限控制后的值:" + brand.get(car1)); brand.set(car1, "dnarb"); System.out.println("修改属性后的值是:" + brand.get(car1)); // 3、调用方法 Method getBrand = carClass.getDeclaredMethod("getBrand"); getBrand.setAccessible(true); System.out.println("调用反射方法得到的值是:" + getBrand.invoke(car1)); } }