java類加載過程_java類的加載過程

在這本書里面,在講到類初始化的五種情況時,提及了一個比較有趣的事情。先來看看下面的代碼

public class SubClass {

static{

System.err.println("I m your son");

}

public static final int name = 111;

}

這個時候如果調用SubClass.name,是根本不會觸發SubClass初始化的(這里是因為name是一個常量,和下面的例子不一樣,如果這里把final去掉,是會觸發Subclass的初始化的,因為對于靜態字段而言,如果靜態字段被引用,就會調用getstatic指令和putstatic指令,那么自然就會引發類的初始化,詳情看下面關于觸發類初始化的五種情況)。再來看看另一種情況;

public class SuperClass {

static{

System.err.println("I am your father");

}

public static int value = 123;

}

public class SubClass extends SuperClass{

static{

System.err.println("I m your son");

}

}

這個時候如果調用SubClass.value(靜態字段和靜態方法是可以繼承但是無法被覆蓋,所以這里調用value,只會導致直接定義這個靜態變量的類被初始化),同樣也是不會使得SubClass這個類進行初始化。那么問題來了,到底類在什么時候會進行初始化,類的初始化順序到底是怎樣的?讓我們接著往下看。

一. 類加載的過程

虛擬機加載類主要有五個過程:加載、驗證、準備、解析和初始化。

加載:加載是“類加載”的一個過程,希望讀者沒有混淆這兩個概念。

在這個過程虛擬機主要完成三件事,

? 通過一個類的全限定名___[解釋全限定名]___來獲取此類的二進制字節流,這點上,虛擬機并沒有指明要從哪里獲取類的二進制字節流,因此發展出了很多不一樣的加載方式。比如jar,zip等壓縮包中加載,從網絡獲取[如Applet],或者由其他文件生成[如從JSP生成]。

? 將字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。

? 在Java堆[這個沒有強制規定,比如HotSpot則選擇在方法區中生成這個對象]中生成一個代表這個類的java.lang.Class對象,作為程序訪問方法區中的各種數據的外部入口[也就是說當常量池表中的數據被轉換成運行時數據結構的時候,實際上[堆/方法區]有一個Class對象的實例可以訪問到方法區的各類數據,包括常量池表,代碼等]。

如果加載對象是普通的類或者接口(統稱為C),則是通過類加載器(L)去加載C的二進制表示來創建。但是如果加載的是數組類,那情況就有所不同了,數組類本身不通過類加載器創建,它是由Java虛擬機直接創建的。但是數組類內部的元素類型最終還是要靠類加載器去加載。[后續可以添加類加載器的詳細解釋]

驗證

驗證是鏈接階段的第一步,這一階段的目的是為了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機本身的安全。驗證大致上有以下4個過程:

1) 文件格式驗證:

a) 檢查魔數,主、次版本號是否在當前虛擬機處理范圍。

b) 常量池的常量是否不被支持[通過檢查tag],指向常量的各種索引值中是否有指向不存在的常量或不符合類型的常量。

c) CONSTANT_Utf8_info類型的常量中是否有不符合UTF8編碼的數據。

d) Class文件中各個部分及文件本身是否有被刪除或者附加其他信息等等。

這個節點的主要目的是保證輸入的字節流能被正確的解析并存儲于方法區內,格式上符合描述一個java類型信息的要求。這個階段是基于二進制流,只要通過了這個階段的驗證,字節流才會進入內存的方法區中存儲。所以后續的三個階段基于方法區的存儲結構進行的,不會再直接操作字節流。

2) 元數據驗證:這個階段是對字節碼描述的信息進行語義分析,以保證其描述的信息符合Java語言規范的要求。主要驗證包括以下幾點:

a) 這個類是否有父類(除了java.lang.Object外,所有的類都應該有父類)。

b) 這個類的父類是否繼承了不允許被繼承的類(被final修飾的類)。

c) 如果這個類不是抽象類,那么應該實現其父類或接口中要求實現的方法。

