java中的反射机制


七月的第四周,来写反射。

反射是java很有意思的机制,有一点注入灵魂的感觉。


写程序可以认为是女娲捏泥人的过程,女娲和了一团稀泥,揉捏出一个小人,放在地上,活了。

你设身处地想象一下,如果你是女娲造的其中一个小人,在落地之前一直都没有活。

那在你还没活的时候,你是不知道外面的世界长什么样子的,也不知道将来会遇到哪些小人。你不知道还有小王、小张、小李一起被造出来,你也不知道他们是什么样子的。

总之你只知道你自己的事情,你对其他的一无所知。

这就是你在活之前的状态。


写程序如果按照正常理解,就应该是程序运行,安分守己地完成自己的任务。

如果写了一个名叫 apple 的类,程序运行的时候, apple 就应该安分守己地做 apple 的事情,它不会知道,在程序中还有 bananapeachwatermelon ,也没有办法知道,除了自己以外的其他类能够做什么。

反射是做什么的呢?

就像是你刚被女娲捏出形状时,女娲对你说:“以后你将遇到一个叫‘小王’的人,记得找他玩。”然后你活了之后,果然找到了小王,愉快地跟他玩了起来。

程序在运行的时候,具有了访问别人的能力,甚至还能用别人的东西,这就是反射。


反射(reflect)是一个让人听了一头雾水的名字,但实际上确实也没有别的更好的表述方法,这就是一种比较难以形容的特性。如果暂时忘了这个名字,直接去理解这种特性,隐约理解之后再回来看反射这两个字,会稍微容易一些。

维基百科对于反射的描述,还是大概能看懂的,不然你试着搜搜百度百科的解释,当你精通概念之后回看才能勉强看懂。维基百科是这么说的:

在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。

java是一门具有动态性的语言,反射就是动态性的典型代表,这里的动态,说的就是程序正在运行。程序一边运行着,一边去查看自己的状态,然后做出修改,全程不停,就是动态。(但是java只能说有动态性,不能算动态语言)


反射的原理是较为复杂的,这里面涉及到java虚拟机相关的内容,我基本也是小白,尽量按照自己的理解写清楚。

打开编程软件之后,我首先创建一个文件,然后在里面写上一段代码,例如:

1
2
3
4
5
public class Hello {
public static void main(String args[]){
System.out.println("Hello word!");
}
}

这个文件,它的文件名是Hello.java(就像是新建一个word文件,随便写了句话,保存之后文件名叫做Hello.doc一样),现在这个文件的后缀名是.java,这是我们的源代码文件。

你一定听过编译这个词,编译的意思是说,你写完代码之后,会有一种叫做编译器的东西帮我们把代码变成一堆010101……的二进制数字文件,也就是翻译成机器语言。编译器翻译完,会生成一个Hello.class文件,这时这个文件的后缀名是.class,这是我们的二进制流文件。

现在有了翻译给机器听的Hello.class文件之后,这个文件就要加载进java虚拟机当中,让电脑自己去读程序运行了。java虚拟机读取Hello.class文件的过程,是类加载过程

我们今天说的反射,它的原理就是这个类加载过程

反射前

类加载的具体过程比较复杂,涉及到java虚拟机的很多内容,我们来说简化的过程。

简单来说呢,.java文件里写了很多代码,但是归归类可以分成这么几种:

  • 干垃圾
  • 湿垃圾
  • 有害垃圾
  • 可回收垃圾

java虚拟机中,也有相对应的区域,来分类存放写好的代码(以二进制数字的形式):

  • 干垃圾桶
  • 湿垃圾桶
  • 有害垃圾桶
  • 可回收垃圾桶

类加载就是一一对应,垃圾分类的过程。

反射就是要在垃圾分类之后,问垃圾管理员要垃圾。

你好,我是收废品的,请问有废易拉罐吗,给我一个。


我们再来重新看一下维基百科对于反射的定义:反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。

比如我想知道,某一个类,它叫什么,它有哪些成员变量。

java虚拟机会在类加载过程中,生成一个Class类的对象,比如我之前写的那个Hello.java文件,java虚拟机将实例化一个Hello对象,我们之后可用直接用这个对象的各个方法,来进行反射操作(我们终于走到反射了)。

1
2
3
4
5
Class testClass = Hello.class;
// 获取类的名字
testClass.getName();
// 获取类的成员方法
testClass.getDeclaredFields();

所以现在,我们通过反射,可以获知某一个类叫什么名字、父类是什么、限定符是什么、有什么成员变量、有什么构造方法……等等关于这个类的一切,我想知道什么,全都可以知道了。

那么我们现在梳理一下步骤:

  1. 实例化一个Class类
  2. 调用这个Class类的方法,以获取到想要的东西

如果Class类的功能,你觉得不够用,或者用起来不顺手,还有别的几种类可以去用,分别是Constructor(构造方法)、Field(成员变量)、Method(成员方法)和其他。

相关的方法列表有人帮忙总结出来了,详见下图。

1564150307754


参考文献:

https://www.imooc.com/article/8021

https://docs.oracle.com/javase/tutorial/reflect/index.html

https://zh.wikipedia.org/wiki/%E5%8F%8D%E5%B0%84_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)


第一篇怎么写的这么烂呜呜呜……不改了,没时间了,到新的一周了= =