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

最初使用反射的时候,总是不理解,既然可以通过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,下面我将对这些类做一个简单的说明。

  • Field类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
  • Constructor类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。
  • Method类:提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。
  • Class类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
  • Object类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。

获取类的Class对象

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

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

  • Class类提供的静态方法:forName(String className),参数className表示所需类的完全限定名。
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
         */

    }

}
  • 运用.class语法
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
         */

    }

}
  • Object类提供的方法:getClass()
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.

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

  • 性能第一 Performance Overhead Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.
    反射包括了一些动态类型,所以JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程序中使用反射。

  • 安全限制 Security Restrictions Reflection requires a runtime permission which may not be present when running under a security manager. This is in an important consideration for code which has to run in a restricted security context, such as in an Applet. 使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。。

  • 内部暴露 Exposure of Internals Since reflection allows code to perform operations that would be illegal in non-reflective code, such as accessing private fields and methods, the use of reflection can result in unexpected side-effects, which may render code dysfunctional and may destroy portability. Reflective code breaks abstractions and therefore may change behavior with upgrades of the platform. 由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,672评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,645评论 18 399
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,111评论 0 62
  • 人生会面临许多选择,这些选择会让人生感到迷茫,不知道怎样做抉择,或许有的时候选择错啦,有的时候不知道什么是对,什么...
    衡鑫阅读 288评论 0 0
  • 1、好种子 a.在腾讯公益为“先心儿童”捐款6元,祝福他们都能有机会接受治疗,健康快乐成长。 b.继续准备分享文章...
    我是一面镜子阅读 142评论 0 0