现在的位置: 首页 > 综合 > 正文

java中的反射

2013年09月18日 ⁄ 综合 ⁄ 共 8894字 ⁄ 字号 评论关闭
一、得到字节码的方式:

     1.类名.class  例如:System.class
     2.对象.getClass()
     3.Class.forName("类名")  方法的作用:就是把你需要的类的字节码从硬盘中加载到java虚拟机中缓存起来
     总之在反射中很多情况都要用到一个类的那份二进制字节码文件
二、反射:反射就是把java中的成分映射为相应java类
在反射中的几个重要的类
     1.Constructor类:利用它能产生出一个获取java类的构造方法的对象,例如:
          //利用constructor的对象得到String类的相应的构造方法
  Constructor constructor=String.class .getConstructor (StringBuffer.class);
Class.getConstructor(Class<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
     Constructor类中的常用方法:
       1)getConstrustor()
 
       2)newInstance()
newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。

     2.Field类:利用它产生的对象能够获取java类中的成员变量,例如:
        ReflectPoint pt1= new ReflectPoint(3,
5);
Field fieldy=pt1.getClass().getField( "y");//这句只是获取到了Reflect这个类的变量y,而并非是对象的y的值
             System. out.println(fieldy.get(pt1));
             
             Field fieldx=pt1.getClass().getDeclaredField("x" );
             fieldx.setAccessible( true);//把对象中似有的成员设置成可访问的
             System. out.println(fieldx.get(pt1));

          Field类中常用的方法:
            1)get()
Object get(Object obj)
返回指定对象上此 Field 表示的字段的值。
          2)getField()

getField

public Field getField(String name)
               throws NoSuchFieldException,
                      SecurityException
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。name 参数是一个 String,用于指定所需字段的简称。
Field fieldy=pt1.getClass().getField( "y");//这句只是获取到了Reflect这个类的变量y,而并非是对象的y的值


3.Method类:利用它产生的能获取java类中的成员方法

          //成员方法的反射
          try {
             Method methodCharAt=String.class .getMethod ("charAt" , int.class );
             System. out.println(methodCharAt.invoke(str1,
1));
         } catch (SecurityException
e) {
              // TODO Auto-generated
catch block
             e.printStackTrace();
         } catch (NoSuchMethodException e)
{
              // TODO Auto-generated
catch block
             e.printStackTrace();
         }
Method类中常用的方法:
4.数组的反射:
private static void print(Object
obj) {
          Class clazz=obj.getClass();
          if(clazz.isArray()){
              int len=Array.getLength(obj);
              for(int i=0;i<len;i++){
                 System. out.println(Array.get(obj,
i));
             }
         } else{
             System. out.println(obj);
         }
    }
        


Java中的类反射机制

一、反射的概念:
Java中,反射是一种强大的工具。它使您能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代表链接。反射允许我们在编写与执行时,使我们的程序代码能够接入装载到 JVM中的类的内部信息,而不是源代码中选定的类协作的代码。这使反射成为构建灵活的应用的主要工具。但需注意的是:如果使用不当,反射的成本很高。

二、 Java中的类反射:
Reflection  Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。 Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。
1.检测类:
1.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 是用来描述某个类中单个方法的一个类。

1.2 Java类反射中的主要方法

对于以下三类组件中的任何一类来说 -- 构造函数、字段和方法 --
java.lang.Class
 提供四种独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用
l         Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数,

l         Constructor[] getConstructors() -- 获得类的所有公共构造函数

l         Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数 (与接入级别无关)

l         Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数 (与接入级别无关)

获得字段信息的 Class 反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:

l         Field getField(String name) -- 获得命名的公共字段

l         Field[] getFields() -- 获得类的所有公共字段

l         Field getDeclaredField(String name) -- 获得类声明的命名的字段

l         Field[] getDeclaredFields() -- 获得类声明的所有字段

用于获得方法信息函数:

l         Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法

l         Method[] getMethods() -- 获得类的所有公共方法

l         Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法

l         Method[] getDeclaredMethods() -- 获得类声明的所有方法

1.3开始使用 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 中定义的第一个方法的原型。

2.处理对象:

如果要作一个开发工具像 debugger之类的,你必须能发现filed
values,
以下是三个步骤 :

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编程语言定义一种多级别方法来处理反射的安全性。基本模式是对反射实施与应用于源代码接入相同的限制:
n         从任意位置到类公共组件的接入

n         类自身外部无任何到私有组件的接入

n         受保护和打包(缺省接入)组件的有限接入

不过至少有些时候,围绕这些限制还有一种简单的方法。我们可以在我们所写的类中,扩展一个普通的基本类 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类的许可权限。

四、反射性能:

下面的程序是字段接入性能测试的一个例子,包括基本的测试方法。每种方法测试字段接入的一种形式 -- 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;
    }
}
Java语言反射提供一种动态链接程序组件的多功能方法。它允许程序创建和控制任何类的对象 (根据安全性限制),无需提前硬编码目标类。这些特性使得反射特别适用于创建以非常普通的方式与对象协作的库。例如,反射经常在持续存储对象为数据库、 XML或其它外部格式的框架中使用。 Java
reflection
 非常有用,它使类和数据结构能按名称动态检索相关信息,并允许在运行着的程序中操作这些信息。

但反射有两个缺点。第一个是性能问题。用于字段和方法接入时反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。即使测试中最坏情况下的计时图显示的反射操作只耗用几微秒。仅反射在性能关键的应用的核心逻辑中使用时性能问题才变得至关重要。

许多应用中更严重的一个缺点是使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂,正如性能比较的代码实例中看到的一样。解决这些问题的最佳方案是保守地使用反射——仅在它可以真正增加灵活性的地方——记录其在目标类中的使用。

利用反射实现类的动态加载

首先定义一个接口来隔离类:

package org.bromon.reflect;
public interface Operator
{
public java.util.List act(java.util.List params)
}

根据设计模式的原理,我们可以为不同的功能编写不同的类,每个类都继承 Operator接口,客户端只需要针对 Operator接口编程就可以避免很多麻烦。比如这个类:

package org.bromon.reflect.*;
public class Success implements Operator
{
public java.util.List act(java.util.List params)
{
List result=new ArrayList();
result.add(new String( “操作成功” ));
return result;
}
}

很幸运,我使用的是 Java,只有Java 才提供这样的反射机制,或者说内省机制,可以实现我们的无理要求。编写一个配置文件 emp.properties:

#成功响应
1000=Success
#向客户发送普通文本消息
2000=Load
#客户向服务器发送普通文本消息
3000=Store

抱歉!评论已关闭.