21、描述一下JVM 加載class文件的原理機(jī)制?
JVM 中類的裝載是由類加載器(ClassLoader) 和它的子類來(lái)實(shí)現(xiàn)的,Java中的類加載器是一個(gè)重要的Java 運(yùn)行時(shí)系統(tǒng)組件,它負(fù)責(zé)在運(yùn)行時(shí)查找和裝入類文件中的類。
1.由于Java的跨平臺(tái)性,經(jīng)過(guò)編譯的Java源程序并不是一個(gè)可執(zhí)行程序,而是一個(gè)或多個(gè)類文件。當(dāng)Java程序需要使用某個(gè)類時(shí),JVM會(huì)確保這個(gè)類已經(jīng)被加載、連接(驗(yàn)證、準(zhǔn)備和解析)和初始化。類的加載是指把類的.class文件中的數(shù)據(jù)讀入到內(nèi)存中,通常是創(chuàng)建一個(gè)字節(jié)數(shù)組讀入.class文件,然后產(chǎn)生與所加載類對(duì)應(yīng)的Class對(duì)象。加載完成后,Class對(duì)象還不完整,所以此時(shí)的類還不可用。當(dāng)類被加載后就進(jìn)入連接階段,這一階段包括驗(yàn)證、準(zhǔn)備(為靜態(tài)變量分配內(nèi)存并設(shè)置默認(rèn)的初始值)和解析(將符號(hào)引用替換為直接引用)三個(gè)步驟。最后JVM對(duì)類進(jìn)行初始化,包括:1如果類存在直接的父類并且這個(gè)類還沒(méi)有被初始化,那么就先初始化父類;2如果類中存在初始化語(yǔ)句,就依次執(zhí)行這些初始化語(yǔ)句。
2.類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴(kuò)展加載器(Extension)、系統(tǒng)加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。從JDK 1.2開(kāi)始,類加載過(guò)程采取了父親委托機(jī)制(PDM)。PDM更好的保證了Java平臺(tái)的安全性,在該機(jī)制中,JVM自帶的Bootstrap是根加載器,其他的加載器都有且僅有一個(gè)父類加載器。類的加載首先請(qǐng)求父類加載器加載,父類加載器無(wú)能為力時(shí)才由其子類加載器自行加載。JVM不會(huì)向Java程序提供對(duì)Bootstrap的引用。下面是關(guān)于幾個(gè)類加載器的說(shuō)明:
a)Bootstrap:一般用本地代碼實(shí)現(xiàn),負(fù)責(zé)加載JVM基礎(chǔ)核心類庫(kù)(rt.jar);
b)Extension:從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類庫(kù),它的父加載器是Bootstrap;
c)System:又叫應(yīng)用類加載器,其父類是Extension。它是應(yīng)用最廣泛的類加載器。它從環(huán)境變量classpath或者系統(tǒng)屬性java.class.path所指定的目錄中記載類,是用戶自定義加載器的默認(rèn)父加載器。
22、char 型變量中能不能存貯一個(gè)中文漢字?為什么?
char類型可以存儲(chǔ)一個(gè)中文漢字,因?yàn)镴ava中使用的編碼是Unicode(不選擇任何特定的編碼,直接使用字符在字符集中的編號(hào),這是統(tǒng)一的唯一方法),一個(gè)char類型占2個(gè)字節(jié)(16bit),所以放一個(gè)中文是沒(méi)問(wèn)題的。
注:使用Unicode意味著字符在JVM內(nèi)部和外部有不同的表現(xiàn)形式,在JVM內(nèi)部都是Unicode,當(dāng)這個(gè)字符被從JVM內(nèi)部轉(zhuǎn)移到外部時(shí)(例如存入文件系統(tǒng)中),需要進(jìn)行編碼轉(zhuǎn)換。所以Java中有字節(jié)流和字符流,以及在字符流和字節(jié)流之間進(jìn)行轉(zhuǎn)換的轉(zhuǎn)換流,如InputStreamReader和OutputStreamReader,這兩個(gè)類是字節(jié)流和字符流之間的適配器類,承擔(dān)了編碼轉(zhuǎn)換的任務(wù);
23、抽象類(abstract class)和接口(interface)有什么異同?
1)、抽象類可以有構(gòu)造方法,接口中不能有構(gòu)造方法。(雖然抽象類有構(gòu)造方法,但它也不能被實(shí)例化)
2)、抽象類中可以有普通成員變量,接口中沒(méi)有普通成員變量。
3)、抽象類和接口中都可以包含靜態(tài)成員變量。抽象類中的靜態(tài)成員變量的訪問(wèn)類型可以是任意類型,但接口中定義的變量只能是public static final,并且默認(rèn)為:public staic final類型。(接口畢竟要被子類實(shí)現(xiàn),所以成員變量必須是public,如果是其他訪問(wèn)類型,那這個(gè)變量存在還有什么意義)
4)、抽象類中可以包含非抽象的普通方法,接口中的方法必須是抽象的,不能有非抽象的普通方法。
5)、抽象類中的抽象方法訪問(wèn)類型可以是public, protected和默認(rèn)。但接口抽象方法只能是public類型的,且不管接口的方法是否使用public abstract修飾默認(rèn)即為public abstract類型。(例如:在接口中定義void add();方法? 是和public void? add(); 、public abstract void add()等價(jià)的)
6)、抽象類中可以包含靜態(tài)方法,而接口中不能包含靜態(tài)方法
7)、一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,但只能繼承一個(gè)抽象類。
8)、abstract class在Java語(yǔ)言中體現(xiàn)了一種繼承關(guān)系,要想使得繼承關(guān)系合理,父類和派生類之間必須存在"is-a"關(guān)系,即父類和派生類在概念本質(zhì)上應(yīng)該是相同的。對(duì)于interface來(lái)說(shuō)則不然,并不要求interface的實(shí)現(xiàn)者和interface定義在概念本質(zhì)上是一致的, 僅僅是實(shí)現(xiàn)了interface定義的契約而已。
24、靜態(tài)嵌套類(Static Nested Class)和內(nèi)部類(Inner Class)的不同?
嵌套類可以分為兩種,靜態(tài)的和非靜態(tài)的,即靜態(tài)嵌套類和非靜態(tài)嵌套類。非靜態(tài)嵌套類又叫做內(nèi)部類(Inner Class)。我們通常所說(shuō)的靜態(tài)內(nèi)部類其實(shí)是不嚴(yán)格的,嚴(yán)格的說(shuō)應(yīng)該叫做靜態(tài)嵌套類(Static Nested Class)。
class OuterClass {? ? ? ...? ??
? ? ? ? ? ? ? ? ? ? class InnerClass {? ? ? ? ? ...? ? ? }? ? ??
? ? ? ? ? ? ? ? ? ? ? static class StaticNestedClass {? ? ? ? ? ...? ? ? }? ??
? }
上述代碼中的InnerClass就是內(nèi)部類,StaticNestedClass就是靜態(tài)嵌套類。
內(nèi)部類與靜態(tài)嵌套類雖然都是嵌套類,但在使用上是有一些區(qū)別的。
內(nèi)部類的實(shí)例化對(duì)象需要綁定一個(gè)外圍類的實(shí)例化對(duì)象,而靜態(tài)嵌套類的實(shí)例化對(duì)象不能也無(wú)法綁定外圍類的實(shí)例化對(duì)象。
25、Java 中會(huì)存在內(nèi)存泄漏嗎,請(qǐng)簡(jiǎn)單描述。
內(nèi)存泄露就是指一個(gè)不再被程序使用的對(duì)象或變量一直被占據(jù)在內(nèi)存中。java中有垃圾回收機(jī)制,它可以保證一對(duì)象不再被引用的時(shí)候,即對(duì)象變成了孤兒的時(shí)候,對(duì)象將自動(dòng)被垃圾回收器從內(nèi)存中清除掉。由于Java使用有向圖的方式進(jìn)行垃圾回收管理,可以消除引用循環(huán)的問(wèn)題,例如有兩個(gè)對(duì)象,相互引用,只要它們和根進(jìn)程不可達(dá)的,那么GC也是可以回收它們的
java中的內(nèi)存泄露的情況:
1.長(zhǎng)生命周期的對(duì)象持有短生命周期對(duì)象的引用就很可能發(fā)生內(nèi)存泄露,盡管短生命周期對(duì)象已經(jīng)不再需要,但是因?yàn)殚L(zhǎng)生命周期對(duì)象持有它的引用而導(dǎo)致不能被回收,這就是java中內(nèi)存泄露的發(fā)生場(chǎng)景,通俗地說(shuō),就是程序員可能創(chuàng)建了一個(gè)對(duì)象,以后一直不再使用這個(gè)對(duì)象,這個(gè)對(duì)象卻一直被引用,即這個(gè)對(duì)象無(wú)用但是卻無(wú)法被垃圾回收器回收的,這就是java中可能出現(xiàn)內(nèi)存泄露的情況,例如,緩存系統(tǒng),我們加載了一個(gè)對(duì)象放在緩存中(例如放在一個(gè)全局map對(duì)象中),然后一直不再使用它,這個(gè)對(duì)象一直被緩存引用,但卻不再被使用。
檢查java中的內(nèi)存泄露,一定要讓程序?qū)⒏鞣N分支情況都完整執(zhí)行到程序結(jié)束,然后看某個(gè)對(duì)象是否被使用過(guò),如果沒(méi)有,則才能判定這個(gè)對(duì)象屬于內(nèi)存泄露。
2.如果一個(gè)外部類的實(shí)例對(duì)象的方法返回了一個(gè)內(nèi)部類的實(shí)例對(duì)象,這個(gè)內(nèi)部類對(duì)象被長(zhǎng)期引用了,即使那個(gè)外部類實(shí)例對(duì)象不再被使用,但由于內(nèi)部類持久外部類的實(shí)例對(duì)象,這個(gè)外部類對(duì)象將不會(huì)被垃圾回收,這也會(huì)造成內(nèi)存泄露。
3.當(dāng)一個(gè)對(duì)象被存儲(chǔ)進(jìn)HashSet集合中以后,就不能修改這個(gè)對(duì)象中的那些參與計(jì)算哈希值的字段了,否則,對(duì)象修改后的哈希值與最初存儲(chǔ)進(jìn)HashSet集合中時(shí)的哈希值就不同了,在這種情況下,即使在contains方法使用該對(duì)象的當(dāng)前引用作為的參數(shù)去HashSet集合中檢索對(duì)象,也將返回找不到對(duì)象的結(jié)果,這也會(huì)導(dǎo)致無(wú)法從HashSet集合中單獨(dú)刪除當(dāng)前對(duì)象,造成內(nèi)存泄露。
26、抽象的(abstract)方法是否可同時(shí)是靜態(tài)的(static),是否可同時(shí)是本地方法(native),是否可同時(shí)被synchronized修飾?
都不能。
1)、abstract與static
what
abstract:用來(lái)聲明抽象方法,抽象方法沒(méi)有方法體,不能被直接調(diào)用,必須在子類overriding后才能使用。
static:用來(lái)聲明靜態(tài)方法,靜態(tài)方法可以被類及其對(duì)象調(diào)用。
how
static與abstract不能同時(shí)使用。
why
用static聲明方法表明這個(gè)方法在不生成類的實(shí)例時(shí)可直接被類調(diào)用,而abstract方法不能被調(diào)用,兩者矛盾。
2)、abstract與native
what
native:用來(lái)聲明本地方法,該方法的實(shí)現(xiàn)由非Java語(yǔ)言實(shí)現(xiàn),比如C。一般用于java與外環(huán)境交互,或與操作系統(tǒng)交互。
how
native可以與所有其它的java 標(biāo)識(shí)符連用,但是abstract除外。
why
因?yàn)?native 暗示這些方法是有實(shí)現(xiàn)體的,只不過(guò)這些實(shí)現(xiàn)體是非java 的,但是abstract卻顯然的指明這些方法無(wú)實(shí)現(xiàn)體。
3)、abstract與synchronized
what
synchronized:用于防止多個(gè)線程同時(shí)調(diào)用一個(gè)對(duì)象的該方法,與static連用可防止多個(gè)線程同時(shí)調(diào)用一個(gè)類的該方法。
how
abstract與synchronized不能同時(shí)使用
why
從synchronized的功能也可以看出,用synchronized的前提是該方法可以被直接調(diào)用,顯然和abstract連用。
27、靜態(tài)變量和實(shí)例變量的區(qū)別?
在語(yǔ)法定義上的區(qū)別:靜態(tài)變量前要加static關(guān)鍵字,而實(shí)例變量前則不加。
在程序運(yùn)行時(shí)的區(qū)別:實(shí)例變量屬于某個(gè)對(duì)象的屬性,必須創(chuàng)建了實(shí)例對(duì)象,其中的實(shí)例變量才會(huì)被分配空間,才能使用這個(gè)實(shí)例變量。靜態(tài)變量不屬于某個(gè)實(shí)例對(duì)象,而是屬于類,所以也稱為類變量,只要程序加載了類的字節(jié)碼,不用創(chuàng)建任何實(shí)例對(duì)象,靜態(tài)變量就會(huì)被分配空間,靜態(tài)變量就可以被使用了??傊瑢?shí)例變量必須創(chuàng)建對(duì)象后才可以通過(guò)這個(gè)對(duì)象來(lái)使用,靜態(tài)變量則可以直接使用類名來(lái)引用。
28、是否可以從一個(gè)靜態(tài)(static)方法內(nèi)部發(fā)出對(duì)非靜態(tài)(non-static)方法的調(diào)用?
不可以。
因?yàn)榉莝tatic方法是要與對(duì)象關(guān)聯(lián)在一起的,必須創(chuàng)建一個(gè)對(duì)象后,才可以在該對(duì)象上進(jìn)行方法調(diào)用,而static方法調(diào)用時(shí)不需要?jiǎng)?chuàng)建對(duì)象,可以直接調(diào)用。也就是說(shuō),當(dāng)一個(gè)static方法被調(diào)用時(shí),可能還沒(méi)有創(chuàng)建任何實(shí)例對(duì)象,如果從一個(gè)static方法中發(fā)出對(duì)非static方法的調(diào)用,那個(gè)非static方法是關(guān)聯(lián)到哪個(gè)對(duì)象上的呢?這個(gè)邏輯無(wú)法成立,所以,一個(gè)static方法內(nèi)部不能發(fā)出對(duì)非static方法的調(diào)用。簡(jiǎn)單來(lái)說(shuō),static方法先存在,非static方法后存在,static方法存在的時(shí)候有可能非static方法可能還沒(méi)有存在,所以邏輯來(lái)說(shuō)是不通的。
29、如何實(shí)現(xiàn)對(duì)象克?。?/b>
clone方法的含義就是克隆出一個(gè)一模一樣的對(duì)象,這樣是有兩個(gè)對(duì)象的。
實(shí)現(xiàn)clone方法的步驟()
(1)實(shí)現(xiàn)Cloneable接口
(2)重載Object類中的clone()方法,重載時(shí)需定義為public
(3)在重載方法中,調(diào)用super.clone()

