Klass模型
java中的类,在jvm中都有一个对应的Klass类实例与之对应。存储类的元信息:
常量,成员属性,方法
类的元信息都在元空间
普通的java类在jvm对应的是InstanceKlass实例
它有三个子类:
- InstanceMirrorKlass:用于表示java.lang.Class对象。实际上是c++类的实例,存放在堆区。也叫镜像类
- InstanceRefKlass:用于表示java/lang/ref/reference的子类
- InstanceClassLoaderKlass:用于表示遍历某个加载器加载的类
java中的数组不是静态数据类型,是动态数据类型,既运行时期。java中的数组元信息是由ArrayKlass子类表示
- TypeArrayKlass:表示基本类型的数组
- ObjArrayKlass:表示引用类型的数组
类加载的过程
类加载是有7个步骤组成,但是类的加载只有前面5个
加载
- 通过类的全限类名获取class文件
- 解析成运行时数据既InstanceKlass实例
- 在堆区生成该类的calss对象,既:InstanceMirrorKlass实例
那么什么时候加载?
主动使用
- 遇到new,getstaic,pubstatic或者invokestatic字节码指令
- 反射
- 初始化一个类的子类回去加载父类
- 启动类(main函数所在的类)
- 当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化
例子:
预加载:包装类,String,Thread
从那里获取class文件?
- 从压缩包中读取,如jar,war
- 从网络中获取,如web applet
- 动态生成动态代理,cglib
- 从数据库读取
- 从加密文件
验证
- 文件格式验证
- 元数据验证
- 字节码验证
- 符号引用验证
准备
为静态变量分配内存,赋予初值。实例变量是在创建对象时候完成赋值,没有赋初值一说
通常情况下初始值是零值,如果被final修饰,在编译的视乎会给属性添加ConstantValue属性,准备阶段直接完成赋值,没有赋予初值这一步
解析
将常量池的符号引用转化为直接引用
符号引用:指向运行时常量池的引用
直接引用:指向内存地址
解析后的信息存储在ConstantPoolCache类实例中
1.类或者接口的解析
2.字段解析
3方法解析
4.接口解析
何时解析?
1.加载阶段解析常量池时
2.用的时候
初始化
执行静态代码块,完成静态变量的赋值
静态字段,静态代码快,字节码层面或生产clinit方法
方法中语句的先后顺序与代码的编写顺序有关
简而言之:初始化阶段就是执行类构造器clinit方法的过程,它是由编译器自动收集类中的所有类变量的赋值动作和static中的语句合并产生的。
/**
* 结果:
* 1
* 2
*/
public class Test_21 {
public static void main(String[] args) {
Test_21_A obj = Test_21_A.getInstance();
System.out.println(Test_21_A.val1);
System.out.println(Test_21_A.val2);
}
}
class Test_21_A {
public static int val1;
public static Test_21_A instance = new Test_21_A();
Test_21_A() {
val1++;
val2++;
}
public static int val2 = 1;
public static Test_21_A getInstance() {
return instance;
}
}
准备阶段:val1=0,instance=null,val2=0;
初始化阶段:clinint(){
val1=0;
val1=0+1=1;
val2=1;
}
val2=1执行两次覆盖
public class Test_21 {
public static void main(String[] args) {
Test_21_A obj = Test_21_A.getInstance();
System.out.println(Test_21_A.val1);
System.out.println(Test_21_A.val2);
}
}
class Test_21_A {
public static int val1;
public static int val2 = 1;
public static Test_21_A instance = new Test_21_A();
Test_21_A() {
val1++;
val2++;
}
public static Test_21_A getInstance() {
return instance;
}
}
准备阶段:val1=0,val2=0,instance=null
初始化:clinit(){
val1=0,val2=1
val1=0+1=1;
val21=1+1=2
}
public class Test_10 {
public static void main(String[] args) {
System.out.printf(Test_10_B.str);
}
}
class Test_10_A {
public static String str = "A str";
static {
System.out.println("A Static Block");
}
}
class Test_10_B extends Test_10_A {
static {
str += "#11";
System.out.println("B Static Block");
}
}
A Static Block
A str
优先加载父类
public class Test_9 {
public static void main(String[] args) {
System.out.printf(new Test_9_B().str);
}
}
class Test_9_A {
static {
System.out.println("A Static Block");
}
}
class Test_9_B extends Test_9_A {
public String str = "A str";
static {
System.out.println("B Static Block");
}
}
A Static Block
B Static Block
A str
public class Test_6 {
public static void main(String[] args) {
System.out.println(Test_6_A.str);
}
}
class Test_6_A {
public static final String str = "A Str";
static {
System.out.println("Test_6_A Static Block");
}
}
final修饰准备阶段就已经被初始化微ConstantValue属性所指定的初始值
A Str
public class Test_4 {
public static void main(String[] args) {
Test_4 arrs[] = new Test_4[1];
}
}
class Test_4_A {
static {
System.out.println("Test_4_A Static Block");
}
}
通过数组定义来应用类,不会触发此类的初始化
public class Test_2 {
public static void main(String[] args) {
System.out.printf(Test_2_B.str);
}
}
class Test_2_A {
static {
System.out.println("A Static Block");
}
}
class Test_2_B extends Test_2_A {
public static String str = "B str";
static {
System.out.println("B Static Block");
}
}
A Static Block
B Static Block
B str