类的初始化和对象的初始化

在类加载过程中,准备阶段是正式为类变量(static成员变量)分配内存并设置初始值的阶段,而初始化阶段是真正开始执行类中定义的java程序代码(字节码)并按程序猿的意图去初始化类变量的过程。更直接的说初始化阶段是执行类构造器clinit方法的过程。clinit()方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块static{}中的语句合并产生的,其中编译器收集的顺序是由语句在源文件中出现的顺序锁决定。
 类构造器clinit()与实例构造器innit()不同,它不需要程序员进行显式调用,虚拟机会保证在子类类构造器clinit()执行之前,父类的类构造clinit()执行完毕。由于父类的构造器clinit()先执行,也就意味着父类中定义的静态代码块/静态变量的初始化要优先于子类的静态代码块/静态变量的初始化执行。特别地,类构造器clinit()对于类或者接口来说并不是必需的,如果一个类中没有静态代码块,也没有对类变量的赋值操作,那么编译器可以不为这个类生产类构造器clinit()。此外,在同一个类加载器下,一个类只会被初始化一次,但是一个类可以任意地实例化对象。也就是说,在一个类的生命周期中,类构造器clinit()最多会被虚拟机调用一次,而实例构造器init()则会被虚拟机调用多次,只要程序员还在创建对象。
1、一个实例变量在对象初始化的过程中会被赋值几次?

我们知道,JVM在为一个对象分配完内存之后,会给每一个实例变量赋予默认值,这个时候实例变量被第一次赋值,这个赋值过程是没有办法避免的。如果我们在声明实例变量x的同时对其进行了赋值操作,那么这个时候,这个实例变量就被第二次赋值了。如果我们在实例代码块中,又对变量x做了初始化操作,那么这个时候,这个实例变量就被第三次赋值了。如果我们在构造函数中,也对变量x做了初始化操作,那么这个时候,变量x就被第四次赋值。也就是说,在Java的对象初始化过程中,一个实例变量最多可以被初始化4次。

2、类的初始化过程与类的实例化过程的异同?

类的初始化是指类加载过程中的初始化阶段对类变量按照程序猿的意图进行赋值的过程;而类的实例化是指在类完全加载到内存中后创建对象的过程。
  3、假如一个类还未加载到内存中,那么在创建一个该类的实例时,具体过程是怎样的?

我们知道,要想创建一个类的实例,必须先将该类加载到内存并进行初始化,也就是说,类初始化操作是在类实例化操作之前进行的,但并不意味着:只有类初始化操作结束后才能进行类实例化操作。也就是说当类还没有初始化完成也就是没有给静态变量或者静态代码块初始化时也是可以执行的
  也许这个类只是刚刚完成准备阶段分配了内存空间并且赋值了默认值!这个时候也是可以将这个类实例化的!!!!!

public class StaticTest {
    public static void main(String[] args) {
        staticFunction();

    }
    static StaticTest st=new StaticTest();
    static{
        System.out.println("1");
    }
    {
        System.out.println("2");
    }
    StaticTest(){
        System.out.println("3");
        System.out.println("a="+a+",b="+b);
    }
    public static void staticFunction(){
        System.out.println("4");
    }
    int a=110;
    static int b=112;
}
/**
 * JVM想要执行程序,发现StaticTest类没有加载进来,于是开始加载
 * 类的加载过程 分为准备阶段 为类变量和静态代码块分配空间,
 * 另一个阶段是类的初始化阶段执行类变量的赋值和静态代码块 先执行到
 * static StaticTest st=new StaticTest();发现要实例化一个对象,
 * 当对象建好了赋给引用这行代码才算结束
 *
 * Java对象的初始化过程主要涉及三种执行对象初始化的结构,分别是实例变量初始化,实例代码块初始化以及构造函数初始化。
 * 我们在定义实例变量的同时,还可以直接对实例变量进行赋值或者使用实例代码块对其进行赋值。如果我们以这两种方式为实例变量进行
 * 初始化,那么他们将在执行构造函数之前完成这些初始化操作。
 * 实际上如果我们对实例变量进行直接赋值或者使用实例代码块赋值,那么编译器会将其中的代码放到类的构造函数中去,并且这些代码会
 * 被放在对超类构造函数的调用语句之后。
 *     StaticTest(){
 *
 *         System.out.println("2");
 *
 *         int a=110;
 *         System.out.println("3");
 *         System.out.println("a="+a+",b="+b);
 *     }
 *
 *     会输出2,3,a=110因为此时b还没有被赋值故b=0;
 *
 *     以上 static StaticTest st=new StaticTest(); 这行代码才执行完毕。
 *
 *     接下来执行
 *     static{
 *         System.out.println("1");
 *     }
 *     故输出1;
 *     最后执行:static int b=112;  到这里类加载过程的初始化阶段才算完成
 *
 *
 *     接下来在main方法中调用staticFunction()输出4
 *
 *
 *     故最终的结果是:
 *     2
 *     3
 *     a=110,b=0
 *     1
 *     4
 */
public class ClassLoaderLinkInitTest {
    public static void main(String[] args) {
        Son son = new Son();//我觉得是程序的运行需要类加载整个过程完成!
        System.out.println("---end---");
    }
}

class Son extends Father{
    private int i=1;
    private long l=2L;
    static int ssi=3;

    {
        System.out.println("1son init block");
    }
    static{
        System.out.println("2son static block");
    }
    Son(){
        l=3L;
        System.out.println("3son constructor");
    }
}

class Father{
    int ii;
    static int fsi=4;
    static Son son=new Son();
    {
        System.out.println("4Father init block");
    }
    static{
        System.out.println("5Father static block");
    }
    Father(){
        ii=1;
        System.out.println("6Father constructor");
    }
}

父类和子类的类加载过程的准备阶段为类变量和静态代码块分配空间
类加载过程的初始化阶段为 类变量和静态代码块赋值

先对父类进行初始化方法
先为fsi复制

static int fsi=4;

然后遇到

static Son son=new Son();

这行代码要加子类对象实例化 并赋给引用 才算结束
实例化Son会调用父类无参的构造方法,而javac编译器已经把"实例变量"的初始化语句和"实例代码块"的初始化语句按照代码中的顺序编译到了构造方法中 故会先执行父类中的这两部分即

    
System.out.println("4Father init block");



ii=1;
System.out.println("6Father constructor");
    

然后再执行子类中的

private int i=1;
private long l=2L;
System.out.println("1son init block");
l=3L;
System.out.println("3son constructor");

以上才代表下面这条代码执行结束

 static Son son=new Son();

接下来执行

static{
        System.out.println("5Father static block");
    }

到这里父类初始化过程结束,接下来该子类

先执行

static int ssi=3;

然后

 static{
        System.out.println("2son static block");
    }

子类完成初始化

接下来执行主方法中的

Son son = new Son();

又会去调用父类的无参构造,而父类的无参构造又包括父类实例变量初始化和实例语句快以及无参构造中的方法 实际上编译器已经将他们合在一起了
所以接下来会执行父类中的

System.out.println("4Father init block");
ii=1;
System.out.println("6Father constructor");

接着是子类的实例变量和实例语句块和构造方法

private int i=1;
private long l=2L;
System.out.println("1son init block");
l=3L;
System.out.println("3son constructor");

故最终结果是

/**输出结果如下:
 * 4Father init block
 * 6Father constructor
 * 1son init block
 * 3son constructor
 * 5Father static block
 * 2son static block
 * 4Father init block
 * 6Father constructor
 * 1son init block
 * 3son constructor
 * ---end---
 */
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值