个人喜欢从码农到CTO

JAVA反射其实就是那么一回事

2017-09-03  本文已影响76人  Bobby0322

最初使用反射的时候,总是不理解,既然可以通过new 一个对象的方式得到对象,然后通过对象去调用属性和方法,那么为什么还需要反射去调用呢?后来使用多了发现这就是一个先绑定还是后绑定的问题,很多初使用反射的开发人员通常都会有类似这种疑虑:既然在开发时就能够写好代码,干嘛还放到运行期去做,不光繁琐,而且效率也受影响。觉得主要是适用性的问题,如果你的系统没有那么高的扩展性和灵活性要求,你大可不必考虑反射。但在架构设计时,很多东西都需要考虑复用性,并且在某些特定的场景下你得不到具体的类时,你就必须用到反射。

什么是反射:

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提 出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。其中 LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射机制的语言。

什么是Java中的类反射:

Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性和方法。Java 的这一能力在实际应用中用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。
Reflection 是 Java 被视为动态(或准动态)语言的关键,允许程序于执行期 Reflection APIs 取得任何已知名称之 class 的內部信息,包括 package、type parameters、superclass、implemented interfaces、inner classes, outer class, fields、constructors、methods、modifiers,並可于执行期生成instances、变更 fields 內容或唤起 methods。

Java反射机制: JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。简而言之: 就是根据一个已经实例化了的对象来还原类的完整信息
反射:获取运行时类型信息

Java类反射中所必须的类:

Java的类反射所需要的类并不多,它们分别是:Field、Constructor、Method、Class、Object,下面我将对这些类做一个简单的说明。

获取类的Class对象

Class类的实例表示正在运行的Java程序中的类和接口,每一个类都有对应的Class对象,不管一个类生成了多少个对象,这些对象都对应内存里的同一个Class对象。Class类没有public的构造方法,Class对象是在加载类时由Java虚拟机自动构建的。

有以下几种方式来获取一个类的Class对象:

package com.boer.tdf.act.demo.reflection;

/**
 *
 * 获取类的Class对象
 *
 * 1.Class类提供的静态方法.
 *
 * Class类提供的静态方法:forName(String className),参数className表示所需类的完全限定名。
 */
public class GetClassObject01 {

    public static void main(String[] args) throws Exception {

        Class<?> classType = Class.forName("java.lang.String");

        System.out.println(classType);

        /**
         * 输出:class java.lang.String
         */

    }

}
package com.boer.tdf.act.demo.reflection;

/**
 *
 * 获取类的Class对象
 *
 * 2.运用.class语法.
 */
public class GetClassObject02 {

    public static void main(String[] args) throws Exception {

        Class<?> classType = String.class;
        System.out.println(classType);

        /**
         * 输出:class java.lang.String
         */

    }

}
package com.boer.tdf.act.demo.reflection;

import java.util.HashMap;
import java.util.Map;

/**
 * 获取类的Class对象.
 *
 * 3.Object类提供的方法:getClass()
 */
public class GetClassObject03 {

    public static void main (String[] args) throws Exception {

        Map map = new HashMap();
        Class<?> classType = map.getClass();

        System.out.println(classType);

        /**
         * 输出:class java.util.HashMap
         */


    }

}

获取类的Field(成员变量)对象

package com.boer.tdf.act.demo.reflection;

import java.lang.reflect.Field;

/**
 * 获取类的Field(成员变量)对象.
 *
 * 类的每一个成员变量都对应一个Field对象,Class类提供了以下方法来获取类的成员变量对应的Field对象:
 *
 * 1.Field getDeclaredField(String name):
 * 根据传入的变量名称返回此Class对象所表示的类或接口中声明的变量对应的Field对象。
 *
 * 2.Field[] getDeclaredFields():返回一个Field类型的数组,
 * 包含此Class对象所表示的类或接口中声明的所有变量的Field对象。
 *
 * 3.Field getField(String name):根据传入的变量名返回一个Field对象,
 * 注意与getDeclaredField(String name)不同的是,此方法返回的是public变量对应的Field对象。
 *
 * 4.Field[] getFields():返回一个Field类型的数组,
 * 注意与Field[] getDeclaredFields()方法不同的是,此方法返回的是所有public变量对应的Field对象。


 */
public class GetFieldObject {

    public static void main(String[] args) throws Exception {
        // 首先,获得String类的Class对象
        Class<?> classType = Class.forName("java.lang.String");

        // 获得String类中声明的所有成员变量的Field对象的数组

        Field[] fields = classType.getDeclaredFields();

        for(Field field : fields) {
            System.out.println(field);
        }

        System.out.println("----------------------------------------");

        // 获得String类中声明的public成员变量的Field对象的数组
        Field[] publicFields = classType.getFields();

        for(Field field : publicFields) {
            System.out.println(field);
        }


    }
}


