Java 反射
一、概念
Java反射:
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.
反射被广泛地用于那些需要在运行时检测或修改程序行为的程序中。这是一个相对高级的特性,只有那些语言基础非常扎实的开发者才应该使用它。如果能把这句警示时刻放在心里,那么反射机制就会成为一项强大的技术,可以让应用程序做一些几乎不可能做到的事情。Java反射具备如下一些特点:
- 可扩展性,应用程序可以使用外部的用户使用一个类的全限定名称加载的扩展Object对象。
- 类信息浏览及可视化开发技术,枚举类的成员,使用反射方式来加载类信息以编写正确的代码。
- 调试和测试工具,检查私有成员,使用反射手段执行代码测试。
反射的缺点:反射的功能很强大,但应当被合理的使用,尽量避免使用反射。
- 性能开销
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无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
- 安全限制
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,那么这就是个问题了。
- 内部暴露
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.
由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
二、Java Class对象
概述:Java中的对象不是引用类型就是原始类型,所有的引用类型都是java.lang.Object的直接或者间接子类,原始类型包括boolean, byte, short, int, long, char, float, 和double。对于每一个Object类型JVM虚拟机都为其实例化一个不可改变的java.lang.Class 实例,并提供了方法来检查对象的运行时属性包括成员和类型信息,Class还提供了创建新类和对象的方法,最重要的它是所有反射API的入口点。
获取Class对象
所有反射操作的入口点都是java.lang.Class对象,除了java.lang.reflect.ReflectPermission外java.lang.reflect包下的类都没有公共的构造器,要获取这些类的实例需要调用Class对象的适当方法,能否获取一个Class对象取决于代码能否接收一个Object对象,类名,类型或者一个已经存在的Class对象。
- Object.getClass(), 如果一个Object对象的实例是有效的,则最简单的获得该对象的Class对象的方式为Object.getClass()。
public class T1 {
public enum E {
A, B
}
@Test
public void test_1() {
/* 如果一个Object对象的实例是有效的,则最简单的获得该对象的Class对象的方式为Object.getClass() */
"foo".getClass();
/* 返回Console对应的Class对象 */
Class c = System.console().getClass();
/* 获取枚举类型的Class对象 */
Class d = E.A.getClass();
/* 获取数组对应的Class对象 */
byte[] bytes = new byte[1024];
Class e = bytes.getClass();
/*获取泛型类的Class对象,因为Set为接口,因而返回的是HashSet的Class对象*/
Set<String> s = new HashSet<String>();
Class f = s.getClass();
}
}
- .class语法,如果类型是有效的但是没有对应的实例则可以通过变量后追加.class来获取对应的Class对象,这是最简单的获取原始数据类型的Class对象。
public class T2 {
@Test
public void test_1(){
boolean b;
Class c = b.getClass(); // 编译错误
Class a = boolean.class; // 正确
Class d = java.io.PrintStream.class;//正确
Class e = int[][][].class;//正确
}
}
- Class.forName(),如果一个类的全限定名称是有效的则可以通过Class.forName()静态方法获取一个Class实例,不能用于原始类型。数组类型的类名可以通过调用 Class.getName()来获得,该语法适用于引用类型和原始类型
public class T3 {
@Test
public void test(){
/*如果一个类的全限定名称是有效的则可以通过Class.forName()静态方法获取一个Class实例,不能用于原始类型。数组类型的类名可以通过调用 Class.getName()来获得,该语法适用于引用类型和原始类型*/
try {
Class a=Class.forName("cn.com.xiaofen.reobject.T1");
Class cDoubleArray = Class.forName("[D");//double[].class
Class cStringArray = Class.forName("[[Ljava.lang.String;");//String[][].class
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
- 原始类型的包装类的TYPE字段,每一个原始类型及void在java.lang包下都存在一个包装类,每一个包装类型存在一个字段TYPE等于原始类型的Class对象。
public class T4 {
@Test
public void test() {
/* 每一个原始类型及void在java.lang包下都存在一个包装类,每一个包装类型存在一个字段TYPE等于原始类型的Class对象 */
Class c = Double.TYPE;//==double.class;
Class d = Void.TYPE;//==void.class;
}
}
- 方法返回Class对象,存在几种能够返回Class对象的API,只不过只能在已知的Class对象中调用
public class T5 {
@Test
public void test(){
/*存在几种能够返回Class对象的API,只不过只能在已知的Class对象中调用*/
/*返回父类的Class对象*/
Class c = javax.swing.JButton.class.getSuperclass();
/*返回Class兑现的所有公共类、接口、枚举、成员,包括继承的成员*/
Class<?>[] d = Character.class.getClasses();
/*返回Class中的所有接口和显示申明的枚举*/
Class<?>[] e = Character.class.getDeclaredClasses();
/*返回当前类的申明Class对象*/
Class f=Character.class.getDeclaringClass();
//java.lang.reflect.Field.getDeclaringClass()
//java.lang.reflect.Method.getDeclaringClass()
//java.lang.reflect.Constructor.getDeclaringClass()
}
}
三、检查类修饰符和类型
一个类可以声明一个或多个修饰符影响其运行时行为,常见的修饰符如下:
- 访问修饰符: public, protected, and private
- 强制重写: abstract
- 实例成员: static
- 不可修改: final
- 严格的浮点数行为: strictfp
- 注解
并不是所有的修饰符都适用于所有的类,java.lang.reflect.Modifier 类申明了所有可用的修饰符。Modifier提供了方法来解码Class.getModifiers()返回的修饰符的集合。
Hello.java
@ThreadSafe
public class Hello<T,S> implements Serializable{
private static final long serialVersionUID = 1L;
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void hello (){
System.out.println(getMessage());
}
}
T1.java
public class T1 {
@Test
public void test() throws ClassNotFoundException {
String str = "cn.com.xiaofen.modifier.Hello";
Class c = Class.forName(str);
/*打印标准类名*/
out.format("Class:%n %s%n%n", c.getCanonicalName());
/*打印访问修饰符*/
out.format("Modifiers:%n %s%n%n", Modifier.toString(c.getModifiers()));
/*返回一个数组TypeVariable对象代表通过这个通用的声明,声明的类型变量。 */
out.format("Type Parameters:%n");
TypeVariable[] tv = c.getTypeParameters();
if (tv.length != 0) {
out.format(" ");
for (TypeVariable t : tv)
out.format("%s ", t.getName());
out.format("%n%n");
} else {
out.format(" -- No Type Parameters --%n%n");
}
/*打印实现的接口*/
out.format("Implemented Interfaces:%n");
Type[] intfs = c.getGenericInterfaces();
if (intfs.length != 0) {
for (Type intf : intfs)
out.format(" %s%n", intf.toString());
out.format("%n");
} else {
out.format(" -- No Implemented Interfaces --%n%n");
}
/*打印继承结构*/
out.format("Inheritance Path:%n");
List<Class> l = new ArrayList<Class>();
printAncestor(c, l);
if (l.size() != 0) {
for (Class<?> cl : l)
out.format(" %s%n", cl.getCanonicalName());
out.format("%n");
} else {
out.format(" -- No Super Classes --%n%n");
}
/*打印添加的注解*/
out.format("Annotations:%n");
Annotation[] ann = c.getAnnotations();
if (ann.length != 0) {
for (Annotation a : ann)
out.format(" %s%n", a.toString());
out.format("%n");
} else {
out.format(" -- No Annotations --%n%n");
}
}
private static void printAncestor(Class<?> c, List<Class> l) {
Class<?> ancestor = c.getSuperclass();
if (ancestor != null) {
l.add(ancestor);
printAncestor(ancestor, l);
}
}
}
四、发现类成员
Class对象提供了两种方法访问字段、方法和构造器,使用方法列举成员和使用方法来查找特定的成员。
Class Methods for Locating Fields | |||
Class API | List of members? | Inherited members? | Private members? |
no | no | yes | |
no | yes | no | |
yes | no | yes | |
yes | yes | no |
Class Methods for Locating Methods | |||
Class API | List of members? | Inherited members? | Private members? |
no | no | yes | |
no | yes | no | |
yes | no | yes | |
yes | yes | no |
Class Methods for Locating Constructors | |||
Class API | List of members? | Inherited members? | Private members? |
no | N/A1 | yes | |
no | N/A1 | no | |
yes | N/A1 | yes | |
yes | N/A1 | no |
T2.class
public class T2 {
@Test
public void test() {
try {
String str="cn.com.xiaofen.modifier.Hello";
Class<?> c = Class.forName(str);
out.format("Class:%n %s%n%n", c.getCanonicalName());
Package p = c.getPackage();
out.format("Package:%n %s%n%n", (p != null ? p.getName() : "-- No Package --"));
printMembers(c.getConstructors(), "Constuctors");
printMembers(c.getFields(), "Fields");
printMembers(c.getMethods(), "Methods");
printClasses(c);
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
private static void printMembers(Member[] mbrs, String s) {
out.format("%s:%n", s);
for (Member mbr : mbrs) {
if (mbr instanceof Field)
/*打印字段*/
out.format(" %s%n", ((Field) mbr).toGenericString());
else if (mbr instanceof Constructor)
/*打印构造器*/
out.format(" %s%n", ((Constructor) mbr).toGenericString());
else if (mbr instanceof Method)
/*打印方法*/
out.format(" %s%n", ((Method) mbr).toGenericString());
}
if (mbrs.length == 0)
out.format(" -- No %s --%n", s);
out.format("%n");
}
private static void printClasses(Class<?> c) {
out.format("Classes:%n");
Class<?>[] clss = c.getClasses();
for (Class<?> cls : clss)
out.format(" %s%n", cls.getCanonicalName());
if (clss.length == 0)
out.format(" -- No member interfaces, classes, or enums --%n");
out.format("%n");
}
}
示例代码地址https://github.com/poai/Reflect.git