Java深入源碼級(jí)面試題

和你一起終身學(xué)習(xí),這里是程序員Android
1、哪些情況下的對(duì)象會(huì)被垃圾回收機(jī)制處理掉?
利用可達(dá)性分析算法,虛擬機(jī)會(huì)將一些對(duì)象定義為GC Roots,從GC Roots出發(fā)沿著引用鏈向下尋找,如果某個(gè)對(duì)象不能通過GC Roots尋找到,虛擬機(jī)就認(rèn)為該對(duì)象可以被回收掉。
1.1 哪些對(duì)象可以被看做是GC Roots呢?
1)虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象;
2)方法區(qū)中的類靜態(tài)屬性引用的對(duì)象,常量引用的對(duì)象;
3)本地方法棧中JNI(Native方法)引用的對(duì)象;
1.2 對(duì)象不可達(dá),一定會(huì)被垃圾收集器回收么?
即使不可達(dá),對(duì)象也不一定會(huì)被垃圾收集器回收
1)先判斷對(duì)象是否有必要執(zhí)行finalize()方法,對(duì)象必須重寫finalize()方法且沒有被運(yùn)行過。
2)若有必要執(zhí)行,會(huì)把對(duì)象放到一個(gè)隊(duì)列中,JVM會(huì)開一個(gè)線程去回收它們,這是對(duì)象最后一次可以逃逸清理的機(jī)會(huì)。
2、講一下常見編碼方式?
編碼的意義:計(jì)算機(jī)中存儲(chǔ)的最小單元是一個(gè)字節(jié)即8bit,所能表示的字符范圍是255個(gè),而人類要表示的符號(hào)太多,無法用一個(gè)字節(jié)來完全表示,固需要將符號(hào)編碼,將各種語言翻譯成計(jì)算機(jī)能懂的語言。
1)ASCII碼:
總共128個(gè),用一個(gè)字節(jié)的低7位表示,0?31控制字符如換回車刪除等;32~126是打印字符,可通過鍵盤輸入并顯示出來;
2)ISO-8859-1
用來擴(kuò)展ASCII編碼,256個(gè)字符,涵蓋了大多數(shù)西歐語言字符。
3)GB2312
雙字節(jié)編碼,總編碼范圍是A1-A7,A1-A9是符號(hào)區(qū),包含682個(gè)字符,B0-B7是漢字區(qū),包含6763個(gè)漢字;
4)GBK
為了擴(kuò)展GB2312,加入了更多的漢字,編碼范圍是8140~FEFE,有23940個(gè)碼位,能表示21003個(gè)漢字。
5)UTF-16
ISO試圖想創(chuàng)建一個(gè)全新的超語言字典,世界上所有語言都可通過這本字典Unicode來相互翻譯,而UTF-16定義了Unicode字符在計(jì)算機(jī)中存取方法,用兩個(gè)字節(jié)來表示Unicode轉(zhuǎn)化格式。不論什么字符都可用兩字節(jié)表示,即16bit,固叫UTF-16。
6)UTF-8:
UTF-16統(tǒng)一采用兩字節(jié)表示一個(gè)字符,但有些字符只用一個(gè)字節(jié)就可表示,浪費(fèi)存儲(chǔ)空間,而UTF-8采用一種變長技術(shù),每個(gè)編碼區(qū)域有不同的字碼長度。不同類型的字符可以由1~6個(gè)字節(jié)組成。
3、utf-8編碼中的中文占幾個(gè)字節(jié);int型幾個(gè)字節(jié)?
utf-8是一種變長編碼技術(shù),utf-8編碼中的中文占用的字節(jié)不確定,可能2個(gè)、3個(gè)、4個(gè),int型占4個(gè)字節(jié)。
4、靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別,什么場景使用?
代理是一種常用的設(shè)計(jì)模式,
目的是:
為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)對(duì)象的訪問,將兩個(gè)類的關(guān)系解耦。代理類和委托類都要實(shí)現(xiàn)相同的接口,因?yàn)榇碚嬲{(diào)用的是委托類的方法。
區(qū)別:
1)靜態(tài)代理:
由程序員創(chuàng)建或是由特定工具生成,在代碼編譯時(shí)就確定了被代理的類是哪一個(gè)是靜態(tài)代理。靜態(tài)代理通常只代理一個(gè)類;
2)動(dòng)態(tài)代理:
在代碼運(yùn)行期間,運(yùn)用反射機(jī)制動(dòng)態(tài)創(chuàng)建生成。動(dòng)態(tài)代理代理的是一個(gè)接口下的多個(gè)實(shí)現(xiàn)類;
實(shí)現(xiàn)步驟:
a.實(shí)現(xiàn)InvocationHandler接口創(chuàng)建自己的調(diào)用處理器;
b.給Proxy類提供ClassLoader和代理接口類型數(shù)組創(chuàng)建動(dòng)態(tài)代理類;
c.利用反射機(jī)制得到動(dòng)態(tài)代理類的構(gòu)造函數(shù);
d.利用動(dòng)態(tài)代理類的構(gòu)造函數(shù)創(chuàng)建動(dòng)態(tài)代理類對(duì)象;
使用場景:
Retrofit中直接調(diào)用接口的方法;Spring的AOP機(jī)制;
5、Java的異常體系
Java中Throwable是所有異常和錯(cuò)誤的超類,兩個(gè)直接子類是Error(錯(cuò)誤)和Exception(異常):
1)Error是程序無法處理的錯(cuò)誤,由JVM產(chǎn)生和拋出
如OOM、ThreadDeath等。這些異常發(fā)生時(shí),JVM一般會(huì)選擇終止程序。
2)Exception是程序本身可以處理的異常
又分為運(yùn)行時(shí)異常(RuntimeException)(也叫Checked Eception)和非運(yùn)行時(shí)異常(不檢查異常Unchecked Exception)。
運(yùn)行時(shí)異常
有NullPointerException\IndexOutOfBoundsException等,這些異常一般是由程序邏輯錯(cuò)誤引起的,應(yīng)盡可能避免。
非運(yùn)行時(shí)異常
有IOException\SQLException\FileNotFoundException以及由用戶自定義的Exception異常等。
6、談?wù)勀銓?duì)解析與分派的認(rèn)識(shí)。
解析
指方法在運(yùn)行前,即編譯期間就可知的,有一個(gè)確定的版本,運(yùn)行期間也不會(huì)改變。解析是靜態(tài)的,在類加載的解析階段就可將符號(hào)引用轉(zhuǎn)變成直接引用。
分派
可分為靜態(tài)分派和動(dòng)態(tài)分派,重載屬于靜態(tài)分派,覆蓋屬于動(dòng)態(tài)分派。
靜態(tài)分派是指在重載時(shí)通過參數(shù)的靜態(tài)類型而非實(shí)際類型作為判斷依據(jù),在編譯階段,編譯器可根據(jù)參數(shù)的靜態(tài)類型決定使用哪一個(gè)重載版本。
動(dòng)態(tài)分派則需要根據(jù)實(shí)際類型來調(diào)用相應(yīng)的方法。
7、修改對(duì)象A的equals方法的簽名,那么使用HashMap存放這個(gè)對(duì)象實(shí)例的時(shí)候,會(huì)調(diào)用哪個(gè)equals方法?
會(huì)調(diào)用對(duì)象的equals方法,如果對(duì)象的equals方法沒有被重寫,equals方法和==都是比較棧內(nèi)局部變量表中指向堆內(nèi)存地址值是否相等。
8、Java中實(shí)現(xiàn)多態(tài)的機(jī)制是什么?
多態(tài)是指程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編譯時(shí)不確定,在運(yùn)行期間才確定,一個(gè)引用變量到底會(huì)指向哪個(gè)類的實(shí)例。這樣就可以不用修改源程序,就可以讓引用變量綁定到各種不同的類實(shí)現(xiàn)上。
Java實(shí)現(xiàn)多態(tài)有三個(gè)必要條件:繼承、重定、向上轉(zhuǎn)型,在多態(tài)中需要將子類的引用賦值給父類對(duì)象,只有這樣該引用才能夠具備調(diào)用父類方法和子類的方法。
9、如何將一個(gè)Java對(duì)象序列化到文件里?
ObjectOutputStream.writeObject()負(fù)責(zé)將指定的流寫入,ObjectInputStream.readObject()從指定流讀取序列化數(shù)據(jù)。
//寫入
try {
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("D:/student.txt"));
os.writeObject(studentList);
os.close();
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
10、說說你對(duì)Java反射的理解
在運(yùn)行狀態(tài)中,對(duì)任意一個(gè)類,都能知道這個(gè)類的所有屬性和方法,對(duì)任意一個(gè)對(duì)象,都能調(diào)用它的任意一個(gè)方法和屬性。這種能動(dòng)態(tài)獲取信息及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為java語言的反射機(jī)制。
反射的作用:
開發(fā)過程中,經(jīng)常會(huì)遇到某個(gè)類的某個(gè)成員變量、方法或?qū)傩允撬接械模蛑粚?duì)系統(tǒng)應(yīng)用開放,這里就可以利用java的反射機(jī)制通過反射來獲取所需的私有成員或是方法。
獲取類的Class對(duì)象實(shí)例
Class clz = Class.forName("com.zhenai.api.Apple");根據(jù)Class對(duì)象實(shí)例獲取Constructor對(duì)象
Constructor appConstructor = clz.getConstructor();使用Constructor對(duì)象的
newInstance方法獲取反射類對(duì)象Object appleObj = appConstructor.newInstance();獲取方法的Method對(duì)象
Method setPriceMethod = clz.getMethod("setPrice", int.class);利用invoke方法調(diào)用方法
setPriceMethod.invoke(appleObj, 14);通過getFields()可以獲取Class類的屬性,但無法獲取私有屬性,而getDeclaredFields()可以獲取到包括私有屬性在內(nèi)的所有屬性。帶有Declared修飾的方法可以反射到私有的方法,沒有Declared修飾的只能用來反射公有的方法,其他如Annotation\Field\Constructor也是如此。
11、說說你對(duì)Java注解的理解
注解是通過@interface關(guān)鍵字來進(jìn)行定義的,形式和接口差不多,只是前面多了一個(gè)@
public @interface TestAnnotation {
}
使用時(shí)@TestAnnotation來引用,要使注解能正常工作,還需要使用元注解,它是可以注解到注解上的注解。元標(biāo)簽有@Retention @Documented @Target @Inherited @Repeatable五種
@Retention說明注解的存活時(shí)間,取值有RetentionPolicy.SOURCE 注解只在源碼階段保留,在編譯器進(jìn)行編譯時(shí)被丟棄;RetentionPolicy.CLASS 注解只保留到編譯進(jìn)行的時(shí)候,并不會(huì)被加載到JVM中。RetentionPolicy.RUNTIME可以留到程序運(yùn)行的時(shí)候,它會(huì)被加載進(jìn)入到JVM中,所以在程序運(yùn)行時(shí)可以獲取到它們。
@Documented 注解中的元素包含到j(luò)avadoc中去
@Target 限定注解的應(yīng)用場景,ElementType.FIELD給屬性進(jìn)行注解;ElementType.LOCAL_VARIABLE可以給局部變量進(jìn)行注解;ElementType.METHOD可以給方法進(jìn)行注解;ElementType.PACKAGE可以給一個(gè)包進(jìn)行注解 ElementType.TYPE可以給一個(gè)類型進(jìn)行注解,如類、接口、枚舉
@Inherited 若一個(gè)超類被@Inherited注解過的注解進(jìn)行注解,它的子類沒有被任何注解應(yīng)用的話,該子類就可繼承超類的注解;
注解的作用:
1)提供信息給編譯器:編譯器可利用注解來探測錯(cuò)誤和警告信息
2)編譯階段:軟件工具可以利用注解信息來生成代碼、html文檔或做其它相應(yīng)處理;
3)運(yùn)行階段:程序運(yùn)行時(shí)可利用注解提取代碼
注解是通過反射獲取的,可以通過Class對(duì)象的isAnnotationPresent()方法判斷它是否應(yīng)用了某個(gè)注解,再通過getAnnotation()方法獲取Annotation對(duì)象
12、說一下泛型原理,并舉例說明
泛型就是將類型變成參數(shù)傳入,使得可以使用的類型多樣化,從而實(shí)現(xiàn)解耦。Java泛型是在Java1.5以后出現(xiàn)的,為保持對(duì)以前版本的兼容,使用了擦除的方法實(shí)現(xiàn)泛型。擦除是指在一定程度無視類型參數(shù)T,直接從T所在的類開始向上T的父類去擦除,如調(diào)用泛型方法,傳入類型參數(shù)T進(jìn)入方法內(nèi)部,若沒在聲明時(shí)做類似public T methodName(T extends Father t){},Java就進(jìn)行了向上類型的擦除,直接把參數(shù)t當(dāng)做Object類來處理,而不是傳進(jìn)去的T。即在有泛型的任何類和方法內(nèi)部,它都無法知道自己的泛型參數(shù),擦除和轉(zhuǎn)型都是在邊界上發(fā)生,即傳進(jìn)去的參在進(jìn)入類或方法時(shí)被擦除掉,但傳出來的時(shí)候又被轉(zhuǎn)成了我們?cè)O(shè)置的T。在泛型類或方法內(nèi),任何涉及到具體類型(即擦除后的類型的子類)操作都不能進(jìn)行,如new T(),或者T.play()(play為某子類的方法而不是擦除后的類的方法)
13、Java中String的了解
1)String類是final型,固String類不能被繼承,它的成員方法也都默認(rèn)為final方法。String對(duì)象一旦創(chuàng)建就固定不變了,對(duì)String對(duì)象的任何改變都不影響到原對(duì)象,相關(guān)的任何改變操作都會(huì)生成新的String對(duì)象。
2)String類是通過char數(shù)組來保存字符串的,String對(duì)equals方法進(jìn)行了重定,比較的是值相等。
String a = "test"; String b = "test"; String c = new String("test");
a、b和字面上的test都是指向JVM字符串常量池中的"test"對(duì)象,他們指向同一個(gè)對(duì)象。而new關(guān)鍵字一定會(huì)產(chǎn)生一個(gè)對(duì)象test,該對(duì)象存儲(chǔ)在堆中。所以new String("test")產(chǎn)生了兩個(gè)對(duì)象,保存在棧中的c和保存在堆中的test。而在java中根本就不存在兩個(gè)完全一模一樣的字符串對(duì)象,故在堆中的test應(yīng)該是引用字符串常量池中的test。
String str1 = "abc"; //棧中開辟一塊空間存放引用str1,str1指向池中String常量"abc"
String str2 = "def"; //棧中開辟一塊空間存放引用str2,str2指向池中String常量"def"
String str3 = str1 + str2;//棧中開辟一塊空間存放引用str3
//str1+str2通過StringBuilder的最后一步toString()方法返回一個(gè)新的String對(duì)象"abcdef"
//會(huì)在堆中開辟一塊空間存放此對(duì)象,引用str3指向堆中的(str1+str2)所返回的新String對(duì)象。
System.out.println(str3 == "abcdef");//返回false
因?yàn)閟tr3指向堆中的"abcdef"對(duì)象,而"abcdef"是字符池中的對(duì)象,所以結(jié)果為false。JVM對(duì)String str="abc"對(duì)象放在常量池是在編譯時(shí)做的,而String str3=str1+str2是在運(yùn)行時(shí)才知道的,new對(duì)象也是在運(yùn)行時(shí)才做的。
14、String為什么要設(shè)計(jì)成不可變的?
1)字符串常量池需要String不可變
因?yàn)镾tring設(shè)計(jì)成不可變,當(dāng)創(chuàng)建一個(gè)String對(duì)象時(shí),若此字符串值已經(jīng)存在于常量池中,則不會(huì)創(chuàng)建一個(gè)新的對(duì)象,而是引用已經(jīng)存在的對(duì)象。如果字符串變量允許必變,會(huì)導(dǎo)致各種邏輯錯(cuò)誤,如改變一個(gè)對(duì)象會(huì)影響到另一個(gè)獨(dú)立對(duì)象。
2)String對(duì)象可以緩存hashCode。
字符串的不可變性保證了hash碼的唯一性,因此可以緩存String的hashCode,這樣不用每次去重新計(jì)算哈希碼。在進(jìn)行字符串比較時(shí),可以直接比較hashCode,提高了比較性能;
3)安全性
String被許多java類用來當(dāng)作參數(shù),如url地址,文件path路徑,反射機(jī)制所需的Strign參數(shù)等,若String可變,將會(huì)引起各種安全隱患。
至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺得很優(yōu)秀,歡迎點(diǎn)擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請(qǐng)聯(lián)系小編刪除。同時(shí)感謝您的閱讀,期待您的關(guān)注。
