類加載及類加載器概述

聲明:該文章內(nèi)容摘自于網(wǎng)絡(luò)及《深入理解Java虛擬機(jī)》,本人只是進(jìn)行了內(nèi)容合并,并非個(gè)人文章,只為知識(shí)共享,內(nèi)容有不正確的地方還請(qǐng)大神大牛們指出。

一類加載及加載器概述

????????java類的加載是由虛擬機(jī)來(lái)完成的,虛擬機(jī)把描述類的Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),解析和初始化,最終形成能被java虛擬機(jī)直接使用的java類型,這就是虛擬機(jī)的類加載機(jī)制。

????????JVM中用來(lái)完成上述功能的具體實(shí)現(xiàn)就是類加載器。類加載器讀取.class字節(jié)碼文件將其轉(zhuǎn)換成java.lang.Class類的一個(gè)實(shí)例,每個(gè)實(shí)例用來(lái)表示一個(gè)java類,通過(guò)該實(shí)例的newInstance()方法可以創(chuàng)建出一個(gè)該類的對(duì)象。

二類加載過(guò)程


類加載過(guò)程詳述:

????????類從被加載到虛擬機(jī)內(nèi)存中開始,到卸載出內(nèi)存為止,它的整個(gè)生命周期包括:

????????加載(Loading)、驗(yàn)證(Verification)、準(zhǔn)備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)7個(gè)階段。其中準(zhǔn)備、驗(yàn)證、解析3個(gè)部分統(tǒng)稱為連接(Linking)。

????????加載、驗(yàn)證、準(zhǔn)備、初始化和卸載這5個(gè)階段的順序是確定的,類的加載過(guò)程必須按照這種順序按部就班地開始,而解析階段則不一定:它在某些情況下可以在初始化階段之后再開始,這是為了支持Java語(yǔ)言的運(yùn)行時(shí)綁定(也稱為動(dòng)態(tài)綁定或晚期綁定)。

加載(裝載):查找和導(dǎo)入Class文件

常見類加載時(shí)機(jī):

????????1、new 2、java.lang.reflect反射 3、加載父類 4、加載主類(main)?5、獲取運(yùn)行時(shí)常量或者調(diào)用靜態(tài)方法

在加載階段(可以參考java.lang.ClassLoader的loadClass()方法),虛擬機(jī)需要完成以下3件事情:

? ? ? ? (1)通過(guò)一個(gè)類的全限定名來(lái)獲取定義此類的二進(jìn)制字節(jié)流(并沒有指明要從一個(gè)Class文件中獲取,可以從其他渠道,譬如:網(wǎng)絡(luò)、動(dòng)態(tài)生成、數(shù)據(jù)庫(kù)等);

? ? ? ? (2)將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu);

? ? ? ? (3)在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問(wèn)入口;

????????加載階段和連接階段(Linking)的部分內(nèi)容(如一部分字節(jié)碼文件格式驗(yàn)證動(dòng)作)是交叉進(jìn)行的,加載階段尚未完成,連接階段可能已經(jīng)開始,但這些夾在加載階段之中進(jìn)行的動(dòng)作,仍然屬于連接階段的內(nèi)容,這兩個(gè)階段的開始時(shí)間仍然保持著固定的先后順序。

????????方法區(qū)和堆都是各個(gè)線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)已經(jīng)被JVM加載的類信息、常量、靜態(tài)變量、JIT編譯器編譯后的代碼等數(shù)據(jù);程序中的字面量(literal)如直接書寫的100、"hello"和常量都是放在常量池中,常量池是方法區(qū)的一部分。

????????String str = new String("hello");

????????上面的語(yǔ)句中變量str放在棧上,用new創(chuàng)建出來(lái)的字符串對(duì)象放在堆上,而"hello"這個(gè)字面量是放在方法區(qū)的。

鏈接

????????鏈接階段要做的是將加載到JVM中的二進(jìn)制字節(jié)流的類數(shù)據(jù)信息合并到JVM的運(yùn)行時(shí)狀態(tài)中,經(jīng)由驗(yàn)證、準(zhǔn)備和解析三個(gè)階段。

驗(yàn)證:檢查載入Class文件數(shù)據(jù)的正確性

????????驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。

驗(yàn)證階段大致會(huì)完成4個(gè)階段的檢驗(yàn)動(dòng)作:

? ??????文件格式驗(yàn)證:驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范;例如:是否以魔術(shù)0xCAFEBABE開頭、主次版本號(hào)是否在當(dāng)前虛擬機(jī)的處理范圍之內(nèi)、常量池中的常量是否有不被支持的類型。

? ??????元數(shù)據(jù)驗(yàn)證:對(duì)字節(jié)碼描述的信息進(jìn)行語(yǔ)義分析(注意:對(duì)比javac編譯階段的語(yǔ)義分析),以保證其描述的信息符合Java語(yǔ)言規(guī)范的要求;例如:這個(gè)類是否有父類,除了java.lang.Object之外。

? ??????字節(jié)碼驗(yàn)證:通過(guò)數(shù)據(jù)流和控制流分析,確定程序語(yǔ)義是合法的、符合邏輯的。

? ??????符號(hào)引用驗(yàn)證:確保解析動(dòng)作能正確執(zhí)行。

????????驗(yàn)證階段是非常重要的,但不是必須的,它對(duì)程序運(yùn)行期沒有影響,如果所引用的類經(jīng)過(guò)反復(fù)驗(yàn)證,那么可以考慮采用-Xverifynone參數(shù)來(lái)關(guān)閉大部分的類驗(yàn)證措施,以縮短虛擬機(jī)類加載的時(shí)間。

準(zhǔn)備:給類的靜態(tài)變量分配存儲(chǔ)空間

????????準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。這時(shí)候進(jìn)行內(nèi)存分配的僅包括類變量(被static修飾的變量),而不包括實(shí)例變量,實(shí)例變量將會(huì)在對(duì)象實(shí)例化時(shí)隨著對(duì)象一起分配在堆中。

????????其次,這里所說(shuō)的初始值“通常情況”下是數(shù)據(jù)類型的零值,假設(shè)一個(gè)類變量的定義為: Public static int?value=123; ???????

????????那變量value在準(zhǔn)備階段過(guò)后的初始值為0而不是123.因?yàn)檫@時(shí)候尚未開始執(zhí)行任何java方法,而把value賦值為123的putstatic指令是程序被編譯后,存放于類構(gòu)造器()方法之中,所以把value賦值為123的動(dòng)作將在初始化階段才會(huì)執(zhí)行。

????????至于“特殊情況”是指:public static final int value=123,即當(dāng)類字段的字段屬性是ConstantValue時(shí),會(huì)在準(zhǔn)備階段初始化為指定的值,所以標(biāo)注為final之后,value的值在準(zhǔn)備階段初始化為123而非0。

解析:將符號(hào)引用轉(zhuǎn)成直接引用

????????解析階段是虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過(guò)程。解析動(dòng)作主要針對(duì)類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符7類符號(hào)引用進(jìn)行。

初始化:對(duì)類的靜態(tài)變量,靜態(tài)代碼塊執(zhí)行初始化操作

????????類初始化階段是類加載過(guò)程的最后一步,到了初始化階段,才真正開始執(zhí)行類中定義的java程序代碼。在準(zhǔn)備階段,變量已經(jīng)付過(guò)一次系統(tǒng)要求的初始值,而在初始化階段,則根據(jù)程序猿通過(guò)程序制定的主管計(jì)劃去初始化類變量和其他資源,或者說(shuō):

????????初始化階段是執(zhí)行類構(gòu)造器()方法的過(guò)程.()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊static{}中的語(yǔ)句合并產(chǎn)生的,編譯器收集的順序是由語(yǔ)句在源文件中出現(xiàn)的順序所決定的,靜態(tài)語(yǔ)句塊只能訪問(wèn)到定義在靜態(tài)語(yǔ)句塊之前的變量,定義在它之后的變量,在前面的靜態(tài)語(yǔ)句塊可以賦值,但是不能訪問(wèn)。

????????Java保證了一個(gè)對(duì)象被初始化前其父類也必須被初始化:Java強(qiáng)制要求任何類的構(gòu)造函數(shù)中的第一句必須是調(diào)用父類構(gòu)造函數(shù)或者是類中定義的其他構(gòu)造函數(shù)。如果沒有構(gòu)造函數(shù),系統(tǒng)添加默認(rèn)的無(wú)參構(gòu)造函數(shù),如果我們的構(gòu)造函數(shù)中沒有顯示的調(diào)用父類的構(gòu)造函數(shù),那么編譯器自動(dòng)生成一個(gè)父類的無(wú)參構(gòu)造函數(shù)。


