## 類的初始化
#### 初始化順序
首先來看下,當一個類從main入口方法,對于靜態變量、靜態初始化塊、變量、初始化塊、構造器,它們的初始化順序:
```
public class ClassLoadTest {
private static User user = new User();
static {
System.err.println("static code block");
}
{
System.err.println("code block");
}
private Student student = new Student();
public ClassLoadTest(){
System.err.println("Constructor");
}
public static void main(String[] args) {
System.err.println("mian ==>");
new ClassLoadTest();
}
}
class Student{
public Student(){
System.err.println("student initint===>");
}
}
class User {
public User() {
System.err.println("user initing===>");
}
}
```
結論:
正常類的加載順序:靜態變量/靜態代碼塊 -> main方法 -> 非靜態變量/代碼塊 -> 構造方法
_**說明:靜態代碼塊與靜態變量的執行順序同代碼定義的順序;非靜態變量與代碼塊的執行順序同代碼執行順序**_
#### 繼承的情況
```
class Parent {
/* 靜態變量 */
public static String p_StaticField = "父類--靜態變量";
/* 變量 */
public String p_Field = "父類--變量";
protected int i = 9;
protected int j = 0;
/* 靜態初始化塊 */
static {
System.out.println( p_StaticField );
System.out.println( "父類--靜態初始化塊" );
}
/* 初始化塊 */
{
System.out.println( p_Field );
System.out.println( "父類--初始化塊" );
}
/* 構造器 */
public Parent()
{
System.out.println( "父類--構造器" );
System.out.println( "i=" + i + ", j=" + j );
j = 20;
}
}
public class SubClass extends Parent {
/* 靜態變量 */
public static String s_StaticField = "子類--靜態變量";
/* 變量 */
public String s_Field = "子類--變量";
/* 靜態初始化塊 */
static {
System.out.println( s_StaticField );
System.out.println( "子類--靜態初始化塊" );
}
/* 初始化塊 */
{
System.out.println( s_Field );
System.out.println( "子類--初始化塊" );
}
/* 構造器 */
public SubClass()
{
System.out.println( "子類--構造器" );
System.out.println( "i=" + i + ",j=" + j );
}
/* 程序入口 */
public static void main( String[] args )
{
System.out.println( "子類main方法" );
new SubClass();
}
}
```
初始化順序:
父類--靜態變量/父類--靜態初始化塊
子類--靜態變量/子類--靜態初始化塊
父類--變量/父類--初始化塊
父類--構造器
子類--變量/子類--初始化塊
子類--構造器
結論:
* 子類的靜態變量和靜態初始化塊的初始化是在父類的變量、初始化塊和構造器初始化之前就完成了;
* 靜態變量、靜態初始化塊順序取決于它們在類中出現的先后順序
* 變量、初始化塊初始化順序取決于它們在類中出現的先后順序
**分析**
* \(1\)訪問SubClass.main\(\)\(這是一個static方法\),于是裝載器就會為你尋找已經編譯的SubClass類的代碼(也就是SubClass.class文件)。在裝載的過程中,裝載器注意到它有一個基類,于是再裝載基類。不管你創不創建基類對象,這個過程總會發生。如果基類還有基類,那么第二個基類也會被裝載,依此類推;
* \(2\)執行基類的static初始化,然后是下一個派生類的static初始化,依此類推。這個順序非常重要,因為派生類的“static初始化”有可能要依賴基類成員的正確初始化;
* \(3\)當所有必要的類都已經裝載結束,開始執行main\(\)方法體,并用new SubClass()創建對象;
* \(4\)類SubClass存在父類,則調用父類的構造函數,你可以使用super來指定調用哪個構造函數。基類的構造過程以及構造順序,同派生類的相同。首先基類中各個變量按照字面順序進行初始化,然后執行基類的構造函數的其余部分;
* \(5\)對子類成員數據按照它們聲明的順序初始化,執行子類構造函數的其余部分;
#### static變量
```
public class Test {
static {
i = 0; // 給變量復制可以正常編譯通過
// System.out.print(i); // 這句編譯器會提示“非法向前引用”
}
static int i = 1;
static int j = 1;
static{
j = 2;
}
public static void main(String[] args){
System.out.println(Test.i); //1
System.out.println(Test.j); //2
}
}
```
#### 不觸發初始化實例
```
/**
* 被動使用類字段演示一:
* 通過子類引用父類的靜態字段,不會導致子類初始化
**/
class SuperClass {
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
class SubClass extends SuperClass {
static {
System.out.println("SubClass init!");
}
}
/**
* 非主動使用類字段演示
**/
public class NotInitialization {
public static void main(String[] args) {
// System.out.println(SubClass.value);
//SuperClass init!
//123
/**
* 被動使用類字段演示二:
* 通過數組定義來引用類,不會觸發此類的初始化
**/
SuperClass[] sca = new SuperClass[10];
}
```
#### 被動使用類字段
```
/**
* 被動使用類字段演示三:
*
* 常量在編譯階段會存入調用類的常量池中,本質上沒有直接引用到定義常量的類,
* 因此不會觸發定義常量的類的初始化。
**/
public class ConstClass {
static {
System.out.println("ConstClass init!");
}
public static final String HELLOWORLD = "hello world";
}
public class Test {
public static void main(String[] args){
System.out.println(ConstClass.HELLOWORLD);
}
}
```
輸出
```
hello world
```
這里沒有初始化ConstClass類,是因為在編譯的時候,常量(static final 修飾的)會存入調用類的常量池【這里說的是main函數所在的類的常量池】,調用的時候本質上沒有引用到定義常量的類,而是直接訪問了自己的常量池
#### 靜態方法調用
當調用目標類的靜態變量或靜態方法時,不會觸發該類的代碼塊或構造方法的執行,示例如下:
```
public class ClassLoadTest {
public static void main(String[] args) {
// System.err.println(Handler.user);
Handler.print();
}
}
class Handler {
public static User user = new User();
static {
System.err.println("static code block");
}
{
System.err.println("code block");
}
public Handler(){
System.err.println("Constructor");
}
public static void print(){
System.err.println("static method");
}
}
class User {
public User() {
System.err.println("user initing===>");
}
}
```
## 參考 {#articleHeader13}
java類的初始化順序--[http://blog.sina.com.cn/s/blog\_4cc16fc50100bjjp.html](http://blog.sina.com.cn/s/blog_4cc16fc50100bjjp.html)
Java類加載的時機 ——[https://blog.csdn.net/imzoer/article/details/8038249](https://blog.csdn.net/imzoer/article/details/8038249)
- java演變
- JDK各個版本的新特性
- JDK1.5新特性
- JDK1.6新特性
- JDK1.7新特性
- JDK1.8新特性
- JAVA基礎
- 面向對象特性
- 多態
- 方法重載
- 方法重寫
- class
- 常量
- 訪問修飾符
- 類加載路徑
- java-equals
- 局部類
- java-hashCode
- Java類初始化順序
- java-clone方法
- JAVA對象實例化的方法
- 基礎部分
- JAVA基礎特性
- JAVA關鍵字
- javabean
- static
- 日期相關
- final
- interface
- 函數式接口
- JAVA異常
- 異常屏蔽
- try-with-resource資源泄露
- JAVA引用
- WeakReference
- SoftReference
- PhantomReference
- 位運算符
- try-with-resource語法糖
- JDK冷知識
- JAVA包裝類
- JAVA基本類型與包裝類
- java.lang.Boolean
- java.lang.Integer
- java.lang.Byte
- java.lang.Short
- java.lang.Long
- java.lang.Float
- java.lang.Double
- java.lang.Character
- 日期相關
- TemporalAdjusters
- String
- 字符串常量池
- String拼接
- String編譯期優化
- StringBuilder&StringBuffer
- intern
- 注解
- java標準注解
- 內置注解
- 元注解
- 自定義注解
- 注解處理器
- JVM注解
- Java8 Annotation新特性
- 反射-Reflective
- Reflection
- Class
- Constructor
- Method
- javabean-property
- MethodHandles
- 泛型
- 類型擦除
- bridge-method
- Accessor&Mutator方法
- enum
- JAVA數組
- finalize方法
- JAR文件
- JAVA高級編程
- CORBA
- JMX
- SPI
- Java SPI使用約定
- ServiceLoader
- 實際應用
- IO
- 工具類
- JDK常用工具類
- Objects
- System
- Optional
- Throwable
- Collections
- Array
- Arrays
- System
- Unsafe
- Number
- ClassLoader
- Runtime
- Object
- Comparator
- VarHandle
- 數據結構
- 棧-Stack
- 隊列(Queue)
- Deque
- PriorityQueue
- BlockingQueue
- SynchronousQueue
- ArrayBlockingQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- ConcurrentLinkedQueue
- 列表
- 迭代器
- KV鍵值對數據類型
- HashMap
- TreeMap
- Hash沖突
- ConcurrentHashMap
- JDK1.7 ConcurrentHashMap結構
- jdk7&jdk8區別
- 集合
- Vector
- Stack
- HashSet
- TreeSet
- ArrayList
- LinkedList
- ArrayList && LinkedList相互轉換
- 線程安全的集合類
- 集合類遍歷性能
- 并發容器
- CopyOnWriteArrayList
- ConcurrentHashMap
- 同步容器
- BitMap
- BloomFilter
- SkipList
- 設計模式
- 設計模式六大原則
- 單例模式
- 代理模式
- 靜態代理
- 動態代理
- JDK動態代理
- cglib動態代理
- spring aop
- 策略模式
- SpringAOP策略模式的運用
- 生產者消費者模式
- 迭代器模式
- 函數式編程
- 方法引用
- 性能問題
- Lambda
- Lambda類型檢查
- Stream
- findFirst和findAny
- reduce
- 原始類型流特化
- 無限流
- 收集器
- 并行流
- AOP
- 靜態織入
- aspect
- aspect的定義
- AspectJ與SpringAOP
- 動態織入
- 靜態代理
- 動態代理
- JDK動態代理
- CGLib動態代理
- Spring AOP
- SpringAOP五種通知類型
- @Before
- @AfterReturning
- @AfterThrowing
- @After
- @Around
- Aspect優先級
- SpringAOP切點表達式
- within
- execution
- 嵌套調用
- 系統優化與重構
- 重疊構造器模式
- 工具類構造器優化
- 常見面試題
- new Object()到底占用幾個字節
- 訪問修飾符
- cloneable接口實現原理
- 異常分類以及處理機制
- wait和sleep的區別
- 數組在內存中如何分配
- 類加載為什么要使用雙親委派模式,有沒有什么場景是打破了這個模式
- 類的實例化順序
- 附錄
- JAVA術語
- FAQ
- 墨菲定律
- 康威定律
- 軟件設計原則
- 阿姆達爾定律
- 字節碼工具
- OSGI