/**
 * 输出结果:
 *
 * private final char[] java.lang.String.value
 private int java.lang.String.hash
 private static final long java.lang.String.serialVersionUID
 private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields
 public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER
 ----------------------------------------
 public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER

 从结果输出可以看出getDeclaredFields()与getFields()的区别:
 getDeclaredFields()返回的是所有属性的Field对象;
 而getFields()返回的是声明为public的属性的Field对象。
 */

获取类的Method对象

package com.boer.tdf.act.demo.reflection;

import java.lang.reflect.Method;

/**
 * 获取类的Method对象.
 *
 * 类中的每一个方法都对应一个Method对象,
 * Class类提供了以下方法来获取类中的方法对应的Method对象:

 1)Method getDeclaredMethod(String name, Class<?>... parameterTypes):
 返回一个Method对象,参数name表示方法名,可变参数parameterTypes是一个Class对象的数组,
 代表方法的参数的Class类型;

 2)Method[] getDeclaredMethods():返回Method对象的一个数组,
 这些对象反映此Class对象所表示的类或接口声明的所有方法,
 包括公共、保护、默认访问和私有方法,但不包括继承的方法;

 3)Method getMethod(String name, Class<?>... parameterTypes):返回一个Method对象,
 注意和此Method对象对应的方法是公共(public)方法;

 4)Method[] getMethods():返回一个Method数组,
 这些对象反映此Class对象所表示的类或接口中声明的公共(public)方法
 (也包括父类或父接口中声明的public方法)。
 */
public class GetMethodObject {

    public static void main(String[] args) throws Exception {
        // 首先,获得类的Class对象
        Class<?> classType = Class.forName("java.lang.reflect.Proxy");

        // 获得类中声明的所有方法的Method对象的数组,不包括继承的父类的方法

        Method[] methods = classType.getDeclaredMethods();

        for(Method method : methods) {
            System.out.println(method);
        }

        System.out.println("----------------------------------------------------------------------");

        // 获得类中的public方法的Method对象的数组,也包括继承的父类的public方法

        Method[] publicMethods = classType.getMethods();

        for(Method method : publicMethods) {
            System.out.println(method);
        }


    }
}


/**
 * 输出结果:
 *
 static java.lang.Object java.lang.reflect.Proxy.access$200()
 static java.lang.Class java.lang.reflect.Proxy.access$300(java.lang.ClassLoader,java.lang.String,byte[],int,int)
 public static boolean java.lang.reflect.Proxy.isProxyClass(java.lang.Class)
 private static native java.lang.Class java.lang.reflect.Proxy.defineClass0(java.lang.ClassLoader,java.lang.String,byte[],int,int)
 private static void java.lang.reflect.Proxy.checkNewProxyPermission(java.lang.Class,java.lang.Class)
 private static void java.lang.reflect.Proxy.checkProxyAccess(java.lang.Class,java.lang.ClassLoader,java.lang.Class[])
 public static java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.getInvocationHandler(java.lang.Object) throws java.lang.IllegalArgumentException
 public static java.lang.Class java.lang.reflect.Proxy.getProxyClass(java.lang.ClassLoader,java.lang.Class[]) throws java.lang.IllegalArgumentException
 private static java.lang.Class java.lang.reflect.Proxy.getProxyClass0(java.lang.ClassLoader,java.lang.Class[])
 public static java.lang.Object java.lang.reflect.Proxy.newProxyInstance(java.lang.ClassLoader,java.lang.Class[],java.lang.reflect.InvocationHandler) throws java.lang.IllegalArgumentException
 ----------------------------------------------------------------------
 public static boolean java.lang.reflect.Proxy.isProxyClass(java.lang.Class)
 public static java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.getInvocationHandler(java.lang.Object) throws java.lang.IllegalArgumentException
 public static java.lang.Class java.lang.reflect.Proxy.getProxyClass(java.lang.ClassLoader,java.lang.Class[]) throws java.lang.IllegalArgumentException
 public static java.lang.Object java.lang.reflect.Proxy.newProxyInstance(java.lang.ClassLoader,java.lang.Class[],java.lang.reflect.InvocationHandler) throws java.lang.IllegalArgumentException
 public final void java.lang.Object.wait() throws java.lang.InterruptedException
 public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
 public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
 public boolean java.lang.Object.equals(java.lang.Object)
 public java.lang.String java.lang.Object.toString()
 public native int java.lang.Object.hashCode()
 public final native java.lang.Class java.lang.Object.getClass()
 public final native void java.lang.Object.notify()
 public final native void java.lang.Object.notifyAll()



 *
 */

用反射机制调用对象的方法

package com.boer.tdf.act.demo.reflection;

import java.lang.reflect.Method;

