反射

 .问题的提出

在Java运行时而非编译时环境中:(1)对于任意一个类,能否知道这个类有哪些属性和方法?(2)对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。

Java 反射机制主要提供了以下功能:(1)"在运行时"判断任意一个类所具有的成员变量和方法(当然在这之前需要 "在运行时"判断任意一个对象所属的类);(2)"在运行时"调用任意一个对象的方法(当然在这之前需要 "在运行时"构造任意一个类的对象)。

2.Reflection是什么:Reflection是Java被视为动态语言的一个关键性质。

(1)这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息(包括其modifiers[诸如public, static 等等]、superclass[例如Object]、实现之interfaces[例如Serializable],也包括fields和methods的所有信息)。

(2)并可于运行时改变fields内容或调用methods。

一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言,但java却有动态机制:Reflection:用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义,指method的代码),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。

3.Reflection的相关类及其方法

(1)在JDK中,主要由以下类来实现Java反射机制

java.lang.Class类:代表一个类。

java.lang.reflect.Field 类:代表类的成员变量(成员变量也称为类的属性)。

java.lang.reflect.Method类:代表类的方法。

java.lang.reflect.Constructor 类:代表类的构造方法。

java.lang.reflect.Array类:提供了动态创建数组,以及访问数组的元素的静态方法

package edu.hust.reflect;

import java.lang.reflect.Method;

public class DumpReflect {

    public static void printMethodOfClass(String className) {
        Class<?> classType = null;
        try {
            classType = Class.forName(className);
        } catch (ClassNotFoundException e) {
            System.err.println("没有这个类,请确认类的完整名称是正确的!!!");
        }
        
        Method[] methods = classType.getDeclaredMethods();
        
        for (Method method : methods) {
            System.out.println(method.toString() + " 方法的缩写为:" + method.getName());
        }
        
    }
    
    public static void main(String[] args) {
        printMethodOfClass("java.lang.Thread");
    }
}
package edu.hust.reflect;

import java.lang.reflect.Method;

public class InvokeTester {
    public int add(int parma1, int param2) {
        return parma1 + param2;
    }
    
    public String echo(String echo_msg) {
        return "The message of echo is: " + echo_msg;
    }
    
    public static void main(String[] args) throws Exception {
        //Class类的实例对象可以理解为一个类. 即相对于Class类来说, 它是一个对象, 但相对于其他java元素来说, 它还是一个类.
        Class<?> classType = InvokeTester.class;
        
        Object obj = classType.getConstructor(new Class[] {}).newInstance(new Object[] {});
        
        /*
         * 生成InvokeTester对象的add()/echo()方法. classType.getMethod()第一个参数是classType中某方法的名字, 第二个参数是classType中某方法的参数
         * 
         * 尽管add()方法的两个参数以及返回值都是int类型,调用addMethod对象的invoke()方法时,只能传递Integer 类型的参数,并且invoke()方法的返回类型也是Integer类型.
         * 
         * */
        Method addMethod = classType.getMethod("add", new Class[] {int.class, int.class});
        Method echoMethod = classType.getMethod("echo", new Class[] {String.class});
        
        Object result = addMethod.invoke(obj, new Object[] {new Integer(100), new Integer(150)});
        System.out.println((Integer) result);
        
        result = echoMethod.invoke(obj, new Object[] {"hi, forrest"});
        System.out.println((String) result);
        
    }
}
(2)java.lang.Class类是Reflection API 中的核心类,它有以下方法

getName():获得类的完整名字。

getFields():获得类的public类型的属性;返回Field类型数组。

getDeclaredFields():获得类的所有属性;返回Field类型数组。

getMethods():获得类的public类型的方法;返回Method类型数组。

getDeclaredMethods():获得类的所有方法;返回Method类型数组。

getConstructors():获得类的public类型的构造方法;返回Constructor类型数组。

getField(String name):获得类的public类型的指定名称的属性

getDeclaredField(String name):获得类的指定名称的属性

getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。

getDeclaredMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。

getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。

newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

package edu.hust.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class CloneBean {

    public static Object cloneBean(Object originalObj) throws Exception {
        // 获得对象的类型
        Class<?> classType_Original = originalObj.getClass();
        
        /*
         * 通过默认构造方法创建一个新的对象
         * 
         * 使用classType_Original.getConstructor(new Class[] {}).newInstance(new Object[] {});方法获得对象, 该对象对应的类的构造方法必须显式的声明出来。
         * 即, public User(){}这句话必须写出来(虽然如果不写, 系统也会自动生成这样一个constructor)
         * 
         * Class类的getConstructor()方法获得一个Constructor对象,然后调用Constructor对象的newInstance()方法构造一个实例, 这里要注意:getConstructor()与newInstance()参数必须保持一致.
         * 
         * 使用classType_Original.newInstance();方法获得对象, 该对象对应的类的构造方法不必显式的声明出来。
         * 但这种方法只适合于:只有一个无参constructor, 而没有其他任何constructor的类
         * 
         * */   
        Object cloneObj = classType_Original.getConstructor(new Class[] {}).newInstance(new Object[] {});
        //Object cloneOb = classType_Original.newInstance();
        
        // 获得对象的所有属性
        Field[] fields = classType_Original.getDeclaredFields();
        
        for (Field field : fields) {
            // 获得和属性对应的getXXX()/setXXX()方法的名字
            String getMethodName = "get" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
            String setMethodName = "set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1);
            
            // 获得和属性对应的getXXX()/setXXX()方法
            Method getMethod = classType_Original.getMethod(getMethodName, new Class[] {});
            Method setMethod = classType_Original.getMethod(setMethodName, new Class[] {field.getType()});
            
            // 调用原对象的getXXX()方法获得bean的属性值, 调用拷贝对象的setXXX()方法将这些属性值赋给拷贝对象.
            Object value = getMethod.invoke(originalObj, new Object[] {});
            setMethod.invoke(cloneObj, new Object[] {value});
        }
        return cloneObj;
    }
    
    public static void main(String[] args) {
        User1 originalUser = new User1();
        originalUser.setUsername("forrest");
        originalUser.setPassword("vivian");
        User1 cloneUser;
        try {
            cloneUser = (User1) cloneBean(originalUser);
            System.out.println("cloneUser.getUsername():" + cloneUser.getUsername());
            System.out.println("cloneUser.getPassword():" + cloneUser.getPassword());
        } catch (Exception e) {
            // 这里将Exception吞掉了, 这种方法万万要不得, 不过本例只用来演示而已.
        }
    }
    
}

class User1 {
    private String username;
    private String password;
    public User1() {}
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
运行时生成instances:在Reflection动态机制中有两种方法生成对象实体,一个针对"无自变量constructor";一个针对"带参数constructor"。如果欲调用的是"带参数constructor"就比较麻烦些,不再调用Class的newInstance(),而是调用Constructor 的newInstance(),首先准备一个Class[]做为constructor的参数类型,然后以此为自变量调用getConstructor()获得一个专属constructor。接下来再准备一个Object[] 做为constructor实参值,调用上述专属constructor的newInstance()。

运行时调用methods:为什么获得Method object时不需指定回返类型?因为method overloading机制要求signature必须唯一,而回返类型并非signature的一个成份。换句话说,只要指定了method名称和参数列,就一定指出了一个独一无二的method。

运行时变更fields内容:与先前两个动作相比,"变更field内容"轻松多了,因为它不需要参数和自变量。首先调用Class的getField()并指定field名称。获得特定的Field object之后便可直接调用Field的get()和set()方法。

package edu.hust.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class NewInstance {
    public static void main(String[] args) throws Exception {
        Class<?> classType = User2.class;
        
        Object obj1 = classType.newInstance();
        
        //(1) 运行时生成instances
        Class<?>[] parameterTypes = new Class[] {String.class, String.class};
        Constructor<?> construcotr = classType.getConstructor(parameterTypes);
        Object[] initargs = new Object[] {"forrest", "vivian"};
        Object obj2 = construcotr.newInstance(initargs);
        //以上四句可用下面一句代替, 上面的四句是一步步解释了通过constructor获得instance的整个过程, 而下面一句是通用写法.
        //Object obj2 = classType.getConstructor(new Class[] {String.class, String.class}).newInstance(new Object[] {"forrest", "vivian"});
        
        System.out.println(obj1.getClass().getMethod("getUsername", new Class[] {}).invoke(obj1, new Object[] {}));
        System.out.println(obj2.getClass().getMethod("getUsername", new Class[] {}).invoke(obj2, new Object[] {}));
        
        
        //(2) 运行时调用methods
        parameterTypes = new Class[] {String.class};
        Method getUsername_Method = obj1.getClass().getMethod("getUsername", new Class[] {});
        Method getPassword_Method = obj1.getClass().getMethod("getPassword", new Class[] {});
        Method setUsername_Method = obj1.getClass().getMethod("setUsername", parameterTypes);
        Method setPassword_Method = obj1.getClass().getMethod("setPassword", parameterTypes);
        
        Object[] arg1 = new Object[] {"土豆"};
        Object[] arg2 = new Object[] {"红薯"};
        setUsername_Method.invoke(obj1, arg1);
        setPassword_Method.invoke(obj1, arg2);
        Object usernameObj = getUsername_Method.invoke(obj1, new Object[] {});
        Object passwordObj = getPassword_Method.invoke(obj1, new Object[] {});
        
        System.out.println(usernameObj);
        System.out.println(passwordObj);
        
        
        //(3) 运行时变更fields内容
        Field email = classType.getDeclaredField("email");
        Field address = classType.getDeclaredField("address");
        
        email.set(obj1, "牛奶"); //这里要求属性不可以是private类型(如果是调用包外类的属性, 不可以是friendly类型, 如果..不可以是protected类型).
        address.set(obj1, "奶牛");
        
        Object emailObj = email.get(obj1);
        Object addressObj = address.get(obj1);
        
        System.out.println(emailObj);
        System.out.println(addressObj);
        
    }
}

class User2 {
    private String username;
    private String password;
    
