【java】静态变量和静态代码块那些事

大金(内蒙的) 发表于 2021/03/27 21:16:34 2021/03/27
【摘要】 今日题目:今天你的静态变量和静态代码块执行了吗? 话不多说,开始今天的题目讲解吧。 先介绍个常识:静态成员属性的初始化早于静态代码块;静态代码块是指的类的初始化操作,初始化早于对象的创建;类静态域的只会初始化一次。 题目一:输出啥?class Father{ public static int m = 33; static{ System.out.println(...

今日题目:

今天你的静态变量和静态代码块执行了吗?

话不多说,开始今天的题目讲解吧。

先介绍个常识:

静态成员属性的初始化早于静态代码块;
静态代码块是指的类的初始化操作,初始化早于对象的创建;
类静态域的只会初始化一次。

题目一:输出啥?

class Father{
    public static int m = 33;
    static{
        System.out.println("父类被初始化");
    }
}

class Child extends Father{
    static{
        System.out.println("子类被初始化");
    }
}

class StaticTest{
    public static void main(String[] args){
        System.out.println(Child.m);
        System.out.println(new Child());
   }
}

答案:
父类被初始化
33
子类被初始化
Child1@2781e022

题目二:输出啥?

class Const{  
    public static final String NAME = "我是常量";  
    static{  
        System.out.println("初始化Const类");  
    }  
}  

public class FinalTest{  
    public static void main(String[] args){  
        System.out.println(Const.NAME);  
    }  
}  

答案:我是常量
详细说明:
static final的变量是在类加载的准备阶段(还没有到初始化):
为类的静态变量分配内存,并将其赋默认值

对于该阶段有以下几点需要注意:

题目三:输出啥?

class Const{  
    static{  
        System.out.println("初始化Const类");  
    }  
}  
  
public class ArrayTest{  
    public static void main(String[] args){  
        Const[] con = new Const[5];  
    }  
}  

输出:空

题目四:输出啥?

class Other {
    public static Other o1 = new Other();

    public static Other o2 = new Other();
    {
        System.out.println("构造块");
    }
    static {
        System.out.println("静态块");
    }

    public static void main(String[] args) {
        Other other = new Other();
    }
}

答案:
构造块
构造块
静态块
构造块

脑瓜子是不是嗡嗡的!!!

image.png

下面介绍一下,类加载过程,帮你debug下。

类加载分为以下步骤:

整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。

  • 加载:二进制.class文件各种文件加载一遍,类加载器去加载的
  • 验证:验证类的正确性,像是格式啊、方法重写啥的啊
  • 准备:静态域的初始化赋值,这个是给个默认值啊。
  • 解析:符号引用(编程原理)解析为直接引用。
  • 初始化:静态域的代码里面赋值。

初始化过程,就是常识中的静态域加载的过程。

以下四种情况触发。

  • 遇到new、getstatic、putstatic、invokestatic这四条字节码指令时,如果类还没有进行过初始化,则需要先触发其初始化。生成这四条指令最常见的Java代码场景是:使用new关键字实例化对象时、读取或设置一个类的静态字段(static)时(被static修饰又被final修饰的,已在编译期把结果放入常量池的静态字段除外)、以及调用一个类的静态方法时。
  • 使用Java.lang.refect包的方法对类进行反射调用时,如果类还没有进行过初始化,则需要先触发其初始化。
  • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
  • 当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先执行该主类。

除了上面四种情况,有几种特殊情况。类的初始化会被动加载。

  • 通过子类引用父类中的静态字段,这时对子类的引用为被动引用,因此不会初始化子类,只会初始化父类。
  • 常量在编译阶段会存入调用它的类的常量池中,本质上没有直接引用到定义该常量的类,因此不会触发定义常量的类的初始化。这里实际上完成了“准备”阶段。
  • 通过数组定义来引用类,不会触发类的“初始化”。

在看一遍题目:

所以,结合常识:
静态成员变量的初始化早于静态代码块;
静态代码块是指的类的初始化操作,初始化早于对象的创建;
类静态域的只会初始化一次。
再看一遍上面的代码,是不是能得出答案了呢?

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。