资深软件开发工程师,业余马拉松选手。
除了等基本类型外,Java的其他类型全部都是(包括)。例如:
- ...
仔细思考,我们可以得出结论:(包括)的本质是数据类型()。无继承关系的数据类型无法赋值:
而是由JVM在执行过程中动态加载的。JVM在第一次读取到一种类型时,将其加载进内存。
每加载一种,JVM就为其创建一个类型的实例,并关联起来。注意:这里的类型是一个名叫的。它长这样:
以类为例,当JVM加载类时,它首先读取文件到内存,然后,为类创建一个实例并关联起来:
这个实例是JVM内部创建的,如果我们查看JDK源码,可以发现类的构造方法是,只有JVM能创建实例,我们自己的Java程序是无法创建实例的。
所以,JVM持有的每个实例都指向一个数据类型(或):
一个实例包含了该的所有完整信息:
由于JVM为每个加载的创建了对应的实例,并在实例中保存了该的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个实例,我们就可以通过这个实例获取到该实例对应的的所有信息。
这种通过实例获取信息的方法称为反射(Reflection)。
如何获取一个的实例?有三个方法:
方法一:直接通过一个的静态变量获取:
方法二:如果我们有一个实例变量,可以通过该实例变量提供的方法获取:
方法三:如果知道一个的完整类名,可以通过静态方法获取:
因为实例在JVM中是唯一的,所以,上述方法获取的实例是同一个实例。可以用比较两个实例:
注意一下实例比较和的差别:
用不但匹配指定类型,还匹配指定类型的子类。而用判断实例可以精确地判断数据类型,但不能作子类型比较。
通常情况下,我们应该用判断数据类型,因为面向抽象编程的时候,我们不关心具体的子类型。只有在需要精确判断一个类型是不是某个的时候,我们才使用判断实例。
因为反射的目的是为了获得某个实例的信息。因此,当我们拿到某个实例时,我们可以通过反射获取该的信息:
要从实例获取获取的基本信息,参考下面的代码:
注意到数组(例如)也是一种类,而且不同于,它的类名是。此外,JVM为每一种基本类型如也创建了实例,通过访问。
如果获取到了一个实例,我们就可以通过该实例来创建对应类型的实例:
上述代码相当于。通过可以创建类实例,它的局限是:只能调用的无参数构造方法。带参数的构造方法,或者非的构造方法都无法通过被调用。
JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载。例如:
当执行时,由于用到了,因此,JVM首先会把加载到内存。然而,并不会加载,除非程序执行到方法,JVM发现需要加载类时,才会首次加载。如果没有执行方法,那么根本就不会被加载。
这就是JVM动态加载的特性。
动态加载的特性对于Java程序非常重要。利用JVM动态加载的特性,我们才能在运行期根据条件加载不同的实现类。例如,Commons Logging总是优先使用Log4j,只有当Log4j不存在时,才使用JDK的logging。利用JVM动态加载特性,大致的实现代码如下:
这就是为什么我们只需要把Log4j的jar包放到classpath中,Commons Logging就会自动使用Log4j的原因。
JVM为每个加载的及创建了对应的实例来保存及的所有信息;
获取一个对应的实例后,就可以获取该的所有信息;
通过Class实例获取信息的方法称为反射(Reflection);
JVM总是动态加载,可以在运行期根据条件来控制加载class。
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/1132.html