在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
在运行时判断任意一个对象所属的类。
在运行时构造任意一个类的对象。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法
这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods
尽管Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
java中,无论生成某个类的多少个对象(实例),这些对象都会对应同一个Class对象。
–Class类:代表一个类(这个类很特殊,位于java.lang包下)。
–Field 类:代表类的成员变量(成员变量也称为类的属性)。
–Method类:代表类的方法。
–Constructor 类:代表类的构造方法。
–Array类:提供了动态创建数组,以及访问数组的元素的静态方法
使用反射生成对象和调用方法:
import java.lang.reflect.Method; public class InvokeTester { 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 { Class<?> classType = InvokeTester.class; Object invokeTester = classType.newInstance(); Method addMethod = classType.getMethod("add", new Class[]{int.class,int.class}); Object result = addMethod.invoke(invokeTester, new Object[]{1,2}); System.out.println((Integer)result); System.out.println("----------"); Method echoMethod = classType.getMethod("echo", new Class[]{String.class}); Object result1 = echoMethod.invoke(invokeTester, new Object[]{"world"}); System.out.println((String)result1); } }
使用反射调用方法的步骤:
a)要想使用反射,首先需要获得待处理类或对象所对应的Class对象。
b)获取某个类或某个对象所对应的Class对象的常用的3种方式:
1)使用Class类的静态方法forName,Class.forName(“java.lang.String”)
2)使用类的.class语法:String.class,如这个例子中的Class<?> classType = InvokeTester.class;
3)使用对象的getClass()方法:String s = “aa”;Class<?> clazz=s.getClass();
c)通过获取的Class对象,产生一个实例
d)获得想要调用方的Method对象,如Method addMethod = classType.getMethod("add", new Class[]{int.class,int.class});
e)通过获得的Method对象,在特定实例(对象)上调用方法:Object result = addMethod.invoke(invokeTester, new Object[]{1,2});
使用反射实现对象拷贝的程序:
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ReflectTester { // 该方法实现对Custmoer对象的拷贝 public Object copy(Object object) throws Exception { Class<?> classType = object.getClass(); // Constructor cons = classType.getConstructor(new Class[]{}); // // Object obj = cons.newInstance(new Object[]{}); // 以上两行代码等价于下面一行,不带参数 // Object obj2 = classType.newInstance(); Object objectCopy = classType.getConstructor(new Class[] {}).newInstance(new Object[] {}); Field[] fields = classType.getDeclaredFields(); for (Field field : fields) { String name = field.getName(); String firstLetter = name.substring(0, 1).toUpperCase(); String getMethodName = "get" + firstLetter + name.substring(1); String setMethodName = "set" + firstLetter + name.substring(1); 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 cu = new Customer("tom", 30); cu.setId(1L); ReflectTester test = new ReflectTester(); Customer cu1 = (Customer) test.copy(cu); System.out.println(cu1.getId() + cu1.getName() + cu1.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; } }
若想通过类的不带参数的构造方法来生成对象,两种方式:
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”,4});
–getName():获得类的完整名字。
–getFields():获得类的public类型的属性。
–getDeclaredFields():获得类的所有属性。
–getMethods():获得类的public类型的方法。
–getDeclaredMethods():获得类的所有方法。
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
- getConstructors():获得类的public类型的构造方法。
- getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
6、关于java.lang.reflect.Array类,Array 类提供了动态创建和访问数组元素的各种静态方法。
import java.lang.reflect.Array; public class ArrayTest { public static void main(String[] args) throws Exception { Class<?> classType = Class.forName("java.lang.String"); Object array = Array.newInstance(classType,10); Array.set(array,5,"hello"); String str = (String)Array.get(array,5); System.out.println(str); } }
Array类提供了newInstance方法,对于一维数组,第一个参数相当于数组中元素的类型,第二个参数是数组的长度。set方法用于给特定数组对象的第几个元素赋值。
对于多维数组
import java.lang.reflect.Array; public class ArrayTest2 { public static void main(String[] args) { int[] dims =new int[]{5,10,15}; Object array = Array.newInstance(Integer.TYPE,dims); Object arrayObj = Array.get(array,3); //Class<?> classType = arrayObj.getClass().getComponentType(); arrayObj = Array.get(arrayObj,5); Array.set(arrayObj,10,34); int[][][] arrayCast = (int[][][])array; System.out.println(arrayCast[3][5][10]); } }
Array.newInstance(Integer.TYPE,dims);相当于定义了一个多维数组,数组的维度使用一个int数组指定,这里是dims,也就是说arra是一个三维数组,维度分别是5,10,15,
Object arrayObj = Array.get(array,3);是取array数组的第一维的下标为3的对象,返回一个二维数组,arrayObj = Array.get(arrayObj,5);就是取二维数组的下标为5的一行,得到一维数组,Array.set(arrayObj,10,34);设置一维数组的下标10位置元素为34。array就是一个三维数组,所以可以转换。
class Private2 { public String name = "zhangsan"; private String getName() { return this.name; } } public class ArrayTest { public static void main(String[] args) throws Exception { Private2 p = new Private2(); Class<?> classType = p.getClass(); p.name = "lisi"; Method method2 = classType.getDeclaredMethod("getName",null);// getStr : 方法名. String.class:参数类型 method2.setAccessible(true); Object str = method2.invoke(p, null); System.out.println(str); } }
对私有成员变量的访问:
public class Private2 { private String name = "zhangsan"; public String getName() { return this.name; } } public class TestPrivate2 { public static void main(String[] args) throws Exception { Private2 p = new Private2(); Class<?> classType = p.getClass(); Field field = classType.getDeclaredField("name"); field.setAccessible(true); field.set(p,"lisi"); String str = p.getName(); System.out.println(str); } }
众所周知Java有个Object class,是所有Java classes的继承根源,其内声明了数个methods:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class object。Class class十分特殊。它和一般classes一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class object。如果您想借由“修改Java标准库源码”来观察Class object的实际生成时机(例如在Class的constructor内添加一个println()),不能够!因为Class并没有public constructor 。
Class是Reflection起源。针对任何您想探勘的class,唯有先为它产生一个Class object,接下来才能经由后者唤起为数十多个的Reflection APIs。
Class的getSuperclass()返回类的父类的Class对象。