三類加載器

Java 有以下兩個(gè)重要的設(shè)計(jì)特性:

????????[if !supportLists]1、[endif]Java 是與平臺(tái)無(wú)關(guān)的;

????????[if !supportLists]2、[endif]Java 是建立在分布式網(wǎng)絡(luò)中的架構(gòu)。

????????為了實(shí)現(xiàn)這兩個(gè)目標(biāo),Java 必須對(duì)如何加載代碼庫(kù)進(jìn)行獨(dú)特設(shè)計(jì)。要實(shí)現(xiàn)與平臺(tái)無(wú)關(guān)性,那么Java 就不能依靠文件系統(tǒng)來(lái)加載自己的類庫(kù),因?yàn)橛行┣度胧较到y(tǒng)甚至沒有自己的文件系統(tǒng)。由于其分布式特性,Java 需要設(shè)計(jì)為從網(wǎng)絡(luò)地址中加載各種代碼類,從文件系統(tǒng)中加載類將不能夠工作。為了解決這個(gè)問(wèn)題,Java 架構(gòu)引入了類加載器的概念。類加載器的作用是從網(wǎng)絡(luò)或硬盤中加載類,不依賴于操作系統(tǒng)。

????????在JVM當(dāng)中預(yù)定義了三種類型的類加載器:啟動(dòng)類加載器Bootstrap類加載器擴(kuò)展類加載器Extension類加載器,系統(tǒng)類加載器(System類加載器)。每個(gè)加載器其實(shí)就是個(gè)類的對(duì)象。另外還有線程上下文類加載器用戶自定義類加載器。

3.1 啟動(dòng)類加載器(JVM的各提供商使用本地代碼來(lái)實(shí)現(xiàn)Bootstrap類加載器)

????????啟動(dòng)類加載器(BootstrapClassLoader)引導(dǎo)類裝入器是用本地代碼實(shí)現(xiàn)的類裝入器,它負(fù)責(zé)將 /lib下面的核心類庫(kù)或-Xbootclasspath選項(xiàng)指定的jar包加載到內(nèi)存中。由于引導(dǎo)類加載器涉及到虛擬機(jī)本地實(shí)現(xiàn)細(xì)節(jié),開發(fā)者無(wú)法直接獲取到啟動(dòng)類加載器的引用,所以不允許直接通過(guò)引用進(jìn)行操作。

????????啟動(dòng)類加載器是最低層的加載器,它是由C++編寫的,因?yàn)榧虞d器是一個(gè)類,也需要通過(guò)類加載器加載,啟動(dòng)類加載器就能完成這個(gè)功能。

3.2 擴(kuò)展類加載器

????????擴(kuò)展類加載器(ExtensionClassLoader)是由ExtClassLoader類實(shí)現(xiàn)的。它負(fù)責(zé)將< Java_Runtime_Home >/lib/ext或者由系統(tǒng)變量-Djava.ext.dir指定位置中的類庫(kù)加載到內(nèi)存中。開發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器。

3.3 系統(tǒng)類加載器

????????系統(tǒng)類加載器(SystemClassLoader)是由AppClassLoader類實(shí)現(xiàn)。它負(fù)責(zé)將系統(tǒng)類路徑j(luò)ava -classpath或-Djava.class.path變量所指的目錄下的類庫(kù)

????????加載到內(nèi)存中。還用于加載入口函數(shù)類(僅含有main()方法的類)。開發(fā)者可以直接使用系統(tǒng)類加載器。

四雙親委派機(jī)制

4.1 定義

????????JVM加載類時(shí)默認(rèn)采用雙親委派?機(jī)制。

????????通俗的講就是,當(dāng)某個(gè)類加載器收到加載類的請(qǐng)求時(shí),首先會(huì)將加載任務(wù)任務(wù)委托給加載器的父類,然后父類再委托給父類的父類,以此類推。如果父類成功可以成功加載,就返回成功,如果父類加載器無(wú)法完成任務(wù)時(shí),才會(huì)自己加載。

