java面试题网站:www.javaoffers.com
java中的构造器有两种:
          分别是 实例构造器<init>和类构造器<clinit>

构造器的作用:
          构造器的产生过程实际上是一个代码收敛的过程编译器会把语句块(对于实例构造器而言是“{}”块,对于类构造器而言是“static{}”块)、变量初始化(实例变量和类变量)、调用父类的实例构造器(仅仅是实例构造器,<clinit>()方法中无须调用父类的<clinit>()方法,虚拟机会自动保证父类构造器的执行,但在<clinit>()方法中经常会生成调用java.lang.Object的<init>()方法的代码)等操作收敛到<init>()和<clinit>()方法之中,并且保证一定是按先执行父类的实例构造器,然后初始化变量,最后执行语句块的顺序进行.

<init> 和 <clinit> 区别:
           init 针对的是实例, cinit针对是类, 数量上来来讲init构造器至少存在一个. cinit构造器只存在一个. 因为类对象在jvm内存中只会存在一个(同一个类加载器)

实例<init>构造器与构造函数的区别与联系:
          构造器是由javac 命令生成.而构造函数则实质为我们写的java代码 构造方法(也叫构造函数), <init>构造器的生成和构造函数相互对应,如果我们的java代码没有显示的构造函数,那编译器将会添加一个没有参数的、访问性(public、protected或private)与当前类一致的默认构造函数.并根据默认构造函数生成<init>.

实例代码块"{}" 优先级高于构造函数中的赋值动作.多个"{}"按先后顺序最终合并到<init>中.

通过代码案例分析:

package com.mh.others.class_;

import com.mh.others.log.LOGUtils;
import java.util.Date;

/**
 * 研究 <init> 和构造函数的关系
 */
public class InitClassStu {

    private final String name;
    private final Integer age;
    private String nike = "nike";

    private Date birthday;
    public InitClassStu(String name){

    }
    public InitClassStu(){
        birthday = new Date();
    }
    //实例"{}" 在生成<init>构造器时优先于构造函数
    { 
        age = null;
    }
    {
        name = null;
    }

    public static void main(String[] args) {
        InitClassStu cmj = new InitClassStu("cmj");
        LOGUtils.printLog(cmj.nike);
        InitClassStu initClassStu = new InitClassStu();
        LOGUtils.printLog(initClassStu.nike);
    }
/**
 * Compiled from "InitClassStu.java"
 * public class com.mh.others.class_.InitClassStu {
 *   public com.mh.others.class_.InitClassStu(java.lang.String);
 *     Code:
 *        0: aload_0
 *        1: invokespecial #1                  // Method java/lang/Object."<init>":()V
 *        4: aload_0
 *        5: ldc           #2                  // String nike
 *        7: putfield      #3                  // Field nike:Ljava/lang/String;
 *       10: aload_0
 *       11: aconst_null
 *       12: putfield      #4                  // Field age:Ljava/lang/Integer;
 *       15: aload_0
 *       16: aconst_null
 *       17: putfield      #5                  // Field name:Ljava/lang/String;
 * 												//此处以上均为"{}"中的赋值动作
 *       20: return
 *
 *   public com.mh.others.class_.InitClassStu();
 *     Code:
 *        0: aload_0
 *        1: invokespecial #1                  // Method java/lang/Object."<init>":()V
 *        4: aload_0
 *        5: ldc           #2                  // String nike
 *        7: putfield      #3                  // Field nike:Ljava/lang/String;
 *       10: aload_0
 *       11: aconst_null
 *       12: putfield      #4                  // Field age:Ljava/lang/Integer;
 *       15: aload_0
 *       16: aconst_null
 *       17: putfield      #5                  // Field name:Ljava/lang/String;
 *       20: aload_0
 *       21: new           #6                  // class java/util/Date
 *       24: dup                               //此处以上均为"{}"中的赋值动作,优先级高于构造函数中的赋值动作
 *       25: invokespecial #7                  // Method java/util/Date."<init>":()V
 *       28: putfield      #8                  // Field birthday:Ljava/util/Date;
 *       31: return
 *
 *   public static void main(java.lang.String[]);
 *     Code:
 *        0: new           #9                  // class com/mh/others/class_/InitClassStu
 *        3: dup                               //压入栈顶
 *        4: ldc           #10                 // String cmj  将一个常量加载到操作数栈
 *        6: invokespecial #11                 // Method "<init>":(Ljava/lang/String;)V  调用了有参构造器
 *        9: astore_1
 *       10: aload_1
 *       11: getfield      #3                  // Field nike:Ljava/lang/String;
 *       14: invokestatic  #12                 // Method com/mh/others/log/LOGUtils.printLog:(Ljava/lang/String;)V
 *       17: new           #9                  // class com/mh/others/class_/InitClassStu
 *       20: dup
 *       21: invokespecial #13                 // Method "<init>":()V  调用了无参构造器
 *       24: astore_2
 *       25: aload_2
 *       26: getfield      #3                  // Field nike:Ljava/lang/String;
 *       29: invokestatic  #12                 // Method com/mh/others/log/LOGUtils.printLog:(Ljava/lang/String;)V
 *       32: return
 * }
 *
 *
 * <>参考资料 java虚拟机第二版 </>
 * 6.4.2 加载和存储指令加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈(见第2章关于内存区域的介绍)之间来回传输,
 * 这类指令包括如下内容。将一个局部变量加载到操作栈:iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>。
 * 将一个数值从操作数栈存储到局部变量表:istore、istore_<n>、lstore、lstore_<n>、fstore、fstor_<n>、dstore、dstore_<n>、astore、astore_<n>。
 * 将一个常量加载到操作数栈:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>。
 * 扩充局部变量表的访问索引的指令:wide。
 * 存储数据的操作数栈和局部变量表主要就是由加载和存储指令进行操作,除此之外,还有少量指令,如访问对象的字段或数组元素的指令也会向操作数栈传输数据。
 * 上面所列举的指令助记符中,有一部分是以尖括号结尾的(例如iload_<n>),这些指令助记符实际上是代表了一组指令(例如iload_<n>,
 * 它代表了iload_0、iload_1、iload_2和iload_3这几条指令)。这几组指令都是某个带有一个操作数的通用指令(例如iload)的特殊形式,对于这若干组特殊指令来说,
 * 它们省略掉了显式的操作数,不需要进行取操作数的动作,实际上操作数就隐含在指令中。除了这点之外,它们的语义与原生的通用指令完全一致(例如iload_0的语义与操作数为0时的iload指令语义完全一致)。
 * 这种指令表示方法在本书以及《Java虚拟机规范》中都是通用
 *
 */
}

参考资料: <<java虚拟机第二版>>

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