最初使用反射的时候,总是不理解,既然可以通过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. 由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。