这方面知识一直没有整理,但大家经常讨论类的静态变量、成员变量、静态初始化块、非静态初始化块、构造器,及继承父类时,它们的初始化顺序都是怎样的,所以找了个机会认真整理一下,帮助大家脱坑:
首先介绍一下这几个家伙,捋清它们是干嘛的:
静态变量 / 成员变量: 是类的属性,静态变量属于类,被static修饰,成员变量属于对象实例。
静态成员方法 / 普通成员方法: 静态成员方法属于类所有,类实例化前即可使用,普通成员方法可以访问类中的任何成员,静态成员方法只能访问类中的静态成员。
静态初始化块 / 非静态初始化块: 初始化块是类的成员之一,每次类的创建会隐式的调用它。本质上是一个代码块,或方法体。静态的被static修饰。
构造器: 用于实例化对象,分为有参无参。
这些都是类的基本结构。下面通过代码示例分析它们的执行顺序:
示例1
public class DateTest {
public static void main(String[] args) {
System.out.println((int)(Math.random()*40+1));
System.out.println(Demo1.a);//加载类
//new Demo1();执行两次,第一次不执行实例化,第二次执行实例化
}
}
class Demo1{
static int a =100;
{
System.out.println("我是普通初始化块");
}
static{
System.out.println("我是静态初始化块");
}
}
第一次输出:
第二次输出:
两次比较后我们就可以知道:
普通初始化块:创建对象时隐式调用
静态初始化块:类加载时隐式调用
示例2
public class DateTest {
public static void main(String[] args) {
new Demo2();
new Demo2();
new Demo2();
new Demo2();
new Demo2();
}
}
class Demo2{
static int a =100;
{
System.out.println("我是普通初始化块");
}
static{
System.out.println("我是静态初始化块");
}
}
由这个例子我们又知道:
静态初始化块只在类加载时调用一次,而普通初始化块随着创建对象而执行。
示例3
public class DateTest {
public static void main(String[] args) {
Demo3 qiyu = new Demo3();
}
}
class Demo3{
public Demo3(){
System.out.println("我是构造器");
}
String a=msg("普通属性1");
public static String msg(String info){
System.out.println(info);
return info;
}
static{
System.out.println("静态初始化块2");
}
static String b=msg("静态属性1");
{
System.out.println("我是普通初始化块1");
}
String c=msg("普通属性2");
{
System.out.println("我是普通初始化块2");
}
static String d=msg("静态属性2");
static{
System.out.println("静态初始化块1");
}
}
由本例可知:在一个类中如果有多个不同的初始化块,初始化属性,构造器,
执行顺序是:静态初始化块|静态变量 > 普通初始化块|普通变量 > 构造器
示例四
public class DateTest {
public static void main(String[] args) {
Demo3 qiyu = new Demo3();
}
}
class Demo3{
public Demo3(){
System.out.println("我是构造器");
}
String a=msg("普通属性1");
public static String msg(String info){
System.out.println(info);
return info;
}
static{
System.out.println("静态初始化块2");
new Demo3();
}
static String b=msg("静态属性1");
{
System.out.println("我是普通初始化块1");
}
String c=msg("普通属性2");
{
System.out.println("我是普通初始化块2");
}
static String d=msg("静态属性2");
static{
System.out.println("静态初始化块1");
}
}
本例比较有特色,产生了嵌套,即在某个静态初始化块中new了新对象,我们发现,一开始先执行静态初始化块,没问题,但后面并没有执行下一层的静态初始化块,而执行的普通属性和普通初始化块,这是由于如果一直优先执行静态初始化块的话会产生死循环,此时虚拟机将执行静态块的权利只赋予最先进行实例化的类,这样就能防止死循环的产生。