动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
Java 反射机制主要提供了以下功能
• 在运行时判断任意一个对象所属的类。
• 在运行时构造任意一个类的对象。
• 在运行时判断任意一个类所具有的成员变量和方法。
• 在运行时调用任意一个对象的方法
Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection
APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(
例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改
变fields内容或调用methods
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
– Class类:代表一个类。
– Field 类:代表类的成员变量(成员变量也称为类的属性)。
– Method类:代表类的方法。
– Constructor 类:代表类的构造方法。
– Array类:提供了动态创建数组,以及访问数组的元素的静态方法
获取某个类或者对象所对应的Class对象的常用3种方式:
1) 使用Class类的静态方法forName: Class.forName("java.lang.String");
2) 使用类的.class语法:String.class
3) 使用对象的getClass()方法:String s = "aaa"; Class<?> clazz = s.getClass();
在java.lang.Object 类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API 中的核心类,它有以下方法
– getName():获得类的完整名字。
– getFields():获得类的public类型的属性。
– getDeclaredFields():获得类的所有属性。
– getMethods():获得类的public类型的方法。
– getDeclaredMethods():获得类的所有方法。
• getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指 定方法的参数类型。
• getConstructors():获得类的public类型的构造方法。
• getConstructor(Class[] parameterTypes): 获得类的特定构造方法,parameterTypes 参数 指定构造方法的参数类型。
• newInstance():通过类的不带参数的构造方法创建这个类的一个对象
若想通过类的不带参数的构造方法来生成对象,有两种方式:
1) 先获得Class对象,然后通过Class对象的newInstance方法
Class<?> clazz = String.class;
Object obj = class.newInstatnce();
2) 先获得Class对象,然后获取对应的Constructor对象,使用Constructor的newInstance()方法
Class<?> clazz = Customer.class;
Constructor cons = class.getConstructor(new Class[]{});
Object obj = cons.newInstance(new Object[]{});
若想通过类的带参数的构造方法生成对象,只有一种方式
Class<?> clazz = Customer.class;
Constructor cons = class.getConstructor(new Class[]{String.class, int.class});
Object obj = cons.newInstance(new Object[]{"hello", 3});
Note: Integer.TYPE返回的是int, 而Integer.class返回的是Integer类所对应的Class对象
获得对象的所有属性:
• Field fields[]=classType.getDeclaredFields();
• Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性
首先调用Class的getField()并指定field名称。获得特定的Field object之后便可直接调用Field的get()和set()
Class<?> clazz = Class.forName("Test");
Field f = clazz.getField("d");
Test obj = new Test();
f.get(obj);
f.set(obj, 123);
方法调用:
InvokerTester invokeTester = new InvokerTester();
Class<?> clazz = invokeTester.getClass();
Method addMethod=clazz.getMethod("add",new Class[]{int.class,int.class});
Object result=addMethod.invoke(invokeTester, new Object[]{new Integer(100),new Integer(200)});
• Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回
由于数组的特殊性, Array类提供了一系列的静态方法用来创建数组和对数组中的元素进行访问和操作。
例如有以下方法:
public void arrayInput(String[] inputs) {}
使用反射可以这样调用:
Class<?> clazz = ...
Object object = clazz.newInstance();
Method method = clazz.getMethod("arrayInput", String[].class);
//Method 1
Object array = Array.newInstance(String.class, 2);//等价于 new String[2]
Array.set(array, 0, "Hello"); //等价于array[0] = "Hello"
Array.set(array, 1, "World"); //等价于array[1] = "World"
method.invoke(object, array);
//Method 2
String[] stringArray = new String[]{"Cafe","Baby"};
arrayInputMethod.invoke(object, new Object[] { stringArray });
还有一个方法是生成多维数组的:
使用Java反射API的时候可以绕过Java默认的访问控制检查,比如可以直接获取到对象的私有域的值或是调用私有方法。只需要在获取到Constructor、Field和Method类的对象之后,调用setAccessible方法并设为true即可。有了这种机制,就可以很方便的在运行时刻获取到程序的内部状态
在编程过程中发现连private static final的都能改变~~
field.setAccessible(true);//平时想要改变private的就用这个
Field modifiersField = Field.class.getDeclaredField("modifiers");//获取modifiers
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); //与取过反的Final类型,这个很经典,也很危险
field.set(null, newValue);
处理泛型
Java 5中引入了泛型的概念之后,Java反射API也做了相应的修改,以提供对泛型的支持。由于类型擦除机制的存在,泛型类中的类型参数等信息,在运行时刻是不存在的。JVM看到的都是原始类型。对此,Java 5对Java类文件的格式做了修订,添加了Signature属性,用来包含不在JVM类型系统中的类型信息。比如以java.util.List接口为例,在其类文件中的Signature属性的声明是<E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;> ,这就说明List接口有一个类型参数E。在运行时刻,JVM会读取Signature属性的内容并提供给反射API来使用。
class Customer
{
public List<String> list;
}
public static void main(String[] args) throws Exception{
Class<?> clazz = Customer.class;
Field field = clazz.getField("list");
Type type = field.getGenericType();
if(type instanceof ParameterizedType)
{
ParameterizedType type1 = (ParameterizedType)type;
Type[] types = type1.getActualTypeArguments();
for(Type t : types)
{
if(t instanceof Class)
{
Class<?> clz = (Class)t;
System.out.println(clz.getName());
}
}
}
}
打印出来的结果即java.lang.String。