一、类的加载,连接和初始化

1. 查找并加载类的二进制数据

      即加载class文件到内存中,将其放在内存的 运行时数据区的方法区,在堆区创建一个java.lang.Class对象,用于封装类在方法区的数据结构。就是说,所创建类的所有属性,方法都能通过Class反射访问到,Class这个对象是虚拟机创建的,是唯一的一个对象。加载的最终产品是这个由虚拟机产生的Class对象。

2. 连接

      先验证,确保class文件的正确性;
      再准备,为类的静态变量分配内存,将其初始化默认值(静态变量不属于任何一个对象,它属于类);
      接着解析,将类的符号引用转化为直接引用。例如,在一个类(类A)的方法中,调用另一个类(类B)的方法(方法C),在解析以前,类B的方法C在A类中只是一个符号引用,由方法C的全名和相关描述符组成。解析会将这个符号引用替换为类B的方法C在方法区的内存位置(这个直接引用就是真正的指针,指向在内存的地址)。虽然java没有使用指针,但是java虚拟机与底层打交道,必须要用指针。
      仔细思考解析,class文件中不会直接与内存打交道,不能直接使用内存中的地址,而java虚拟机加载class文件后,会用指针替换掉,据我猜测,这应该也是java能够在不同操作系统运行的原因之一。

3. 初始化

      为类的静态变量赋予指定的初始值,会进行显式初始化,指赋予初始值或在静态代码块中赋值。

二、java程序对类的使用

1.主动使用,会导致类的初始化
  • new一个对象,创建类的实例
  • 访问类的静态变量(除了final 变量后面跟基本数据类型和String)
  • 调用类的静态方法
  • 通过反射
  • 初始化子类 (当初始化一个类的时候,如果发现其父类还没进行过初始化,则需要先触发其父类的初始化)
  • 标明为启动类 (带有mian方法的类)
2.被动使用,不会会导致类的初始化
  • 通过子类引用父类的静态字段,不会导致子类初始化
  • 通过数组定义类引用类,不会触发此类的初始化
  • 调用一个类的常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
//根据参考例子自己写的
package com.thinkInJava.chapter;
public class MyObject {
	/*static{
		System.out.println("MyObject初始化");
	}*/
	public static void main(String[] args) {
		//会初始化的6种情况
		//第1种情况,实例化一个类,就能初始化这个类
		//Test test = new Test();
		//第2种情况,调用这个类的静态变量(有特殊情况不能初始化)
		//int num =Test.num;
		//第3种情况,调用静态方法
		//Test.method();
		//第4种情况,使用反射能初始化一个类
		/*try {
			Class class1 = Class.forName("com.thinkInJava.chapter.Test");
		} catch (Exception e) {
			e.printStackTrace();
		}*/
		//第5种情况 调用这个类的子类
		//TestChild testChild = new TestChild();
		//第6种情况,带有main方法的这个类
		//将main方法上面的代码块注释去掉,可以测试
		
		//不会初始化的
		//1.子类引用父类静态变量
		//int num = TestChild.num;
		//2.数组引用
		//Test [] tests = new Test[3];
		//3.带有final staitc 原生数据类型和String
		//int num = Test.NUM;
		//String string =Test.STRING;
		//但是如果不是原生数据类型或String,是final static 类还是会初始化
		//TestObject testObject = Test.object;
	}
}
class Test{
	public static String str  ="str";
	public static int num = 1;
	public final static String STRING = "string";
	public final static int NUM = 1;
	public final static TestObject object = new TestObject();
	static{
		System.out.println("Test初始化");
	}
	public static void method(){
	}
}
class TestObject{
}
class TestChild extends Test{
	static{
		System.out.println("TestChild 初始化");
	}
}