一、简介
JAVA 的反射机制可以在java运行时动态获取类的信息以及动态调用对象的方法。
二、功能
· 在运行时判断任意一个对象所属的类。
· 在运行时构造任意一个类的对象。
· 在运行时判断任意一个类所具有的成员变量的方法。
· 在运行是调用任意一个对象的方法。
三、 在JDK中,主要由一下类来实现Java反射机制,这些类都位于 java.lang.reflec 包中
· Class 类: 代表一个类。(特殊:位于java.lang包下。每个类都会有与之关联的Class)
· Field 类: 代表类的成员变量(成员变量也成为类的属性)。
· Methoud 类: 代表类的方法。
· Constructor 类: 代表类的构造方法。
· Array 类: 提供了动态创建数组,以及访问数组的元素的静态方法。
四、 使用反射调用类的方法
· Java 中,无论生成某个类的多少个对象,这些对象都会对应于同一个 Class 对象。
/**
* 读取命令行参数指定的类名,然后打印这个类所具有的方法
*/
package com.bob.reflection;
import java.lang.reflect.Method;
public class DumpMethods {
public static void main(String[] args) throws Exception {
//使用反射第一步首先获你想操作类的对应的Class对象
Class<?> classType = Class.forName("java.lang.String"); //通过Class的静态方法获取类名
Method[] methodType = classType.getDeclaredMethods(); //获得所有声明的方法
for(Method method : methodType){
System.out.println(method);
}
}
}
package com.bob.reflection;
import java.lang.reflect.Method;
public class InvokeTest {
public int add(int param1, int param2){
return param1 + param2;
}
public String echo(String message){
return "Hello:" + message;
}
public static void main(String[] args) throws Exception {
// InvokeTest test = new InvokeTest();
// System.out.println(test.add(1, 2));
// System.out.println(test.echo("Bob"));
//使用反射完成功能
//第一步还是获取类所对应的Class对象
Class<?> classType = InvokeTest.class; //通过类名.class语法方式获得类的Class对象
//使用newInstance()方法生成invokeTest实例
Object invokeTest = classType.newInstance();
//获取类中的指定方法。参数1:方法名; 参数2:方法参数对应的Class对象所构成的Class类型数组
Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class} ); //addMethod方法会对应上add方法
//使用Method类中的invoke()方法调用目标方法。
//参数1:调用哪个对象的方法; 参数2:方法实际接受的参数
Object result = addMethod.invoke(invokeTest, new Object[]{1, 2}); //调用invokeTest对象的addMethod()方法,传递参数
System.out.println((Integer)result); //可以不加Integer,默认返回的是对象
System.out.println("-----------------------------");
//获得类中的指定方法。调用方法前先获得Method对象
Method echoMethod = classType.getDeclaredMethod("echo", new Class[]{String.class});
//调用对应的目标方法
Object result2 = echoMethod.invoke(invokeTest, new Object[]{"Tom"}); //调用invokeTest对象的echoMethod方法,传入参数
System.out.println((String)result2); //可省略强制转换。invoke()方法的返回值总是对象,如果实际是基本类型会自动转换。
}
}
总结: 想要使用反射机制调用某个类中的方法,大致步骤如下:
1. 获得类对应的Class对象。
2. 可以使用 newInstance() 方法生成类的实例。
3. 获得 Method 对象。使用Class对象的 getMethod() 方法。参数1:需要获得方法的名称; 参数2: 这个方法接受哪些参数,把这些参数所对应的Class对象以数组或者离散的方式传入进去。
4. Method 对象获得后使用 invoke() 方法实现真正的调用。参数1: 你所需要调用的是哪个对象的方法; 参数2: 方法接受的参数。
备注:步骤3和4中的参数2,是对应关系。可以把第一个看成描述参数,第二个是实际参数。
五、 获取某个类或某个对象所对应的 Class 对象的常用的3中方式:
1. 使用 Class 类的静态方法 forName(): Class.forName("java.lang.String");
2. 使用类的.class 语法:String.class;
3. 使用对象的 getClass() 方法:String s = "aaa"; Class<?> class = s.getClass();
六、通过类的构造方法来生成对象
若想通过类的不带参数的构造方法来生成对象,我们有两种方式:
1) 先获得 Class 对象,然后通过该 Class 对象的 newInstance()方法直接生成即可:
Class<?> classType = String.class;
Object obj = classType.newInstance();
2) 先获得 Class 对象,然后通过该对象获得对应的 Constructor 对象,再通过该 Constructor对象的 newInstance()方法生成:
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{});
Object obj = cons.newInstance(new Object[]{});
若想通过类的带参数的构造方法生成对象,只能使用下面这一种方式:
Class<?> classType = Customer.class;
Constructor cons = classType.getConstructor(new Class[]{String.class, int.class});
Object obj = cons.newInstance(new Object[]{“hello” , 3});
package com.bob.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionTester {
//实现对Customer对象的拷贝
public Object copy(Object object) throws Exception{
Class<?> classType = object.getClass(); //返回调用getClass()方法运行期的Class对象
// System.out.println(classType.getName());
//要实现将原有的对象拷贝到新的对象首先创建一个新的对象,然后将属性拷贝到新对象
Constructor cons = classType.getConstructor(new Class[]{}); //{}里面代表构造方法的参数
Object objectCopy = cons.newInstance(new Object[]{}); //通过构造方法生成对象
//以上两行代码等价于下面一行。(无参时)
//Object objectCopy = classType.newInstance();
//获得对象的所有成员变量
Field[] fields = classType.getDeclaredFields();
for(Field field : fields){
String name = field.getName(); //获得所有属性的名字
//要调用方法需要首先获得Method对象,Method对象的第一个参数是方法的名称。
//方法名规律是属性的首字母大写+前缀
String firstLetter = name.substring(0, 1).toUpperCase(); //将属性的值从第0个开始拷贝到第0个(首字母),然后转换为大写
String getMethodName = "get" + firstLetter + name.substring(1); //获得方法的名称
String setMethodName = "set" + firstLetter + name.substring(1, name.length()); //从第1个开始,到最后一个
//转向Method对象。描述方法
Method getMethod = classType.getMethod(getMethodName, new Class[]{});
Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()}); //获取属性的类型
//调用方法
Object value = getMethod.invoke(object, new Object[]{}); //在目标对象上调用目标对象的方法
setMethod.invoke(objectCopy, new Object[]{value}); //将值拷贝到指定对象
}
return objectCopy;
}
public static void main(String[] args) throws Exception {
Customer customer = new Customer("Tom", 20);
customer.setId(new Long(10010));
ReflectionTester reflectionTester = new ReflectionTester();
Customer obj2 = (Customer)reflectionTester.copy(customer);
// Object obj =reflectionTester.copy(customer);
// Customer obj2 = (Customer)obj;
System.out.println(obj2.getId() + ", " + obj2.getName() + ", " + obj2.getAge());
}
}
class Customer{
private Long id; //使用包装类型
private String name;
private int age;
public Customer(){
}
public Customer(String name, int age){
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
七、常用方法
·Class类是Reflectoin API 中的核心类,它有以下方法:
- getName(): 获得类的完整名字。
- getFields(): 获得类的 public 类型的属性。
- getDeclaredFields():获得类的所有属性。
- getMethods(): 获得类的 public 类型的方法。
- getDeclaredMethods():获得类的所有方法。
- getMehod(String name, Class[] parameterTypes): 获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
- getConstructors():获得类的 public 类型的构造方法。
- getConstructor(Class[] paramterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
八、 Array类
java.lang.Array 类提供了动态创建和访问数组元素的各种静态方法。
一维数组
package com.bob.reflection;
import java.lang.reflect.Array;
public class ArrayTester1 {
public static void main(String[] args) throws Exception {
Class<?> classType = Class.forName("java.lang.String"); //获得字符串类的Class对象
Object array = Array.newInstance(classType, 10); //参数1:数组当中的每个元素的类型
Array.set(array, 5, "hello"); //设置array数组的第5个索引的值
String str = (String)Array.get(array, 5); //获取array数组的第5个元素
System.out.println(str);
}
}
二维数组:
Integer.TYPE 返回的是int, 而Integer.class返回的是 Integer 类所对应的 Class 对象。
package com.bob.reflection;
import java.lang.reflect.Array;
public class ArrayTester2 {
public static void main(String[] args) {
int[] dims = new int[]{5, 10, 15};
Object array = Array.newInstance(Integer.TYPE, dims); //数组的维度由dims决定
Object arrayObj = Array.get(array, 3);
// Class<?> clasType = arrayObj.getClass().getComponentType();
arrayObj = Array.get(arrayObj, 5);
Array.setInt(arrayObj, 10, 37);
int[][][] arrayCase = (int[][][]) array;
System.out.println(arrayCase[3][5][10]);
}
}
九、使用反射访问私有成员变量、方法
package com.bob.reflection;
public class Private {
private String sayHello(String name){ //私有方法
return "Hello: " + name;
}
}
package com.bob.reflection;
import java.lang.reflect.Method;
public class PrivateTest {
public static void main(String[] args) throws Exception {
Class<?> classType = Class.forName("com.bob.reflection.Private");
//通过构造方法创建对象
Object pri = classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
//使用getDeclaredMethod()方法获得声明的方法。getMethod()方法只能获得public权限的方法
Method myMethod = classType.getDeclaredMethod("sayHello", new Class[]{String.class});
//AccessibleObject类提供了一个标志控制是否执行java访问控制检查
myMethod.setAccessible(true); //压制java语言的访问控制检查。Method类继承自AccessibleObject类
String str = (String)myMethod.invoke(pri, new Object[]{"Bob"});
System.out.println(str);
}
}
java访问私有成员变量
package com.bob.reflection;
public class PrivateMember {
private String name = "zhangsan";
private String name2 = "lisi";
public String getName(){
return name;
}
}
package com.bob.reflection;
import java.lang.reflect.Field;
public class PrivateMemberTest {
public static void main(String[] args) throws Exception{
// Class<?> classType = Class.forName("com.bob.reflection.PrivateMember");
Class<?> classType = PrivateMember.class;
Object obj = classType.getConstructor(new Class[]{}).
newInstance(new Object[]{});
Field member = classType.getDeclaredField("name"); //获得私有属性名称
Field member2 = classType.getDeclaredField("name2");
member.setAccessible(true); //压制java语言默认访问控制检查
member2.setAccessible(true); //压制java对第二个属性的访问修饰检查
member.set(obj, "Bob");
member2.set(obj, "Tom");
Object obj2 = classType.getMethod("getName", new Class[]{})
.invoke(obj, new Object[]{});
System.out.println((String)obj2);
System.out.println(member2.get(obj)); //直接是用Field的get()
}
}