Scala/Java中的反射机制探索

最近看Spark源码,看到在Spark这种底层架构中用到很多Scala/Java的反射机制,在网上看了一些关于Scala/Java反射机制的原理和用处,总结如下。scala的多线程的实现依赖于JVM的,在反射机制中也是完全套用了Java的反射机制,所以本文讨论的实际上就是Java的反射机制。

反射机制的用处:1、在已有一个类的对象,在运行之前并不能确定这个对象对应的Class的时候,需要在运行时动态的确定所需要的类的时候

     2、在给定的接口,当时无法得到接口内部的信息的时候,需要通过反射的方法来定义接口,这个在JDBC中使用的比较多。

     3、网上有很多说反射在黑客技术中使用的比较多,个人猜测是可以通过反射获得对象队形的class的内部所有的信息和类结构,field、constructor、method等。


1、在应用层面

在你编写好并且编译过后,会生成Class文件,这些Class文件在被JVM的ClassLoader加载之后都会有对应的java.lang.Class<T>这个类的实例。每个类的自有的方法属性(类的结构)会自然的包含在这个实例上,因此就可以获取到对应的方法属性,无论这写方法属性是否private.


2、在jvm层面

首先,Class文件是以8位字节为基础的二进制文件,这些二进制文件是按照严格的结构顺序保存的,里面的结构顺序为:

版本号、常量池、访问标志、类索引、字段表集合等

      知道了Class文件的物理结构了,就可以随心所欲的获取关于这些类的所有信息了。

从上面可以得知,在反射获取一个类的信息(Fields/Method/Constructor)之前,需要加载这个类的class文件,通过class文件中的物理结构来获取这个类的所有信息。

假设有类

class Secret(sec:String){
    private val secrecy = sec
    def getSecrecy(){
        return "Inner Secret"
    }
}

我们有了一个Secret类的对象:

val s = new Secret("Top Secret")

我们可以通过以下方法Secret的class对象:

val clz = Class.forName("Secret") // 在forName内填写需要的类名称,如果是在同一个包中,但是不是同一个文件内则需要填写完整的包路径,比如com.test.Secret

此时,Java虚拟机会加载Secret的class文件,然后在方法区创建Secret类实例,即Secret的class对象。

然后我们使用getDeclaredFiled()来获取这个类的field

val f = clz.getDeclaredField("secrecy")

设置这个field的访问权限

f.setAccessible(true)

println(f.get(s)) //输出为Top Secret 通过这种方法就能够范围s对象的私有成员变量了


以上是Field的反射方法,Constructor和Method的反射方法类似。


至于Class.forName是如何实现通过类名获得对应的class的,这个在JVM中是native方法,使用的是cpp实现的。总之就是取实现是的字节码文件,然后blalabla....没有细看,有兴趣的同学可以探究下JVM中这方面的实现。


贴上自己做实验用的代码:

class Secret(sec: String = "defalut") {
  def this(seret: Secret) {
    this(seret.getSecrecy())
  }

  private val secrecy = sec

  def getSecrecy() = {
    "Inner Secret"
  }
}


object testReflection {
  def main(args: Array[String]): Unit = {
    //    val semaphore = new Semaphore(5)
    //    (1 to 10).foreach { _ =>
    //      new Thread(new EtaThread(semaphore)).start()
    //    }
    
    val first = new Secret()
    val cls = Class.forName("Secret")
    val constructor = cls.getConstructor(classOf[Secret])
    val s = constructor.newInstance(first)

    val f = s.getClass.getDeclaredField("secrecy")
    f.setAccessible(true)
    println(f.get(s))


    val m: Method = Class.forName("Secret").getDeclaredMethod("getSecrecy")
    m.setAccessible(true)
    println(m.invoke(s))
  }
}


【参考文献】

http://a.codekk.com/detail/Android/Mr.Simple/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20Java%20%E5%8F%8D%E5%B0%84%20Reflection

https://www.zhihu.com/question/46883050


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值