⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 java编程 的动态性,第 2部分:引入反射.htm

📁 javn的Java书籍JAVA集合框架.rar我看过了是很有帮助的
💻 HTM
📖 第 1 页 / 共 4 页
字号:
}
</CODE></PRE></TD></TR></TBODY></TABLE>
      <P>清单2中的代码获得构造函数并使用它来创建使用<CODE>String</CODE>s <CODE>"a"</CODE> 和 
      <CODE>"b"</CODE>的<CODE>TwoString</CODE> 类的一个实例:</P><A 
      name=IDA3GMQD><B>清单2:构造函数的反射调用</B></A><BR>
      <TABLE bgColor=#cccccc border=1 cellPadding=5 cellSpacing=0 
        width="100%"><TBODY>
        <TR>
          <TD><PRE><CODE>
    Class[] types = new Class[] { String.class, String.class };
    Constructor cons = TwoString.class.getConstructor(types);
    Object[] args = new Object[] { "a", "b" };
    TwoString ts = cons.newInstance(args);
</CODE></PRE></TD></TR></TBODY></TABLE>
      <P>清单2中的代码忽略了不同反射方法抛出的多种可能选中的例外类型。例外在 Javadoc API 
      描述中详细记录,因此为了简明起见,我将在所有程序实例中忽略它们。</P>
      <P>尽管我在讨论构造函数主题,Java编程语言还定义了一种您可以用来使用<I>无参数</I>(或缺省)构造函数创建类的一个实例的特殊快捷方式。这种快捷方式嵌入到<CODE>Class</CODE>定义中,如下:</P>
      <BLOCKQUOTE><CODE>Object newInstance()</CODE> -- 使用缺省函数创建新的实例</BLOCKQUOTE>
      <P>即使这种方法只允许您使用一种特殊的构造函数,如果这正是您需要的,那么它将提供一种非常方便的快捷方式。当与JavaBeans协作时这项技术尤其有用,JavaBeans需要定义公共、无参数构造函数。</P>
      <P><A name=IDAWHMQD><SPAN 
      class=atitle3>通过反射增加字段</SPAN></A>获得字段信息的<CODE>Class</CODE> 
      反射调用不同于那些用于接入构造函数的调用,在参数类型数组中使用了字段名:</P>
      <UL>
        <LI><CODE>Field getField(String name)</CODE> -- 获得命名的公共字段<BR><BR>
        <LI><CODE>Field[] getFields()</CODE> -- 获得类的所有公共字段<BR><BR>
        <LI><CODE>Field getDeclaredField(String name)</CODE> -- 
        获得类声明的命名的字段<BR><BR>
        <LI><CODE>Field[] getDeclaredFields()</CODE> -- 获得类声明的所有字段 </LI></UL>
      <P>尽管与构造函数调用类似,在字段方面仍存在一个重要的区别:前两个变量返回可以通过类接入的公共字段的信息 -- 
      即使它们来自于祖先类。后两个变量返回类直接声明的字段的信息 -- 与字段的接入类型无关。</P>
      <P>调用返回的<CODE>java.lang.reflect.Field</CODE>实例定义所有主类型的<CODE>getXXX</CODE> 
      和 <CODE>setXXX</CODE> 方法,以及与对象引用协作的通用<CODE>get</CODE> 和 <CODE>set</CODE> 
      方法。您可以根据实际的字段类型自行选择一种适当的方法,而<CODE>getXXX</CODE> 
      方法将自动处理扩展转换(如使用<CODE>getInt</CODE> 方法来检索一个字节值)。</P>
      <P>清单3显示使用字段反射方法的一个实例,以方法的格式根据名称增加对象的<CODE>int</CODE>字段 :</P><B>清单</B><A 
      name=IDAKSMQD><B>3:通过反射增加一个字段</B></A><BR>
      <TABLE bgColor=#cccccc border=1 cellPadding=5 cellSpacing=0 
        width="100%"><TBODY>
        <TR>
          <TD><PRE><CODE>
