java反射详解

来源:http://www.bug315.com/article/111.htm

 

一、在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

 

二、反射能干什么?

    a、在运行时判断任意一个对象所属的类。

    b、在运行时构造任意一个类的对象。

    c、在运行时判断任意一个类所具有的成员变量和方法。

    d、在运行时调用任意一个对象的方法(包括私有方法,但是破坏了封装性)

 

三、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是常被并提的两个术语

 

五、Java Reflection API 简介

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中

    a、Class类:代表一个类。

    b、Field 类:代表类的成员变量(成员变量也称为类的属性)。

    c、Method类:代表类的方法。

    d、Constructor 类:代表类的构造方法。

    e、Array类:提供了动态创建数组,以及访问数组的元素的静态方法

 

1、例程Reflect类演示了Reflection API的基本作用,它读取命令行参数指定的类名,然后打印这个类所具有的方法信息

package  reflected;
import  java.lang.reflect.Method;
/**
  * 演示反射机制
  * @author HuangXin
  * 通过类的全名,获得类中的所有方法
  */
public  class  Reflect {
     public  static  void  main(String[] args) throws  Exception{
         Class<?> classType = Class.forName(args[ 0 ]); //获得指定类的class对象
         Method[] methods = classType.getDeclaredMethods(); //返回classType对象中的所有已经声明的方法
         for (Method m : methods){ //打印所有的方法名
             System.out.println(m);
         }
     }
}

 运行:java Reflect java.lang.String

2、例程ReflectTester 类进一步演示了Reflection API的基本使用方法。ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object 同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将它返回。这个例子只能复制简单的JavaBean,假定JavaBean 的每个属性都有public 类型的getXXX()和setXXX()方法。

package  reflected;
import  java.lang.reflect.Field;
import  java.lang.reflect.Method;
/**
  * 演示使用反射机制调用getter和settled方法,操作属性
  * @author HuangXin
  *
  */
public  class  RefelctTester {
     //复制一份副本
     public  Object copy(Object object)  throws  Exception{
         Class<?> classType = object.getClass();
         //使用与所有的构造方法
         Object objectType = classType.getConstructor( new  Class[]{}).newInstance( new  Object[]{});
         //Object objectType = classType.newInstance();
         //等价于
         //Object objectType = classType.newInstance();
         //获得所有的属性
         Field[] fields = classType.getDeclaredFields();
         for ( int  i= 0 ;i<fields.length;i++){
             Field field = fields[i];
             String fieldName = field.getName();
             String first = fieldName.substring( 0 , 1 ).toUpperCase();
             String getMethodName =  "get"  + first + fieldName.substring( 1 );
             String setMethodName =  "set"  + first + fieldName.substring( 1 );
             Method getMeth = classType.getMethod(getMethodName, new  Class[]{});
             Method setMeth = classType.getMethod(setMethodName, new  Class[]{field.getType()});
             Object value = getMeth.invoke(object,  new  Object[]{});
             setMeth.invoke(objectType,  new  Object[]{value});
         }
         return  objectType;
     }
 
     public  static  void  main(String[] args)  throws  Exception {
         User user =  new  User();
         user.setId( new  Long( 1 ));
         user.setUsername( "zhangjia" );
         user.setAge( new  Integer( 22 ));
         User userCopy = (User) new  RefelctTester().copy(user);
         System.out.println(userCopy.getId()+ "," +userCopy.getUsername()+ "," +userCopy.getAge());
         System.out.println(user.getId()+ "," +user.getUsername()+ "," +user.getAge());
     }
}
 
class  User{
     private  Long id;
     private  String username;
     private  int  age;
     
     public  User(){
     
     }
     
     public  Long getId() {
         return  id;
     }
     
     public  void  setId(Long id) {
         this .id = id;
     }
     
     public  String getUsername() {
         return  username;
     }
     
     public  void  setUsername(String username) {
         this .username = username;
     }
     
     public  int  getAge() {
         return  age;
     }
     
     public  void  setAge( int  age) {
         this .age = age;
     }
}

(1)获得对象的类型:ReflectTester 类的copy(Object object)方法依次执行以下步骤

    Class classType=object.getClass();

    System.out.println("Class:"+classType.getName());

 

    在java.lang.Object 类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API 中的核心类,它有以下方法

    getName():获得类的完整名字。

    getFields():获得类的public类型的属性。

    getDeclaredFields():获得类的所有属性。

    getMethods():获得类的public类型的方法。

    getDeclaredMethods():获得类的所有方法。

    getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。

    getConstructors():获得类的public类型的构造方法。

    getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。

    newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

 

(2)通过默认构造方法创建一个新对象:

    Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});

    以上代码先调用Class类的getConstructor()方法获得一个Constructor 对象,它代表默认的构造方法,然后调用Constructor对象的newInstance()方法构造一个实例。

 

(3)获得对象的所有属性:

    Field fields[]=classType.getDeclaredFields();

    Class 类的getDeclaredFields()方法返回类的所有属性,包括public、protected、默认和private访问级别的属性

 

(4)获得每个属性相应的getXXX()和setXXX()方法,然后执行这些方法,把原来对象的属性拷贝到新的对象中

 

3、在例程InvokeTest类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package  reflected;
import  java.lang.reflect.Method;
/**
  * 演示用反射机制调用类中的方法
  * @author
 
  */
public  class  InvokeTest {
     public  int  add( int  sum1, int  sum2){
         return  sum1+sum2;
     }
     
     public  String echo(String message){
         return  "goods:" +message;
     }
     
     public  static  void  main(String[] args)  throws   Exception {
         //获得INnvokeTest类的Class对象
         Class<?> classType = InvokeTest. class ;
         //创建classType对象的实例(此种方式只能创建默认的构造方法,即没有参数的构造方法)
         Object invokeTester = classType.newInstance();
         //返回指定的方法,方法可以用方法名和参数列表区别
         Method addMethod = classType.getMethod( "add" , new  Class[]{ int . class , int . class });
         //调用方法
         Object result = addMethod.invoke(invokeTester,  new  Object[]{ 100 , 200 });
         System.out.println((Integer)result);
         //等价一下代码
         //InvokeTest i = new InvokeTest();
         //int result = i.add(100,200);
         //System.out.println(result);
         Method echoMethod = classType.getMethod( "echo" , new  Class[]{String. class });
         Object result2 = echoMethod.invoke(invokeTester,  new  Object[]{ "huangxin" });
         System.out.println((String)result2);
     }
}

add()方法的两个参数为int 类型,获得表示add()方法的Method对象的代码如下:

    Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});

    Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回

 

    在本例中,尽管InvokeTester 类的add()方法的两个参数以及返回值都是int类型,调用add Method 对象的invoke()方法时,只能传递Integer 类型的参数,并且invoke()方法的返回类型也是Integer 类型,Integer 类是int 基本类型的包装类:

    Object result=addMethod.invoke(invokeTester,

    new Object[]{new Integer(100),new Integer(200)});

    System.out.println((Integer)result); //result 为Integer类型

 

4、java.lang.Array 类提供了动态创建和访问数组元素的各种静态方法。例程ArrayTester1 类的main()方法创建了一个长度为10 的字符串数组,接着把索引位置为5 的元素设为“hello”,然后再读取索引位置为5 的元素的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package  com.langsin.reflection;
import  java.lang.reflect.Array;
public  class  ArrayTester1
{
     public  static  void  main(String args[])  throws  Exception
     {
         Class<?> classType = Class.forName( "java.lang.String" );
         // 创建一个长度为10的字符串数组
         Object array = Array.newInstance(classType,  10 );
         // 把索引位置为5的元素设为"hello"
         Array.set(array,  5 "hello" );
         // 获得索引位置为5的元素的值
         String s = (String) Array.get(array,  5 );
         System.out.println(s);
     }
}

 

5、例程ArrayTester2 类的main()方法创建了一个 5 x 10 x 15 的整型数组,并把索引位置为[3][5][10] 的元素的值为设37

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package  com.langsin.reflection;
import  java.lang.reflect.Array;
public  class  ArrayTester2 {
     public  static  void  main(String args[]){
         int [] dims =  new  int [] {  5 10 15  };
         Object array = Array.newInstance(Integer.TYPE, dims);
         Object arrayObj = Array.get(array,  3 );
         Class<?> cls = arrayObj.getClass().getComponentType();
         System.out.println(cls);
         arrayObj = Array.get(arrayObj,  5 );
         Array.setInt(arrayObj,  10 37 );
         int  arrayCast[][][] = ( int [][][]) array;
         System.out.println(arrayCast[ 3 ][ 5 ][ 10 ]);
     }
}

 

六、Class

    众所周知Java有个Object class,是所有Java classes的继承根源,其内声明了数个应该在所有Java class中被改写的methods:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class object。

 

    Class class十分特殊。它和一般classes一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types

(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class object。如果您想借由“修改Java标准库源码”来观察Class object的实际生成时机(例如在Class的constructor内添加一个println()),不能够!因为Class并没有public constructor

 

    Class是Reflection起源。针对任何您想探勘的class,唯有先为它产生一个Class object,接下来才能经由后者唤起为数十多个的Reflection APIs

 

Java允许我们从多种途径为一个class生成对应的Class object:

a、运用getClass()注:每个类都有此函数

1
2
String str =  "abc" ;
Class c1 = str.getClass();

 

b、运用Class.getSuperclass()

1
2
3
Button b =  new  Button();
Class c1 = b.getClass();
Class c2 = c1.getSuperclass();

 

c、运行static method Class.forName()(最常用)

1
Class c1 = Class.forName( "java.lang.String" );

 

d、运用.class语法

1
2
Class c1 = String. class ;
Class c5 =  int []. class ;

 

e、运用primitive wrapper classes的TYPE语法

1
2
Class c1 = Boolean.TYPE;
Class c2 = Byte.TYPE;

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package  reflected;
import  java.awt.Button;
public  class  GetSuperClassTest {
     /**
      * @param args
      */
     public  static  void  main(String[] args) {
         // TODO Auto-generated method stub
         Button button =  new  Button();
         Class<?> c1 = button.getClass(); //返回button的class对象
         System.out.println(c1);
         Class<?> c2 = c1.getSuperclass(); //返回button的父对象
         System.out.println(c2);
         Class<?> c3 = c2.getSuperclass(); //返回button的父对象的父对象
         System.out.println(c3);
     }
}

 

七、运行时生成instances

    欲生成对象实体,在Reflection 动态机制中有两种作法,一个针对“无自变量ctor”,一个针对“带参数ctor”。如果欲调用的是“带参数ctor“就比较麻烦些,不再调用Class的newInstance(),而是调用Constructor 的newInstance()。首先准备一个Class[]做为ctor的参数类型(本例指定为一个double和一个int),然后以此为自变量调用getConstructor(),获得一个专属ctor。接下来再准备一个Object[] 做为ctor实参值(本例指定3.14159和125),调用上述专属ctor的newInstance()。

如果是无参构造方法:

1
2
Class c = Test. class ;
Object obj = c.newInstance();

 

有参构造方法:

1
2
3
4
5
Class c = Test. class ;
Class[] classType = {Integer. class ,String. class };
Constructor constructor = c.getConstructor(classType);
Object[] param = { 23 , "haha" };
Object obj = constructor.newInstance(param);

 

八、运行时调用methods

    这个动作和上述调用“带参数之ctor”相当类似。首先准备一个Class[]做为参数类型(本例指定其中一个是String,另一个是Hashtable),然后以此为自变量调用getMethod(),获得特定的Method object。接下来准备一个Object[]放置自变量,然后调用上述所得之特定Method object的invoke()。

 

    为什么获得Method object时不需指定回返类型?因为method overloading机制要求signature必须唯一,而回返类型并非signature的一个成份。换句话说,只要指定了method名称和参数列,就一定指出了一个独一无二的method。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Class c = Test.calss;
Class types =  new  Class[ 2 ];
types[ 0 ] = Class.forName( "java.lang.String" );
types[ 1 ] = Class.forName( "java.lang.Integer" );
Methoethod = c.getMethod( "testMethod" ,types);
Test obj =  new  Test();
Object[] param = { "haha" , 23 };
Object r = method.invoke(obj,param);
 
// ******************************
package  reflected;
import  java.lang.reflect.Method;
@SuppressWarnings ( "unchecked" )
public  class  MethodTest {
     public  static  void  test(Object obj) throws  Exception{
         Class<MyMethod> type = (Class<MyMethod>)obj.getClass();
         Object objectType = type.getConstructor( new  Class[]{}).newInstance( new  Object[]{});
         Method method = type.getMethod( "prin" , new  Class[]{});
         method.invoke(objectType,  new  Object[]{});
         method = type.getMethod( "add" new  Class[]{ int . class , int . class });
         Object result = method.invoke(objectType,  new  Object[]{ 3 , 4 });
         System.out.println(result);
         System.out.println(result.getClass().getName());
     }
 
     public  static  void  main(String[] args) {
         MyMethod p =  new  MyMethod();
         try  {
             test(p);
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
}
 
class  MyMethod{
     public  MyMethod(){}
 
     public  void  prin(){
         System.out.println( "Hello World!" );
     }
     
     public  int  add( int  x, int  y){
         return  x + y;
     }
}

 

 

查看全文

 

 

 

你可能感兴趣的:(java反射)