一、什么是反射?
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
二、反射的特性
- 运行时生成对象实例;
- 运行时调用方法;
- 运行时更改属性。
三、反射机制获取类的方法
1.对象调用 getClass() 方法来获取。
Person p1 = new Person();
Class c1 = p1.getClass();
通常应用在:比如你传过来一个 Object类型的对象,而我不知道你具体是什么类,用这种方法。
2.类名.class 的方式得到。
Class c2 = Person.class;
该方法最为安全可靠,程序性能更高,这说明任何一个类都有一个隐含的静态成员变量 class。
3.通过 Class 对象的 forName(“全限定名称”) 静态方法来获取。
Class c3 = Class.forName("test.reflect.Person");
此方法用的最多,但可能抛出 ClassNotFoundException 异常。
4.需要注意的是:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 equals 比较,发现都是true,如下所示:
Class class1 = Person.class;
Person person = new Person();
Class class2= person.getClass();
if(class1.equals(class2)){
System.out.println("class1.equals(class2)");
}
四、Class具有的部分方法
- getName():获得类的完整名字;
- getFields():获得类的public类型的属性;
- getDeclaredFields():获得类的所有属性。包括private 声明的和继承类;
- getMethods():获得类的public类型的方法;
- getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类;
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型;
- getConstructors():获得类的public类型的构造方法;
- getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型‘;
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
五、Class.forName(classname)方法解析
Class.forName(classname)方法,实际上是调用了Class类中的Class.forName(classname,true,currentLoader)方法。
/**
* 参数:
* name - 所需类的完全限定名;
* initialize - 是否必须初始化类;
* loader - 用于加载类的类加载器,currentLoader则是通过调用ClassLoader.getCallerClassLoader()获取当前类加载器的。
*/
@CallerSensitive
public static Class<?> forName(String className) throws ClassNotFoundException {
return forName0(className, true,ClassLoader.getClassLoader(Reflection.getCallerClass()));
}
//本地方法
private static native Class<?> forName0(String name, boolean initialize,ClassLoader loader)
throws ClassNotFoundException;
类要想使用,必须用类加载器加载,所以需要加载器。反射机制,不是每次都去重新反射,而是提供了cache,每次都会需要类加载器去自己的cache中查找,如果可以查到,则直接返回该类。
六、JDK源码对反射的使用实例
LongAdder中,运用反射获取某属性的偏移值,方便Unsafe类直接获取某属性的值。
// Unsafe mechanics Unsafe相关的初始化
private static final sun.misc.Unsafe UNSAFE;
private static final long valueOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
// 获取类中属性的偏移值
valueOffset = UNSAFE.objectFieldOffset (ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
七、什么时候应该使用反射?
反射构建出无法直接访问的类:例如可以把完整的包+类名称放到properties中,java中获取,然后就可以根据这个配置获取class了,然后你就可以干很多事(Class.forName(“name”))。
八、总结
使用反射虽然会很大程度上提高代码灵活性,但不能滥用反射,因为 通过反射创建对象时性能稍微低一些。实际上,只有当程序需要动态创建某个类的对象时才会考虑使用反射。通常在开发通用性比较广的框架,基础平台时可能大量使用反射。因为在很多java框架中都需要根据配置信息创建Java对象,从配置文件读取的只是某个类的字符串类名,程序员需要根据字符串来创建对应的实例,就可以使用反射来实现。