Object(源头)
Java的一切都是对象
Object是反射的源头
Class是反射的导演
Class(导演)
在Java程序运行时,JVM会对所有的对象维护一个独一无二的类型标识,这就是Class对象。
Java的基本类型和关键字void也对应一个Class对象。
相同元素类型和维数的数组也对应一个Class对象。
获取Class对象的几种方法:
- 一个对象通过.class
- 一个对象通过getClass方法
- Class.forName(String)
Class对象有一些重要的方法:
- getConstructor系列,用于获取构造方法
- getField系列,用于获取属性
- getMethod系列,用于获取方法
上述系列中包含Declared系列,可以获取当前对象的所有类型的反射(包含private),但是不能获取父类的反射
Constructor,Field,Method都是Class导演的三大利器,它们都位于java.lang.reflect
包下,接下来将分别对这三大利器进行展示。
Constructor
通过Class对象获取到的Constructor对象之后,最常用的操作就是用来实例化对象,调用newInstance方法即可。
其实在Class对象中也有一个newInstance方法,也可以用来实例化对象,它们的区别是什么呢?
- Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数,要求被调用的构造函数是可见的,也即必须是public类型的;
- Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数,在特定的情况下,可以调用私有的构造函数。
Demo
//Class.newInstance()
//只能调用public属性的无参构造函数
A a = (A)Class.forName(A.class.getName()).newInstance();
//Constructor.newInstance()
Class c= Class.forName(A.class.getName());
/*以下调用无参的、私有构造函数*/
//获得无参构造
Constructor c0=c.getDeclaredConstructor();
//设置无参构造是可访问的
c0.setAccessible(true);
A a0=(A)c0.newInstance();
//调用无参构造函数,生成对象实例
/*以下调用带参的、私有构造函数*/
Constructor c1=c.getDeclaredConstructor(new Class[]{int.class,int.class});
c1.setAccessible(true);
//调用有参构造函数,生成对象实例
A a1=(A)c1.newInstance(new Object[]{5,6});
复制代码
使用场景:
如果使用接口模式,使用new创建一个门的对象,Door door = new WoodenDoor(),当以换成其他门,需要修改代码,Door door = new OtherDoor()。所以我们需要使用工厂模式,需要什么门就生产什么门,如果我们再使用newInstance()方法来生产,则只需要修改配置文件即可。
Field
通过Class对象获取到的Field对象之后,我们就可以自由的查看和设置对象的属性值。
关键方法:
- get(Object object)查看特定对象的属性值
- set(Object object, Object value)给特定对象设置属性
- setAccessible(boolean flag)让private成员拥有public权限
Demo
//获取Class对象
Class aClass = MyObject.class
//通过Class对象获取Field对象
Field field = aClass.getDeclaredField("someField");
//设置可访问权限
field.setAccessible(true);
MyObject objectInstance = new MyObject();
//获取特定的对象的变量属性值
Object value = field.get(objectInstance);
//给特定对象的变量设置属性
field.set(objetInstance, value);
复制代码
因为Constructor,Field,Method都继承自AccessibleObject类,所有都拥有setAccessible方法,个人感觉setAccessible有点窥探隐私的感觉,哈哈哈,不知道Class导演怎么看。
Method
通过Class对象获取到的Method对象之后,想都不用想啊,获取一个方法不调用它干嘛,所以我们最常用的操作应该就是invoke方法。
相信大家对invoke并不会陌生,因为很多的异常,最后都会定位到invoke方法。
java.lang.NullPointerException
at ......
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
复制代码
invoke(Object receiver, Object… args),可以分为两种,传递receiver参数表示调用特定对象的方法,传null表示调用静态方法。
//获取一个方法名为doSomesthing,参数类型为String的方法
Method method = MyObject.class.getMethod("doSomething", String.class);
//调用静态的doSomesthing方法,传递参数"parameter-value1"
Object returnValue = method.invoke(null, "parameter-value1");
复制代码
最后说两句
本文只是对Clss对象以及reflect包下的对象进行简单的使用说明,关于反射的实现和原理,还有待于深入研究。例如AccessibleObject对象以及对应相关xxxAccessor的实现。