JAVA反射机制

反射(Reflection)是java语言的一个特性,它允程序在运行时来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。这个能特定我们不常看到,但是在其他的比如C或者C++语言中很不就存在这个特性。

Java的反射机制的实现要借助于4个类:class,Constructor,Field,Method;其中class代表的时类对象,Constructor是类的构造器对象,Field是类的属性对象,Method是类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组 成部分。Class:程序运行时,java虚拟机会保存所有对象的运行时类型信息。这项信息记录了每个对象所属的类,虚拟机通常使用运行时类型信息选择正确的方法来执行。要得到这些信息就要借助于class类对象。在Object类中定义了getClass()方法。我 们可以通过这个方法获得指定对象的类对象,然后分析这个对象就可以得到这些信息。

Java类反射中的主要方法

 

对于构造函数(Constructor)、字段(Field)和方法(Method),java.lang.Class 提供独立的反射调用,以不同的方式来获得信息。调用都遵循一种标准格式。以下是用于查找构造函数的一组反射调用:

 

public Constructor<?>[] getConstructors()

      返回类中所有的public构造器集合, 默认构造器的下标为0
public Constructor<T> getConstructor(Class<?>... parameterTypes)

   返回指定public构造器,参数为构造器参数类型集合
public Constructor<?>[] getDeclaredConstructors()

返回类中所有的构造器,包括私有
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

返回获得字段信息的Class 反射调用在参数类型数组中使用了字段名:

 

注意,以上的四个方法全部需要抛出异常,当我们获得私有的方法的时候,要用setAccessible设置一下可访问的权限

 1         /**
 2          *
获取构造方法Constructor
 3          * getConstructor()  only for public
 4          * getDeclaredConstructor()  global access all
 5          *
 6          * */
 7        
 8         //
指定参数列表获取特定的方法
 9       Constructor con =cls1.getDeclaredConstructor(new Class[]{String.class});
10         con.setAccessible(true); //
设置可访问的权限
11         Object obj = con.newInstance(new Object[]{"liyang"});
12         System.out.println(obj);  //
打印一下这个对象的信息
13        
14         //
获取所有的构造方法集合
15         Constructor con1[] = cls1.getDeclaredConstructors();
16         con1[1].setAccessible(true);
17         Object obj1 = con1[1].newInstance(new Object[]{"tom"});
18         System.out.println(obj1);

  
 

获取字段信息

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 类来描述类和接口等。

 

获得一个 类的类对象的方法(以String为例子):

1.  Class c = Class.forName("java.lang.String");//完整的包名并需要捕获异常

2.  Class c = int.class;或者Class c = Integer.TYPE;

3.  String s;Class clazz = s.getClass();

 

reflection的工作机制

import java.lang.reflect.*;// 里面的Methods是用来描述类中方法的一个类

public class DumpMethod {

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 DumpMethod java.util.ArrayList

 

处理对象:

 

a.创建一个Class对象

b.通过getField 创建一个Field对象

c.调用Field.getXXX(Object)方法(XXX是Int,Float等,如果是对象就省略;Object是指实例).

 

例如:

import java.lang.reflect.*;

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);

}

    }

}  

调用类的方法 Method
     Method f = cls1.getMethod("getName", null);
     Object name = f.invoke(obj, null);
     System.out.println("we invoke method : "+ name);

 

安全性和反射:

 

在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,我们可能希望框架能够全面接入代码,无需考虑常规的接入限制。但是,在其它情况下,不受控制的接入会带来严重的安全性风险,例如当代码在不值得信任的代码共享的环境中运行时。

 

由于这些互相矛盾的需求,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类的许可权 限。

反射性能总是慢于只直接执行相同的操作。

 

一下是对应各个部分的例子:

 

具体的应用:

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 def

        o = 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、 动态检测对象的身份,替代instanceof

 

public static Boolean isKindOf(Object obj, String type)

        throws ClassNotFoundException

{

    // get the class def for obj and type

    Class c = obj.getClass();

    Class tClass = Class.forName(type);

    while ( c!=null ) {

        if ( c==tClass )

             return true;

        c = c.getSuperclass();

    }

 

    return false;

}

拿到一个类的类型信息,就可以利用反射获取其各种成员以及方法了。(注:Class 从JDK1.5版本后就开始更多为泛型服务了)那么我们怎么拿到一个类型的信息呢?假设我们有一个Role类:
    package yui;
    public class Role {
         private String name;
         private String type;
         public Role(){
             System.out.println("Constructor Role() is invoking");
        }
        private Role(String name){
            this.name = name;
            System.out.println("Constructor Role(String name) is invoking.");
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        public String getType() {
           return type;
        }
        public void setType(String type) {
           this.type = type;
        }
        @Override
        public String toString(){
           return "This is a role called "+this.name;
        }
   }

现在得到cls1就可以创建一个Role类的实例了,利用Class的newInstance方法相当于调用类的默认的构造器
        Object o = cls1.newInstance(); //创建一个实例
        //Object o1 = new Role();   //与上面的方法等价

这样就创建了一个对象,缺点是我们只能利用默认构造函数,因为Class的newInstance是不接受参数的,后面会讲到可接受参数的newInstance,第二,如果类的构造函数是private的,比如Class,我们仍旧不能实例化其对象。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值