解釋:
(1)clone()方法是定義在java.lang.Object類中,該方法是一個(gè)protected的方法,所以重載時(shí)要把clone()方法的屬性設(shè)置為public,這樣其它類才能調(diào)用這個(gè)clone類的clone()方法
(2)實(shí)現(xiàn)Cloneable接口:Cloneable接口是不包含任何方法的!其實(shí)這個(gè)接口僅僅是一個(gè)標(biāo)志,而且這個(gè)標(biāo)志也僅僅是針對(duì)Object類中clone()方法的,如果clone類沒(méi)有實(shí)現(xiàn)Cloneable接口,并調(diào)用了Object的clone()方法(也就是調(diào)用了super.Clone()方法),那么Object的clone()方法就會(huì)拋出CloneNotSupportedException異常。
淺克隆與深克隆
所謂淺克隆就是說(shuō)被克隆的對(duì)象各個(gè)域都是基本類型,而不存在引用類型。如有存在引用類型的域,則需要進(jìn)行深克隆。深克隆就是在重載clone()方法中,要對(duì)其引用類型的域也進(jìn)行克隆
30、GC 是什么?為什么要有GC?
GC是垃圾收集的意思,內(nèi)存處理是編程人員容易出現(xiàn)問(wèn)題的地方,忘記或者錯(cuò)誤的內(nèi)存回收會(huì)導(dǎo)致程序或系統(tǒng)的不穩(wěn)定甚至崩潰,Java提供的GC功能可以自動(dòng)監(jiān)測(cè)對(duì)象是否超過(guò)作用域從而達(dá)到自動(dòng)回收內(nèi)存的目的,Java語(yǔ)言沒(méi)有提供釋放已分配內(nèi)存的顯示操作方法。Java程序員不用擔(dān)心內(nèi)存管理,因?yàn)槔占鲿?huì)自動(dòng)進(jìn)行管理。要請(qǐng)求垃圾收集,可以調(diào)用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉顯示的垃圾回收調(diào)用。
垃圾回收可以有效的防止內(nèi)存泄露,有效的使用可以使用的內(nèi)存。垃圾回收器通常是作為一個(gè)單獨(dú)的低優(yōu)先級(jí)的線程運(yùn)行,不可預(yù)知的情況下對(duì)內(nèi)存堆中已經(jīng)死亡的或者長(zhǎng)時(shí)間沒(méi)有使用的對(duì)象進(jìn)行清除和回收,程序員不能實(shí)時(shí)的調(diào)用垃圾回收器對(duì)某個(gè)對(duì)象或所有對(duì)象進(jìn)行垃圾回收。在Java誕生初期,垃圾回收是Java最大的亮點(diǎn)之一,因?yàn)榉?wù)器端的編程需要有效的防止內(nèi)存泄露問(wèn)題,然而時(shí)過(guò)境遷,如今Java的垃圾回收機(jī)制已經(jīng)成為被詬病的東西。移動(dòng)智能終端用戶通常覺(jué)得iOS的系統(tǒng)比Android系統(tǒng)有更好的用戶體驗(yàn),其中一個(gè)深層次的原因就在于android系統(tǒng)中垃圾回收的不可預(yù)知性。
注:垃圾回收機(jī)制有很多種,包括:分代復(fù)制垃圾回收、標(biāo)記垃圾回收、增量垃圾回收等方式。標(biāo)準(zhǔn)的Java進(jìn)程既有棧又有堆。棧保存了原始型局部變量,堆保存了要?jiǎng)?chuàng)建的對(duì)象。Java平臺(tái)對(duì)堆內(nèi)存回收和再利用的基本算法被稱為標(biāo)記和清除,但是Java對(duì)其進(jìn)行了改進(jìn),采用“分代式垃圾收集”。這種方法會(huì)跟Java對(duì)象的生命周期將堆內(nèi)存劃分為不同的區(qū)域,在垃圾收集過(guò)程中,可能會(huì)將對(duì)象移動(dòng)到不同區(qū)域:
伊甸園(Eden):這是對(duì)象最初誕生的區(qū)域,并且對(duì)大多數(shù)對(duì)象來(lái)說(shuō),這里是它們唯一存在過(guò)的區(qū)域。
幸存者樂(lè)園(Survivor):從伊甸園幸存下來(lái)的對(duì)象會(huì)被挪到這里。
終身頤養(yǎng)園(Tenured):這是足夠老的幸存對(duì)象的歸宿。年輕代收集(Minor-GC)過(guò)程是不會(huì)觸及這個(gè)地方的。當(dāng)年輕代收集不能把對(duì)象放進(jìn)終身頤養(yǎng)園時(shí),就會(huì)觸發(fā)一次完全收集(Major-GC),這里可能還會(huì)牽扯到壓縮,以便為大對(duì)象騰出足夠的空間。
與垃圾回收相關(guān)的JVM參數(shù):
-Xms / -Xmx --- 堆的初始大小 / 堆的最大大小
-Xmn --- 堆中年輕代的大小
-XX:-DisableExplicitGC --- 讓System.gc()不產(chǎn)生任何作用
-XX:+PrintGCDetail --- 打印GC的細(xì)節(jié)
-XX:+PrintGCDateStamps --- 打印GC操作的時(shí)間戳
31、String s=new String(“xyz”);創(chuàng)建了幾個(gè)字符串對(duì)象?
兩個(gè)對(duì)象,一個(gè)是靜態(tài)存儲(chǔ)區(qū)的"xyz",一個(gè)是用new創(chuàng)建在堆上的對(duì)象。