Java中的反射机制!!!------阿冬专栏

 

java的反射机制


《第一篇》
反射是Java程序开发语言的特征之一。
它允许动态地发现和绑定类、方法、字段,以及所有其他的由语言所产生的元素。

Java反射机制主要提供了以下功能:
运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法。通过反射甚至可以调用到private的方法;

Java反射所需要的类并不多,主要有java.lang.Class类和java.lang.reflect包中的Field、Constructor、Method、Array类。

Class类:Class类的对象表示正在运行的 Java 应用程序中的类和接口。
Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。它封装了反射类的构造方法。  
Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。它是用来封装反射类方法的一个类。
Array类提供了动态创建数组和访问数组的静态方法。该类中的所有方法都是静态方法 

class类

JVM为每种类型管理着一个独一无二的Class对象----每个类(型)都有一个Class对象。
Java程序运行过程中,当需要创建某个类的实例时,JVM首先检查所要加载的类对应的Class对象是否已经存在。如果还不存在,JVM就会根据类名查找对应的字节码文件并加载,接着创建对应的Class对象,最后才创建出这个类的实例。
运行中的类或接口在JVM中都会有一个对应的Class对象存在,它保存了对应类或接口的类型信息。要想获取类或接口的相应信息,需要先获取这个Class对象。 

获得Class对象

调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法。
例如, MyObject x=new MyObject(); Class c1 = x.getClass();
使用Class类的forName()静态方法获得与字符串对应的Class对象
Class c2=Class.forName("java.lang.String"); 
注意:参数字符串必须是类或接口的完全限定名
使用类型名.class获取该类型对应的Class对象。
例如,Class cl1 = Manager.class; Class cl2 = int.class;    Class cl3 = double[].class;

使用反射机制来获取类的详细信息、创建类的对象、访问属性值、调用类的方法等。

 获取类型信息

要在运行时获取某个类型的信息,需先获取这个类型所对应的Class对象,然后调用Class类提供的相应方法来获取。
获取指定类对应的Class对象 Class 
clazz = Class.forName("java.util.ArrayList");
获取指定类的包名clazz.getPackage()获取此Class对象所对应的实体所在的包的信息描述类java.lang.Package的一个对象。
String packageName = clazz.getPackage().getName();
获取指定类的修饰符 通过Class对象的getModifiers()方法可以获取此Class对象所对应的实体的用整数表示的类修饰符值。
int mod = clazz.getModifiers(); String modifier = Modifier.toString(mod);
获取指定类的完全限定名 className = clazz.getName();
获取指定类的父类 superClazz = clazz.getSuperclass();
获取指定类实现的接口 
通过Class对象的getInterfaces()方法可以获取此Class对象所对应的实体实现的所有接口的Class对象数组。
Class[] interfaces = clazz.getInterfaces();

获取指定类的成员变量

通过Class对象的getFields()方法可获取此Class对象所对应的实体的所有public字段(成员变量)。如果要获取所有字段,可以使用getDeclaredFields()方法。

注意:方法返回的是java.lange.reflect.Field类的对象数组。Field类用来代表字段的详细信息。通过调用Field类的相应方法可以获取字段的修饰符、数据类型、字段名等信息。

获取类的构造方法 通过Class对象的getConstructors()方法可获取该Class对象所对应的实体的所有public构造方法。如果要获取所有的构造方法,可以使用getDeclaredConstructors()方法。
Constructor[] constrcutors = clazz.getDeclaredConstructors();
获取类的成员方法 通过Class对象的getMethods()方法获取到的是该Class对象所对应的实体的所有public成员方法。如果要获取所有成员方法,可以使用getDeclaredMethods()方法。
Method[] methods = clazz.getDeclaredMethods(); 

 创建对象      
      以前,创建对象的方法通常都是通过new操作符调用该类的构造方法来创建的。
例如,Date currentDate = new Date();
      大多数情况下,这种方式已足够满足需求。因为在编译期间,已经知道要创建的对象所对应的类名称。
但是,如果现在编写一个开发工具软件,将可能直到运行时才知道要创建的对象所对应的类名称。
例如,一个GUI开发工具可以让用户拖拽各种图形组件到设计界面上。

public Object create(String className){   
       根据类名来创建出它的对象
       返回这个新创建的对象
}

使用无参构造方法 

如果要使用无参的构造方法创建对象,只需调用这个类对应的Class对象的newInstance()方法。 Class c = Class.forName("java.util.ArrayList"); List list = (List) c.newInstance();
需要注意的是:如果指定名称的类没有无参构造方法,在调用newInstance()方法时会抛出一个NoSuchMethodException异常。 

使用带参数的构造方法 

要使用带参数的构造方法来创建对象,可以分为如下3个步骤来完成。
第1步  获取指定类对应的Class对象。
第2步  通过Class对象获取满足指定参数类型要求的Constructor对象。
第3步  调用指定Constructor对象的newInstance方法,传入对应的参数值,创建对象。

