`
xifangyuhui
  • 浏览: 185897 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java反射学习笔记

阅读更多

java反射作用:

1、自省:通过对象自身获得类结构,继而获取并修改字段值,调用方法,构造实例。

2、String->Object:通过类字符串构造类实例

传统创建一个对象 或者是调用对象的一个方法 ,需要在程序里将创建对象的语句,即new一个对象,都需要在程序里面写死,即在程序中硬编码。众所周知,硬编码是一种不好的编程方式,最好的方法是在程序中通过配置信息(String字符串)动态创建对象或是动态调用对象的方法。

Class对象 :类对象,它是JAVA自省机制的起点 一个对象要知道自己有什么,首先肯定需要知道自己是什么,即首先获知它是哪个类的对象,那么怎么获知这个对象呢?

这里不得不提到类加载 ,类经过编译后会生成一个Class字节码文件 当生成某个类的实例时,首先需要JVM将对应类的Class文件加载 进来,JVM会在类路径下(ClassPath)下根据包名去相应路径下搜索目标类的Class文件,找到后将其读到虚拟机中,这就是所谓的 类加载 ,这样JVM便获知了类的信息。读到JVM中后,更进一步地说,JVM需要将类存起来,即 封装 ,这样JVM便会把类信息封装在一个对象中,这个记录了类信息的对象就是 类对象 ,即Class对象,类对象是JVM加载类的一个产物,只要类对象没有消失,就不需要再加载类对象,这样JVM通过类对象就知道了类的组成结构,继而也就能创建类的实例了。当我们 首次使用某个类的时候才会发生类加载 ,什么才算使用呢?创建一个类的对象,访问一个类的静态属性或者方法,注意声明一个类的引用时不会产生类加载,JVM不需要知道这个类是什么,它只在栈空间中为这个引用分配四个字节就行了,在真正使用类的时候才会加载类。

大家都知道面向对象的一个重要特征便是 封装 ,JVM会把类对象中的每个字段封装成一个Field对象,把每个构造方法封装成一个Constructor对象,把每个方法封装成一个Method对象,这些对象再一同封装为类对象,我们可以理解为封装的封装。

程序中如何获取类对象呢?三种方法:

  • 类名.class: 八种基本数据类型也通过.class获得类对象,需要注意,int.class和Integer.class是不一样的 它们是两个不同的类对象。
  • 对象.getClass(): 来获取对象所属类的类对象,这个方法是继承于Object的,因此所有类都拥有这个方法。
  • Class.forName(类名字符串): 通过Class的静态方法forName得到类字符串代表的类对象

获 取了类对象之后,我们继而可以获得该类中的Field对象,Constructor对象,Method对象,再通过上面这些对象获得字段,构造方法,方法 的详细信息。这里需要注意的是getFileds()与getDeclaredFields()的区别,其实大家看API也能看到,这里简述一下:

  • getDeclaredFields ()    返回类中的所有字段对象,甚至包括私有的字段,但是需要注意的是它不会返回父类中的字段。
  • getFields () 返回该类及其父类的公有字段,即public修饰的字段。

获取Method[]以及Constructor[]同样的道理。

另外,获得指定的Method时, 除了传递方法的名字外,还需要传递参数类对象数组 ,以此参数类对象数组来判断调用的是哪个方法,因为方法可以重载嘛,而参数的类型,顺序,个数决定了不同的重载方法。

通过反射,我们 还可以对对象进行一些操作 。上面已经讲到我们可以通过反射获得类成员变量本身的一些信息,这里我们不仅仅是想获得类的结构信息,我们还想获取和修改字段的值,另外可能还要调用某个方法,构造一个对象实例等等。即对此类进行 动态操作 ,我们可以把获取类结构看做对类的 静态操作 。但是要注意,如果类的字段 是private的而且没有提供get和set接口 ,我们还可以操作该字段吗?答案是可以的。

那么我们 怎么获取和修改另外一个类的私有字段呢?

  • 首先通过对象获得类对象(调用对象的getClass());
  • 接着需要得到私有成员字段包装的Field对象(调用类对象的getDeclaredFields());
  • 接下来下面大家可以查看一下Field对象的API,其中有一个get方法:

public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException

 

参数object指的是针对哪个对象获取相应字段的值,如果你直接通过调用get()并传入对象去获取私有字段的值,会得到 IllegalAccessException 异常。这里我们需要用到另外一个关键的方法: setAccessible(boolean flag) ,该方法来自于Field,Constructor,Method的父类 AccessibleObject ,大致浏览该类的API大家可以发现,该类主要用来对 Field, Constructor,Method的访问控制检测进行压制( It provides the ability to flag a reflected object as suppressing default Java language access control checks when it is used ), 访问控制检测大家应该都知道是什么意思,这里调用setAccessible(true),顾名思义,表示设置可访问性,其是对当前Filed或者 Constructor或者Method对象进行访问控制检测压制,这样我们便可以通过get(Object obj)来获得私有字段的值了。

  • 我们还可以设置该字段的值,调用set方法:

public void set(Object obj, Object value) throws IllegalArgumentException, illegalAccessException

进行赋值。

调用私有方法怎么做呢?

  • 首先调用类对象的

 

   getDeclaredMethod ( String  name, Class <?>... parameterTypes) throws NoSuchMethodException , SecurityException

传递两个参数,一个是参数的名字,另外一个是方法的参数类对象数组,用来确定调用的是哪个方法。

  • 接着查看Method类的API可以看到invoke方法:

invoke ( Object  obj, Object ... args)    Invokes the underlying method represented by this Method object, on the specified object with the specified  parameters.

 

这里需要传递两个参数,第一个参数表示对哪个对象调用invoke方法,第二参数是一个Object数组,即参数值
。返回一个Object对象,即调用方法的返回值。如果调用的是一个void方法,那么调用invoke方法返回的是null。

如何通过类对象创建实例呢? 第一种方法:直接调用类对象的newInstance()来构造一个实例。不知道大家注意到没有,创建实例的时候并没有传递参数,也就是说原来的类中必须有一个无参数的构造方法,否则该方法不能调用,那么带参数的构造方法怎么调用呢?
第二种方法:通过类对象调用:

public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException

 

传递的参数大家应该都清楚了,和上面获取指定Method一样,传递一个参数类对象数组,用于确认到底获取那个构造方法。接下来,再调用Constructor的


public T newInstance(Object... initargs) throws InstantiationException, 

 LegalAccessException,IllegalArgumentException, InvocationTargetException

并传递参数就可以创建对象了。

 

大家 现在应该知道JAVABean中为什么要必须包含一个无参的构造方法以及包含一系列public的get,set方法了,这样 工具便可以通过反射使用类对象的newInstance直接创建对象了,同时由于方法名都是有规律的,只要知道字段名,便可以通过构造get/set+字段名(首字母大写)字符串得到对应字段的Method对象,继而调用它们来获取和设置值了 ,这一切都可以由工具自动来完成。

 

 

0
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics