java基础:new一个对象的过程到底发生了什么?

(本文只是作者读书的总结,不作为自己的著作和商业用途,仅供自己记录和学习之用,如
涉及到版权问题,本文可以立刻删除。仅此公示。)

1.基础知识

Java对象的创建操作包含两个过程:
1.类的初始化过程 (分配内存、赋予默认值(零值))
JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类 的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化。
2.实例化过程 (赋予给定值)
java虚拟机就会为其分配内存来存放自己及其从父类继承过来的实例变量。在为这些实例变量分配内存的同时,这些实例变量先会被赋予默认值(零值)。在内存分配完成之后,Java虚拟机才会对新创建的对象赋予我们程序员给定的值。

为此为了理解的深入,我们还需要再来看一下类的生命周期。如下:

在这里插入图片描述

2.类的初始化过程

(1).加载(Loading)

java采用双亲委派模型来进行类的加载的,所以在描述类加载过程前,我们先看一下它的工作过程: 在这里插入图片描述

双亲委派模型的工作过程是:如果一个类加载器(ClassLoader)收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器(Bootstrap ClassLoader)中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。
使用双亲委托机制的好处是:能够有效确保一个类的全局唯一性,当程序中出现多个全限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。

“加载”是“类加载”过程的一个阶段,在加载阶段,虚拟机会完成下面三件事情:
1.通过一个类的全限定名来获取定义此类的二进制字节流(一般会由类加载器实现);
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
3. 在内存中(对于HotSpot虚拟就而言就是方法区)生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口;

(2).验证(Verification)

1.格式验证:验证是否符合class文件规范;
2.语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法是否被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同);
3.操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否可以通过符号引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等);

(3).准备(Preparation)

为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作 范围内)被final修饰的static变量(常量),会直接赋值;

(4).解析(Resolution)

将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。解析需要静态绑定的内容。 // 所有不会被重写的方法和域都会被静态绑定

注:以上(2)、(3)、(4)三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。

初始化(initialization)

因为子类存在对父类的依赖,所以类的加载顺序是先加载父类后加载子类,初始化也一样。不过,父类初始化时,子类静态变量的值也有有的,是默认值。

最终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。

注意:static代码块只有jvm能够调用如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。

3.实例化过程

  1. 在堆区分配对象需要的内存
    分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量。
  2. 对所有实例变量赋默认值
    将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值。
  3. 执行实例初始化代码
    初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法。
  4. 如果有类似于Child c = new Child()形式的c引用的话,在栈区定义Child类型引用变量c,然后将堆区对象的地址赋值给它。
package com.example.demo;

/**
 * @Description:
 * @Author: caosc
 * @CreateDate: 2019/10/12 14:45
 */
public class NewChild extends NewSuper {

    //静态变量
    private static String childStaticFiled = "子类静态变量";

    //普通变量
    private String childFiled = "子类普通变量";

    //静态初始化代码块
    static {
        System.out.println("【" + childStaticFiled + "】");
        System.out.println("==子类静态初始化代码块==");
    }

    //初始化代码块
    {
        System.out.println("【" + childFiled  + "】");
        System.out.println("==子类初始化代码块==");
    }

    public NewChild(){
        System.out.println("==子类构造器==");
    }

   /* public static void main(String[] args) {
        System.out.println("==子类main方法==");
        new NewSuper();
    }*/

}

package com.example.demo;

/**
 * @Description:
 * @Author: caosc
 * @CreateDate: 2019/10/12 14:46
 */
public class NewSuper {

    //静态变量
    private static String superStaticFiled = "父类静态变量";

    //普通变量
    private String superFiled = "父类普通变量";

    //静态初始化代码块
    static {
        System.out.println("【" + superStaticFiled + "】");
        System.out.println("==父类静态初始化代码块==");
    }

    //初始化代码块
    {
        System.out.println("【" + superFiled  + "】");
        System.out.println("==父类初始化代码块==");
    }

    public NewSuper(){
        System.out.println("==父类构造器==");
    }

}
package com.example.demo;

/**
 * @Description:
 * @Author: caosc
 * @CreateDate: 2019/10/12 14:24
 */
public class NewTest {

    public static void main(String[] args) {
        NewChild n1 = new NewChild();
        System.out.println("=========================================");
        NewChild n2 = new NewChild();
    }
}

运行结果:
【父类静态变量】
父类静态初始化代码块
【子类静态变量】
子类静态初始化代码块
【父类普通变量】
父类初始化代码块
父类构造器
【子类普通变量】
子类初始化代码块
子类构造器
=========================================
【父类普通变量】
父类初始化代码块
父类构造器
【子类普通变量】
子类初始化代码块
子类构造器

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值