调用方法 使用反射可以取得指定类的指定方法的对象代表,方法的对象代表就是java.lang.reflect.Method类的实例,通过Method类的invoke方法可以动态调用这个方法。
public Object invoke(Object obj, Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException
该方法的第一个参数是一个对象类型,表示要在指定的这个对象上调用这个方法
第二个参数是一个可变参数,用来给这个方法传递参数值;
invoke方法的返回值用来表示动态调用指定方法后的实际返回值。

访问成员变量的值 

使用反射可获取类的成员变量的对象代表,成员变量的对象代表是java.lang.reflect.Field类的实例,可以使用它的getXXX方法来获取指定对象上的值,也可以调用它的setXXX方法来动态修改指定对象上的值,
其中的XXX表示成员变量的数据类型。

       在实际开发中,为了达到各个类之间的依赖关系剥离(也就是经常说的解耦),在对类的调用和实例化的时候,通过在配置文件中配置相应的类名,在程序中读取类名,然后通过反射技术在程序中加载和实例化,如常见的数据库驱动程序类,为了达到不依赖特定数据库驱动类,将用到的数据库驱动类名放到配置文件中(常用的有XML文件、Properties文件和文本文件),然后在程序中加载驱动,来实现对数据库的解耦,也就是说只要修改配置文件,就可以方便地更改数据库类型。 

  Properties文件的处理

Java中的properties文件是一种配置文件,主要用于表达配置信息,文件类型为*.properties,格式为文本文件,文件的内容是格式是“键=值”的格式,也就是说文件的每一行都是先定义一个键名,然后等于号后面是值,在properties文件中,可以用“#”来作注释,
properties文件在Java编程中用到的地方很多,操作很方便。最常见的操作该类文件的方法是通过Properties类来完成。
JDK 中的 Properties 类存在于包java.util 中,该类继承自 Hashtable ,主要方法包括:
getProperty(String key):用指定的键在此属性列表中搜索属性。也就是通过参数 key ,得到key所对应的 value。

load(InputStream  inStream) :从输入流中读取属性列表(键和元素对)。通过对指定的文件进行装载来获取该文件中的所有键-值对,以供getProperty(String key) 来搜索。
setProperty(String  key, String  value):调用Hashtable的方法put 。来设置“键-值”对。
store(OutputStream out, String comments):以适合使用load方法加载到Properties表中的格式,将此Properties表中的属性列表(键和元素对)写入输出流。
clear ():清除所有装载的“键-值”对。


《第二篇》

Java Reflection (JAVA反射) 详解 Java语言反射提供一种动态链接程序组件的多功能方法。它允许程序创建和控制任何类的对象(根据安全性限制),无需提前硬编码目标类。这些特性使得反射特别适用于创建以非常普通的方式与对象协作的库。例如,反射经常在持续存储对象为数据库、XML或其它外部格式的框架中使用。Java reflection 非常有用,它使类和数据结构能按名称动态检索相关信息,并允许在运行着的程序中操作这些信息。Java 的这一特性非常强大,并且是其它一些常用语言,如 C、C++、Fortran 或者 Pascal 等都不具备的。 但反射有两个缺点。第一个是性能问题。用于字段和方法接入时反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。即使测试中最坏情况下的计时图显示的反射操作只耗用几微秒。仅反射在性能关键的应用的核心逻辑中使用时性能问题才变得至关重要。 许多应用中更严重的一个缺点是使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂,正如性能比较的代码实例中看到的一样。解决这些问题的最佳方案是保守地使用反射——仅在它可以真正增加灵活性的地方——记录其在目标类中的使用。  Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。  Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。  JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。1. 一个简单的例子  考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。import java.lang.reflect.*;public class DumpMethods {public static void main(String args[]) {try {Class c = Class.forName(args[0]);Method m[] = c.getDeclaredMethods();for (int i = 0; i < m.length; i++)System.out.println(m[i].toString());} catch (Throwable e) {System.err.println(e);}}}  按如下语句执行:java DumpMethods java.util.Stack  它的结果输出为:public java.lang.Object java.util.Stack.push(java.lang.Object)public synchronized java.lang.Object java.util.Stack.pop()public synchronized java.lang.Object java.util.Stack.peek()public boolean java.util.Stack.empty()public synchronized int java.util.Stack.search(java.lang.Object)  这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。  这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。2.开始使用 Reflection  用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。   下面就是获得一个 Class 对象的方法之一:Class c = Class.forName("java.lang.String");  这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:Class c = int.class;  或者Class c = Integer.TYPE;  它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。  第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。  一旦取得这个信息,就可以进行第三步了——使用 reflection API 来操作这些信息,如下面这段代码:Class c = Class.forName("java.lang.String");Method m[] = c.getDeclaredMethods();System.out.println(m[0].toString());  它将以文本方式打印出 String 中定义的第一个方法的原型。  在下面的例子中,这三个步骤将为使用 reflection 处理特殊应用程序提供例证。模拟 instanceof 操作符  得到类信息之后,通常下一个步骤就是解决关于 Class 对象的一些基本的问题。例如,Class.isInstance 方法可以用于模拟 instanceof 操作符:class A {}public class instance1 {public static void main(String args[]) {try {Class cls = Class.forName("A");boolean b1 = cls.isInstance(new Integer(37));System.out.println(b1);boolean b2 = cls.isInstance(new A());System.out.println(b2);} catch (Throwable e) {System.err.println(e);}}}  在这个例子中创建了一个 A 类的 Class 对象,然后检查一些对象是否是 A 的实例。Integer(37) 不是,但 new A()是。3.找出类的方法  找出一个类中定义了些什么方法,这是一个非常有价值也非常基础的 reflection 用法。下面的代码就实现了这一用法:import java.lang.reflect.*;public class method1 {private int f1(Object p, int x) throws NullPointerException {if (p == null)throw new NullPointerException();return x;}public static void main(String args[]) {try {Class cls = Class.forName("method1");Method methlist[] = cls.getDeclaredMethods();for (int i = 0; i < methlist.length; i++) {Method m = methlist[i];System.out.println("name = " + m.getName());System.out.println("decl class = " + m.getDeclaringClass());Class pvec[] = m.getParameterTypes();for (int j = 0; j < pvec.length; j++)System.out.println("param #" + j + " " + pvec[j]);Class evec[] = m.getExceptionTypes();for (int j = 0; j < evec.length; j++)System.out.println("exc #" + j + " " + evec[j]);System.out.println("return type = " + m.getReturnType());System.out.println("-----");}} catch (Throwable e) {System.err.println(e);}}}  这个程序首先取得 method1 类的描述,然后调用 getDeclaredMethods 来获取一系列的 Method 对象,它们分别描述了定义在类中的每一个方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods,你还能获得继承来的各个方法的信息。   取得了 Method 对象列表之后,要显示这些方法的参数类型、异常类型和返回值类型等就不难了。这些类型是基本类型还是类类型,都可以由描述类的对象按顺序给出。  输出的结果如下:name = f1decl class = class method1param #0 class java.lang.Objectparam #1 intexc #0 class java.lang.NullPointerExceptionreturn type = int-----name = maindecl class = class method1param #0 class [Ljava.lang.String;return type = void4.获取构造器信息  获取类构造器的用法与上述获取方法的用法类似,如:import java.lang.reflect.*;public class constructor1 {public constructor1() {}protected constructor1(int i, double d) {}public static void main(String args[]) {try {Class cls = Class.forName("constructor1");Constructor ctorlist[] = cls.getDeclaredConstructors();for (int i = 0; i < ctorlist.length; i++) {Constructor ct = ctorlist[i];System.out.println("name = " + ct.getName());System.out.println("decl class = " + ct.getDeclaringClass());Class pvec[] = ct.getParameterTypes();for (int j = 0; j < pvec.length; j++)System.out.println("param #" + j + " " + pvec[j]);Class evec[] = ct.getExceptionTypes();for (int j = 0; j < evec.length; j++)System.out.println("exc #" + j + " " + evec[j]);System.out.println("-----");}} catch (Throwable e) {System.err.println(e);}}}  这个例子中没能获得返回类型的相关信息,那是因为构造器没有返回类型。  这个程序运行的结果是:name = constructor1decl class = class constructor1-----name = constructor1decl class = class constructor1param #0 intparam #1 double5.获取类的字段(域)   找出一个类中定义了哪些数据字段也是可能的,下面的代码就在干这个事情:import java.lang.reflect.*;public class field1 {private double d;public static final int i = 37;String s = "testing";public static void main(String args[]) {try {Class cls = Class.forName("field1");Field fieldlist[] = cls.getDeclaredFields();for (int i = 0; i < fieldlist.length; i++) {Field fld = fieldlist[i];System.out.println("name = " + fld.getName());System.out.println("decl class = " + fld.getDeclaringClass());System.out.println("type = " + fld.getType());int mod = fld.getModifiers();System.out.println("modifiers = " + Modifier.toString(mod));System.out.println("-----");}} catch (Throwable e) {System.err.println(e);}}}  这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier,它也是一个 reflection 类,用来描述字段成员的修饰语,如“private int”。这些修饰语自身由整数描述,而且使用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。这个程序的输出是:name = ddecl class = class field1type = doublemodifiers = private-----name = idecl class = class field1type = intmodifiers = public static final-----name = sdecl class = class field1type = class java.lang.Stringmodifiers =  和获取方法的情况一下,获取字段的时候也可以只取得在当前类中申明了的字段信息 (getDeclaredFields),或者也可以取得父类中定义的字段 (getFields) 。6.根据方法的名称来执行方法  文本到这里,所举的例子无一例外都与如何获取类的信息有关。我们也可以用 reflection 来做一些其它的事情,比如执行一个指定了名称的方法。下面的示例演示了这一操作:import java.lang.reflect.*;public class method2 {public int add(int a, int b) {return a + b;}public static void main(String args[]) {try {Class cls = Class.forName("method2");Class partypes[] = new Class[2];partypes[0] = Integer.TYPE;partypes[1] = Integer.TYPE;Method meth = cls.getMethod("add", partypes);method2 methobj = new method2();Object arglist[] = new Object[2];arglist[0] = new Integer(37);arglist[1] = new Integer(47);Object retobj = meth.invoke(methobj, arglist);Integer retval = (Integer) retobj;System.out.println(retval.intValue());} catch (Throwable e) {System.err.println(e);}}}  假如一个程序在执行的某处的时候才知道需要执行某个方法,这个方法的名称是在程序的运行过程中指定的 (例如,JavaBean 开发环境中就会做这样的事),那么上面的程序演示了如何做到。  上例中,getMethod用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的Method 对象之后,在正确的对象实例中执行它。执行该方法的时候,需要提供一个参数列表,这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象,它封装了返回值 84。7.创建新的对象  对于构造器,则不能像执行方法那样进行,因为执行一个构造器就意味着创建了一个新的对象 (准确的说,创建一个对象的过程包括分配内存和构造对象)。所以,与上例最相似的例子如下:import java.lang.reflect.*;public class constructor2 {public constructor2() {}public constructor2(int a, int b) {System.out.println("a = " + a + " b = " + b);}public static void main(String args[]) {try {Class cls = Class.forName("constructor2");Class partypes[] = new Class[2];partypes[0] = Integer.TYPE;partypes[1] = Integer.TYPE;Constructor ct = cls.getConstructor(partypes);Object arglist[] = new Object[2];arglist[0] = new Integer(37);arglist[1] = new Integer(47);Object retobj = ct.newInstance(arglist);} catch (Throwable e) {System.err.println(e);}}}  根据指定的参数类型找到相应的构造函数并执行它,以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象,而不是在编译的时候创建对象,这一点非常有价值。8.改变字段(域)的值  reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它,下面的例子可以说明这一点:import java.lang.reflect.*;public class field2 {public double d;public static void main(String args[]) {try {Class cls = Class.forName("field2");Field fld = cls.getField("d");field2 f2obj = new field2();System.out.println("d = " + f2obj.d);fld.setDouble(f2obj, 12.34);System.out.println("d = " + f2obj.d);} catch (Throwable e) {System.err.println(e);}}}  这个例子中,字段 d 的值被变为了 12.34。9.使用数组  本文介绍的 reflection 的最后一种用法是创建的操作数组。数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:import java.lang.reflect.*;public class array1 {public static void main(String args[]) {try {Class cls = Class.forName("java.lang.String");Object arr = Array.newInstance(cls, 10);Array.set(arr, 5, "this is a test");String s = (String) Array.get(arr, 5);System.out.println(s);} catch (Throwable e) {System.err.println(e);}}}  例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。


《第三篇》


在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方
  Java反射在我们Java学习的过程中是非常重要的知识点。可能有些同学认为这个学习起来不容易理解,其实就我个人而言还是比较简单,学习起来也比较容易理解。下面我给大家总结一下Java反射学习的要点,同时给出几个比较好的例子。
  1、Java反射的概念
  反射含义:可以获取正在运行的Java对象。
  2、Java反射的功能
  1)可以判断运行时对象所属的类
  2)可以判断运行时对象所具有的成员变量和方法
  3)通过反射甚至可以调用到private的方法
  4)生成动态代理
  3、实现Java反射的类
  1)Class:它表示正在运行的Java应用程序中的类和接口
  2)Field:提供有关类或接口的属性信息,以及对它的动态访问权限
  3)Constructor:提供关于类的单个构造方法的信息以及对它的访问权限
  4)Method:提供关于类或接口中某个方法信息
  注意:Class类是Java反射中最重要的一个功能类,所有获取对象的信息(包括:方法/属性/构造方法/访问权限)都需要它来实现
  4、编写Java反射程序的步骤:
  1)必须首先获取一个类的Class对象
  例如:
  Class c1 = Test.class;
  Class c2 = Class.forName(“com.reflection.Test”);
  Class c3 = new Test().getClass();
  2)然后分别调用Class对象中的方法来获取一个类的属性/方法/构造方法的结构
  注意:如果要能够正常的获取类中方法/属性/构造方法应该重点掌握如下的反射类
  Field
  Constructor
  Method
  示例:此程序例子告诉大家如何操作Class/Field/Constructor/Method等与Java反射相关的类
  package com.reflection;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Field;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  public class TestReflection {
  private String username;
  private String password;
  private int[] age;
  public void setUserName(String username) {
  this.username = username;
  }
  private void setPassWord(String password) {
  this.password = password;
  }
  public static void test01() throws ClassNotFoundException {
  Class c1 = TestReflection.class;
  Class c2 = Class.forName("com.reflection.TestReflection");
  //获取指定的包名
  String package01 = c1.getPackage().getName();
  String package02 = c2.getPackage().getName();
  System.out.println("package01 = " + package01);
  System.out.println("package02 = " + package02);
  //获取类的修饰符
  int mod = c1.getModifiers();
  String modifier = Modifier.toString(mod);
  System.out.println("modifier = " + modifier);
  //获取指定类的完全限定名
  String className = c1.getName();
  System.out.println("className = " + className);
  //获取指定类的父类
  Class superClazz = c1.getSuperclass();
  String superClazzName = superClazz.getName();
  System.out.println("superClazzName = " + superClazzName);
  //获取实现的接口
  Class[] interfaces = c1.getInterfaces();
  for (Class t : interfaces) {
  System.out.println("interfacesName = " + t.getName());
  }
  //获取指定类的成员变量
  Field[] fields = c1.getDeclaredFields();
  for (Field field : fields) {
  modifier = Modifier.toString(field.getModifiers()); //获取每个
  字段的访问修饰符
  Class type = field.getType(); //获取字段的数据类型所对应的
  Class对象
  String name = field.getName(); //获取字段名
  if (type.isArray()) { //如果是数组类型则需要特别处理
  String arrType = type.getComponentType().getName() +
  "[]";
  System.out.println("" + modifier + " " + arrType + " "
  + name + ";");
  } else {
  System.out.println("" + modifier + " " + type + " " +
  name + ";");
  }
  }
  //获取类的构造方法
  Constructor[] constructors = c1.getDeclaredConstructors();
  for (Constructor constructor : constructors) {
  String name = constructor.getName(); //构造方法名
  modifier = Modifier.toString(constructor.getModifiers()); //获取访问修饰符
  System.out.println("" + modifier +" " + name + "(");
  Class[] paramTypes = constructor.getParameterTypes(); //获取构造方法中的参数
  for (int i = 0; i < paramTypes.length; i++) {
  if (i > 0) {
  System.out.print(",");
  }
  if (paramTypes[i].isArray()) {
  System.out.println(paramTypes
  [i].getComponentType().getName()+"[]");
  } else {
  System.out.print(paramTypes[i].getName());
  }
  }
  System.out.println(");");
  }
  //获取成员方法
  Method[] methods = c1.getDeclaredMethods();
  for (Method method: methods) {
  modifier = Modifier.toString(method.getModifiers());
  Class returnType = method.getReturnType(); //获取方法的返回类型
  if (returnType.isArray()) {
  String arrType = returnType.getComponentType
  ().getName()+"[]";
  System.out.print(""+modifier+" " + arrType + " " +
  method.getName() + "(");
  } else {
  System.out.print("" + modifier + " " +
  returnType.getName() + " " + method.getName() + "(");
  }
  Class[] paramTypes = method.getParameterTypes();
  for (int i = 0; i < paramTypes.length; i++) {
  if (i > 0) {
  System.out.print(",");
  }
  if (paramTypes[i].isArray()) {
  System.out.println(paramTypes
  [i].getComponentType().getName()+"[]");
  } else {
  System.out.print(paramTypes[i].getName());
  }
  }
  System.out.println(");");
  }
  }
  public static void test02() throws InstantiationException,
  IllegalAccessException, SecurityException, NoSuchMethodException,
  IllegalArgumentException, InvocationTargetException {
  //反射调用方法,可以通过Method类的invoke方法实现动态方法的调用
  //public Object invoke(Object obj, Object... args)
  //第一个参数代表对象
  //第二个参数代表执行方法上的参数
  //若反射要调用类的某个私有方法,可以在这个私有方法对应的Mehtod对象上先
  调用setAccessible(true)
  Class c1 = TestReflection.class;
  TestReflection t1 = (TestReflection) c1.newInstance(); //利用反射来创
  建类的对象
  System.out.println("username == " + t1.username);
  System.out.println("password == " + t1.password);
  Method method = c1.getDeclaredMethod("setUserName", String.class);
  method.invoke(t1, "Java反射的学习");
  System.out.println("username == " + t1.username);
  method = c1.getDeclaredMethod("setPassWord", String.class);
  method.setAccessible(true);
  method.invoke(t1, "反射执行某个Private修饰的方法");
  System.out.println("password == " + t1.password);
  }
  public static void main(String[] args) throws ClassNotFoundException,
  SecurityException, IllegalArgumentException, InstantiationException,
  IllegalAccessException, NoSuchMethodException, InvocationTargetException {
  // test01();
  test02();
  }
  }


