什么是Java反射
Java反射机制是在运行状态中,对于任何一个类,都能知道这个类的属性和方法, 对于任何一个对象,都能够调用它的任意一个方法和属性。
Java反射主要是用来
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法,生成动态代理。
反射的三种方式
- 通过 Class.forName() 方法加载字符串,就可以得到该字符串做代表的Class对象
Class<?> cls = Class.forName("com.java.Demo");
- 通过类名调用class属性得到该类的Class对象。
Class<?> cls = Demo.class;
- 通过实例的 getClass() 方法得到Class对象
Demo demo = new Demo();
Class <?> cls = demo.getClass();
这三种方法,最后都是要得到一个Class对象,然后获取字段和函数的的方法,都是围绕着这个Class对象来实现的。
生成对象
- 先获得Class对象,然后通过该Class对象的newInstance()方法获取对象。
Class<?> classType = String.class;
Object obj = classType.newInstance();
- 先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成
Class<?> classType = Customer.class;
// 获得Constructor对象,此处获取第一个无参数的构造方法的
Constructor cons = classType.getConstructor(new Class[] {});
// 通过构造方法来生成一个对象
Object obj = cons.newInstance(new Object[] {});
但是,上面这种情况只适用于默认的 无参构造函数 ,但是如果是有参构造函数,则会报错。
Exception in thread "main" java.lang.InstantiationException: com.gzl.demo.InvokeTest
at java.lang.Class.newInstance(Class.java:418)
at com.gzl.demo.InvokeTest.main(InvokeTest.java:33)
Caused by: java.lang.NoSuchMethodException: com.gzl.demo.InvokeTest.<init>()
at java.lang.Class.getConstructor0(Class.java:2971)
at java.lang.Class.newInstance(Class.java:403)
... 1 more
这个时候,可以用下面的方法:
Class<?> classType = InvokeTest.class;
//getConstructor(Class<?>... parameterTypes),传递的是可变Class参数
Constructor cons2 = classType.getConstructor(new Class[] {String.class, int.class});
//当是基本数据类型传入的时候,需要一个包装类来包装它,但是jdk1.5也可以直接传入基本数据类型了,因为编译器会帮你自动装箱,拆箱
Object obj2 = cons2.newInstance(new Object[] {"ZhangSan",20});
两种不同的方法的区别
通过反射创建新的类示例,有两种方式:
Class.newInstance()
Constructor.newInstance()
以下对两种调用方式给以比较说明:
Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
Class.newInstance() 抛出所有由被调用构造函数抛出的异常。
Class.newInstance() 要求被调用的构造函数是可见的,也即必须是public类型的;
Constructor.newInstance() 在特定的情况下,可以调用私有的构造函数。
获取函数Method
一行代码获取所有的方法。
Method[] methods = clazz.getDeclaredMethods();
可以看看Method的API
大概就是几个Get方法,常用的包括:
getModifiers()
以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。getName()
以 String 形式返回此 Method 对象表示的方法名称。getReturnType()
返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。invoke(Object obj, Object… args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
在使用invoke方法之前可以先用class.getMethod(String name,Class
getxxx(field,method,constructor)和getDeclaredxxx(field,method,constructor)的区别
- getxxx() 只能访问类中的私有字段,而共有的字段是无法访问的,能访问从其他类继承下来的公共方法。
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。返回数组中的元素没有排序,也没有任何特定的顺序。
如果类或接口没有可访问的公共字段,或者表示一个数组类、一个基本类型或 void,则此方法返回长度为 0 的数组。
特别地,如果该 Class对象表示一个类,则此方法返回该类及其所有超类的公共字段。
如果该 Class 对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段。
该方法不反映数组类的隐式长度字段。用户代码应使用 Array 类的方法来操作数组。
- getDeclaredxxx()
返回一个构造函数对象数组,反映由这个类对象所表示的类所声明的所有构造函数。
这些都是public、protected、default和private。返回的数组中的元素没有排序。
如果类有一个默认的构造函数,它被包含在返回的数组中。如果这个类对象表示一个接口,一个原始类型,一个数组类,或void,此方法返回一个长度为0的数组。
一个例子获取所有需要的方法
记得新建一个类,然后改一下forName()里面包的路径。
package com.gzl.demo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Test {
public static void main(String[] args){
try {
//获取Student的Class对象
Class<?> clazz = Class.forName("com.gzl.demo.Student");
//获取该类中所有的属性
Field[] fields = clazz.getDeclaredFields();
System.out.println("下面打印类中所有属性");
//遍历所有的属性
for (Field field : fields) {
field.setAccessible(true);
//打印属性信息,包括访问控制修饰符,类型及属性名
System.out.println(" 修饰符:" + Modifier.toString(field.getModifiers()));
System.out.println(" 类型:" + field.getType().toString());
System.out.println(" 属性名:" + field.getName());
System.out.println();
}
//获取该类中的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(" 修饰符:" + Modifier.toString(method.getModifiers()));
System.out.println(" 方法名:" + method.getName());
System.out.println(" 返回类型:" + method.getReturnType());
//获取方法的参数对象
Class<?>[] clazzes = method.getParameterTypes();
for (Class<?> class1 : clazzes) {
System.out.println(" 参数类型:" + class1);
System.out.println();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}