????????具體的講,當(dāng)在應(yīng)用程序中加載一個(gè)類時(shí),JVM 自動(dòng)請(qǐng)求System 類加載器,System 類加載器又會(huì)請(qǐng)求Extension類加載器來(lái)加載,Extension類加載器又會(huì)請(qǐng)求Bootstrap類加載器來(lái)加載。如果Bootstrap類加載器在核心庫(kù)中不能查找到請(qǐng)求的類,則返回請(qǐng)求給Extension類加載器,由Extension 類加載器在自己的路徑中查找,如果仍然找不到,則返回請(qǐng)求給System 類加載器,如果System 類加載器在CLASSPATH 中不能找到該類,則返回ClassNotFoundException異常。new String() ??和 ?new MyObject() 的區(qū)別

下圖展示了雙親委派機(jī)制的運(yùn)行邏輯。


4.2 實(shí)現(xiàn)

ExtClassLoader和AppClassLoader都是繼承于java.lang.ClassLoader類,ClassLoader類中有個(gè)loadClass方法來(lái)實(shí)現(xiàn)雙親委派,ExtClassLoader和AppClassLoader都沒有重寫這個(gè)方法(JDK1.8)

????protected Class loadClass(String name, boolean resolve)

????????throws ClassNotFoundException

????{

????????synchronized (getClassLoadingLock(name)) {

????????????// 首先獲得當(dāng)前類的類加載器,返回加載器或null

????????????Class c = findLoadedClass(name);

????????????if (c == null) {

????????????????// 如果當(dāng)前類沒有被加載,就委托給父類加載

????????????????long t0 = System.nanoTime();

????????????????try {

????????????????????if (parent != null) {

????????????????????????// 如果存在父類加載器,就委派給父類加載器加載

????????????????????????c = parent.loadClass(name, false);

????????????????????} else {

???????????????????????// 如果父類加載器不存在,就檢查是否有啟動(dòng)類加載器加載的類

?????????????????????// 通過(guò)調(diào)用本地方法native findBootstrapClass0(String name)

????????????????????????c = findBootstrapClassOrNull(name);

????????????????????}

????????????????} catch (ClassNotFoundException e) {

????????????????}

????????????????if (c == null) {

????????????????????// 如果調(diào)用父類加載后,未能成功加載,就自己加載

????????????????????long t1 = System.nanoTime();

????????????????????c = findClass(name);


?????????????????// this is the defining class loader; record the stats

????????????????sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

????????sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

????????????????????sun.misc.PerfCounter.getFindClasses().increment();

????????????????}

????????????}

????????????if (resolve) {

????????????????resolveClass(c);

????????????}

????????????return c;

????????}

????}

下面我們來(lái)看看ExtClassLoader和AppClassLoader的父類加載器是誰(shuí)。

????public static void main(String[] args) { ?

????????try { ?

????????????System.out.println(ClassLoader.getSystemClassLoader()); ?

????????????System.out.println(ClassLoader.getSystemClassLoader().getParent()); ?

????????????System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent()); ?

????????} catch (Exception e) { ?

????????????e.printStackTrace(); ?

????????} ?

????} ?

輸出結(jié)果:

sun.misc.Launcher$AppClassLoader@6d06d69c ?

sun.misc.Launcher$ExtClassLoader@70dea4e ?null

????????說(shuō)明AppClassLoader的父加載器是ExtClassLoader,ExtClassLoader的父加載器是空,再結(jié)合上面loadClass()方法的源碼,當(dāng)父加載器為null時(shí)調(diào)用本地方法native findBootstrapClass0(String name) ,也就是調(diào)用BootstrapClassLoader加載器

?初始化

????????Java在編譯之后會(huì)在字節(jié)碼文件中生成方法,稱之為實(shí)例構(gòu)造器,該實(shí)例構(gòu)造器會(huì)將語(yǔ)句塊,變量初始化,調(diào)用父類的構(gòu)造器等操作收斂到方法中,收斂順序(這里只討論非靜態(tài)變量和語(yǔ)句塊)為:1. 父類變量初始化?2. 父類語(yǔ)句塊?3. 父類構(gòu)造函數(shù)?4. 子類變量初始化?5. 子類語(yǔ)句塊?6. 子類構(gòu)造函數(shù)。所謂收斂到方法中的意思就是,將這些操作放入到中去執(zhí)行。

