写点什么

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

  • 2022 年 1 月 04 日
  • 本文字数:1719 字

    阅读完需:约 6 分钟

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

 

本文分享自华为云社区《【java】静态变量和静态代码块那些事》,作者: 大金(内蒙的)。

今日题目:


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

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

先介绍个常识:


静态成员属性的初始化早于静态代码块;

静态代码块是指的类的初始化操作,初始化早于对象的创建;

类静态域的只会初始化一次。

题目一:输出啥?


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 的变量是在类加载的准备阶段(还没有到初始化):

为类的静态变量分配内存,并将其赋默认值

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

  • 只对 static 修饰的静态变量进行内存分配、赋默认值(如 0、0L、null、false 等)。

  • 对 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(); }}
复制代码


​答案:

构造块

构造块

静态块

构造块

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

类加载分为以下步骤:


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

  • 加载:二进制.class 文件各种文件加载一遍,类加载器去加载的

  • 验证:验证类的正确性,像是格式啊、方法重写啥的啊

  • 准备:静态域的初始化赋值,这个是给个默认值啊。

  • 解析:符号引用(编程原理)解析为直接引用。

  • 初始化:静态域的代码里面赋值。

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


以下四种情况触发。

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

  • 使用 Java.lang.refect 包的方法对类进行反射调用时,如果类还没有进行过初始化,则需要先触发其初始化。

  • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

  • 当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先执行该主类。

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


  • 通过子类引用父类中的静态字段,这时对子类的引用为被动引用,因此不会初始化子类,只会初始化父类。

  • 常量在编译阶段会存入调用它的类的常量池中,本质上没有直接引用到定义该常量的类,因此不会触发定义常量的类的初始化。这里实际上完成了“准备”阶段。

  • 通过数组定义来引用类,不会触发类的“初始化”。

在看一遍题目:


所以,结合常识:

静态成员变量的初始化早于静态代码块;

静态代码块是指的类的初始化操作,初始化早于对象的创建;

类静态域的只会初始化一次。

再看一遍上面的代码,是不是能得出答案了呢?


点击关注,第一时间了解华为云新鲜技术~

发布于: 1 小时前
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
今天你的静态变量和静态代码块执行了吗?