public int incrementField(String name, Object obj) throws... {
    Field field = obj.getClass().getDeclaredField(name);
    int value = field.getInt(obj) + 1;
    field.setInt(obj, value);
    return value;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
      <P>这种方法开始展示了反射带来的某些灵活性。与特定的类协作不同,<CODE>incrementField</CODE> 使用传 
      入的对象的<CODE>getClass</CODE> 方法来查找类信息,然后直接在该类中查找命名的字段。</P>
      <P><A name=IDA4SMQD><SPAN 
      class=atitle3>通过反射增加方法</SPAN></A><BR>获得方法信息的<CODE>Class</CODE> 
      反射调用与用于构造函数和字段的调用非常类似:</P>
      <UL>
        <LI><CODE>Method getMethod(String name, Class[] params)</CODE> -- 
        使用特定的参数类型,获得命名的公共方法<BR><BR>
        <LI><CODE>Method[] getMethods()</CODE> -- 获得类的所有公共方法<BR><BR>
        <LI><CODE>Method getDeclaredMethod(String name, Class[] params)</CODE> 
        -- 使用特写的参数类型,获得类声明的命名的方法<BR><BR>
        <LI><CODE>Method[] getDeclaredMethods()</CODE> -- 获得类声明的所有方法 </LI></UL>
      <P>与字段调用一样,前两个变量返回可以通过类接入的公共方法的信息 -- 
      即使它们来自于祖先类。后两个变量返回类声明的方法的信息,与方法的接入类型无关。</P>
      <P>调用返回的<CODE>java.lang.reflect.Method</CODE>实例定义一种<CODE>invoke</CODE>方法,您可以用来在正在定义的类的一个实例上调用方法。这种<CODE>invoke</CODE> 
      方法使用两个参数,为调用提供类实例和参数值数组。</P>
      <P>清单4进一步阐述字段实例,显示反射正在运行的方法的一个实例。这种方法增加一个定义有<CODE>get</CODE> 和 
      <CODE>set</CODE>方法的<CODE>int</CODE> 
      JavaBean属性。例如,如果对象为一个整数<CODE>count</CODE>值定义了<CODE>getCount</CODE> 和 
      <CODE>setCount</CODE> 
      方法,您可以在一次调用中向该方法传递“count”作为<CODE>name</CODE>参数,以增加该值。</P><A 
      name=IDATVMQD><B>清单4:通过反射增加一个JavaBean 属性</B></A><BR>
      <TABLE bgColor=#cccccc border=1 cellPadding=5 cellSpacing=0 
        width="100%"><TBODY>
        <TR>
          <TD><PRE><CODE>
public int incrementProperty(String name, Object obj) {
    String prop = Character.toUpperCase(name.charAt(0)) +
        name.substring(1);
    String mname = "get" + prop;
    Class[] types = new Class[] {};
    Method method = obj.getClass().getMethod(mname, types);
    Object result = method.invoke(obj, new Object[0]);
    int value = ((Integer)result).intValue() + 1;
    mname = "set" + prop;
    types = new Class[] { int.class };
    method = obj.getClass().getMethod(mname, types);
    method.invoke(obj, new Object[] { new Integer(value) });
    return value;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
      <P>为了遵循JavaBeans惯例,我把属性名的首字母改为大写,然后预先考虑 <CODE>get</CODE> 
      来创建读方法名,<CODE>set</CODE>来创建写方法名。JavaBeans读方法仅返回值,而写方法使用值作为唯一的参数,因此我规定方法的参数类型以进行匹配。最后,该惯例要求方法为公共,因此我使用查找格式,查找类上可调用的公共方法。</P>
      <P>这一实例是第一个我使用反射传递主值的实例,因此现在我们来看看它是如何工作的。基本原理很简单:无论什么时候您需要传递主值,只需用相应封装类的一个实例(在<CODE>java.lang</CODE>包中定义)来替换该类主值。这可以应用于调用和返回。因此,当我在实例中调用<CODE>get</CODE>方法时,我预计结果为实际<CODE>int</CODE>属性值的<CODE>java.lang.Integer</CODE>封装。</P>
      <P><A name=IDAZWMQD><SPAN 
      class=atitle3>反射数组</SPAN></A><BR>数组是Java编程语言中的对象。与所有对象一样,它们都有类。如果您有一个数组,使用标准<CODE>getClass</CODE>方法,您可以获得该数组的类,就象任何其它对象一样。但是,<I>不通过</I>现有的实例来获得类不同于其它类型的对象。即使您有一个数组类,您也不能直接对它进行太多的操作 
      -- 反射为标准类提供的构造函数接入不能用于数组,而且数组没有任何可接入的字段,只有基本的<CODE>java.lang.Object</CODE> 
      方法定义用于数组对象。</P>
      <P>数组的特殊处理使用<CODE>java.lang.reflect.Array</CODE>类提供的静态方法的集合。该类中的方法使您能够创建新数组,获得数组对象的长度,读和写数组对象的索引值。</P>
      <P>清单5显示了一种重新调整现有数组大小的有效方法。它使用反射来创建相同类型的新数组,然后在返回新数组之前,在老数组中复制所有数据。</P><A 
      name=IDATXMQD><B>清单 5:通过反射来扩展一个数组</B></A><BR>
      <TABLE bgColor=#cccccc border=1 cellPadding=5 cellSpacing=0 
        width="100%"><TBODY>
        <TR>
          <TD><PRE><CODE>
public Object growArray(Object array, int size) {
    Class type = array.getClass().getComponentType();
    Object grown = Array.newInstance(type, size);
    System.arraycopy(array, 0, grown, 0,
        Math.min(Array.getLength(array), size));
    return grown;
}
</CODE></PRE></TD></TR></TBODY></TABLE>
      <P><A name=IDA3XMQD><SPAN 
      class=atitle2>安全性和反射</SPAN></A><BR>在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,您可能希望框架能够全面接入您的代码,无需考虑常规的接入限制。但是,在其它情况下,不受控制的接入会带来严重的安全性风险,如当代码在不值得信任的代码共享的环境中运行时。</P>
      <P>由于这些互相矛盾的需求,Java编程语言定义一种多级别方法来处理反射的安全性。基本模式是对反射实施与应用于源代码接入相同的的限制:</P>
      <UL>
        <LI>从任意位置到类公共组件的接入 
        <LI>类自身外部无任何到私有组件的接入 
        <LI>受保护和打包(缺省接入)组件的有限接入 </LI></UL>
      <P>不过-至少某些时候,围绕这些限制有一种简单的方法。我在前面实例中使用的<CODE>Constructor</CODE>、<CODE>Field</CODE> 
      和 <CODE>Method</CODE> 类都扩展了一个普通的基本类--&nbsp; 
      <CODE>java.lang.reflect.AccessibleObject</CODE> 
      类。该类定义一种<CODE>setAccessible</CODE>方法,使您能够启动或关闭对这些类中其中一个类的实例的接入检测。唯一的问题在于如果使用了安全性管理器,它将检测正在关闭接入检测的代码是否许可了这样做。如果未许可,安全性管理器抛出一个例外。</P>
      <P>清单6展示了一个程序,在<A 
      href="http://www-900.ibm.com/developerWorks/cn/java/j-dyn0603/#code1">清单 
      1</A><CODE>TwoString</CODE> 类的一个实例上使用反射来显示安全性正在运行:</P><A 
      name=IDAMZMQD><B>清单 6:反射安全性正在运行</B></A><BR>
      <TABLE bgColor=#cccccc border=1 cellPadding=5 cellSpacing=0 
        width="100%"><TBODY>
        <TR>
          <TD><PRE><CODE>
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);
        }
    }
}
</CODE></PRE></TD></TR></TBODY></TABLE>
      <P>如果您编译了这一程序,不使用任何特定参数直接从命令行运行,它将在<CODE>field.get(inst)</CODE>调用中抛出一个<CODE>IllegalAccessException</CODE>。如果您未注释<CODE>field.setAccessible(true)</CODE>代码行,那么重新编译并重新运行该代码,它将取得成功。最后,如果您在命令行添加了JVM参数<CODE>-Djava.security.manager</CODE>以实现安全性管理器,它将再次失败,除非您定义了<CODE>ReflectSecurity</CODE>类的许可权限。</P>
      <P><A name=IDAM0MQD><SPAN 
      class=atitle2>反射性能</SPAN></A><BR>反射是一种强大的工具,但也存在一些不足。一个主要的缺点是对性能有影响。使用反射基本上是一种解释操作,您可以告诉JVM您希望做什么并且它满足您的要求。这类操作总是慢于只直接执行相同的操作。为了阐述使用反射的性能成本,我为本文准备了一组基准程序(见<A 
      href="http://www-900.ibm.com/developerWorks/cn/java/j-dyn0603/#resources">参考资料</A>,完整代码链接)。</P>
      <P>清单7是字段接入性能测试的一个摘用,包括基本的测试方法。每种方法测试字段接入的一种形式 -- <CODE>accessSame</CODE> 
      与同一对象的成员字段协作,<CODE>accessOther</CODE> 
      使用可直接接入的另一对象的字段,<CODE>accessReflection</CODE> 
      使用可通过反射接入的另一对象的字段。在每种情况下,方法执行相同的计算 -- 循环中简单的加/乘顺序。</P><A 
      name=IDAH1MQD><B>清单 7:字段接入性能测试代码</B></A><BR>
      <TABLE bgColor=#cccccc border=1 cellPadding=5 cellSpacing=0 
        width="100%"><TBODY>
        <TR>
          <TD><PRE><CODE>
public int accessSame(int loops) {
    m_value = 0;
    for (int index = 0; index &lt; 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 &lt; 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 &lt; loops; index++) {
            int value = (field.getInt(timing) +
                ADDITIVE_VALUE) * MULTIPLIER_VALUE;
            field.setInt(timing, value);
        }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -