JAVA反射
JAVA的反射是指在运行期获取类的各种信息,并且可以实例化对象,调用对应的方法。反射是一个高级特性,需要开发人员有较好的基础知识。
反射的具有以下优点:
-
扩展性
反射能够通过对象完整的名称来创建一个对象,然后使用这个这个对象。
-
类浏览器和可视的开发环境
一个类浏览器需要遍历一个类的所有成员(类似IDE里面显示类的结构)。可视的开发环境是反射可以帮助开发人员在编程的时候编出正确的代码(例如智能拼写)
-
程序调试和测试工具
程序调试能够通过反射检查类的私有变量。测试工具能够利用反射来系统地调用类的API集合,保证测试单元的高覆盖率。
越是强大的工具,越需要谨慎的使用,如果能够不使用反射就能够实现相同的功能,那就不要使用反射。反射也有缺点:
- 费用高昂
因为反射是动态的调用对象, JVM无法对其进行优化,会导致使用反射实现功能的成本高于不使用反射的成本,所以反射不适用于处理对于性能要求特别高的应用。关于反射的性能,请参考这里.
- 安全限制
反射需要程序的运行时权限.如果JVM的安全性管理器并没有提供这个权限,那就完蛋了。
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// Reflective call to get caller class is only needed if a security manager
// is present. Avoid the overhead of making this call otherwise.
caller = Reflection.getCallerClass();
balabalalba....
}
- 暴露了程序的内部构件
反射能够执行一些本来不允许的操作,例如访问类的私有变量和使用私有函数等等,这样破坏了类的封装和抽象,而且会产生一些意想不到的结果。(访问了人家的私有变量,动了人家的小老婆,结果某天人家换了小老婆)
-
通过反射,我们可以获得类的一些相关内容:
-
Class
-
类名
-
修饰符
-
包信息
-
父类
-
实现的接口
-
构造函数
-
方法
-
变量
-
注解
等等
Classes
java中的对象不是引用就是私有类型。所有的引用类型都继承自java.lang.object. classes, enums, arrays, interfaces 都是引用类型。私有类型有 boolean, byte, short, int, long, char, float, double.
对于所有类型的对象,JVM都提供了一个不变的实例(instance)负责在运行时检查这个对象的属性,这个实例的类型就是java.lang.Class。同时Class还有能力去创建新的类和对象。最重要的就是,所有的反射API都是以它为基础的。
-
Retrieving Class Objects
描述了如何获取一个Class。前面说过所有反射的入口(最先调用的操作)都是java.lang.Class。
反射所有的异常都在java.lang.reflect.ReflectPermission中.
java.lang.reflect中所有的类都没有公有的构造函数(为什么不需要呢,因为它由JVM进行创建管理,不需要用户来创建)。 -
[Examining Class Modifiers and Types](前面说过所有反射的入口(最先调用的操作)都是java.lang.Class.
反射所有的异常都在java.lang.reflect.ReflectPermission中.
java.lang.reflect中所有的类都没有公有的构造函数(为什么不需要呢,因为它由JVM进行创建管理,不需要用户来创建)。
)描述了如何获取类的描述信息。 -
Discovering Class Members 描述了如何获取类中的constructors, fields, methods, and nested classes
-
Troubleshooting描述了在使用Class中遇到的一些常见的错误。
修饰符 Modifers
import java.lang.reflect.Modifier;
import static java.lang.System.out;
public class ModifyDemo {
public static void main(String... args) {
try {
Class<?> c = Class.forName("java.util.Arrays");
out.format("Class:%n %s%n%n", c.getCanonicalName());
out.format("Modifiers:%n %s%n%n",
Modifier.toString(c.getModifiers()));
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
上面的例子中,通过c.getModifiers
获得了java.util.Arrays
的修饰符,程序结果如下:
Class:
java.util.Arrays
Modifiers:
public
包信息
import static java.lang.System.out;
public class PackageDemo {
public static void main(String... args) {
try {
Class<?> c = Class.forName("java.util.Arrays");
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 --"));
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
输出结果如下:
Class:
java.util.Arrays
Package:
java.util
构造函数
package com.zjl.learn;
import java.lang.reflect.*;
public class ConstructorDemo{
public ConstructorDemo(){ }
public ConstructorDemo(int a, int b){
System.out.println("a="+a+"b="+b);
}
public static void main(String args[]){
try {
// 获取CALSS对象
Class cls = Class.forName("com.zjl.learn.ConstructorDemo");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
// 获取带参数的Constructor对象
Constructor ct= cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
} catch (Throwable e) {
System.err.println(e); }
}
}
上面的例子中,我们使用了
Class.forName("com.zjl.learn.ConstructorDemo");
获取类的对象,然后通过
Constructor ct= cls.getConstructor(partypes);
获取Constructor对象。
这里注意,forName
函数中的参数应该是带有包名的全路径,否则会抛出 java.lang.ClassNotFoundException
异常。
如果构造函数传入的参数错误,会抛出NoSuchMethodException
异常。