java类的初始化顺序
最近项目上工作强度还是蛮紧张的,技术还是公司框架自身封装好的(spring+mvc+mybatis+js+jquery+json+shiro+cacheEnable+swagger+ajax+反射等等几十项框架及插件),基础的原理还是没有脱离java的基础知识。今天抽个空把java类的初始化顺序整理一下(最近发现的一个规律,我之前用心总结的博客内容,在工作中用到的频次还是非常高的,所以好记性不如烂笔头,坚持总结、实践吧,骚年)。
一、构造器初始化
package com.edu; class Window { Window(int marker) { System.out.println("window(" + marker + ")"); } } class House { Window w1 = new Window(1); House() { w3 = new Window(33); } Window w2 = new Window(2); void f() { System.out.println("f()"); } Window w3 = new Window(3); } public class OrderOfInitialization { public static void main(String[] args) { House h = new House(); h.f(); } } 结果: window(1) window(2) window(3) window(33) f()
①:初始化可以放在构造器内部;
②:House类在new对象时,会对内部的所有变量进行初始化,所以你看到的是先进行window(1)、window(2)、window(3)的初始化;
③:window(3)被初始化了两次
二、静态数据的初始化
package com.edu; /** * 静态数据的初始化 * * @author tery * */ class Bowl { Bowl(int marker) { System.out.println("Bowl(" + marker + ")"); } void f1(int marker) { System.out.println("f1(" + marker + ")"); } } class Table { static Bowl bowl1 = new Bowl(1); Table() { System.out.println("Table"); bowl2.f1(1); } void f2(int marker) { System.out.println("f2(" + marker + ")"); } static Bowl bowl2 = new Bowl(2); } class CupBoard { Bowl bowl3 = new Bowl(3); static Bowl bowl4 = new Bowl(4); CupBoard() { System.out.println("CupBoard()"); bowl4.f1(2); } void f3(int marker) { System.out.println("f3(" + marker + ")"); } static Bowl bowl5 = new Bowl(5); } public class StaticInitialization { public static void main(String[] args) { System.out.println("Creating new CupBoard() in main"); new CupBoard(); // System.out.println("Creating new CupBoard() in main"); // new CupBoard(); // table.f2(1); // cupboard.f3(1); } // static Table table=new Table(); // static CupBoard cupboard=new CupBoard(); }
结果:
Creating new CupBoard() in main
Bowl(4)
Bowl(5)
Bowl(3)
CupBoard()
f1(2)
为了看清楚,把注释掉的代码逐步放开:
public class StaticInitialization { public static void main(String[] args) { System.out.println("Creating new CupBoard() in main"); new CupBoard(); // System.out.println("Creating new CupBoard() in main"); // new CupBoard(); // table.f2(1); // cupboard.f3(1); } static Table table=new Table(); // static CupBoard cupboard=new CupBoard(); } 结果: Bowl(1) Bowl(2) Table f1(1) Creating new CupBoard() in main Bowl(4) Bowl(5) Bowl(3) CupBoard() f1(2)
public class StaticInitialization { public static void main(String[] args) { System.out.println("Creating new CupBoard() in main"); new CupBoard(); // System.out.println("Creating new CupBoard() in main"); // new CupBoard(); // table.f2(1); // cupboard.f3(1); } static Table table=new Table(); static CupBoard cupboard=new CupBoard(); } 结果: Bowl(1) Bowl(2) Table f1(1) Bowl(4) Bowl(5) Bowl(3) CupBoard() f1(2) Creating new CupBoard() in main Bowl(3) CupBoard() f1(2)
public class StaticInitialization { public static void main(String[] args) { System.out.println("Creating new CupBoard() in main"); new CupBoard(); System.out.println("Creating new CupBoard() in main"); new CupBoard(); // table.f2(1); // cupboard.f3(1); } static Table table=new Table(); static CupBoard cupboard=new CupBoard(); } 结果: Bowl(1) Bowl(2) Table f1(1) Bowl(4) Bowl(5) Bowl(3) CupBoard() f1(2) Creating new CupBoard() in main Bowl(3) CupBoard() f1(2) Creating new CupBoard() in main Bowl(3) CupBoard() f1(2)
public class StaticInitialization { public static void main(String[] args) { System.out.println("Creating new CupBoard() in main"); new CupBoard(); System.out.println("Creating new CupBoard() in main"); new CupBoard(); table.f2(1); cupboard.f3(1); } static Table table=new Table(); static CupBoard cupboard=new CupBoard(); } 结果: Bowl(1) Bowl(2) Table f1(1) Bowl(4) Bowl(5) Bowl(3) CupBoard() f1(2) Creating new CupBoard() in main Bowl(3) CupBoard() f1(2) Creating new CupBoard() in main Bowl(3) CupBoard() f1(2) f2(1) f3(1)
小总结:
①无论创建多少个对象,静态数据都只占一份存储区域。static关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且没有对它进行初始化,那么它就会获得基本类型的标准初值,如果它是对象的引用,那么它的默认初始化的值就是null。
②初始化的顺序是先静态对象(如果它们尚未因前面的对象创建过程而被初始化),而后是非静态对象。
③要执行main()(静态方法),必须加载StaticInitialization类,紧接着其静态域table和cupboard被初始化,这将导致他们对应的类也被加载,并且由于它们也都包含静态的Bowl对象,因此Bowl随后也被加载。
三、.class创建对象
package com.edu; import java.util.Random; /** * .class属性的初始化 * @author tery * */ class Initable{ Initable(){ System.out.println("Initable initial..."); } static final String staticFinal="47"; static final int staticFinal2=ClassInitialization.rand.nextInt(1000); static{ System.out.println("Initializing Initable"); } } class Initable2{ static int staticNonFinal=147; static{ System.out.println("Initializing Initable2"); } } class Initable3{ static int staticNonFinal =74; static { System.out.println("Initializing Initable3"); } } public class ClassInitialization { public static Random rand=new Random(47); public static void main(String[] args){ Class initable=Initable.class; System.out.println("After creating Initable ref"); System.out.println(Initable.staticFinal); Initable a=new Initable(); System.out.println(Initable.staticFinal2); // System.out.println(Initable2.staticNonFinal); // System.out.println(Initable3.staticNonFinal); } } 结果: After creating Initable ref 47 Initializing Initable Initable initial... 258
为了使用类而准备的工作实际上包含三个步骤:
①加载,这是由类加载器执行的,该步骤将查找字节码(通常在classpath所指定的路径中查找,但这并非是必须的),并从这些字节码中创建一个Class对象。
②链接,在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。
③初始化,如果该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化块。初始化被延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行。
④在打印Initable.staticFinal时属于调用常数静态域,即使加上Initable(){...}的构造方法,也不会执行,将int型改为String型也是同样的结果。