三个例子彻底搞懂Java类属性,代码块,构造方法的执行顺序
对于标题所述的加载顺序,可能有些同学和我一样知道静态属性和代码块先加载,非静态属性和代码块其次加载,最后才是构造方法加载。但并没有深入的去了解它。那么我们带着两个问题去看待这个加载顺序。
- 是否有可能非静态代码块先于静态方法加载?
- 如果子类继承了父类,那么其加载顺序又是怎样的呢?
对于第一个问题我们直接上一个例子。
public class StaticTest {
public static int temp = 2;
public static StaticTest instance = new StaticTest();
static {
System.out.println("static StaticTest");
}
{
System.out.println("unStatic StaticTest");
}
public StaticTest() {
System.out.println("constract StaticTest");
method();
}
public void method() {
System.out.println("method StaticTest");
}
@Override
public String toString() {
return "toString StaticTest";
}
public static void main(String[] args) {
new StaticTest();
System.out.println("----------------");
new StaticTest();
}
}
打印结果如下
unStatic StaticTest
constract StaticTest
method StaticTest
static StaticTest
unStatic StaticTest
constract StaticTest
method StaticTest
----------------
unStatic StaticTest
constract StaticTest
method StaticTest
可以看到当一个类的静态属性需要自身的构造方法去实现的时候,可以出现非静态代码块先于构造方法和静态方法执行。
针对第二个问题我们上第二个例子
class ParentTest {
public static String PARENT_STATIC_FIELD = "父类-静态属性";
// 父类-静态块
static {
System.out.println(PARENT_STATIC_FIELD);
System.out.println("父类-静态代码块");
}
public String parentField = "父类-非静态属性";
// 父类-非静态块
{
System.out.println(parentField);
System.out.println("父类-非静态代码块");
}
public ParentTest() {
System.out.println("父类—无参构造函数");
}
}
public class InitOderTest extends ParentTest {
public static String STATIC_FIELD = "静态属性";
// 静态块
static {
System.out.println(STATIC_FIELD);
System.out.println("静态代码块");
}
public String field = "非静态属性";
// 非静态块
{
System.out.println(field);
System.out.println("非静态代码块");
}
public InitOderTest() {
System.out.println("无参构造函数");
}
public static void main(String[] args) {
InitOderTest test = new InitOderTest();
}
}
打印结果如下:
父类-静态属性
父类-静态代码块
静态属性
静态代码块
父类-非静态属性
父类-非静态代码块
父类—无参构造函数
非静态属性
非静态代码块
无参构造函数
可以得出结论有继承关系的类相关的加载顺序如下:
- 父类的静态代码块和属性
- 子类的静态代码块和属性
- 父类的非静态代码块和属性
- 父类的构造方法
- 子类的非静态代码块和属性
- 子类的构造方法
最后我们来一个实战的列子 代码如下:
public class TestClassLoader {
static class Father {
public static final String TAG = "Father";
static {
System.out.println("static Father");
}
{
System.out.println("unStatic Father");
}
public Father() {
System.out.println("constract Father");
method();
}
public void method() {
System.out.println("method Father");
}
@Override
public String toString() {
return "toString Father";
}
}
static class Son extends Father {
public static Son instance = new Son();
static {
System.out.println("static Son");
}
{
System.out.println("unStatic Son");
}
public Son() {
System.out.println("constract Son");
method();
}
public void method() {
System.out.println("method Son");
}
@Override
public String toString() {
return "toString Son";
}
}
public static void main(String[] args) {
System.out.println("1.---------------------");
System.out.println(Son.TAG);
Son[] sons = new Son[10];
System.out.println(sons);
System.out.println("2.---------------------");
System.out.println(Son.instance);
System.out.println("3.---------------------");
Son son = new Son();
Father father = son;
father.method();
System.out.println(son);
}
}
打印结果如下:
1.---------------------
Father
[LTestClassLoader$Son;@61bbe9ba
2.---------------------
static Father
unStatic Father
constract Father
method Son
unStatic Son
constract Son
method Son
static Son
toString Son
3.---------------------
unStatic Father
constract Father
method Son
unStatic Son
constract Son
method Son
method Son
toString Son
综上所述值得我们注意的是,静态属性和代码块只会在类的加载过程中执行一次,而在执行构造方法之前必然要先执行非静态代码块和属性。