`
anson_xu
  • 浏览: 502722 次
  • 性别: Icon_minigender_1
  • 来自: 惠州
社区版块
存档分类

java反射机制

    博客分类:
  • java
阅读更多

 

Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods 的所有信息,并可于运行时改变fields内容或调用methods。
 
一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。
 
尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒 影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称 的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透 class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
 
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中:
Class类:代表一个类。
Field 类:代表类的成员变量(成员变量也称为类的属性)。
Method类:代表类的方法。
Constructor 类:代表类的构造方法。
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。

1) java.lang.Class

           在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时的类型标识。这个信息保存着每个对象所属的类足迹。

      虚拟机利用运行时信息选择相应的方法执行。保存这些信息的类被称为Class,Object类中的getClass()方法将会返回一个Class类

      的实例。获取Class类对象有三种方式:

       a)Object类getClass()方法

         eg: Employee e

             .....

             Class cl = e.getClass();

       b)Class类的静态方法forName()

         eg: String className = "java.util.Date";

             Class cl = Class.forName(className);

       c)任意类型的class属性

         eg: Class cl1 = Date.class;

             Class cl2 = int.class;

             Class cl3 = Double[].class;

      :) 很有嚼头的两句话:

         :) 如同用一个Employee对象表示一个特定的顾员属性一样,一个Class对象将表示一个特定类的属性。

         :) 一个Calss对象实际上表示的是一个类型,而这个类型未必一定是一种类。例如,int不是类,但int.class是一个Class类型的对象。

 

   2) java.lang.reflect.Constructor

   3) java.lang.reflect.Field   

   4) java.lang.reflect.Method

      分别描述类的构造器,域,方法。

   5) java.lang.reflect.AccessibleObject

      为反射对象设置可访问标志。使得对象的私有属性也可以被查询和设置,是Field, Method和Constructor类的共公超类

利用反射对私有属性/方法进行设置/调用

文章分类:Java编程 关键字: 开发 反射 私有


因一时兴起看了一些有关 Java 反射( Reflection )的东西。以下要说明的问题是如何直接对某个特定类的私有属性( private field )不使用其暴露的 set 方法而是直接进行设值操作,或调用类的私有方法( private method )。

首先要说明的是,这在 java 里是允许这么做的。虽然这样直接访问私有属性或调用私有方法,会破坏了 OO 的一大基本原则:封装,但 Java 里是千真万确的提供了这么做的基础的。一些 Open source framework 的“豪华”功能也是依赖于此的。

此前在网上看到不少兄弟提出过这样的问题,有人略带讽刺的回复说这样做是不可以的。在这里不才给出一个简单的示例来说明如何完成的这个被看成 Mission Imposable 的。

首先我们建立一个测试用的类( TargetClass ):

 

package org.rossalee.test;

 

public class TargetClass {

    public String name ;

    private String age ;

 

    public TargetClass() {

       super ();

    }

 

    public void showName() {

       System. out .println( name );

    }

 

    private void showAge() {

       System. out .println( age );

    }

}

 

这个目标类里有一个 public 权限 String 类型 name 属性和一个 private 权限 String 类型的“ age ”属性,以及一个 public 方法“ showName() ”和 private 方法“ showAge() ”。

一般来说我们是可以直接操作该类的 name 属性或调用 showName() 方法的。这都是很正常。但如果想要对 age 属性进行设值的话,那就只能依靠 TargeClass 提供一个修饰为 public setAge(String age) 的设值方法,才能进行设值。但我们上面这个类里并没有提供,所以那这个 age 属性基本上是没有办法进行再设值的了。同样,我们也不可以调 TargetClass 类的 shwoAge() 方法。因为他也是 private 的。

但我们使用 Java Core API 提供的 Reflect 就可以完成这个功能。

以下是实现对私有属性进行操作的基本步骤:

<!-- -->一、              <!-- -->使用 Class 对象提供的 static 方法 forName() 方法,将类进行加载,成为 Java 里一种特殊的对象——类对象。

<!-- -->二、              <!-- -->使用 Class 对象提供的方法 getDeclaredFields() ,此方法   返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。即有公共的也有私有的。

<!-- -->三、              <!-- -->调用对于要进行操作的 field 对象的 setAccessible() 。参数传入“ true ”。此方法从 java.lang.reflect.AccessibleObject 对象中继承而来。执行该方法后,就可以对 age 属性进行操作了。但设值是要有特定的方法来实现的。另外,还有一点要说明的是,此时加载的类对象是没有实例化的,所以最终我们要进行设值的并不是这个类对象,而 TargetClass 类型的对象。这个对象你可以自己 new 出来,也可以通过调用类对象的 newInstance() 方法实例出来。在这里我们暂定这个 new 出来的 TargetClass 对象为 tc ,即:

TargetClass tc=new TargetClass()

<!-- -->四、              <!-- -->这时我们要用 Field 对象提供的 set(Object obj, Object value) 方法进行设置。这个方法有两个参数: obj 就传入我们在一步 new 出来的 tc Value 就传入 age 所属类型的值,这里的 age String 类型,这里传一个 String 进来就可以了。

 

对于私有方法的调用也基本相同,但只是最后使用的是 Method 对象的 invoke(Object obj, Object... args) 方法。 Obj 一样也是 tc args 则是 showAge() 方法的参数,这里是无参的,所以传一个 null 进去就可以了。

 

实现代码如下:

 

package org.rossalee;

 

import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;

 

import org.rossalee.test.TargetClass;

 

public class TestMain {

 

    public static void main(String[] args) {

       Class clazz = null ;

       try {

           clazz = Class.forName ( "org.rossalee.test.TargetClass" );

       } catch (ClassNotFoundException e) {

           e.printStackTrace();

       }

       Field[] f = clazz.getDeclaredFields();

       for ( int i = 0; i < f. length ; i++) {

           System. out .println( "Field " + i + " : " + f[i].getName());

       }

 

       Method[] m = clazz.getDeclaredMethods();

       for ( int i = 0; i < m. length ; i++) {

           System. out .println( "Method " + i + " : " + m[i].getName());

       }

 

       TargetClass tc = null ;

       try {

           tc = (TargetClass) clazz.newInstance();

       } catch (InstantiationException e) {

           e.printStackTrace();

       } catch (IllegalAccessException e) {

           e.printStackTrace();

       }

       f[1].setAccessible( true );

       try {

           f[1].set(tc, "kenshin" );

       } catch (IllegalArgumentException e) {

           e.printStackTrace();

       } catch (IllegalAccessException e) {

           e.printStackTrace();

       }

       m[1].setAccessible( true );

       try {

           m[1].invoke(tc, null );

       } catch (IllegalArgumentException e) {

           e.printStackTrace();

       } catch (IllegalAccessException e) {

           e.printStackTrace();

       } catch (InvocationTargetException e) {

           e.printStackTrace();

       }

    }

}

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics