Java反射机制(一)

Reflection Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时通过Reflection APIs取得任何一个已知名称class的内部信息,包括其modifierssuperclassinterfaces,也包括fieldsmethods的所有信息,并可于运行时改变fields的值或执行methods。下面通过例子介绍Java反射机制和Reflection APIs。让大家知道如何反射class的结构、如何对某个“运行时才获知名称的class”生成一个实例、为其fields设值、调用其methods。本文将谈到java.lang.Class,以及java.lang.reflect中的MethodFieldConstructor等等classes

Class class

众所周知Java有个Object class,是所有Java classes的继承根源,其内声明了数个应该在所有Java class中被重写的methodshashCode()equals()clone()toString()getClass()等。其中getClass()返回一个Class objectClass class十分特殊。它和一般classes一样继承自Object,其实体用以表达Java程序运行时的classesinterfaces,也用来表达arrayprimitive Java typesboolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被装载,或当类装载器(class loader)的defineClass()JVM调用时,JVM 便自动产生一个Class object。如果您想借由“修改Java标准库源码”来观察Class object的实际生成时机(例如在Classconstructor内添加一个println()),不能够!因为Class并没有public constructor(参看下在的源码)。

ClassReflection的起点。针对任何您想射的class,唯有先为它产生一个Class object,接下来才能经这个对象和数十个Reflection APIs协同工作,完成反射任务。

public   final
    
class  Class < T >   implements  java.io.Serializable, 
                  java.lang.reflect.GenericDeclaration, 
                  java.lang.reflect.Type,
                              java.lang.reflect.AnnotatedElement 
{
    
private static final int ANNOTATION= 0x00002000;
    
private static final int ENUM      = 0x00004000;
    
private static final int SYNTHETIC = 0x00001000;

    
private static native void registerNatives();
    
static {
        registerNatives();
}

    
/*
     * Constructor. Only the Java Virtual Machine creates Class
     * objects.
     
*/

private Class() {}

}


仔细看一下上面的源码。他的构造方法是private并且为空,这就意味着不允许任何人使用编程的方式产生Class object。其object 只能由JVM 产生。

Class object的取得方法:

Java允许我们从多种方法为一个class生成对应的Class object。整理如下:

Class object 产生方法

示例

运用getClass()

String str = "abc";

Class c1 = str.getClass();

 

运用Class.getSuperclass()

Button b = new Button();

Class c1 = b.getClass();

Class c2 = c1.getSuperclass();

运用static method

Class.forName()(最常被使用)

Class c1 = Class.forName ("java.lang.

String");

Class c2 = Class.forName ("java.awt.Button");

Class c3 = Class.forName ("java.util.

LinkedList$Entry");

Class c4 = Class.forName ("I");

Class c5 = Class.forName ("[I");

运用.class 语法

Class c1 = String.class;

Class c2 = java.awt.Button.class;

Class c3 = Main.InnerClass.class;

Class c4 = int.class;

Class c5 = int[].class;

 

运用 primitive wrapper classesTYPE 语法

Class c1 = Boolean.TYPE;

Class c2 = Byte.TYPE;

Class c3 = Character.TYPE;

Class c4 = Short.TYPE;

Class c5 = Integer.TYPE;

Class c6 = Long.TYPE;

Class c7 = Float.TYPE;

Class c8 = Double.TYPE;

Class c9 = Void.TYPE;

 

Java classes 组成分析

首先以Employee为例,将Java class的定义进行分解,每一块对应相应的Reflection API
package  com.rong.reflect; // (1)

import  java.io.Serializable; // (2)
import  java.util.ArrayList;
import  java.util.List;

public   class  Employee  // (3)(4)
     extends  Person  // (5)
     implements  Serializable  {//(6)
    private static final long serialVersionUID = 1L;
    
private String dept;//(7)
    private List<String> email = new ArrayList<String>();//(8)
    
    
class Work{}//(9)
    
    
public Employee() {//(10)
        super();
    }

    
public String getDept() {//(11)
        return dept;
    }

    
public void setDept(String dept) {
        
this.dept = dept;
    }

    
public List<String> getEmail() {
        
return email;
    }

    
public void setEmail(List<String> email) {
        
this.email = email;
    }

    
public void addEmail(String mail){
        
if(!getEmail().contains(mail)){
            getEmail().add(mail);
        }

    }

    
public void removeEmail(String mail){
        
if(getEmail().contains(mail)){
            getEmail().remove(mail);
        }

    }

}

上面的Java class成份,分别对应于下表的Reflection API,其中出现的PackageMethodConstructorField等等classes,都定义于java.lang.reflect

Java class

部模块

Java class 内部模块说明

相应之Reflection API,多半为Class methods

返回值类型(return type)

(1) package

class隶属哪个package

getPackage()

Package

(2) import

class导入哪些classes

--

--

(3)modifier

  class(或methods,fields)的属性

int getModifiers()

Modifier.toString(int)Modifier.isInterface(int)

int

String

bool

(4) class name or Interface name

class/interface名称

getName()

String

(5) base class

base class(只可能一个)

getSuperClass()

Class

(6)Implemented interfaces

实现有哪些interfaces

getInterfaces()

Class[]

(7) fields

字段(成员变量)不论 public private 或其它access level,皆可获得

getDeclaredFields()

Field[]

(8) type parameters

参数化类型的名称

getTypeParameters()

TypeVariable<Class>[]

(9) inner classes

内部classes

getDeclaredClasses()

Class[]

(9') outer class

如果我们观察的class 本身是inner classes,那么相对它就会有个outer class

getDeclaringClass()

Class

(10)

constructors

构造函数不论 public private 或其它access level,皆可获

getDeclaredConstructors()

Constructor[]

 

(11) methods

操作函数不论 public private 或其它access level,皆可获得。

getDeclaredMethods()

 

Method[]

 

 

以上是每一块所对应的Reflection API(并非Reflection APIs 的全部)。通过以上API我们几乎可以还原一个类的源代码(构造方法和方法的定义不能得到).对于导入的类比较麻烦,因为没有直接提供相应的API。我们必须观察所有fields的类型、所有methods(包括constructors)的参数类型和回返类型,去掉重复的,留下唯一。有兴趣可以一起讨论.

Java Reflection API

Java Reflection 有三个动态性质:(1) 运行时生成instances(2) 执行期间调用methods,(3) 运行时更改动fields

运行时生成instances

要生成对象实例,在Reflection 动态机制中有两种作法,一个针对“无参数的构造方法”,一个针对“带参数的构造方法”。下面分别介绍如下:

无参数的构造方法

Class <?>   c   =   null ;
        
try   {
            c 
= Class.forName("com.rong.reflect.Employee");
        }
  catch  (ClassNotFoundException e)  {
            e.printStackTrace();
        }

        
        Object obj 
=   null ;
        
try   {
            obj 
= c.newInstance();
        }
  catch  (InstantiationException e)  {
            e.printStackTrace();
        }
  catch  (IllegalAccessException e)  {
            e.printStackTrace();
        }

        System.out.println(obj.toString());

带参数的构造方法

调用的是“带参数的构造方法就比较麻烦些,其中不再调用ClassnewInstance(),而是调用Constructor newInstance()。首先准备一个Class[]做为构造方法的参数类型(本例指定为一个String和一个List),然后用该变量调用getConstructor(),获得一个指定参数的构造方法.接下来再准备一个Object[] 做为构造方法实参值,调用上述指定构造方法的newInstance()

Class <?>   c   =   null ;
        
try   {
            c 
= Class.forName("com.rong.reflect.Employee");
        }
  catch  (ClassNotFoundException e)  {
            e.printStackTrace();
        }

        Class
<?> [] pTypes  =   new  Class[]  { String.class, List.class } ;
        Constructor
<?>  ctor  =   null ;
        
try   {
            ctor 
= c.getConstructor(pTypes);//指定parameter列表,便可获得特定的构造方法.
        }
  catch  (Exception e)  {
            e.printStackTrace();
        }

        Object obj 
=   null ;
        Object[] arg 
=   new  Object[]  {"烟草事业部"new ArrayList<String>()} // 参数值
         try   {
            obj 
= ctor.newInstance(arg);
        }
  catch  (Exception e)  {
            e.printStackTrace();
        }
 
        System.out.println(obj);

执行期间调用methods

这个动作和上述调用“带参数的构造方法”相当类似。首先准备一个Class[]做为方法的参数类型,然后以此为自变量调用getMethod(),获得特定的Method object。接下来准备一个Object[]放置参数值,然后调用上述所得之特定Method objectinvoke()。获取Method object时不需指定方法的返回类型吗?因为method overloading机制要求signature(签名)必须唯一,而返回类型并非signature的一个成份。换句话说,只要指定了method名称和参数列表,就能确定一个唯一的方法。

Class <?>   c   =   null ;
        
try   {
            c 
= Class.forName("com.rong.reflect.Employee");
        }
  catch  (ClassNotFoundException e)  {
            e.printStackTrace();
        }

        
        Class
<?> [] pTypes  =   new  Class[] {String.class} ;
        Method m 
=   null ;
        
try   {
            m 
= c.getMethod("addEmail",pTypes);
        }
  catch  (Exception e)  {
            e.printStackTrace();
        }
 
        Employee obj 
=   new  Employee();
        Object arg[] 
=   new  Object[ 1 ];
        arg[
0 =   new  String( " rong@163.com " );
        
try   {
            Object r 
= m.invoke(obj, arg);
        }
  catch  (Exception e)  {
            e.printStackTrace();
        }

运行时更改动Fields

与先前两个动作相比,“更改field内容”轻松多了,因为它不需要参数和自变量。首先调用ClassgetField()并指定field名称。获得特定的Field object之后便可直接调用Fieldget()set().

Class <?>   c   =   null ;
        
try   {
            c 
= Class.forName("com.rong.reflect.Employee");
        }
  catch  (ClassNotFoundException e)  {
            e.printStackTrace();
        }

         Field f 
=   null ;
        
try   {
            f 
= c.getField("dept"); //指定field 名称
        }
  catch  (Exception e)  {
            e.printStackTrace();
        }
 
         Employee obj 
=   new  Employee( " 烟草事业部 " new  ArrayList < String > ());
         
try   {
            System.out.println(
"dept = " + (String)f.get(obj));
            f.set(obj, 
"技术平台开发部");
            System.out.println(
"dept = " + (String)f.get(obj));
         }
  catch  (Exception e)  {
            e.printStackTrace();
        }

小结

本文只对java反射API作了基本的介绍.希望对大家有所帮助.在程序中还有一些细节不是很完善.比如:如何访问受保护的域,如何反射类中所导入的其它类.以后将对这些方面进行介绍,并给出java反射在对象持久化中的运用.

以下是java源代码和本文的word文档.

http://dl2.youkuaiyun.com/down4/20070722/22215048468.rar

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值