java高级:类的初始和类实例初始次序

编译到运行java程序大体流程

在这里插入图片描述

Java虚拟机的基本结构及其内存分区:

在这里插入图片描述
在这里插入图片描述

附加英文版的
在这里插入图片描述
JVM中把内存分为直接内存(非堆内存)、方法区、Java栈、Java堆、本地方法栈、PC寄存器等。

直接内存:就是原始的内存区,AIO操作时,数据直接在直接内存中发送到socket转送,零复制,避免了直接内存到堆间的复制。

JVM栈:线程之间私有,每个线程会对应自己的一个JVM栈,栈的基本出栈入栈单元称为栈帧,它是一个方法的表示,如主线程中第一压入的一定是main方法的栈帧,子线程就是run方法。栈帧中有局部变量表、操作数栈、返回地址、动态链接等。当栈内深度超过规定深度报StackOverFlowerError,当无法申请空间或空间小于一个JVM栈所需的内存大小,报OutOfMemoryError.执行引擎就是通过栈中字节码解释执行的。

本地方法栈:同JVM一样,这里不同的是。JVM栈为是字节码的方法服务,这里为native方法服务。

PC寄存器:功能如同CPU中的PC寄存器,指示要执行的字节码指令

Java堆:实例对象和数组存放的区域(因现在有逃逸技术。也可把对象存放在栈上),线程间共享,也是垃圾回收的主要局域,现在主要采用分代收集,分老年代、新生代。堆可以物理上不连续区域,只要逻辑上连续就可以。是runtime memory area最大的区域。
对新创建的对象划分内存方式有两种,若按规整划分使用碰撞指针方式;若非规整划分使用空闲表方式。垃圾回收算法决定了内存划分是否按规整,若回收算法中有compass使用规整划分,mark-swap标记整理的使用非规整划分。堆一般会有频繁的对象创建,难免在分配对象内存单元时,这种并发分配内存地址会出现线程不安全问题,JVM规范有两种解决方法,一是通过CAS加超时等待进行同步;二是按照每个线程,提前给予一定内存,这块内存称为TLAB(thread local allocation buffer)本地线程分配缓冲。对象的访问有句柄和直接指针方式。当堆内空间不足是OutofMemoryError

方法区:用于存放类、接口的元数据信息,加载进来的字节码的类信息、静态变量、常量、方法信息、编译后的代码等都存储在方法区。之中有还有个运行时常量池,class文件(字节码文件)中的常量池在类加载时放入到运行时常量池中。运行时可用intern将常量放入池中,Class对象中的getName、isInstance得到的信息来自于方法区。方法区是线程共享的,当方法区中内存空间不同时,会报OutOfMemoryError

JVM的功能模块主要包括类加载器、执行引擎和垃圾回收系统


类初始化

main方法 所在类会在执行前,先加载和类初始化。类初始化是从父类到子类自上到下
 * 类初始化:先初始化父类,后子类初始
 * 类初始化<clinit>():初始化类变量显示赋值代码、静态代码块,次序是自上到下,
 * <clinit>()只执行一次*
 * 在编译阶段,编译器收集所有的静态字段的赋值语句及静态代码块,
 * 并按语句出现的顺序拼接出一个类初始化方法<clinit>()。
 * 此时,执行引擎会调用这个方法对静态字段进行代码中编写的初始化操作。

1)类加载器会在指定的classpath中找到Son.class这个文件,然后读取字节流中的数据,将其存储在方法区中。

2)会根据Son.class的信息建立一个Class对象,这个对象比较特殊,一般也存放在方法区中,用于作为运行时访问Student类的各种数据的接口。

3)必要的验证工作,格式、语义等

4)为Son中的静态字段分配内存空间,也是在方法区中,并进行零初始化,即数字类型初始化为0,boolean初始化为false,引用类型初始化为null等。

在Son.java中只有一个静态字段:

private static int j=test();