    public String email;
    public String address;
    
    public User2(){}
    public User2(String username, String password) {
        this.setUsername(username);
        this.setPassword(password);
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    
}
(3)invoke()方法

java.lang.reflect.Method类有如下invoke()方法:public Object invoke(Object obj, Object... args)

Parameters: obj - the object which the underlying method is invoked from (方法所属的对象);args - the arguments used for the method call (方法的参数列表)

Returns: the result of dispatching the method represented by this object on obj with parameters args (方法的返回对象, 如方法为void则返回null, 如方法为int等八种基本类型则返回其对应的包装类)

Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。

java.lang.reflect.InvocationHandler接口有如下invoke()方法:Object invoke(Object proxy, Method method, Object[] args) throws Throwable

Processes a method invocation on a proxy instance and returns the result. This method will be invoked on an invocation handler when a method is invoked on a proxy instance that it is associated with.

Parameters: proxy - the proxy instance that the method was invoked on;method - the Method instance corresponding to the interface method invoked on the proxy instance. The declaring class of the Method object will be the interface that the method was declared in, which may be a superinterface of the proxy interface that the proxy class inherits the method through.;args - an array of objects containing the values of the arguments passed in the method invocation on the proxy instance, or null if interface method takes no arguments. Arguments of primitive types are wrapped in instances of the appropriate primitive wrapper class, such as java.lang.Integer or java.lang.Boolean.

Returns: the value to return from the method invocation on the proxy instance. If the declared return type of the interface method is a primitive type, then the value returned by this method must be an instance of the corresponding primitive wrapper class; otherwise, it must be a type assignable to the declared return type. If the value returned by this method is null and the interface method's return type is primitive, then a NullPointerException will be thrown by the method invocation on the proxy instance. If the value returned by this method is otherwise not compatible with the interface method's declared return type as described above, a ClassCastException will be thrown by the method invocation on the proxy instance.

4.如何获得一个Class实例

Class class十分特殊。它和一般classes一样继承自Object,其实体用以表达Java程序运行时的classes和interfaces,也用来表达enum、array、primitive Java types(boolean, byte, char, short, int, long, float, double)以及关键词void。当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class object。Class是Reflection起源。针对任何您想探勘的class,唯有先为它产生一个Class object,接下来才能经由后者唤起为数十多个的Reflection APIs.

得到一个对象的类的实例的方法:

(1)java.lang.Object类的getClass():返回一个对象所对应的Class类的实例,每个class都重载了此方法;还有一个Class.getSuperclass()。

(2)static方法, Class.forName():最常被使用,参数为类的全称(即包+类名)。

(3).class语法:例如,String.class、java.lang.Thread.class、Main.InnerClass.class、int.class、int[].class.

(4)primitive wrapper classes的.TYPE语法:例如,Boolean.TYPE、Integer.TYPE等8中包装类、再加一个Void.TYPE;实际上,凡是可以用.TYPE获得Class对象的类同样也可以通过.class获得Class的对象,这里我们只是提供了另外一种选择。


本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/ForWayfarer/archive/2008/09/24/2970438.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值