/**
 * 用反射机制调用对象的方法.
 *
 *  Java反射机制可以在运行时动态调用类中的方法,
 *  Java Reflection API提供了我们所需的方法来完成动态调用。
 *
 *  要想调用类中的方法首先要创建一个对象,
 *  我们通过类的Class对象来创建它所代表的类的实例,
 *  通过Class对象我们还能获得类中声明的方法的Method对象,
 *  Method类提供了Invoke方法来调用此Method对象所表示的方法。
 */
public class InvokeTester {

    public static int add(int a, int b) {
        return a + b;
    }

    public static String echo(String str) {
        return "hello "+str;
    }

    public static void main(String[] args) throws Exception {

        InvokeTester invoke = new InvokeTester();
        System.out.println(invoke.add(1, 2));
        System.out.println(invoke.echo("Bobby"));

        // 用反射机制调用,首先获得类的Class对象
        Class<?> classType = InvokeTester.class;
        // 通过Class对象获得一个InvokeTester类的实例
        Object invoke2 = classType.newInstance();
        /**
         * 获得add(int a, int b)方法的Method对象,
         * getMethod方法的参数为方法名和方法参数类型的Class对象的数组
         */
        Method addMethod = classType.getMethod("add", int.class, int.class);
        // 通过Method类的invoke方法,调用invoke对象的add方法
        Object result = addMethod.invoke(invoke2, 3, 4);
        System.out.println(result);


        Method echoMethod = classType.getMethod("echo", String.class);
        Object result2 = echoMethod.invoke(invoke, "Samba");
        System.out.println(result2);

    }


}

/**
 * 运行结果:
 * 3
 hello Bobby
 7
 hello Samba
 */

用反射机制调用类的私有方法

package com.boer.tdf.act.demo.reflection;

/**
 * 用反射机制调用类的私有方法.
 *
 * 我们知道正常情况下一个类的私有方法只允许这个类本身来调用,但使用反射机制能打破这种访问限制,
 * 让其他的类也能调用这个类的私有的方法。
 * 这种场景在实际开发中很少用到,Java也不提倡这种用法。
 */
public class Private {

    // 定义一个私有方法
    private String sayHello(String name) {
        return "hello, "+name;
    }


}
package com.boer.tdf.act.demo.reflection;

import java.lang.reflect.Method;

/**
 * 用反射机制调用类的私有方法.
 *
 * 我们知道正常情况下一个类的私有方法只允许这个类本身来调用,
 * 但使用反射机制能打破这种访问限制,让其他的类也能调用这个类的私有的方法。
 * 这种场景在实际开发中很少用到,Java也不提倡这种用法。
 */

public class PrivateTest {

    public static void main(String[] args) throws Exception {
        // 调用Private类的私有方法
        Private p = new Private();

        Class<?> classType = p.getClass();

        Method method = classType.getDeclaredMethod("sayHello", String.class);

        // 取消Java访问检查,如果不设置此项则会报错
        method.setAccessible(true);

        String str = (String)method.invoke(p, "Bobby");

        // 输出:hello, Bobby
        System.out.println(str);


    }
}

/**
 * Method、Field、Constructor类有一个共同的父类AccessibleObject类,
 * 它提供了将反射的对象标记为在使用时取消默认Java语言访问控制检查的能力。
 * 在上面的代码中,我们在反射对象Method中设置accessible标志,
 * 它允许程序以某种通常禁止的方式来操作对象。
 */

用反射机制操作类的私有变量

package com.boer.tdf.act.demo.reflection;

/**
 * 用反射机制操作类的私有变量.
 */
public class Private2 {

    // 定义私有变量
    private String name = "zhangsan";

    public String getName() {
        return name;
    }
}

package com.boer.tdf.act.demo.reflection;

import java.lang.reflect.Field;

/**
 * 用反射机制操作类的私有变量.
 */
public class PrivateTest2 {

    public static void main(String[] args) throws Exception {

        // 改变Private2类的私有变量的值
        Private2 p = new Private2();

        Class<?> classType = p.getClass();
        Field field = classType.getDeclaredField("name");
        // 取消默认java访问控制检查,Field类的父类AccessibleObject类提供的方法
        field.setAccessible(true);
        // Field类的set(Object obj, Object value)方法将指定对象上此Field对象表示的字段设置为指定的新值
        field.set(p, "lisi");
        // 输出:lisi
        System.out.println(p.getName());

    }

}

反射的用途 Uses of Reflection

Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.
反射被广泛地用于那些需要在运行时检测或修改程序行为的程序中。这是一个相对高级的特性,只有那些语言基础非常扎实的开发者才应该使用它。如果能把这句警示时刻放在心里,那么反射机制就会成为一项强大的技术,可以让应用程序做一些几乎不可能做到的事情。

反射的缺点 Drawbacks of Reflection

Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. The following concerns should be kept in mind when accessing code via reflection.

尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心:

上一篇 下一篇

猜你喜欢

热点阅读