Java对象的创建过程

JAVA 专栏收录该内容
25 篇文章 0 订阅

Java对象的创建过程与初始化过程

此文聚焦“对象”而不是“类”

1、Java对象创建过程

图片来源:https://www.cnblogs.com/chenyangyao/p/5296807.html

在这里插入图片描述

2、Java对象的初始化(类的实例化)

类的初始化不在本文范畴

此部分对应上图的调用对象的<init>方法<init>()又叫做实例构造器,与之类似的有<clinit>()称为类构造器(用来初始化类,具体见《JVM类的生命周期与类的加载过程》)。

注意:在此之前,在虚拟机为对象分配内存后,已经进行过一次初始化了

然后才真正按照程序员的意志进行初始化:

  1. 实例变量初始化
  2. 实例代码块初始化
  3. 构造函数初始化

1)实例变量初始化与实例代码块初始化

二者地位相同,编译器会按照二者的相对顺序将其插入到类的构造函数中,位置在超类构造函数之后(如果显示调用了),本构造函数体中的剩余代码之前。

举例:

public class InstanceVariableInitializer {  

    private int i = 1;  
    private int j = i + 1;  

    public InstanceVariableInitializer(int var){
        System.out.println(i);
        System.out.println(j);
        this.i = var;
        System.out.println(i);
        System.out.println(j);
    }

    {               // 实例代码块
        j += 3; 

    }

    public static void main(String[] args) {
        new InstanceVariableInitializer(8);
    }
}
/* Output: 
            1
            5
            8
            5
 *///:~

上例的构造函数等价于:

public InstanceVariableInitializer(int var){
        // 这里隐含了super.InstanceVariableInitializer()
  			// 以下是实例变量与实例代码块等价整合到了构造函数中
				this.i = 1;
  			this.j = i + 1;
  			this.j = this.j + 3;
  			// 本构造函数的函数体
        System.out.println(i);
        System.out.println(j);
        this.i = var;
        System.out.println(i);
        System.out.println(j);
    }

通过查看字节码也可以验证这种整合:,如2)中的第一个例子中的字节码就有体现。
注意:不允许顺序靠前的实例代码块初始化在其后面定义的实例变量,如:

编译不通过

public class InstanceInitializer {  
    {  
        j = i;  
    }  

    private int i = 1;  
    private int j;  
}  

public class InstanceInitializer {  
    private int j = i;  
    private int i = 1;  
}  

取巧可以绕过上述问题:

public class InstanceInitializer {  
    private int j = getI();  
    private int i = 1;  

    public InstanceInitializer() {  
        i = 2;  
    }  

    private int getI() {  
        return i;  
    }  

    public static void main(String[] args) {  
        InstanceInitializer ii = new InstanceInitializer();  
        System.out.println(ii.j);  
    }  
}  

这种方式虽然编译器不报错,但j的结果为0,因为在初始化j时,还没有执行构造函数中的i = 2,因此 i 为内存分配完统一初始化的0值(还没到<init>)

2)构造函数初始化

构造函数在编译生成的字节码中,会被命名成<init>()方法,参数列表与Java语言书写的构造函数的参数列表相同。如:

private int id;
private String name;
private String address;

// 构造函数
public Student(int id, String name) {
    this.id = id;
    this.name = name;
}
{
  address = "kk";
}

// 对应字节码
// access flags 0x1
  public <init>(ILjava/lang/String;)V
   L0
    LINENUMBER 15 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V  // 调用父类构造函数
   L1
    LINENUMBER 20 L1
    ALOAD 0
    LDC "kk"
    PUTFIELD Student.address : Ljava/lang/String; // 将实例代码块织入实例构造器<init>中
   L2
    LINENUMBER 16 L2
    ALOAD 0
    ILOAD 1
    PUTFIELD Student.id : I   // 构造函数中的语句对应
   L3
    LINENUMBER 17 L3
    ALOAD 0
    ALOAD 2
    PUTFIELD Student.name : Ljava/lang/String; // 构造函数中的语句对应
   L4
    LINENUMBER 18 L4
    RETURN
   L5
    LOCALVARIABLE this LStudent; L0 L5 0
    LOCALVARIABLE id I L0 L5 1
    LOCALVARIABLE name Ljava/lang/String; L0 L5 2
    MAXSTACK = 2
    MAXLOCALS = 3

特别地,如果我们在一个构造函数中调用另外一个构造函数,如下所示

public class ConstructorExample {  
    private int i;  

    ConstructorExample() {  
        this(1);  // 调用下面的构造函数
        ....  
    }  

    ConstructorExample(int i) {  
        ....  
        this.i = i;  
        ....  
    }  
}  

对于这种情况,Java只允许在ConstructorExample(int i)内调用超类的构造函数,也就是说,下面两种情形的代码编译是无法通过的:

public class ConstructorExample {  
    private int i;  

    // 1.
    ConstructorExample() {  
        super();  
        this(1);  // Error:Constructor call must be the first statement in a constructor
        ....  
    }  
   // 2.
  ConstructorExample() {  
        this(1);  
        super();  //Error: Constructor call must be the first statement in a constructor
        ....  
    } 

    ConstructorExample(int i) {  
        ....  
        this.i = i;  
        ....  
    }  
}  

总之,java对象的初始化(类的实例化)是一个递归的过程,如下图所示:

在这里插入图片描述

参考:https://blog.csdn.net/justloveyou_/article/details/72466416

  • 1
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值