1、反射机制:在Java运行时环境中,对于任意一个类,我们都可以通过反射机制来动态的获取类的信息以及动态调用对象的方法
2、Java语言的反射机制的作用:
(1)在运行时判断任意一个对象所属的类
(2)在运行时构造任意一个类的对象
(3)在运行时判断任意一个类所具有的成员变量和方法
(4)在运行时调用任意一个对象的方法
3、Java Reflection API,位于java.lang.reflect包中
(1)Class类:代表一个类
(2)Field类:代表类的成员变量
(3)Method类:代表类的方法
(4)Constructor类:代表类的构造方法
(5)Array类:提供了动态创建数组,以及访问数组元素的静态方法
4、使用查看String类中的方法
Class<?> classType = Class.forName("java.lang.String");//获取Class对象的一种方式
Method[] methods = classType.getDeclaredMethods();
for(Method method : methods)
{
System.out.println(method);
}
5、如何动态生成类的实例,获取类的成员变量和成员方法?
Class<?> classType = InvokeTest.class;//获取InvokeTest的Class对象 //获取Class对象的第二种方式
(1)动态生成类的实例
Object invokeTest = classType.newInstance();
System.out.println(invokeTest instanceof InvokeTest);
输出结果:true
(2)动态获取类的成员方法,并调用:
Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});
其中第一个参数”add“是InvokeTest内定义的一个函数的名称:
public int add(int a, int b)
{
return a + b;
}
第二个参数是add方法的参数,这些参数不是参数值本身,而是这些参数对应的class对象所构成的数组。
通过Method的对象addMethod调用Method的invoke方法来调用类的成员方法:
Object result = addMethod.invoke(invokeTest, new Object[]{1,2})
其中,参数一表示动态获取的类的实例,参数二表示所调用方法接收的参数。再来看一个例子:
Method printMethod = classType.getMethod("printStr", new Class[]{String.class});
Object result2 = printMethod.invoke(invokeTest, new Object[]{"tom"});
System.out.println((String)result2);
其中,printStr的定义:
public String printStr(String message)
{
return "hello," +message;
}
小结:new Class[]{}可以看成是描述信息,而new Object[]{}是具体的信息,是成员变量实际接收的参数。两个数组的长度一定是相同的。一个类对应着一个Class对象,一个方法对应着Method对象,通过反射的调用目标对象的目标方法
6、Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象
总结:
1、要想使用反射,首先需要获得待处理类的Class对象
2、获取某个类或者某个对象所对应的Class对象的常用的3中方法:
(1)使用Class类的静态方法forName:Class.forName("java.lang.String");
(2)使用类的.class语法:String.class;
(3)使用对象的getClass()方法: String s = "aa"; Class<?> classType = s.getClass();
3、若要通过类的无参构造函数来生成对象,有两种方式:
(1)先获得Class对象,然后通过Class对象的newInstance()方法直接生成即可:
Class<?> classType = object.getClass();
Object obj = classType.newInstance();
(2)先获得Class对象,然后通过该对象获得对应的Constructor对象,再通过该COnstructor对象的newInstance()方法来生成:
Class<?> classType = object.getClass();
Constructor cons = classType.getConstructor(new Class[]{});
Object obj = cons.newInstance(new Object[]{});//生成目标对象的实例
4、若要通过类的带参数的构造函数来生成对象,只能使用下面一种方法:
Class<?> classType = object.getClass();
Constructor cons = classType.getConstructor(new Class[]{String.class , int.class});
Object obj = cons.newInstance(new Object[]{"hello", 18});
5、完整示例
public class ReflectTest {
//该方法实现对Customer对象的拷贝
public Object copy(Object object) throws Exception
{
Class<?> classType = object.getClass();
//要返回的对象
Object objCopy = 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();//将属性的首字母大写
//获得get方法的名字
String getMethodName = "get" + firstLetter + name.substring(1);
//获得set方法的名字
String setMethodName = "set"+firstLetter + name.substring(1);
//获得get方法的Method的对象
Method getMethod = classType.getMethod(getMethodName, new Class[]{});
//获得set方法的Method的对象
Method setMethod = classType.getMethod(setMethodName, new Class[]{field.getType()});//该参数是Field类型,给谁设置就取谁的类型
//调用get方法
Object value = getMethod.invoke(object, new Object[]{});//从目标对象中取出数据
//调用set方法
setMethod.invoke(objCopy, new Object[]{value});
}
return objCopy;
}
public static void main(String[] args) throws Exception {
ReflectTest test = new ReflectTest();
Customer customer = new Customer("Tom", 20);
customer.setId(1L);
Customer customer2 = (Customer) test.copy(customer);
System.out.println(customer2.getId() + ", " + customer2.getName() + ", " + customer2.getAge());
}
}
class Customer
{
private Long id;
private String name;
private int 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;
}
public Customer()
{
}
public Customer(String name, int age)
{
this.name = name;
this.age = age;
}
}
6、Integer.TYPE 返回的是int(也就是对象的原生数据类型),而Integer.class返回的是Integer类所对应的Class对象
7、对于类的private成员方法或变量在外部是不能被访问的,对于成员变量我们可以通过对其实现get/set方法进行访问,但是对于成员方法就不行了。然而我们可以通过反射PAI的setAccessible方法可以压制Java的访问控制检查:
method.setAccessible(true);
例如:我们定义一个名为Private的类,其有一个private的属性name和private的方法sayHello:
public class Private {
private String name = "zhangsan";
public String getName()
{
return name;
}
private String sayHello(String name)
{
return "hello," + name;
}
}
8、如何通过反射操作类Private的私有变量name?
(1)生成类的对象
Private p = new Private();
(2)获取Private类的Class对象:
Class<?> classType = p.getClass();
(3)获取name属性:
Field field = classType.getDeclaredField("name");
(4)压制Java访问控制检查
field.setAccessible(true);
(5)将name的属性值设置为lisi:
field.set(p, "lisi");
打印输出:System.out.println(p.getName());
输出结果:lisi
说明我们通过反射已将Private的私有成员变量的值修改为了lisi
9、如何通过反射访问Private类的私有成员方法sayHello?
对于类的私有方法的访问和私有变量(该变量没有提供相应的get/set方法)的访问相似
(1)生成类的对象
Private p = new Private();
(2)获取Private类的Class对象:
Class<?> classType = p.getClass();
(3)获取待访问的方法sayHello:Method method = classType.getDeclaredMethod("sayHello", new Class[]{String.class});
(4)压制Java访问控制检查:
method.setAccessible(true);
(5)调用sayHello方法,并保存返回值:
String str = (String) method.invoke(p, new Object[]{"lisi"});
打印结果:System.out.println(str);
输出结果:hello,lisi
小结:通过8、9的操作,我们完成了通过反射访问类的私有成员的访问