d) 類中的字段,方法是否與父類相矛盾(例如覆蓋了父類的final字段,或者出現不符合規則的方法重載)。

這個階段主要目的是對類的元數據信息進行語義校驗,保證不存在不符合Java規范的元數據信息。

3) 字節碼驗證:

4) 符號引用驗證:

準備

準備階段是正式為類變量分配內存并設置類變量的初始值階段,這些變量所使用的內存都將在方法區中分配。這里有幾個值得注意的點:

1) 這里初始化的僅僅是類變量(被static修飾的變量)的初始化,并不包括實例變量。實例變量將會在對象實例化的時候隨著對象一起分配在java堆中。

2) 這里所說的初始值,通常是數據類型的零值,舉個例子:

public static int value = 123;

這句代碼中,value在準備階段的初始值為0,而不是123,因為這個時候還沒開始執行任何的java方法。而把value的值置為123的putstatic指令是程序被編譯后,存放在類構造器()方法中的。所以value置為123是在初始化[第五階段]階段才會執行。[還有一些其他類型的零值,可以參考虛擬機規范]

當然,上述情況也有例外的地方,如果類字段的字段屬性表(參考class文件中的屬性數據結構)中存在ConstatntValue[即同時被final和static修飾]屬性,那么在準備階段,變量value就會被初始化為ConstantValue屬性所指定的值,例如上述變量中,編譯時javac將會為value生成的ConstantValue屬性,在準備階段虛擬機就會根據ConstantValue屬性而將value賦值為123。

解析

解析階段就是虛擬機將常量池內的符號引用[使用一組描述符來描述所引用的目標,符可以是任意形式的字面量,只要使用時能無歧義的定位到目標即可。符號引用與虛擬機實現的內存布局無關,引用的目標并不一定已經加載到內存中]替換為直接引用[直接引用可以是直接指向目標的指針,相對偏移量或者一個能間接定位到目標的句柄。直接引用與內存的布局有關,如果有了直接引用,則目標一定存在]的過程,符號引用在Class文件內的常量池中以CONSTANT_Fieldref_info,CONSTANT_Class_info,CONSTANT_Methodref_info等類型出現。那么,解析階段中的直接引用于符號引用又有什么關聯呢?

對同一個符號引用進行多次解析請求是很常見的,比如你在代碼里面多次new同一個類。這里要分成兩種情況:

1) invokeddynamic指令:這個指令的特殊之處在于,它是為了支持動態語言而存在的,也就是說,必須等到程序實際運行這條指令的時候,解析動作才能進行[目前僅使用java語言并不會生成這條指令]。相對的,其余觸發的解析指定都是“靜態”的,可以在剛剛完成加載階段,還沒開始執行代碼時就進行解析。

2) 除了上述的指令外,虛擬機實現可以對第一次解析的結果進行緩存(在運行時常量池中記錄直接引用,并把常量標識為已解析狀態)。從而避免了多次解析。

解析動作主要針對“類或接口”,“字段”,“類方法”,“接口方法”,“方法類型”,“方法句柄”和“調用點限定符”7類符號引用進行[分別對應7種常量池表的CONSTATN_Class_info,CONSTATN_Fieldref_info,CONSTATN_Methodref_info,CONSTATN_InterfaceMethodref_info,CONSTATN_MethodType_info,CONSTATN_MethodHandle_info,CONSTATN_InvokeDynamic_info,后續三種和動態類型有關,目前java還是靜態類型語言]。

初始化

在虛擬機中嚴格規定需要對類進行初始化的,有下面五種情況:

1) 遇到new,getstatic,putstatic或者invokestatic這4條字節碼指令時。

2) 使用java.lang.reflect包的方法對類進行反射調用的時候。

3) 當初始化一個類,發現其父類并沒有初始化時,需要先初始化父類。

4) 虛擬機啟動時,用戶需要指定一個要執行的主類(包含main()方法的類),虛擬機會先初始化這個類。

