您的位置:首页 > 编程语言 > Java开发

Java反射机制

2015-07-16 20:36 736 查看
Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类包括FIeld、Method以及Constructor类。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor创建新的对象,用set()和get()方法访问和修改Field对象关联的字段,用invoke()方法调用与Method对象相关联的方法。

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

Java 反射机制主要提供了以下功能:

在运行时判断一个对象的所属类型;

在运行时构造任意一个类的对象;

在运行时判断任意一个类所具有的成员变量和方法(包括私有的);

在运行时调用任意一个类生成的对象的方法。

Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods。

在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中

Class类:代表一个类;

Field 类:代表类的成员变量(成员变量也称为类的属性);

Method类:代表类的方法;

Constructor 类:代表类的构造方法

在刚刚学jdbc时用过一行代码,Class.forName(“com.mysql.jdbc.Driver.class”),表示加载驱动的Class对象。下面的例子,使用相同的方法生成Class对象,在运行时,获得类的信息。

[code]import java.lang.reflect.Method;
public class DumpMethods {
    //从命令行接受一个字符串(该字符串是某个类的全名)
    //打印该类中的所有方法申明,包括私有的
    public static void main(String[] args) throws ClassNotFoundException {
        //Class类是java反射的入口
        Class <?> classType = Class.forName("java.lang.String");
        Method [] methods = classType.getDeclaredMethods();
        for(Method method :methods){
                System.out.println(method);
            }           
        }
}
/*
*在运行时获得一个类的类型信息,在本例中传入参数“java.lang.String”在编译器是无法知道该类的一些类型信息的
 *input : java.lang.String
 *output:public boolean java.lang.String.equals(java.lang.Object)
    public boolean java.lang.String.equals(java.lang.Object)
    public int java.lang.String.hashCode()
    public int java.lang.String.compareTo(java.lang.Object)
    . . .
 */


要想使用反射,首先需要获得待操作的类所对应的Class对象。Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。这个Class对象是由JVM生成的,通过它能够获悉整个类的结构。

常用的获取Class对象的3种方式

1、使用Class类的静态方法。 例如:Class<?> classType=Class.forName(“java.lang.String”);

2、使用.class语法。例如:Class<?> classType=String.class;

3、使用对象的getClass()来调用。Object obj = new Object();Class<?> classType=obj.getClass().



Java反射机制也可以在运行时调用任意一个类生成的对象的方法。例程InvokeTester类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法

[code]import java.lang.reflect.Method;
public class InvokeTester {
    public int add(int param1,int param2){
        return param1+param2;
    }
    public String echo(String msg){
        return msg;
    }
    public static void main(String[] args) throws Exception {
        //获得InvokeTester类所对应的Class对象
        Class<?> classType = InvokeTester.class;
        //创建此 Class 对象所表示的类的一个实例,只会去调用其无参构造函数。
        Object invokeTester = classType.newInstance();
        //返回一个 某个Method 对象
        Method addMethod = classType.getMethod("add", new Class[]{int.class,int.class});
        //对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
        Object result=addMethod.invoke(invokeTester, new Object[]{100,200});
        //返回某个Method 对象
        Method echo = classType.getMethod("echo", new Class[]{String.class});
        //对带有指定参数的指定对象调用由此Method 对象表示的底层方法
        Object res = echo.invoke(invokeTester,new Object[]{"Hello reflection"});

        System.out.println((Integer)result);

        System.out.println((String)res);
    }
}


如下例程ReflectTester类进一步演示了Reflection API的基本使用方法。

ReflectTester类有一个copy(Object object)方法,这个方法能够创建一个和参数object同样类型的对象,然后把object对象中的所有属性拷贝到新建的对象中,并将它返回。这个例子只能复制简单的类,假定类的每个属性都有public类型的getXXX()和setXXX()方法。

/**使用类的带参的构造器生成对象

**可以看出此时是不带参数的,相当于 calssType.newInstance();

*/

Object objectCopy = calssType.getConstructor(new Class[]{}).newInstance(new Object[]{});

[code]import java.lang.reflect.Field;
import java.lang.reflect.Method;
class Customer{
    public Customer(){

    }
    public int age;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    private Long id;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

}
public class ReflectTester {
    public Object copy(Object object) throws Exception{
        Class<?> calssType = object.getClass();
        Object objectCopy = calssType.getConstructor(new Class[]{}).newInstance(new Object[]{});
        //获得该class对象代表的申明字段,包括私有的
        Field[] fields  = calssType.getDeclaredFields();
        for(Field field:fields){
            String fieldFirstCharter = field.getName().substring(0,1).toUpperCase();
            String fileName = fieldFirstCharter + field.getName().substring(1);
            String setMethodName = "set"+fileName;
            String getMethodName = "get"+fileName;
            System.out.println("setMethodName:"+setMethodName+" getMethodName:"+getMethodName);
            Method setMethod = calssType.getMethod(setMethodName, new Class[]{field.getType()});
            Method getMethod = calssType.getMethod(getMethodName, new Class[]{});
            Object value = getMethod.invoke(object, new Object[]{});
            setMethod.invoke(objectCopy,new Object[]{value});   
        }
        return objectCopy;
    }
    public static void main(String[] args) throws Exception {
        Customer object = new Customer ();
        object.setAge(19);
        object.setId(new Long(100000));
        object.setName("zhangshan");
        Customer objectCopy = (Customer)new ReflectTester().copy(object);
        System.out.println(objectCopy.getAge());
        System.out.println(objectCopy.getId());
        System.out.println(objectCopy.getName());
    }
}


利用反射,首先是Class对象的获取,之后是Method和Field对象的获取。

以Method为例,从文档中可以看到:getMethod()方法返回的是public的Method对象,而getDeclaredMethod()返回的Method对象可以是非public的。Field的方法同理。

访问私有属性和方法,在使用前要通过AccessibleObject类(Constructor、Field和Method类的基类)中的setAccessible()方法来抑制Java访问权限的检查。

实例1,调用私有方法:

[code]public class PrivateClass { 
     private String sayHello(String name){
            return "Hello: " + name;
        }
}


[code]import java.lang.reflect.Method;

public class TestPrivate {
    public static void main(String[] args) throws Exception {
        PrivateClass privateClass = new PrivateClass();
        Class<?> classType = privateClass.getClass();
        Method method =classType.getDeclaredMethod("sayHello", new Class[]{String.class});
        // 抑制Java的访问控制检查,否则,将会Error: TestPrivate can not access a member of class PrivateClass with modifiers "private"
        method.setAccessible(true);
        Object result = method.invoke(privateClass, new Object[]{"everyone!"});
        System.out.println((String)result);
    }
}


实例2,访问私有属性

[code]public class PrivateClass2 {
    private String name = "Hello everyone!";

    public String getName() {
        return name;
    }

}


[code]import java.lang.reflect.Field;

public class TestPrivate2 {
    public static void main(String[] args) throws Exception {
        PrivateClass2 privateClass2 = new PrivateClass2();
        Class<?>classType = privateClass2.getClass();
        Field field = classType.getDeclaredField("name");
        field.setAccessible(true);
        field.set(privateClass2, "Hello: everyone!");
        System.out.println(privateClass2.getName());
    }
}


反射机制的优点与缺点?

为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念。

静态编译:在编译时确定类型,绑定对象,即通过。

动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

用反射机制能干什么事?

刚开始在使用jdbc时侯,在编写访问数据库时写到想吐,有八个表,每个表都有增删改查中操作 那时候还不知道有反射机制这个概念,所以就对不同的表创建不同的dao类,这样不仅开发速率地,而且代码冗余的厉害,最要命的是看着差不多的,然后直接复制修改,由于容易犯各种低级的错误(大小写啊,多一个或少一个字母啊……),一个错误就可以让你找半天。有了java反射机制,什么都好办了,只需要写一个dao类,四个方法,增删改查,传入不同的对象,就OK啦,无需为每一个表都创建dao类,反射机制会自动帮我们完成剩下的事情,这就是它的好处。说白了,反射机制就是专门帮我们做那些重复的有规则的事情,所以现在很多的自动生成代码的软件就是运用反射机制来完成的,只要你按照规则 输入相关的参数,所以低级的程序员慢慢的就被抹杀了,为什么?因为代码都不用写了,随便一个人都会开发,还要程序员干什么啊?所以我们只有一条出路,那就是努力努力再努力,成为高级程序员,专门开发傻瓜软件,让其他程序员 到 一边凉快去,呵呵~

参考来源:

圣思园张龙老师反射教学视频;

Java反射机制
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: