您的位置:首页 > 编程语言 > Java开发

Java反射机制(一)

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

Class class

众所周知Java有个Object class,是所有Java classes的继承根源,其内声明了数个应该在所有Java class中被重写的methods:hashCode()、equals()、clone()、toString(),getClass()等。其中getClass()返回一个Class object。Class class十分特殊。它和一般classes一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达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协同工作,完成反射任务。


public final


    class Class<T> implements java.io.Serializable, 


                  java.lang.reflect.GenericDeclaration, 


                  java.lang.reflect.Type,<
205a5
br />



                              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 classes的TYPE 语法

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,其中出现的Package、Method、Constructor、Field等等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());



带参数的构造方法

调用的是“带参数的构造方法”就比较麻烦些,其中不再调用Class的newInstance(),而是调用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 object的invoke()。获取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内容”轻松多了,因为它不需要参数和自变量。首先调用Class的getField()并指定field名称。获得特定的Field object之后便可直接调用Field的get()和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.csdn.net/down4/20070722/22215048468.rar
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息