## 1. 類加載過程
類的加載就是將類文件載入內存,并創建對應的Class對象的過程。
類的加載過程一般分為三個階段:**加載階段**、**連接階段**、**初始化階段**

### **1.1 加載階段**
> **加載是類加載過程的一個階段**
1. 通過類的全限定名來獲取類的二進制字節流
2. 將字節流代表的靜態存儲結構轉化為**方法區**的動態運行時結構
3. 在**內存中(堆內存)** 生成java.long.Class對象,作為程序訪問方法區類型數據數據入口
### **1.2 連接階段**
連接階段可以細分為三個階段:
#### **1.2.1 驗證**
> 主要檢查二進制文件的正確性(是否符合當前jvm的規范要求),比如class的版本,class的魔術因子是否正確
驗證階段大致上會完成下面四個階段的檢驗動作:文件格式驗證、元數據驗證、字節
碼驗證和符號引用驗證。
**1. 文件格式驗證** (格式驗證)
第一階段要驗證字節流是否符合Class文件格式的規范,并且能被當前版本的虛擬機處理。這一階段可能包括下面這些驗證點:
* 是否以魔數0xCAFEBABE開頭。
* 主、次版本號是否在當前Java虛擬機接受范圍之內。
* 常量池的常量中是否有不被支持的常量類型(檢查常量tag標志)。
* 指向常量的各種索引值中是否有指向不存在的常量或不符合類型的常量。
* CONSTANT_Utf8_info型的常量中是否有不符合UTF-8編碼的數據。
* Class文件中各個部分及文件本身是否有被刪除的或附加的其他信息。
**2. 元數據驗證**(語法驗證)
第二階段是對字節碼描述的信息進行語義分析,以保證其描述的信息符合《Java語言規范》的要求,這個階段可能包括的驗證點如下:
* 這個類是否有父類(除了java.lang.Object之外,所有的類都應當有父類)。
* 這個類的父類是否繼承了不允許被繼承的類(被final修飾的類)。
* 如果這個類不是抽象類,是否實現了其父類或接口之中要求實現的所有方法。
* 類中的字段、方法是否與父類產生矛盾(例如覆蓋了父類的final字段,或者出現不符合規則的方法重載,例如方法參數都一致,但返回值類型卻不同等)。
**3. 字節碼驗證**(安全驗證)
第三階段是整個驗證過程中最復雜的一個階段,主要目的是通過數據流分析和控制流分析,確定程序語義是合法的、符合邏輯的。在第二階段對元數據信息中的數據類型校驗完畢以后,這階段就要對類的方法體(Class文件中的Code屬性)進行校驗分析,保證被校驗類的方法在運行時不會做出危害虛擬機安全的行為,例如:
保證任意時刻操作數棧的數據類型與指令代碼序列都能配合工作,例如不會出現類似于“在操作棧放置了一個int類型的數據,使用時卻按long類型來加載入本地變量表中”這樣的情況。
保證任何跳轉指令都不會跳轉到方法體以外的字節碼指令上。
保證方法體中的類型轉換總是有效的,例如可以把一個子類對象賦值給父類數據類型,這是安全的,但是把父類對象賦值給子類數據類型,甚至把對象賦值給與它毫無繼承關系、完全不相干的一個數據類型,則是危險和不合法的。
**4. 符號引用驗證**(引用訪問的驗證)
最后一個階段的校驗行為發生在虛擬機將符號引用轉化為直接引用 [3] 的時候,這個轉化動作將在連接的第三階段——解析階段中發生。符號引用驗證可以看作是對類自身以外(常量池中的各種符號引用)的各類信息進行匹配性校驗,通俗來說就是,該類是否缺少或者被禁止訪問它依賴的某些外部類、方法、字段等資源。本階段通常需要校驗下列內容:
* 符號引用中通過字符串描述的全限定名是否能找到對應的類。
* 在指定類中是否存在符合方法的字段描述符及簡單名稱所描述的方法和字段。
* 符號引用中的類、字段、方法的可訪問性(private、protected、public、<package>)是否可被當前類訪問。
符號引用驗證的主要目的是確保解析行為能正常執行,如果無法通過符號引用驗證,Java虛擬機將會拋出一個java.lang.IncompatibleClassChangeError的子類異常,典型的如:
java.lang.IllegalAccessError、java.lang.NoSuchFieldError、java.lang.NoSuchMethodError等。
#### **1.2.2 準備**
> 為類變量(static修飾的靜態變量)分配內存,并為之初始化默認值(不包括實例變量)