此时,并不会执行赋值为test()返回值的操作,而是将其初始化为0。

5)由于已经加载到内存了,所以原来字节码文件中存放的部分方法、字段等的符号引用可以解析为其在内存中的直接引用了,而不一定非要等到真正运行时才进行解析。

6)在编译阶段,编译器收集所有的静态字段的赋值语句及静态代码块,并按语句出现的顺序拼接出一个类初始化方法()。此时,执行引擎会调用这个方法对静态字段进行代码中编写的初始化操作。


类实例初始

	实例初始化<init>:先父类实例初始,后子类实例初始。
	每个类实例初始过程:1.实例成员变量 、非静态代码块是 按序执行 
					2. 最后是自身构造方法, 若子类构造函数指定了使用父类的构造函数,
则父类初始该指定的构造函数

	<init>()方法,是编译器将调用父类的<init>()的语句、构造代码块、实例字段赋值语句,
以及自己编写的构造方法中的语句整合在一起生成的一个方法。保证调用父类的<init>()方法
在最开头,自己编写的构造方法语句在最后,而构造代码块及实例字段赋值语句按出现的顺序
按序整合到<init>()方法中。

例子

/**
 *  父类初始化<clinit>()
 * 步骤1. j = method(); -> System.out.print("(5)");
 * 步骤2. System.out.print("(1)");
 *	父类实例初始化
 * 步骤5. i = test();
 * 步骤6.System.out.print("(3)"); -> System.out.print("(9)");
 * 步骤7.System.out.print(i);		//若不在子类中调有参的父类构造函数,则是默认的无参构造函数,System.out.print("(2)");
 */
class Father{
	private int i = test();
	private static int j = method();
	static {
		System.out.print("(1)");
	}
	public Father() {
		System.out.print("(2)");
	}
	public Father(int i) {
		System.out.print(i);
	}
	{
		System.out.print("(3)");
	}
	public int test() {
		System.out.print("(4)");
		return 0;
	}
	public static int method() {
	
		System.out.print("(5)");
		return 0;
	}
}
/**
 * 
 * 子类初始化<clinit>()
 * 步骤3. j = method(); -> System.out.print("(10)");
 * 步骤4. System.out.print("(6)");
 *	子类实例初始化
 * 步骤8. i = test();-> System.out.print("(9)");
 * 步骤9. System.out.print("(8)");
 * 步骤10.System.out.print("(7)");
 *
 */
public class Son extends Father{
	
	private int i = test();
	private static int j = method();
	static {
		System.out.print("(6)");
	}
	public Son() {
		//super(); 第一行有一个默认的调用父类的无参构造方法
		super(11); //显示的调用父类的有参构造方法
		System.out.print("(7)");
	}
	{
		System.out.print("(8)");
	}
	public int test() {
		System.out.print("(9)");
		return 0;
	}
	public static int method() {
		System.out.print("(10)");
		return 0;
	}
	
	/**
	 * main方法 所在类会在执行前,先加载和类初始化。类初始化是从父类到子类自上到下
	 * 类初始化:先初始化父类,后子类初始
	 * 类初始化<clinit>():初始化类变量显示赋值代码、静态代码块,次序是自上到下,<clinit>()只执行一次
	 * 
	 */
	public static void main(String[] args) {
	//实例初始化<init>:先父类实例初始,后子类实例初始。每个类实例初始过程:1.实例成员变量 、非静态代码块是 按序执行 
		//2. 最后是自身构造方法,  若子类构造函数指定了使用父类的构造函数,则父类初始该指定的构造函数
		//注意;父类在初始    i = test()时是调用的子类的test(),因当前对象是Son且Son类中重学了父类的test();
		Son son1 = new Son();
		System.out.println();
		Son son2 = new Son();
	}

}

//结果
//(5)(1)(10)(6)(9)(3)11(9)(8)(7)
//(9)(3)11(9)(8)(7)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值