《第四篇》

反射机制:所谓的反射机制就是 java语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。下面具体介绍一下java的反射机制。这里你将颠覆原来对java的理解。 Java的反射机制的实现要借助于4个类:class, Constructor,Field,Method;其中class代表的时类对 象,Constructor-类的 构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组 成部分。Class:程序运行时,java运行时系统会对所有的对象进行运行时类型的处理。这项信息记录了每个对象所属的类, 虚拟机通常使用运行时类型信息选择正 确的方法来执行(摘自:白皮书)。但是这些信息我们怎么得到啊,就要借助于class类对象了啊。在Object类中定义了getClass()方法。我 们可以通过这个方法获得指定对象的类对象。然后我们通过分析这个对象就可以得到我们要的信息了。比如:ArrayList arrayList;Class clazz = arrayList.getClass();然后我来处理这个对象clazz。当然了Class类具有很多的方法,这里重点将和Constructor,Field,Method类有关系的方法。Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。Java 的这一能力在实际应用中也许用得不是很多,但是个人认为要想对java有个更加深入的了解还是应该掌握的。1.检测类:reflection的工作机制考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。import java.lang.reflect.*;public class DumpMethods {public static void main(String args[]) {try {Class c = Class.forName(args[0]);Method m[] = c.getDeclaredMethods();for (int i = 0; i < m.length; i++)System.out.println(m[i].toString());} catch (Throwable e) {System.err.println(e);}}}按如下语句执行:java DumpMethods java.util.ArrayList这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。Java 类反射中的主要方法对于以下三类组件中的任何一类来说 -- 构造函数、字段和方法 -- java.lang.Class 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用:Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数,Constructor[] getConstructors() -- 获得类的所有公共构造函数Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关)Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)获得字段信息的Class 反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:Field getField(String name) -- 获得命名的公共字段Field[] getFields() -- 获得类的所有公共字段Field getDeclaredField(String name) -- 获得类声明的命名的字段Field[] getDeclaredFields() -- 获得类声明的所有字段用于获得方法信息函数:Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法Method[] getMethods() -- 获得类的所有公共方法Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法Method[] getDeclaredMethods() -- 获得类声明的所有方法使用 Reflection:用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。下面就是获得一个 Class 对象的方法之一:Class c = Class.forName("java.lang.String");这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:Class c = int.class;或者Class c = Integer.TYPE;它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Intege ) 中预先定义好的 TYPE 字段。第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。一旦取得这个信息,就可以进行第三步了--使用 reflection API 来操作这些信息,如下面这段代码:Class c = Class.forName("java.lang.String");Method m[] = c.getDeclaredMethods();System.out.println(m[0].toString());它将以文本方式打印出 String 中定义的第一个方法的原型。处理对象:a.创建一个Class对象b.通过getField 创建一个Field对象c.调用Field.getXXX(Object)方法(XXX是Int,Float等,如果是对象就省略;Object是指实例).例如:import java.lang.reflect.*;import java.awt.*;class SampleGet {public static void main(String[] args) {Rectangle r = new Rectangle(100, 325);printHeight(r);}static void printHeight(Rectangle r) {Field heightField;Integer heightValue;Class c = r.getClass();try {heightField = c.getField("height");heightValue = (Integer) heightField.get(r);System.out.println("Height: " + heightValue.toString());} catch (NoSuchFieldException e) {System.out.println(e);} catch (SecurityException e) {System.out.println(e);} catch (IllegalAccessException e) {System.out.println(e);}}}安全性和反射:在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,我们可能希望框架能够全面接入代码,无需考虑常规的接入限制。但是,在其它情况下,不受控制的接入会带来严重的安全性风险,例如当代码在不值得信任的代码共享的环境中运行时。由于这些互相矛盾的需求,Java编程语言定义一种多级别方法来处理反射的安全性。基本模式是对反射实施与应用于 源代码接入相同的限制:从任意位置到类公共组件的接入类自身外部无任何到私有组件的接入受保护和打包(缺省接入)组件的有限接入不过至少有些时候,围绕这些限制还有一种简单的方法。我们可以在我们所写的类中,扩展一个普通的基本类 java.lang.reflect.AccessibleObject 类。这个类定义了一种setAccessible方法,使我们能够启动或关闭对这些类中其中一个类的实例的接入检测。唯一的问题在于如果使用了安全性管理 器,它将检测正在关闭接入检测的代码是否许可了这样做。如果未许可,安全性管理器抛出一个例外。下面是一段程序,在TwoString 类的一个实例上使用反射来显示安全性正在运行:public class ReflectSecurity {public static void main(String[] args) {try {TwoString ts = new TwoString("a", "b");Field field = clas.getDeclaredField("m_s1");// field.setAccessible(true);System.out.println("Retrieved value is " +field.get(inst));} catch (Exception ex) {ex.printStackTrace(System.out);}}}如果我们编译这一程序时,不使用任何特定参数直接从命令行运行,它将在field .get(inst)调用中抛出一个IllegalAccessException异常。如果我们不注释 field.setAccessible(true)代码行,那么重新编译并重新运行该代码,它将编译成功。最后,如果我们在命令行添加了JVM参数 -Djava.security.manager以实现安全性管理器,它仍然将不能通过编译,除非我们定义了ReflectSecurity类的许可权 限。反射性能:(转录别人的啊)反射是一种强大的工具,但也存在一些不足。一个主要的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。下面的程序是字段接入性能测试的一个例子,包括基本的测试方法。每种方法测试字段接入的一种形式 -- accessSame 与同一对象的成员字段协作,accessOther 使用可直接接入的另一对象的字段,accessReflection 使用可通过反射接入的另一对象的字段。在每种情况下,方法执行相同的计算 -- 循环中简单的加/乘顺序。程序如下:public int accessSame(int loops) {m_value = 0;for (int index = 0; index < loops; index++) {m_value = (m_value + ADDITIVE_VALUE) *MULTIPLIER_VALUE;}return m_value;}public int accessReference(int loops) {TimingClass timing = new TimingClass();for (int index = 0; index < loops; index++) {timing.m_value = (timing.m_value + ADDITIVE_VALUE) *MULTIPLIER_VALUE;}return timing.m_value;}public int accessReflection(int loops) throws Exception {TimingClass timing = new TimingClass();try {Field field = TimingClass.class.getDeclaredField("m_value");for (int index = 0; index < loops; index++) {int value = (field.getInt(timing) +ADDITIVE_VALUE) * MULTIPLIER_VALUE;field.setInt(timing, value);}return timing.m_value;} catch (Exception ex) {System.out.println("Error using reflection");throw ex;}}在上面的例子中,测试程序重复调用每种方法,使用一个大循环数,从而平均多次调用的时间衡量结果。平均值中不包括每种方法第一次调用的时间,因此初始化时间不是结果中的一个因素。下面的图清楚的向我们展示了每种方法字段接入的时间:图 1:字段接入时间 :我们可以看出:在前两副图中(Sun JVM),使用反射的执行时间超过使用直接接入的1000倍以上。通过比较,IBM JVM可能稍好一些,但反射方法仍旧需要比其它方法长700倍以上的时间。任何JVM上其它两种方法之间时间方面无任何显著差异,但IBM JVM几乎比Sun JVM快一倍。最有可能的是这种差异反映了Sun Hot Spot JVM的专业优化,它在简单基准方面表现得很糟糕。反射性能是Sun开发1.4 JVM时关注的一个方面,它在反射方法调用结果中显示。在这类操作的性能方面,Sun 1.4.1 JVM显示了比1.3.1版本很大的改进。如果为为创建使用反射的对象编写了类似的计时测试程序,我们会发现这种情况下的差异不象字段和方法调用情况下那么显著。使用newInstance()调 用创建一个简单的java.lang.Object实例耗用的时间大约是在Sun 1.3.1 JVM上使用new Object()的12倍,是在IBM 1.4.0 JVM的四倍,只是Sun 1.4.1 JVM上的两部。使用Array.newInstance(type, size)创建一个数组耗用的时间是任何测试的JVM上使用new type[size]的两倍,随着数组大小的增加,差异逐步缩小。随着jdk6.0的推出,反射机制的性能也有了很大的提升。期待中….总结:Java语言反射提供一种动态链接程序组件的多功能方法。它允许程序创建和控制任何类的对象(根据安全性限制),无需提前硬编码目标类。这些特性使得反射 特别适用于创建以非常普通的方式与对象协作的库。例如,反射经常在持续存储对象为数据库、XML或其它外部格式的框架中使用。Java reflection 非常有用,它使类和数据结构能按名称动态检索相关信息,并允许在运行着的程序中操作这些信息。Java 的这一特性非常强大,并且是其它一些常用语言,如 C、C++、Fortran 或者 Pascal 等都不具备的。但反射有两个缺点。第一个是性能问题。用于字段和方法接入时反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中相 对很少涉及的部分,缓慢的性能将不会是一个问题。即使测试中最坏情况下的计时图显示的反射操作只耗用几微秒。仅反射在性能关键的应用的核心逻辑中使用时性 能问题才变得至关重要。许多应用中更严重的一个缺点是使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问 题。反射代码比相应的直接代码更复杂,正如性能比较的代码实例中看到的一样。解决这些问题的最佳方案是保守地使用反射--仅在它可以真正增加灵活性的地方 --记录其在目标类中的使用。一下是对应各个部分的例子:具体的应用:1、 模仿instanceof 运算符号class A {}public class instance1 {public static void main(String args[]){try {Class cls = Class.forName("A");boolean b1= cls.isInstance(new Integer(37));System.out.println(b1);boolean b2 = cls.isInstance(new A());System.out.println(b2);}catch (Throwable e) {System.err.println(e);}}}2、 在类中寻找指定的方法,同时获取该方法的参数列表,例外和返回值import java.lang.reflect.*;public class method1 {private int f1(Object p, int x) throws NullPointerException{if (p == null)throw new NullPointerException();return x;}public static void main(String args[]){try {Class cls = Class.forName("method1");Method methlist[]= cls.getDeclaredMethods();for (int i = 0; i < methlist.length;i++)Method m = methlist[i];System.out.println("name= " + m.getName());System.out.println("decl class = " +m.getDeclaringClass());Class pvec[] = m.getParameterTypes();for (int j = 0; j < pvec.length; j++)System.out.println("param #" + j + " " + pvec[j]);Class evec[] = m.getExceptionTypes();for (int j = 0; j < evec.length; j++)System.out.println("exc #" + j+ " " + evec[j]);System.out.println("return type = " +m.getReturnType());System.out.println("-----");}}catch (Throwable e) {System.err.println(e);}}}3、 获取类的构造函数信息,基本上与获取方法的方式相同import java.lang.reflect.*;public class constructor1 {public constructor1(){}protected constructor1(int i, double d){}public static void main(String args[]){try {Class cls = Class.forName("constructor1");Constructor ctorlist[]= cls.getDeclaredConstructors();for (int i = 0; i < ctorlist.length; i++) {Constructor ct = ctorlist[i];System.out.println("name= " + ct.getName());System.out.println("decl class = " +ct.getDeclaringClass());Class pvec[] = ct.getParameterTypes();for (int j = 0; j < pvec.length; j++)System.out.println("param #"+ j + " " + pvec[j]);Class evec[] = ct.getExceptionTypes();for (int j = 0; j < evec.length; j++)System.out.println("exc #" + j + " " + evec[j]);System.out.println("-----");}}catch (Throwable e) {System.err.println(e);}}}4、 获取类中的各个数据成员对象,包括名称。类型和访问修饰符号import java.lang.reflect.*;public class field1 {private double d;public static final int i = 37;String s = "testing";public static void main(String args[]){try {Class cls = Class.forName("field1");Field fieldlist[]= cls.getDeclaredFields();for (int i= 0; i < fieldlist.length; i++) {Field fld = fieldlist[i];System.out.println("name= " + fld.getName());System.out.println("decl class = " +fld.getDeclaringClass());System.out.println("type= " + fld.getType());int mod = fld.getModifiers();System.out.println("modifiers = " +Modifier.toString(mod));System.out.println("-----");}}catch (Throwable e) {System.err.println(e);}}}5、 通过使用方法的名字调用方法import java.lang.reflect.*;public class method2 {public int add(int a, int b){return a + b;}public static void main(String args[]){try {Class cls = Class.forName("method2");Class partypes[] = new Class[2];partypes[0] = Integer.TYPE;partypes[1] = Integer.TYPE;Method meth = cls.getMethod("add", partypes);method2 methobj = new method2();Object arglist[] = new Object[2];arglist[0] = new Integer(37);arglist[1] = new Integer(47);Object retobj= meth.invoke(methobj, arglist);Integer retval = (Integer)retobj;System.out.println(retval.intValue());}catch (Throwable e) {System.err.println(e);}}}6、 创建新的对象import java.lang.reflect.*;public class constructor2 {public constructor2(){}public constructor2(int a, int b){System.out.println("a = " + a + " b = " + b);}public static void main(String args[]){try {Class cls = Class.forName("constructor2");Class partypes[] = new Class[2];partypes[0] = Integer.TYPE;partypes[1] = Integer.TYPE;Constructor ct= cls.getConstructor(partypes);Object arglist[] = new Object[2];arglist[0] = new Integer(37);arglist[1] = new Integer(47);Object retobj = ct.newInstance(arglist);}catch (Throwable e) {System.err.println(e);}}}7、 变更类实例中的数据的值import java.lang.reflect.*;public class field2 {public double d;public static void main(String args[]){try {Class cls = Class.forName("field2");Field fld = cls.getField("d");field2 f2obj = new field2();System.out.println("d = " + f2obj.d);fld.setDouble(f2obj, 12.34);System.out.println("d = " + f2obj.d);}catch (Throwable e) {System.err.println(e);}}}使用反射创建可重用代码:1、 对象工厂Object factory(String p) {Class c;Object o=null;try {c = Class.forName(p);// get class defo = c.newInstance(); // make a new one} catch (Exception e) {System.err.println("Can't make a " + p);}return o;}public class ObjectFoundry {public static Object factory(String p)throws ClassNotFoundException,InstantiationException,IllegalAccessException {Class c = Class.forName(p);Object o = c.newInstance();return o;}}2、 动态检测对象的身份,替代instanceofpublic static booleanisKindOf(Object obj, String type)throws ClassNotFoundException {// get the class def for obj and typeClass c = obj.getClass();Class tClass = Class.forName(type);while ( c!=null ) {if ( c==tClass ) return true;c = c.getSuperclass();}return false;}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值