【汇总】Java关于“初始化”的亿些细节


Java是如何保证所有变量的初始化的?

  • Java会尽可能保证所有变量的初始化
  • 类变量和类的成员变量,在JVM的类加载子系统中保证了初始化!
  • 方法中的局部变量,会以编译器编译不通过的方式来保证变量初始化!

Java中的0值初始化情况

  • 对于引用对象(包括自定义的对象,String,包装类型,数组)的默认初始化为null,因此如果没有进行显式的初始化,那么不能直接使用…会空指针!
  • 对基本类型0值初始化,差不多都是赋0值,char也是赋0,boolean是赋false字面量;

可以将方法的返回值赋值给变量进行初始化

  • 初始化期间是可以通过方法进行赋值的(如下)
    在这里插入图片描述

显式初始化时“向前引用”的错误

  • 注意一下,(如下图)y先进行初始化,然后y在赋值给x,是合法的!
    在这里插入图片描述
  • 但是,一旦这两行交换位置,那么就会编译不通过,出现了“向前引用”的警告,注意一下就好…
    在这里插入图片描述

成员变量的初始化顺序

  • 在类的内部,变量定义的顺序决定了他们初始化的顺序(即使变量散布在方法和构造器之间,它们也是要优先于方法和构造器调用进行初始化的!)

举个例子证明一下看看:
在这里插入图片描述
我们来看看,输出了什么?
说明了x,y,z分别依次进行初始化后,solution对象才调用了构造器方法!
在这里插入图片描述


静态数据的初始化

  • 首先需要知道!如论创建多少个对象!静态数据只占一份存储空间!
  • static关键字并不能作用于局部变量上!
    在这里插入图片描述
  • static能作用于类变量上,且在加载阶段会进行初始化(类加载子系统的:加载-连接-初始化,之后总结JVM的时候再详细说说)
  • 对于类中的静态变量初始化,如果不创建该类对象,或者不通过Class为入口调用来调用类中的静态变量,那么这个变量是不会进行初始化的!
  • 类中的静态变量只会初始化一次!如果之前进行初始化了,下次再new该类对象,或者调用静态变量,静态变量不会二次初始化!

静态代码块以及代码块

  • 这个静态代码块也是只在类创建,或者Class调用静态变量的时候加载一次,仅仅一次。
    在这里插入图片描述
  • 非静态的实例初始化代码块,就是少了个static的代码块!它的执行时机是在构造方法前!
    在这里插入图片描述

类中整体的初始化顺序

假设这个类中之前没有创建过对象,并且也没有通过类名来调用其中的静态变量!

  • 初始化顺序是先静态对象,后非静态对象。
  • 构造器其实也是static的!虽然没有显式的用static进行修饰!

写一个例子来看看究竟加载顺序是什么!

class Loader {
    public Loader(int x) {
        System.out.println("现在加载的是:" + x);
    }
}

class Father {
    public Father() {
        Loader loader0 = new Loader(0);
        System.out.println("father加载");
    }

    static Loader loader1 = new Loader(1);
    Loader loader2 = new Loader(2);

    static {
        Loader loader3 = new Loader(3);
    }

    Loader loader4 = new Loader(4);

    {
        Loader loader5 = new Loader(5);
    }

    static Loader loader6 = new Loader(6);
}


class Son extends Father {
    public Son() {
        Loader loader7 = new Loader(7);
        System.out.println("son加载");
    }

    static Loader loader8 = new Loader(8);
    Loader loader9 = new Loader(9);

    static {
        Loader loader10 = new Loader(10);
    }

    Loader loader11 = new Loader(11);

    {
        Loader loader12 = new Loader(12);
    }

    static Loader loader13 = new Loader(13);
}

public class Solution {
    public static void main(String[] args) {
        Son son = new Son();
    }
}

输出的顺序是:

现在加载的是:1
现在加载的是:3
现在加载的是:6
现在加载的是:8
现在加载的是:10
现在加载的是:13
现在加载的是:2
现在加载的是:4
现在加载的是:5
现在加载的是:0
father加载
现在加载的是:9
现在加载的是:11
现在加载的是:12
现在加载的是:7
son加载

我们这个时候可以总结一下规律了!
如果有个Son类继承了Father类:
1.首先从父类中上到下顺序初始化static变量(static静态代码块可以看做普通的static修饰静态变量,只按照从上到下的静态变量声明顺序,不会将静态代码块的优先级分别初始化,可以参考一下上面的loader2、3、6对象)

2.然后轮到子类中的static变量按照声明的顺序初始化,static代码块中的声明和外面单独声明一视同仁没有区别:参考loader8、10、13

3.然后轮到父类中的非静态变量按照声明顺序初始化,包括非静态代码块的,参考loader2、4、5

4.【注意】下一个是轮到父类的构造器执行!并执行其中的初始化,参考loader0

5.然后才到子类的非静态变量初始化,参考loader9、11、12

6.最后才是子类的构造器方法的调用,并执行其中的初始化,参考loader7

父类静态变量->子类静态变量->父类非静态变量->父类构造器->子类非静态变量->子类构造器

按照《Java编程思想》所描述的是:

step1:首次创建类的对象时,Java的解释器必须找到类的路径!定位类的class字节码文件
step2:载入类的Class文件,加载的时候会创建一个Class的模板对象,有关静态初始化的所有操作都会在这个时候执行,因此所有的静态初始化只会在Class对象加载的时候进行一次!
step3:当用new 创建对象的时候,首先会在堆上为对象分配足够的内存空间
step4:这块存储空间会被清0,这就自动地将对象中所有基本类型数据都设置成了默认值,引用类型也置为null
step5:执行所有字段定义处的显式初始化操作。
step6:执行构造器(这里会先执行父类的(如果有父类)在执行子类的构造器)。


关于数组初始化

  • 在网上看到一个有趣的回答:
    在这里插入图片描述
  • 不知道说的是什么语言…也不知道除了HotSpot虚拟机别的虚拟机会不会有这样的操作出现。但是hotspot虚拟机确实是没有这个操作。。。
  • 还是那句话!一切的对象都在堆中!
  • new可以省略,那是因为编译器会自动给我们补上这个new
  • 另外注意实例保存的其实是数组的引用,而实例和实例之间是可以互相赋值的,那么就相当于引用的值拷贝!
int[] a = {1,2,3,4};
int[] b = a;
  • 那么a和b指向的是堆中同一个数组对象,操作的时候也是操作的同一个数组对象!除非再new一个新数组!(入下图,arr1将引用传给arr2,然后对arr2中的每个元素+1,再回头输出arr1,发现arr1数组中的元素都改变了!)
    在这里插入图片描述

  • 基本类型的数组和Integer类型数组可以这样初始化↓
    在这里插入图片描述


【end】

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值