?????????Java在編譯之后會(huì)在字節(jié)碼文件中生成方法,稱之為類構(gòu)造器,類構(gòu)造器同實(shí)例構(gòu)造器一樣,也會(huì)將靜態(tài)語(yǔ)句塊,靜態(tài)變量初始化,收斂到方法中,收斂順序?yàn)椋?. 父類靜態(tài)變量初始化?2. 父類靜態(tài)語(yǔ)句塊?3. 子類靜態(tài)變量初始化?4. 子類靜態(tài)語(yǔ)句塊

????????方法是在類加載過(guò)程中執(zhí)行的,而是在對(duì)象實(shí)例化執(zhí)行的,所以一定比先執(zhí)行。所以整個(gè)順序就是:1. 父類靜態(tài)變量初始化?2. 父類靜態(tài)語(yǔ)句塊?3. 子類靜態(tài)變量初始化?4. 子類靜態(tài)語(yǔ)句塊?5. 父類變量初始化?6. 父類語(yǔ)句塊?7. 父類構(gòu)造函數(shù)?8. 子類變量初始化?9. 子類語(yǔ)句塊?10. 子類構(gòu)造函數(shù)

?答疑

1、初始化順序

如下父類代碼

public?class?Parent {

????static?int?a = 1;

????int?b = 1;

????static?{

????????System.out.println("parent static block(a):"?+ (++a));

????}

????{

????????System.out.println("parent ?block(b):"?+ (++b));

????}

????public?Parent() {

????????System.out.println("parent construction");

????}

}

子類代碼

public?class Child extends Parent {

????static?int?a = 1;

????int?b = 1;

????static?{

????????System.out.println("child static block(a):"?+ (++a));

????}

????{

????????System.out.println("child ?block(b):"?+ (++b));

????}

????public?Child() {

????????System.out.println("child construction");

????}

????public?static?void?main(String[] args) {

????????new?Child();

????}

}

最終輸出結(jié)果為

parent static?block(a):2

child static?block(a):2

parent ?block(b):2

parent construction

child ?block(b):2

child construction

2、是否可以從一個(gè)靜態(tài)(static)方法內(nèi)部發(fā)出對(duì)非靜態(tài)(non-static)方法的調(diào)用?

不可以,靜態(tài)方法只能訪問(wèn)靜態(tài)成員,因?yàn)榉庆o態(tài)方法的調(diào)用要先創(chuàng)建對(duì)象,在調(diào)用靜態(tài)方法時(shí)可能對(duì)象并沒有被初始化。

七 Java代碼編譯過(guò)程簡(jiǎn)述

代碼編譯是由Javac編譯器來(lái)完成,流程如下圖1所示:


????????*.java文件經(jīng)過(guò)javac *.java編譯生成*.class文件,java *.class運(yùn)行。

????????Javac是一種編譯器,能將一種語(yǔ)言規(guī)范轉(zhuǎn)化成另外一種語(yǔ)言規(guī)范,通常編譯器都是將便于人理解的語(yǔ)言規(guī)范轉(zhuǎn)化成機(jī)器容易理解的語(yǔ)言規(guī)范。Javac的任務(wù)就是將Java源代碼編譯成Java字節(jié)碼語(yǔ)言,轉(zhuǎn)化為JVM能夠識(shí)別的一種語(yǔ)言,也就是二進(jìn)制代碼,然后由JVM將JVM語(yǔ)言再轉(zhuǎn)化成當(dāng)前這個(gè)機(jī)器能夠識(shí)別的機(jī)器語(yǔ)言。

????????從表面看是將.java文件轉(zhuǎn)化為.class文件。而實(shí)際上是將Java源代碼轉(zhuǎn)化成一連串二進(jìn)制數(shù)字,這些二進(jìn)制數(shù)字是有格式的,只有JVM能夠真確的識(shí)別他們到底代表什么意思。

編譯器把一種語(yǔ)言規(guī)范轉(zhuǎn)化為另一種語(yǔ)言規(guī)范的這個(gè)過(guò)程如下:

????????1)詞法分析:讀取源代碼,一個(gè)字節(jié)一個(gè)字節(jié)的讀進(jìn)來(lái),找出這些詞法中我們定義的語(yǔ)言關(guān)鍵詞如:if、else、while等,識(shí)別哪些if是合法的哪些是不合法的。這個(gè)步驟就是詞法分析過(guò)程。

詞法分析的結(jié)果:就是從源代碼中找出了一些規(guī)范化的token流,就像人類語(yǔ)言中,給你一句話你要分辨出哪些是一個(gè)詞語(yǔ),哪些是標(biāo)點(diǎn)符號(hào),哪些是動(dòng)詞,哪些是名詞。

????????2)語(yǔ)法分析:就是對(duì)詞法分析中得到的token流進(jìn)行語(yǔ)法分析,這一步就是檢查這些關(guān)鍵詞組合在一起是不是符合Java語(yǔ)言規(guī)范。如if的后面是不是緊跟著一個(gè)布爾型判斷表達(dá)式。

語(yǔ)法分析的結(jié)果:就是形成一個(gè)符合Java語(yǔ)言規(guī)定的抽象語(yǔ)法樹,抽象語(yǔ)法樹是一個(gè)結(jié)構(gòu)化的語(yǔ)法表達(dá)形式,它的作用是把語(yǔ)言的主要詞法用一個(gè)結(jié)構(gòu)化的形式組織在一起。這棵語(yǔ)法樹可以被后面按照新的規(guī)則再重新組織。

????????3)語(yǔ)義分析:語(yǔ)法分析完成之后也就不存在語(yǔ)法問(wèn)題了,語(yǔ)義分析的主要工作就是把一些難懂的,復(fù)雜的語(yǔ)法轉(zhuǎn)化成更簡(jiǎn)單的語(yǔ)法。就如難懂的文言文轉(zhuǎn)化為大家都懂的白話文,或者是注釋一下一些不懂的成語(yǔ)。

語(yǔ)義分析結(jié)果:就是將復(fù)雜的語(yǔ)法轉(zhuǎn)化為簡(jiǎn)單的語(yǔ)法,對(duì)應(yīng)到Java就是將foreach轉(zhuǎn)化為for循環(huán),還有一些注釋等。最后生成一棵抽象的語(yǔ)法樹,這棵語(yǔ)法樹也就更接近目標(biāo)語(yǔ)言的語(yǔ)法規(guī)則。

? ? ? ? 4)字節(jié)碼生成:將會(huì)根據(jù)經(jīng)過(guò)注釋的抽象語(yǔ)法樹生成字節(jié)碼,也就是將一個(gè)數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化為另外一個(gè)數(shù)據(jù)結(jié)構(gòu)。就像將所有的中文詞語(yǔ)翻譯成英文單詞后按照英文語(yǔ)法組文英文語(yǔ)句。代碼生成器的結(jié)果就是生成符合java虛擬機(jī)規(guī)范的字節(jié)碼。

????????常量是程序運(yùn)行時(shí)恒定不變的量,許多程序設(shè)計(jì)語(yǔ)言都有某種方法,向編譯器告知一塊數(shù)據(jù)時(shí)恒定不變的,例如C++中的const和Java中的final。根據(jù)編譯器的不同行為,常量又分為編譯時(shí)常量和運(yùn)行時(shí)常量,其實(shí)編譯時(shí)常量肯定就是運(yùn)行時(shí)常量,只是編譯時(shí)常量在編譯的時(shí)候就被計(jì)算執(zhí)行計(jì)算,并帶入到程序中一切可能用到它的計(jì)算式中。

????????以Java為例,static final int a = 1將是一個(gè)編譯時(shí)常量,編譯后的符號(hào)表中將找不到a,所有對(duì)a的引用都被替換成了1。而static final int b = "test".length()將是一個(gè)運(yùn)行時(shí)常量。

(1)a被作為編譯期全局常量,并不依賴于類,而b作為運(yùn)行期的全局常量,其值還是依 賴于類的。

(2)編譯時(shí)常量在編譯時(shí)就可以確定值,上例中的a可以確定值,但是c在編譯器是不可 能確定值的。

(3)由于編譯時(shí)常量不依賴于類,所以對(duì)編譯時(shí)常量的訪問(wèn)不會(huì)引發(fā)類的初始化。而運(yùn)行時(shí)常量訪問(wèn)會(huì)引發(fā)類的初始化。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容