5) 當使用JDK1.7的動態語言支持時,如果一個java.lang.invoke.MethodHandle實例最后的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個方法句柄所對應的類沒有初始化,則需要先觸發其初始化。

對于以上五種初始化場景,虛擬機規范中使用了“只有”,除此之外,所有的引用類的方式都不會觸發初始化。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/533228.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/533228.shtml
英文地址,請注明出處:http://en.pswp.cn/news/533228.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java mvc 導出excel_Java springMVC POI 導出 EXCEL

思路 :將需要導出的數據存放在一個List中創建一個EXCEL表 注意 XSSFWorkbook 只能操作2007以上的版本,XSSFWorkbook 只能操作2003一下的版本,所以需要的時候可以使用 Workbook創建對象處理兼容性遍歷List 并將每條數據 寫入 EXCEL表中具體代碼…

java 排序原理_簡單選擇排序算法原理及java實現(超詳細)

簡單選擇排序的原理簡單選擇排序的原理非常簡單,即在待排序的數列中尋找最大(或者最小)的一個數,與第 1 個元素進行交換,接著在剩余的待排序的數列中繼續找最大(最小)的一個數,與第 2 個元素交換。以此類推,一直到待排…

python對象點方法_python面向對象知識點疏理

面向對象技術簡介類:用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。class類變量:類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體之外。類變量通常不作為實例變量使用。數據成員&#…

ckeditor java 上傳_CKEditor粘貼圖片自動上傳到服務器(Java版)

環境:java,springmvc,ckeditor,tomcat,maven情況:在做項目的時候發現本地圖片粘貼到ckeditor中,img標簽的src中的值是“data:image/png;base64,”開頭的,后面會跟一串字符串,圖片越大字符串越長,這樣的圖片…

java 序列化聲明_顯式聲明默認Java類序列化方法的原因是什么?

我定期看到具有以下結構的Java類:class MyClass implements Serializable {private static final long serialVersionUID 1L;// ...private void writeObject(final java.io.ObjectOutputStream s) throws IOException {s.defaultWriteObject();}private void read…

java 百度poi_Android應用中使用百度地圖API之POI(三)

先看執行后的圖吧:POI(Point of Interest)。中文能夠翻譯為“興趣點”。在地理信息系統中。一個POI能夠是一棟房子、一個商鋪、一個郵筒、一個公交站等 具體:http://developer.baidu.com/map/sdkandev-4.htm主要應用 MKSearch 類:com.baidu.…

java 順序存儲鍵值對_java://Comparator、Comparable的用法(按照要求將map集合的鍵值對進行順序輸出)...

import java.util.*;public class Person implements Comparable//使Person的屬性具有比較性{private String name;private int age;public Person(String name,int age)//初始化構造函數{this.name name;this.age age;}public void set(String name,int age)//重新設置姓名和…

java日歷事件處理_日歷表的事件處理和管理(劉靜)

1 //添加gridview,顯示具體的日期2 SuppressLint("ResourceAsColor")3 private voidaddGridView() {4 LinearLayout.LayoutParams params newLinearLayout.LayoutParams(5 LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);6 //取得屏幕的寬度和高度7 WindowMa…

java第四次上機作業_第十java上機作業

第十java上機作業 第七章上級作業 7.1 public class Rectangle { double width1; double height1; String color“white“; public Rectangle(){} public Rectangle(double width,double height,String color) { this.widthwidth; this.heightheight; this.colorcolor; } publi…

java解析帶斜杠的參數_Java Spring MVC應用程序僅接受帶有斜杠的POST請求

war文件名是forms.war。 web.xml中的url模式為"/"控制器操作的RequestMapping為"/"如果遇到localhost:8080 /表單,則RequestMethod.GET操作正常工作如果針對localhost:8080/forms點擊發布數據,則不會觸發RequestMethod.P…

java文件編碼格式環境變量_Jenkins maven 構建亂碼,修改file.encoding系統變量編碼為UTF-8...

一切都是windows的控制臺默認編碼GBK問題情景:使用jenkins構建,console 輸出的中文亂碼。代碼編碼格式是utf-8,因為Jenkins會默認讀取當前系統的編碼格式,導致構建日志亂碼和selenium自動化測試輸入的中文亂碼。控制臺輸出亂碼摸索…

http multipart java_Http MultiPart請求

我正在嘗試使用httpClient庫上傳圖像(多部分/表單數據) . 我可以使用httpPost方法和byteArrayRequestEntity上傳圖像 . 以下是我使用的代碼:File file new File(imageFilePath);HttpClient client new HttpClient();PostMethod method new PostMethod("http…

java分布式會話redis_詳解springboot中redis的使用和分布式session共享問題

對于分布式使用NginxTomcat實現負載均衡,最常用的均衡算法有IP_Hash、輪訓、根據權重、隨機等。不管對于哪一種負載均衡算法,由于Nginx對不同的請求分發到某一個Tomcat,Tomcat在運行的時候分別是不同的容器里,因此會出現session不…

python3.8使用方法_python3.8新特性

python3.8新特性Python3.8穩定版已發布,官網發布了一篇介紹新特性的文章,在此記錄一下。新增賦值操作符:作用:把較大表達式中的某部分值賦給變量,因為看起來像海象的眼睛和牙齒,: 操作符有個特別的名字:海象操作符范例…

java責任鏈設計模式 訂單_Java責任鏈設計模式實例分析

本文實例講述了Java責任鏈設計模式。分享給大家供大家參考,具體如下:一 代碼abstract class AbstractHandler {private Handler Handler null;// 持有責任鏈中下一個責任處理者的引用public void setHandler( Handler handler ) {this.Handler handler…

java map去重復的數據_使用HashMap去重復數據.

因為HashMap是鍵值對形式,所以一個鍵只對應一個Value,利用這個原理,我們就可以根據某列重復數據做鍵對重復數據進行處理啦~首先先看看我要處理 的數據然后我最后想要的結果:需求:根據groupName每個分組信息只顯示一條數…

centos6.5 MySQL 服務器_啟用CentOS6.5 64位安裝時自帶的MySQL數據庫服務器

本人在虛擬機上又安裝了一臺linux機器,作為MySQL數據庫服務器用,在安裝時選擇了系統自帶的MySQL服務器端,以下是啟用步驟。首先開啟mysqld服務#service mysqld start進入/usr/bin目錄#cd /usr/bin設定mysql數據庫root用戶的密碼#mysqladmin -…

java xml date_W3C XML 模式時間數據類型與java Date進行轉換

W3C XML 模式時間數據格式:"2014-11-17T 09:40:25" 或者 “2014-11-17T 09:40:2508:00”要想把以上格式的時間轉換為能被java所使用個格式有以下3種辦法:(1)、jdk中有這么一個類XMLGregorianCalendar,使用XMLGregorianCalendar類中的toGreGreg…

PHP stomp 連接判斷,php實現通過stomp協議連接ActiveMQ操作示例

本文實例講述了php實現通過stomp協議連接ActiveMQ操作。分享給大家供大家參考,具體如下:前面介紹了php ActiveMQ的安裝與使用,這里再來講述一下php通過stomp協議連接ActiveMQ。一、安裝php的stomp擴展http://pecl.php.net/package/stomp如&am…

authinfo.php,【nginxphp】后臺權限認證方式

一、最常用的方法(代碼中限制)1、如何限制IPfunction get_new_ip(){if(getenv(HTTP_CLIENT_IP)) {$onlineip getenv(HTTP_CLIENT_IP);} elseif(getenv(HTTP_X_FORWARDED_FOR)) {$onlineip getenv(HTTP_X_FORWARDED_FOR);} elseif(getenv(REMOTE_ADDR)) {$onlineip getenv(RE…