JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
就是说在运行时,我们可以写一个程序,这个程序能够获取任意一个类的各种信息,对于对象,我们还可以调用它的方法,看起来有点牛逼,上帝之手,不同于通常我们new对象然后调用方法的地方就是这是动态的。
Java提供了一个反射库java.lang.reflect
还有一个位于java.lang包中的Class类,下面解释一下这个类,这个类是反射中不可少的一环。
在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。JVM利用运行时类型信息选择相应的方法执行。
而保存这些信息的类就是Class类。
一个Class对象就将表示一个特定类的属性。
那么我们要怎么在程序中获得某各类的Class对象呢?Java提供了三种手段。
①所有类的父类Object类有个getClass()方法
源码注释如下
/**
* Returns the runtime class of this {@code Object}. The returned
* {@code Class} object is the object that is locked by {@code
* static synchronized} methods of the represented class.
*
* <p><b>The actual result type is {@code Class<? extends |X|>}
* where {@code |X|} is the erasure of the static type of the
* expression on which {@code getClass} is called.</b> For
* example, no cast is required in this code fragment:</p>
*
* <p>
* {@code Number n = 0; }<br>
* {@code Class<? extends Number> c = n.getClass(); }
* </p>
*
* @return The {@code Class} object that represents the runtime
* class of this object.
* @jls 15.8.2 Class Literals
*/
public final native Class<?> getClass();
②Class类有个重载的静态方法forName(),这里列出最简单的形式
也看下源码
/**
* Returns the {@code Class} object associated with the class or
* interface with the given string name. Invoking this method is
* equivalent to:
*
* <blockquote>
* {@code Class.forName(className, true, currentLoader)}
* </blockquote>
*
* where {@code currentLoader} denotes the defining class loader of
* the current class.
*
* <p> For example, the following code fragment returns the
* runtime {@code Class} descriptor for the class named
* {@code java.lang.Thread}:
*
* <blockquote>
* {@code Class t = Class.forName("java.lang.Thread")}
* </blockquote>
* <p>
* A call to {@code forName("X")} causes the class named
* {@code X} to be initialized.
*
* @param className the fully qualified name of the desired class.
* @return the {@code Class} object for the class with the
* specified name.
* @exception LinkageError if the linkage fails
* @exception ExceptionInInitializerError if the initialization provoked
* by this method fails
* @exception ClassNotFoundException if the class cannot be located
*/
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
关键的还是forName0()这个方法
private static native Class<?> forName0(String name, boolean initialize,
ClassLoader loader,
Class<?> caller)
throws ClassNotFoundException;
/**
* Creates a new instance of the class represented by this {@code Class}
* object. The class is instantiated as if by a {@code new}
* expression with an empty argument list. The class is initialized if it
* has not already been initialized.
*
* <p>Note that this method propagates any exception thrown by the
* nullary constructor, including a checked exception. Use of
* this method effectively bypasses the compile-time exception
* checking that would otherwise be performed by the compiler.
* The {@link
* java.lang.reflect.Constructor#newInstance(java.lang.Object...)
* Constructor.newInstance} method avoids this problem by wrapping
* any exception thrown by the constructor in a (checked) {@link
* java.lang.reflect.InvocationTargetException}.
*
* @return a newly allocated instance of the class represented by this
* object.
* @throws IllegalAccessException if the class or its nullary
* constructor is not accessible.
* @throws InstantiationException
* if this {@code Class} represents an abstract class,
* an interface, an array class, a primitive type, or void;
* or if the class has no nullary constructor;
* or if the instantiation fails for some other reason.
* @throws ExceptionInInitializerError if the initialization
* provoked by this method fails.
* @throws SecurityException
* If a security manager, <i>s</i>, is present and
* the caller's class loader is not the same as or an
* ancestor of the class loader for the current class and
* invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to the package
* of this class.
*/
@CallerSensitive
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
}
// NOTE: the following code may not be strictly correct under
// the current Java memory model.
// Constructor lookup
if (cachedConstructor == null) {
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
int modifiers = tmpConstructor.getModifiers();
if (!Reflection.quickCheckMemberAccess(this, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
Reflection.ensureMemberAccess(caller, this, null, modifiers);
newInstanceCallerCache = caller;
}
}
// Run constructor
try {
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
③如果T是 任意的Java类型(或者void关键字),那么T.class将代表匹配的类对象。
上面三种方法我做下试验,我们写一个Student类和一个测试的main方法
package dailyprg0714;
public class Student {
private String name;
public Student(String name) {
super();
this.name = name;
}
public void say(){
System.out.println("Hello, I'm "+name+" !");
}
}
package dailyprg0714;
public class Test {
public static void main(String[] args) {
Student student = new Student("Jenny");
Class class1 = student.getClass();
System.out.println(class1.toString());
try {
Class class2 = Class.forName("dailyprg0714.Student");
System.out.println(class2.toString());
} catch (Exception e) {
e.printStackTrace();
}
Class class3 = Student.class;
System.out.println(class3.toString());
}
}
我们运行一下,
三种方法都得到了Class对象。
JVM为每个类型管理一个Class对象,也只有一个,我们可以看一下上面获得的三个对象是不是同一个,我们添加两行代码
System.out.println(class1==class2);
System.out.println(class1==class3);
结果都为true,也就是说这个对象是唯一的
获得了这个对象之后我们就能调用Class类提供的方法来分析类了,
在java.lang.reflect包中有三个类Field、Method和Constructor,从名字也能看出来它们分别用于描述类的域、方法和构造器。
而Class类中的getFields、getMethods和getConstructors方法将分别返回类的public域、方法和构造器数组,其中包括超类的公有成员。
另外Class类的getDeclaredFields、getDeclaredMethods和getDeclaredConstructors方法分别返回类中声明的全部域、方法和构造器,包括私有和受保护的,但不包括超类成员。
我们还是拿Student类试验一下,修改下main的代码
package dailyprg0714;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
Student student = new Student("Jenny");
Class class1 = student.getClass();
System.out.println(class1.toString());
try {
Class class2 = Class.forName("dailyprg0714.Student");
System.out.println(class2.toString());
System.out.println(class1==class2);
} catch (Exception e) {
e.printStackTrace();
}
Class class3 = Student.class;
System.out.println(class3.toString());
System.out.println(class1==class3);
System.out.println();
Field[] fields = class1.getDeclaredFields();
Method[] methods = class1.getDeclaredMethods();
Constructor[] constructors = class1.getConstructors();
for(Field field : fields){
System.out.println(field.getName());
}
for(Method method : methods){
System.out.println(method.getName());
}
for(Constructor constructor : constructors){
System.out.println(constructor.getName());
}
}
}
看下输出的结果
当然还能调用Field、Method和Constructor的其他方法,不止是getName来获得更多的信息,具体的还是看源码或者官方文档哈。只要访问权限允许,我们完全可以获得一个Student对象的实例域的具体值,甚至可以修改它的值。对于私有域需要用到
setAccessible方法来设置访问权限。
当然,我们也可以调用任意一个方法,这里的关键就是Method类提供的invoke()方法,它允许调用包装在当前Method对象中的方法。invoke的源码
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
我们也试一下调用student的say方法,在Method遍历的时候加入一段代码,用于执行student这个对象的say方法,如果say是Student类的静态方法那么就不需要传入这个参数了
for(Method method : methods){
System.out.println(method.getName());
try {
method.invoke(student);
} catch (Exception e) {
e.printStackTrace();
}
}
我们看下结果
OK!
反射的更多功能就要去研究反射库了,看下有哪些方法!