这一篇篇幅会很短,整理的东西也不会太深刻,一个经常碰到的问题。
有时候,我们会好奇,我们程序中哪些地方会先执行,哪些地方会后执行,为了凸显效果,我们直接用代码演示。
包括两个部分:
- 执行顺序
- 赋值顺序
1.Java代码执行顺序
//父类
package classexecution;
public class BaseClass {
static{
System.out.println("我是父类静态代码块");
}
{
System.out.println("我是父类普通代码块");
}
public BaseClass(){
System.out.println("我是父类构造器");
}
}
//子类
package classexecution;
public class ExecutionTest extends BaseClass{
static{
System.out.println("我是子类静态代码块");
}
{
System.out.println("我是子类普通代码块");
}
public ExecutionTest(){
System.out.println("我是普通构造器");
}
}
//测试类
public class main {
public static void main(String[] args){
ExecutionTest executionTest = new ExecutionTest();
}
}
//测试结果:
我是父类静态代码块
我是子类静态代码块
我是父类普通代码块
我是父类构造器
我是子类普通代码块
我是普通构造器
这个执行顺序就出来了,很好理解,静态代码块属于启动时就会执行的,执行子类会先加载父类也是我们知道的,所以这个应该不会有什么疑问。
2.赋值顺序
赋值顺序就不太好用代码解释了,我尝试写一下。
在之前的基础上写:
首先有两个是肯定的,静态变量是最先赋值的,且静态变量是独立且唯一的。
1.我们首先来比较父类静态变量和子类静态变量
我们要知道,静态变量不归对象而归类所有,所以当我们两个父子类拥有一个同名的变量的时候,其实并不是同一个。
而类会在首次被“主动使用”时执行初始化,为类(静态)变量赋予正确的初始值。
在Java代码中,一个正确的初始值是通过类变量初始化语句或者静态初始化块给出的。
而我们这里所说的主动使用 包括:
1. 创建类的实例
2. 调用类的静态方法
3. 使用类的非常量静态字段
4. 调用Java API中的某些反射方法
5. 初始化某个类的子类
6. 含有main()方法的类启动时
所以,这里父类静态变量会优先于子类的静态变量赋值。
2.继续比较其他的
public class BaseClass {
public String bString = "我是父类成员变量";
static{
System.out.println("我是父类静态代码块");
}
{
System.out.println("我是父类普通代码块-----"+bString);
bString = "我是父类块赋值";
System.out.println("我是父类普通代码块-----------"+bString);
}
public BaseClass(){
System.out.println("我是父类构造器---------"+bString);
bString = "我是父类构造器赋值";
System.out.println("我是父类构造器-----------"+bString);
}
}
//子类
public class ExecutionTest extends BaseClass{
public static String aString = "我是子类静态变量";
public String bString = "我是子类成员变量";
static{
System.out.println("我是子类静态代码块");
}
{
System.out.println("我是子类普通代码块-----------" +bString);
bString="我是子类块";
System.out.println("我是子类普通代码块-----------" +bString);
}
public ExecutionTest(){
System.out.println("我是普通构造器----------"+bString);
bString="我是子类块";
System.out.println("我是普通构造器----------"+bString);
}
}
//结果:
我是父类静态代码块
我是子类静态代码块
我是父类普通代码块-----我是父类成员变量
我是父类普通代码块-----------我是父类块赋值
我是父类构造器---------我是父类块赋值
我是父类构造器-----------我是父类构造器赋值
我是子类普通代码块-----------我是子类成员变量
我是子类普通代码块-----------我是子类块
我是普通构造器----------我是子类块
我是普通构造器----------我是子类块
这可以初步得出结论
- 父类静态变量赋值
- 子类静态变量赋值
- 父类成员变量赋值
- 父类块赋值
- 父类构造器赋值
- 自身块赋值
- 自身构造器赋值
但是现在有一个疑问:自身成员变量声明时候赋值,他可能在父类成员变量到自身块的任何位置!
继续:
public static int size =1;
A a = new A();
a.name="aaa";
当程序第一次new时,如果是第一次使用这个类,会在实例化的同时初始化。在类的准备阶段,系统会为该类的类变量(成员变量/静态变量)分配内存空间,并指定默认初始化值。
当类初始化完成后,系统将在堆内存中为其分配一块内存区里面包含其类变量的内存,并将其设置默认值。
接着系统把创建的该类的对象赋值给了变量a,由于对象里面包含了 实例变量 name(实例变量可以在创建实例时分配到内存空间并指定内存值),所以会有以下操作:
在堆内存中创建空间,存储name,
从图中可以看到类变量并不属于a对象,它是属于A类的,所以创建对象的时候不需要为size分配内存,系统只是为name分配的内存空间并指定默认初始值。
所以,我们推断出:
静态变量在类创建的时候赋值,且静态变量属于类。
而普通实例变量在类初始化的时候赋值,即在构造器运行之前:
所以得出:
- 父类静态变量赋值
- 子类静态变量赋值
- 父类成员变量赋值
- 父类块赋值
- 父类构造器赋值
- 自身成员变量赋值
- 自身块赋值
- 自身构造器赋值
3.总结
这些可以说是很简单的东西,总结一下,加深印象,根据经验,在实际项目中还是有一定作用,经常会碰到赋值不正确的事情。
思路来源于网络,自我测试完成。