1.并發和并行的區別
你吃飯吃到一半,電話來了,你一直到吃完了以后才去接,這就說明你不支持并發也不支持并行。你吃飯吃到一半,電話來了,你停了下來接了電話,接完后繼續吃飯,這說明你支持并發。你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支持并行。并發的關鍵是你有處理多個任務的能力,不一定要同時。并行的關鍵是你有同時處理多個任務的能力。
`
作者:龔昱陽 Dozer
鏈接:https://www.zhihu.com/question/33515481/answer/58849148
來源:知乎
橋接方法:
當實現具有泛型的類及類中具有表示泛型的方法時,Java虛擬機會自動在字節碼中給該類生成一個用Object代替泛型類的橋接方法,以便于向后兼容。
java中什么是bridge method(橋接方法)
`
虛擬機類加載的過程
虛擬機類加載過程包括加載、驗證、準備、解析、初始化、使用和卸載七個階段。
加載:
加載的過程需要完成進行三個步驟:
①通過類的全限定名獲取定義此類的二進制字節流
②將字節流代表的靜態存儲結構轉化為方法區的運行時數據結構
③在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口
加載過程包括非數組類的加載和數組類的加載
非數組類的加載:執行以上三個加載步驟
數據類的加載:如果數組的組件類型是引用類型,則遞歸進行以上三個步驟。如果不是引用類型,則使用引導類加載器進行加載。
驗證:
包括4個階段的驗證工作:
①文件格式驗證:是否符合Class文件格式的規范,如是否以魔術開頭。
②元數據驗證:對字節碼描述的信息進行語義分析,如類是否有父類。
③字節碼驗證:通過數據流和控制流分析,確認程序語義是合法的、符合邏輯的,如保證跳轉執行不會跳轉到方法體以外的字節碼指令上。
④符號引用驗證:對類自身以外的信息進行匹配性校驗,如符號引用中通過字符串描述的全限定名是否能找到對應的類
準備:
在這個階段正式為類變量分配內存并設置類變量初始值,進行內存分配的變量僅包含類變量(被static修飾的變量),而不包括實例變量。
初始值通常情況下是零值。如果Java代碼中對類變量進行賦值,在準備階段后,類變量的初始值依然是零值。但是,如果該類變量是由final進行修飾的基本數據類型或java.lang.String類型(如public static final String str = "string";),那么該類字段的字段表屬性將存在ConstantValue屬性,在準備階段變量就會被初始化為ConstantValue屬性指定的值。
解析:
將常量池內的符號引用替換為直接引用的過程。符號引用包括以下三類:①類和接口的全限定名。②字段的名稱和描述符。③方法的名稱和描述符。
虛擬機規范在16種操作符號引用的字節碼指令之前(自己去查),會對他們所使用的符號引用進行解析。除了invokedynamic指令,虛擬機實現可以將第一次解析的結果進行緩存。
解析包括類和接口的解析、字段解析、類方法解析和接口方法解析。
初始化:
執行類構造器<clinit>()方法的過程。<clinit>()方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態語句塊中的語句合并產生的。和<init>()方法不同,并不需要顯示地條用父類構造器。虛擬機會保證在子類的<clinit>()方法執行之前,父類的該方法已經執行完畢。如果一個類中沒有靜態語句塊,也沒有對變量的賦值操作,那么編譯器可以不為這個類生成<clinit>方法。
和類不同,接口不能使用靜態語句塊,但存在變量初始化的賦值操作,而且執行接口的<clinit>()方法不需要先執行父接口的該方法,只有當父接口中定義的變量使用時,父接口才會初始化。接口的實現類在初始化時也不會執行接口的<clinit>()方法。
多個線程同時去初始化一個類,只會有一個線程去執行這個類的<clinit>()方法,其他線程會被阻塞。如果執行該方法的線程退出該方法后,其他線程喚醒之后不會再進入該方法。因為在同一個類加載器下,一個類型只會初始化一次。
雙親委派模型
系統提供了三種類加載器:啟動類加載器、擴展類加載器和應用程序類加載器。雙親委派模型要求除了頂層的啟動類加載器外,其他的類加載器都應當有自己的父類加載器。這里類加載器之間的父子關系一般不會繼承的關系來實現,而是都使用組合關系來復用父加載器的代碼。
如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啟動類加載器中,只有當附加在其反饋自己無法完成這個加載請求時,子加載器才會嘗試自己去加載。
多態:
包括繼承、重寫或重載、和向上轉型
面向對象的三個特性:
繼承、封裝和多態
方法調用:
方法調用階段的唯一任務是確定被調用方法的版本,有兩種方式:解析和分派
方法的解析:
調用目標在程序代碼寫好、編譯器進行編譯時就必須確定下來。這類方法的調用被稱為解析。
符合“編譯器可知,運行期不可變”要求的方法,主要包括靜態方法和私有方法兩大類
在Java中提供了5條方法調用字節碼指令,其中invokestatic和invokespecial指令調用的方法,都可以在解析中確定唯一的調用版本,符合這個條件的有靜態方法、私有方法、實力構造器、父類方法4類,它們和final修飾的方法可以稱為非虛方法,與之相反的則是虛方法。
分派:
分派分配靜態分派和動態分派。
Human man = new Man();Human為靜態類型,Man為實際類型
靜態分派:所有依賴靜態類型來定位方法執行版本的分派動作稱為靜態分派。靜態分派的典型應用的方法重載
字面量的強轉過程:char->int->long->float->double->自動裝箱的封裝類型->封裝類型實現的接口類型(Serialiable或者Comparable,兩者同時出現需要自行制定)->父類(如Object)->變長參數
動態分派:在運行期根據實際類型確定方法執行版本的分派動作,典型應用是重寫(@Override)。
單分派和多分派:
宗量:方法的接收者與方法的參數稱為方法的總量
接收者:執行方法的所有者
根據分派基于多少種宗量,可以將分派分為單分派和多分派。靜態分派屬于多分派,動態分派屬于單分派。
Java編譯器的分類
三種:前端編譯器、JIT編譯器和AOT編譯器。
volatile和static的區別: