1、类创建的时机
啥时候触发了类的加载?jvms8里边的描述是:
被其他接口或者类通过自身运行时常量池引用。
通过特定JSE平台类库的方法调用(例如反射)。
触发类的 creation。类加载器并不需要等到某个类被主动使用时才加载它,虚拟机规范允许类加载器在预料某个类将要被使用时预先加载好它。(这里的一个典型的体现是,在一个子类被主动使用时,若发现它有父类,则先加载其父类)。其实这也不违背虚拟机规范中给出的这两个描述。
思考一个问题,上述描述的情况是否能完全覆盖触发类加载的时机呢?
注意:类的加载和初始化不是一个概念,初始化是类加载周期中的一个组成部分,需要进行初始化的类一定会被先加载,但是类加载之后并不一定要被初始化。触发初始化只有之前文章描述的那几种情况。
2、类加载的目的?
将二进制表示的class文件内容加载到内存方法区,并在堆上创建一个java.lang.Class类型的对象,该对象封装了类在方法区内的数据结构,作为对该类方法区内容的访问入口。
前面文章提到的对于synchronized关键字修饰的类方法,其加锁对象便是该对象。
3、
执行静态变量赋初语句
执行静态块中的赋值语句
可以说类初始化方法
注:JVM保证执行子类
4、验证阶段的元数据验证和字节码验证的区别
元数据验证:对字节码表示的类描述信息进行语义分析,以保证符合虚拟机规范
通俗的讲就是对类中的数据类型做检验,包括类是否有父类(因为除了java.lang.Object类外,所有类都应当有父类)、类是否继承了final修饰的类(这是不允许的)、未被abstract修饰的类是否实现了其父类或接口中需要实现的所有方法、类中的方法、字段是否与父类产生矛盾等。
字节码验证:通过数据流和控制流验证,确保程序语义是合法的
通俗的讲是对方法体的验证,例如要保证操作数栈的数据类型和指令代码序列一致、保证方法体内的数据类型转换的有效性、保证跳转指令不会跳到方法体以外的字节码指令上等。
5、解析阶段的目的和对象
解析是将常量池当中的符号引用替换为直接引用的过程
那么什么是直接引用?
直接指向目标的指针、相对偏移量或者是一个间接定位到目标的句柄。
解析针对的对象,也就是哪些符号引用?
类或接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符
6、
前边我们看了类初始化方法
编译器按照其文本顺序,收集成员变量、普通代码块、构造函数代码,组成
注1:在调用子类对象
注2:类在进行实例化的时候会执行对象初始化方法,这必须由主动引用触发。并且,如果没有监测到构造函数的代码,将不会执行对象初始化方法。
注3:我们会看到在实例化一个子类对象时,父类构造器也被执行了。但这不是说也创建了一个父类对象。在Java中,创建一个子类对象时,是不会先创建父类对象的。但是创建子类对象时,会去先调用父类的构造函数。
7、容易忽视的对类变量的引用
1)、对static修饰的类变量引用时,只会触发定义该static变量所在类的初始化。
2)、对final static修饰的在编译期已经确定值的变量的直接引用,并不触发该变量所在类的初始化。而对final static修饰的在编译期不能确定值的变量的直接引用,会触发该变量所在类的初始化。
8、代码示例
关注加载了哪些类以及哪些类执行了初始化以及实例构造器的执行等
测试类型1:验证对编译期不能确定值的final static的值的引用
class Fruit {
final static double default_price = Math.random();
static {
System.out.println("Class Fruit Static Block");
}
public Fruit(){
System.out.println("Construct Fruit");
}
{
System.out.println("Fruit Code Block");
}
}
class Apple extends Fruit {
static int price = 15;
static {
System.out.println("Class Apple Static Block");
}
public Apple() {
System.out.println("Construct Apple");
}
{
System.out.println("Apple Code Block");
}
}
class StaTest {
public static void main(String[] args) {
System.out.println(Apple.default_price);
}
}
结果1
[Loaded StaTest from file:/Users/rttian/Documents/work/project/scala/ScalaTest/target/scala-2.11/classes/]
[Loaded Fruit from file:/Users/rttian/Documents/work/project/scala/ScalaTest/target/scala-2.11/classes/]
[Loaded Apple from file:/Users/rttian/Documents/work/project/scala/ScalaTest/target/scala-2.11/classes/]
Class Fruit
0.026496274796854347
测试类型2:验证对编译期能确定值的final static的值的引用
class Fruit {
final static int default_price = 10;
static {
System.out.println("Class Fruit Static Block");
}
public Fruit(){
System.out.println("Construct Fruit");
}
{
System.out.println("Fruit Code Block");
}
}
class Apple extends Fruit {
static int price = 15;
static {
System.out.println("Class Apple Static Block");
}
public Apple() {
System.out.println("Construct Apple");
}
{
System.out.println("Apple Code Block");
}
}
class StaTest {
public static void main(String[] args) {
System.out.println(Apple.default_price);
}
}
结果2
[Loaded StaTest from file:/Users/rttian/Documents/work/project/scala/ScalaTest/target/scala-2.11/classes/]
10
测试类型3:类实例化
class Fruit {
final static int default_price = 10;
static {
System.out.println("Class Fruit Static Block");
}
public Fruit(){
System.out.println("Construct Fruit");
}
{
System.out.println("Fruit Code Block");
}
}
class Apple extends Fruit {
static int price = 15;
static {
System.out.println("Class Apple Static Block");
}
public Apple() {
System.out.println("Construct Apple");
}
{
System.out.println("Apple Code Block");
}
}
class StaTest {
public static void main(String[] args) {
new Apple();
}
}
结果3
[Loaded StaTest from file:/Users/rttian/Documents/work/project/scala/ScalaTest/target/scala-2.11/classes/]
[Loaded Fruit from file:/Users/rttian/Documents/work/project/scala/ScalaTest/target/scala-2.11/classes/]
[Loaded Apple from file:/Users/rttian/Documents/work/project/scala/ScalaTest/target/scala-2.11/classes/]
Class Fruit Static Block
Class Apple Static Block
Fruit Code Block
Construct Fruit
Apple Code Block
Construct Apple