java.lang.reflect包的浅析

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的实现。

你可能感兴趣的:(java.lang.reflect包的浅析)