Java反射机制的原理

一、什么是反射?

    Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

二、反射的特性

  1. 运行时生成对象实例;
  2. 运行时调用方法;
  3. 运行时更改属性。

三、反射机制获取类的方法

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具有的部分方法

  1. getName():获得类的完整名字;
  2. getFields():获得类的public类型的属性;
  3. getDeclaredFields():获得类的所有属性。包括private 声明的和继承类;
  4. getMethods():获得类的public类型的方法;
  5. getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类;
  6. getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型;
  7. getConstructors():获得类的public类型的构造方法;
  8. getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型‘;
  9. 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对象,从配置文件读取的只是某个类的字符串类名,程序员需要根据字符串来创建对应的实例,就可以使用反射来实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值