[学习]Java反照机制
[学习]Java反射机制
1. 三类成员Constructor, Field, Method
java.lang.reflect.Member 是一种接口,反映有关单个成员(字段或方法)或构造函数的标识信息。
所有已知实现类: Constructor, Field, Method
2. 二个字段PUBLIC, DECLARED
java.lang.reflect.Member 接口有二个字段:
int java.lang.reflect.Member.PUBLIC = 0 [0x0]
Identifies the set of all public members of a class or interface, including inherited members.
说明:标识类或接口的所有公共成员(包括继承成员)的集合。
int java.lang.reflect.Member.DECLARED = 1 [0x1]
Identifies the set of declared members of a class or interface. Inherited members are not included.
说明:标识类或接口的所有已声明成员的集合。不包括继承成员。
注:接口是可以有字段的,接口中的字段必须用public static final来修饰,默认如此,可不必添加。这是与接口只能被实现有关。
3. 三类方法getXXXConstructorX, getXXXFieldX, getXXXMethodX
这些方法根据成员类型,成员访问权限不同可简单归为以上三类。
中间加有Declared的方法能够调用所有已声明成员的集合,但不包括继承成员。
中间没有Declared的方法能够调用所有公共成员(包括继承成员)的集合。
未尾加有s的方法返回所有对应的成员集,e.g. getConstructors()返回所有公共构造方法。
a. 调用构造函数(Constructor)的方法 - getXXXConstructorX(),包括:
getConstructor(), getConstructors(),
getDeclaredConstructor(), getDeclaredConstructors()
b. 调用字段(Field)的方法 - getXXXFieldX()
getField(), getFields(),
getDeclaredField(), getDeclaredFields()
c. 调用方法(Method)的方法 - getXXXMethodX()
getMethod(), getMethods(),
getDeclaredMethod(), getDeclaredMethods()
4. 调用构造函数的方法一般步骤
a. 返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:
Class.forName(className, true, currentLoader)
其中 currentLoader 表示当前类的定义类加载器。
public static Class<?> forName(String className)
throws ClassNotFoundException
b. 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。parameterTypes 参数是 Class 对象的一个数组,这些 Class 对象按声明顺序标识构造方法的形参类型。 如果此 Class 对象表示非静态上下文中声明的内部类,则形参类型作为第一个参数包括显示封闭的实例。
无参构造函数,parameterTypes可以为空,可以为null,可以为new Class[]{}。
有参构造函数,parameterTypes为各参数类型。
e.g.
基本类型:int.class,long.class,...
类/接口类型:String.class,List.class,...
数组类型:long[].class,String[].class,...
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
c. 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。个别参数会自动解包,以匹配基本形参,必要时,基本参数和引用参数都要进行方法调用转换。
无参构造函数,parameterTypes可以为空,可以为null,可以为new Object[]{}
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
5. 调用字段的方法一般步骤
a. 返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:
Class.forName(className, true, currentLoader)
其中 currentLoader 表示当前类的定义类加载器。
public static Class<?> forName(String className)
throws ClassNotFoundException
b. 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。name 参数是一个 String,它指定所需字段的简称。注意,此方法不反映数组类的 length 字段。
一般JavaBean中的字段都被 private 修饰,所以使用getDeclaredField(String name)方法。
注:getField(String name)方法,会因为只能访问公有成员而出错。
public Field getField(String name)
throws NoSuchFieldException, SecurityException
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException
c. 返回指定对象上此 Field 表示的字段的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。
底层字段的值是按以下方式获得的:
如果底层字段是一个静态字段,则忽略 obj 变量;它可能为 null。 否则,底层字段是一个实例字段。如果指定的 obj 变量为 null,则该方法将抛出一个 NullPointerException。如果指定对象不是声明底层字段的类或接口的实例,则该方法将抛出一个 IllegalArgumentException。如果此 Field 对象强制实施 Java 语言访问控制,并且底层字段是不可访问的,则该方法将抛出一个 IllegalAccessException。
如果底层字段是静态的,并且声明该字段的类尚未初始化,则初始化这个类。否则,从底层实例字段或静态字段中获取该值。如果该字段是一个基本类型字段,则在返回前将该值包装在一个对象中,否则照原样返回。 如果字段隐藏在 obj 的类型中,则根据前面的规则获得字段的值。
除了通用的方法Object get(Object obj)外,还有int getInt(Object obj),long getLong(Object obj)等用来返回基本类型值的方法。
注:
1. Object obj 是包含有该字段的类实例
2. 调用方法用来返回某个字段值,需要遵循权限访问规则。若字段在当前方法中不可见,则无法访问,抛出异常。可见,调用字段方法一般用来获取其字段信息,而不会用来直接设置、获取其字段值。若想设置、获取其值,则可以通过getter/setter方法来完成。
public Object get(Object obj)
throws IllegalArgumentException, IllegalAccessException
6. 调用方法的方法一般步骤
a. 返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:
Class.forName(className, true, currentLoader)
其中 currentLoader 表示当前类的定义类加载器。
public static Class<?> forName(String className)
throws ClassNotFoundException
b. 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。name 参数是一个 String,用于指定所需方法的简称。parameterTypes 参数是按声明顺序标识该方法形参类型的 Class 对象的一个数组。如果 parameterTypes 为 null,则按空数组处理。
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException
c. 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。
如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。
如果底层方法是实例方法,则使用动态方法查找来调用它,这一点记录在 Java Language Specification, Second Edition 的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。
如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。
如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。
注:Object obj 是包含有该字段的类实例
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
实例:
/**
* Copyright (c) 2011 Trusted Software and Mobile Computing(TSMC)
* All right reserved.
*
* Created on Aug 19, 2011 12:12:06 PM
* http://jarg.iteye.com/
* Author: Jarg Yee <yeshaoting@gmail.com>
*/
package com.iteye.jarg.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* TODO [学习]Java反射机制
*/
public class ReflectEx
{
public static void main(String[] args) throws Exception
{
Class<?> clazz = Class.forName("com.iteye.jarg.test.ReflectBean");
//无参构造函数参数数组可不写,可用new Class[]{},可用null
Constructor<?> constructor = clazz.getConstructor();
//无参构造函数参数数组可不写,可用new Object[]{},可用null
Object obj = constructor.newInstance();
ReflectBean bean = (ReflectBean)obj;
bean.display("1");
constructor = clazz.getConstructor(String.class);
obj = constructor.newInstance("字段 name 参数值");
bean = (ReflectBean)obj;
bean.display("2");
//虽然getDeclaredField能查看字段相关信息,但是获取字段值还得遵循权限访问规则
//若使用getField("name"),则会因为它只能访问公共成员而出错
Field field = clazz.getDeclaredField("name");
//获取实例相应字段值,而不是字段初始值
System.out.println("3. name:\t" + field.get(bean));
System.out.println("-------------------------------");
Object obj2 = clazz.newInstance();
Method method = clazz.getMethod("setName", String.class);
method.invoke(obj2, "字段 name 全新值");
bean = (ReflectBean)obj2;
bean.display("4");
obj2 = clazz.newInstance();
method = clazz.getMethod("setValue", int[].class);
method.invoke(obj2, new int[]{1,2,3,4});
bean = (ReflectBean)obj2;
System.out.println("5. value:\t" + bean.getValue()[2]);
}
}
class ReflectBean
{
int[] value = new int[4];
String name = "字段 name 初始值";
public ReflectBean(String name)
{
this.name = name;
System.out.println("测试 \"有\" 参构造函数");
}
public ReflectBean()
{
System.out.println("测试 \"无\" 参构造函数");
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public int[] getValue()
{
return value;
}
public void setValue(int[] value)
{
this.value = value;
}
public void display(String id)
{
System.out.println(id + ". name:\t" + name);
System.out.println("-------------------------------");
}
}
输出结果:
测试 "无" 参构造函数 1. name: 字段 name 初始值 ------------------------------- 测试 "有" 参构造函数 2. name: 字段 name 参数值 ------------------------------- 3. name: 字段 name 参数值 ------------------------------- 测试 "无" 参构造函数 4. name: 字段 name 全新值 ------------------------------- 测试 "无" 参构造函数 5. value: 3