正確值是在初始化階段分配的
```
public static int value = 123;
```
準備階段:value = 0;
解析階段 value = 123;正確值
上面提到在“通常情況”下初始值是零值,那言外之意是相對的會有某些“特殊情況”:如果類字段的字段屬性表中存在ConstantValue(final可確定字面量)屬性,那在準備階段變量值就會被初始化為ConstantValue屬性所指定的初始值,假設上面類變量value的定義修改為:
public static final int value = 123;
編譯時Javac將會為value生成ConstantValue屬性,在準備階段虛擬機就會根據Con-stantValue的設置將value賦值為123。
#### **1.2.3 解析**
> 是虛擬機將常量池中的符號引用替換為直接引用的過程
* 符號引用(Symbolic References):
符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。符號引用與虛擬機實現的內存布局無關,引用的目標并不一定是已經加載到虛擬機內存當中的內容。
* 直接引用:
是可以直接指向目標的指針、相對偏移量或者是一個能間接定位到目標的句柄。
### 1.3 初始化階段
> 1. 直到初始化階段,虛擬機才真正執行Java程序的代碼,初始化階段就是執行類**構造器`<clinit>()`方法**的過程,為類的變量賦予正確的初始值(定義類變量指定的值)
> 2. `<clinit>()`方法是由編譯器自動收集類中的所有**類變量的賦值動作**和**靜態語句塊(static{}塊)** 中的語句合并產生的,編譯器收集的順序是由語句在源文件中出現的順序決定的,靜態語句塊中只能訪問到定義在靜態語句塊之前的變量,定義在它之后的變量,在前面的靜態語句塊可以賦值,但是不能訪問
```
public class Test {
static {
i = 0; // 給變量復制可以正常編譯通過
System.out.print(i); // 這句編譯器會提示“非法向前引用”
}
static int i = 1;
}
```
> 3. `<clinit>()`方法與類的構造函數(即在虛擬機視角中的實例構造器`<init>()`方法)不同,它不需要顯式地調用父類構造器,Java虛擬機會保證在子類的<clinit>()方法執行前,父類的<clinit>()方法已經執行完畢。因此在Java虛擬機中第一個被執行的<clinit>()方法的類型肯定是java.lang.Object。
> 4. `<clinit>()`方法對于類或接口來說并不是必需的,如果一個類中沒有靜態語句塊,也沒有對變量的賦值操作,那么編譯器可以不為這個類生成<clinit>()方法。
> 5. java虛擬機保證 `<clinit>()`多線程加鎖同步執行(線程安全-靜態內部類失效單例)
```
static class Parent {
public static int A = 1;
static {
A = 2;
}
}
static class Sub extends Parent {
public static int B = A;
}
public static void main(String[] args) {
System.out.println(Sub.B);
//輸出2,先執行父類的類構造器
}
```

1. 在類的連接階段的準備過程,三個類變量都被初始化的默認值
x=0,y=0,instance=null
2. 類的初始化階段,為類設置正確值
x=0,y=0,instance=new Instance()
這里會先執行Instance的構造方法
得到
x=1,y=1,instance=new Instance()
3. 接著為x,y初始化,x有指定0,y沒有顯示的賦值,所以x被賦值為正確值0,x=0,y=1,y還是1。
**x=0,y=1**
- 計算機網絡
- 基礎_01
- tcp/ip
- http轉https
- Let's Encrypt免費ssl證書(基于haproxy負載)
- what's the http?
- 網關
- 網絡IO
- http
- 工具
- Git
- 初始本地倉庫并上傳
- git保存密碼
- Gitflow
- maven
- 1.生命周期命令
- 聚合與繼承
- 插件管理
- assembly
- 資源管理插件
- 依賴范圍
- 分環境打包
- dependencyManagement
- 版本分類
- 找不到主類
- 無法加載主類
- 私服
- svn
- gradle
- 手動引入第三方jar包
- 打包exe文件
- Windows
- java
- 設計模式
- 七大原則
- 1.開閉原則
- 2. 里式替換原則
- 3. 依賴倒置原則
- 4. 單一職責原則
- 單例模式
- 工廠模式
- 簡單工廠
- 工廠方法模式
- 抽象工廠模式
- 觀察者模式
- 適配器模式
- 建造者模式
- 代理模式
- 適配器模式
- 命令模式
- json
- jackson
- poi
- excel
- easy-poi
- 規則
- 模板
- 合并單元格
- word
- 讀取
- java基礎
- 類路徑與jar
- 訪問控制權限
- 類加載
- 注解
- 異常處理
- String不可變
- 跨域
- transient關鍵字
- 二進制編碼
- 泛型1
- 與或非
- final詳解
- Java -jar
- 正則
- 讀取jar
- map
- map計算
- hashcode計算原理
- 枚舉
- 序列化
- URLClassLoader
- 環境變量和系統變量
- java高級
- java8
- 1.Lambda表達式和函數式接口
- 2.接口的默認方法和靜態方法
- 3.方法引用
- 4.重復注解
- 5.類型推斷
- 6.拓寬注解的應用場景
- java7-自動關閉資源機制
- 泛型
- stream
- 時區的正確理解
- StringJoiner字符串拼接
- 注解
- @RequestParam和@RequestBody的區別
- 多線程
- 概念
- 線程實現方法
- 守護線程
- 線程阻塞
- 筆試題
- 類加載
- FutureTask和Future
- 線程池
- 同步與異步
- 高效簡潔的代碼
- IO
- ThreadLocal
- IO
- NIO
- 圖片操作
- KeyTool生成證書
- 壓縮圖片
- restful
- 分布式session
- app保持session
- ClassLoader.getResources 能搜索到的資源路徑
- java開發規范
- jvm
- 高并發
- netty
- 多線程與多路復用
- 異步與事件驅動
- 五種IO模型
- copy on write
- code style
- 布隆過濾器
- 筆試
- 數據庫
- mybatis
- mybatis與springboot整合配置
- pagehelper
- 分頁數據重復問題
- Java與數據庫之間映射
- 攔截器
- 攔截器應用
- jvm
- 堆內存測試
- 線程棧
- 直接內存
- 內存結構
- 內存模型
- 垃圾回收
- 調優
- 符號引用
- 運行參數
- 方法區
- 分帶回收理論
- 快捷開發
- idea插件
- 注釋模板
- git
- pull沖突
- push沖突
- Excel處理
- 圖片處理
- 合并單元格
- easypoi
- 模板處理
- 響應式編程
- reactor
- reactor基礎
- jingyan
- 規范
- 數據庫