您好,登錄后才能下訂單哦!
今天雙11剁手節,祝大家節日快樂
今天和大家一起分析一下那些面試中可能會問到的java深入源碼級的面試題
對此很多面試中遇到的問題,花了15個小時整理成為了一份983頁的PDF文檔。
(更多完整項目下載。未完待續。源碼。圖文知識后續上傳github。)
可以點擊關于我聯系我獲取完整PDF
(VX:×××)
利用可達性分析算法,虛擬機會將一些對象定義為GC Roots,從GC Roots出發沿著引用鏈向下尋找,如果某個對象不能通過GC Roots尋找到,虛擬機就認為該對象可以被回收掉。
1)虛擬機棧(棧幀中的本地變量表)中引用的對象;
2)方法區中的類靜態屬性引用的對象,常量引用的對象;
3)本地方法棧中JNI(Native方法)引用的對象;
即使不可達,對象也不一定會被垃圾收集器回收,
1)先判斷對象是否有必要執行finalize()方法,對象必須重寫finalize()方法且沒有被運行過。
2)若有必要執行,會把對象放到一個隊列中,JVM會開一個線程去回收它們,這是對象最后一次可以逃逸清理的機會。
編碼的意義:計算機中存儲的最小單元是一個字節即8bit,所能表示的字符范圍是255個,而人類要表示的符號太多,無法用一個字節來完全表示,固需要將符號編碼,將各種語言翻譯成計算機能懂的語言。
1)ASCII碼:總共128個,用一個字節的低7位表示,0?31控制字符如換回車刪除等;32~126是打印字符,可通過鍵盤輸入并顯示出來;
2)ISO-8859-1,用來擴展ASCII編碼,256個字符,涵蓋了大多數西歐語言字符。
3)GB2312:雙字節編碼,總編碼范圍是A1-A7,A1-A9是符號區,包含682個字符,B0-B7是漢字區,包含6763個漢字;
4)GBK為了擴展GB2312,加入了更多的漢字,編碼范圍是8140~FEFE,有23940個碼位,能表示21003個漢字。
5)UTF-16: ISO試圖想創建一個全新的超語言字典,世界上所有語言都可通過這本字典Unicode來相互翻譯,而UTF-16定義了Unicode字符在計算機中存取方法,用兩個字節來表示Unicode轉化格式。不論什么字符都可用兩字節表示,即16bit,固叫UTF-16。
6)UTF-8:UTF-16統一采用兩字節表示一個字符,但有些字符只用一個字節就可表示,浪費存儲空間,而UTF-8采用一種變長技術,每個編碼區域有不同的字碼長度。? 不同類型的字符可以由1~6個字節組成。? ? ? ? ? ? ? ? ? ?
utf-8是一種變長編碼技術,utf-8編碼中的中文占用的字節不確定,可能2個、3個、4個,int型占4個字節。
代理是一種常用的設計模式,目的是:為其他對象提供一個代理以控制對某個對象的訪問,將兩個類的關系解耦。代理類和委托類都要實現相同的接口,因為代理真正調用的是委托類的方法。
1)靜態代理:由程序員創建或是由特定工具生成,在代碼編譯時就確定了被代理的類是哪一個是靜態代理。靜態代理通常只代理一個類;
2)動態代理:在代碼運行期間,運用反射機制動態創建生成。動態代理代理的是一個接口下的多個實現類;
實現步驟:a.實現InvocationHandler接口創建自己的調用處理器;b.給Proxy類提供ClassLoader和代理接口類型數組創建動態代理類;c.利用反射機制得到動態代理類的構造函數;d.利用動態代理類的構造函數創建動態代理類對象;
使用場景:Retrofit中直接調用接口的方法;Spring的AOP機制;
Java中Throwable
是所有異常和錯誤的超類,兩個直接子類是Error(錯誤)和Exception(異常):
1)Error是程序無法處理的錯誤,由JVM
產生和拋出,如OOM
、ThreadDeath
等。這些異常發生時,JVM
一般會選擇終止程序。
2)Exception是程序本身可以處理的異常,又分為運行時異常(RuntimeException)
(也叫Checked Eception)和非運行時異常(不檢查異常Unchecked Exception)。運行時異常有NullPointerException\IndexOutOfBoundsException
等,這些異常一般是由程序邏輯錯誤引起的,應盡可能避免。非運行時異常有IOException
`SQLException\
FileNotFoundException`以及由用戶自定義的Exception異常等。
解析指方法在運行前,即編譯期間就可知的,有一個確定的版本,運行期間也不會改變。解析是靜態的,在類加載的解析階段就可將符號引用轉變成直接引用。
分派可分為靜態分派和動態分派,重載屬于靜態分派,覆蓋屬于動態分派。靜態分派是指在重載時通過參數的靜態類型而非實際類型作為判斷依據,在編譯階段,編譯器可根據參數的靜態類型決定使用哪一個重載版本。動態分派則需要根據實際類型來調用相應的方法。 ? ? ?
會調用對象的equals方法,如果對象的equals方法沒有被重寫,equals方法和==都是比較棧內局部變量表中指向堆內存地址值是否相等。
多態是指程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編譯時不確定,在運行期間才確定,一個引用變量到底會指向哪個類的實例。這樣就可以不用修改源程序,就可以讓引用變量綁定到各種不同的類實現上。
Java實現多態有三個必要條件:繼承、重定、向上轉型,在多態中需要將子類的引用賦值給父類對象,只有這樣該引用才能夠具備調用父類方法和子類的方法。
ObjectOutputStream.writeObject()
負責將指定的流寫入,ObjectInputStream.readObject()
從指定流讀取序列化數據。? ? ?
?//寫入
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();
}
在運行狀態中,對任意一個類,都能知道這個類的所有屬性和方法,對任意一個對象,都能調用它的任意一個方法和屬性。這種能動態獲取信息及動態調用對象方法的功能稱為java語言的反射機制。
反射的作用:開發過程中,經常會遇到某個類的某個成員變量、方法或屬性是私有的,或只對系統應用開放,這里就可以利用java的反射機制通過反射來獲取所需的私有成員或是方法。
1) 獲取類的Class對象實例 Class clz = Class.forName("com.zhenai.api.Apple");
2) 根據Class
對象實例獲取Constructor
對象? Constructor appConstructor = clz.getConstructor();
3) 使用Constructor
對象的newInstance
方法獲取反射類對象 Object appleObj = appConstructor.newInstance();
4) 獲取方法的Method
對象? Method setPriceMethod = clz.getMethod("setPrice", int.class);
5) 利用invoke方法調用方法? setPriceMethod.invoke(appleObj, 14);
6) 通過getFields()
可以獲取Class類的屬性,但無法獲取私有屬性,而getDeclaredFields()
可以獲取到包括私有屬性在內的所有屬性。帶有Declared
修飾的方法可以反射到私有的方法,沒有Declared
修飾的只能用來反射公有的方法,其他如Annotation
`Field\
Constructor`也是如此。
注解是通過@interface關鍵字來進行定義的,形式和接口差不多,只是前面多了一個@
public @interface TestAnnotation {
}
使用時@TestAnnotation
來引用,要使注解能正常工作,還需要使用元注解,它是可以注解到注解上的注解。元標簽有@Retention
?@Documented
@Target
@Inherited
@Repeatable
五種
@Retention
說明注解的存活時間,取值有RetentionPolicy.SOURCE
注解只在源碼階段保留,在編譯器進行編譯時被丟棄;RetentionPolicy.CLASS
注解只保留到編譯進行的時候,并不會被加載到JVM
中。RetentionPolicy.RUNTIME
可以留到程序運行的時候,它會被加載進入到JVM中,所以在程序運行時可以獲取到它們。
@Documented
注解中的元素包含到javadoc
中去
@Target
? 限定注解的應用場景,ElementType.FIELD
給屬性進行注解;ElementType.LOCAL_VARIABLE
可以給局部變量進行注解;ElementType.METHOD
可以給方法進行注解;ElementType.PACKAGE
可以給一個包進行注解 ElementType.TYPE
可以給一個類型進行注解,如類、接口、枚舉
@Inherited
若一個超類被@Inherited
注解過的注解進行注解,它的子類沒有被任何注解應用的話,該子類就可繼承超類的注解;
1)提供信息給編譯器:編譯器可利用注解來探測錯誤和警告信息
2)編譯階段:軟件工具可以利用注解信息來生成代碼、html文檔或做其它相應處理;
3)運行階段:程序運行時可利用注解提取代碼
注解是通過反射獲取的,可以通過Class
對象的isAnnotationPresent()
方法判斷它是否應用了某個注解,再通過getAnnotation()
方法獲取Annotation
對象
泛型就是將類型變成參數傳入,使得可以使用的類型多樣化,從而實現解耦。Java泛型是在Java1.5
以后出現的,為保持對以前版本的兼容,使用了擦除的方法實現泛型。擦除是指在一定程度無視類型參數T,直接從T所在的類開始向上T的父類去擦除,如調用泛型方法,傳入類型參數T進入方法內部,若沒在聲明時做類似public T methodName(T extends Father t){}
,Java就進行了向上類型的擦除,直接把參數t當做Object類來處理,而不是傳進去的T。
即在有泛型的任何類和方法內部,它都無法知道自己的泛型參數,擦除和轉型都是在邊界上發生,即傳進去的參在進入類或方法時被擦除掉,但傳出來的時候又被轉成了我們設置的T。在泛型類或方法內,任何涉及到具體類型(即擦除后的類型的子類)操作都不能進行,如new T(),或者T.play()(play為某子類的方法而不是擦除后的類的方法)
1)String類是final型,固String類不能被繼承,它的成員方法也都默認為final方法。String對象一旦創建就固定不變了,對String對象的任何改變都不影響到原對象,相關的任何改變操作都會生成新的String對象。
2)String類是通過char數組來保存字符串的,String對equals方法進行了重定,比較的是值相等。
String a = "test"; String b = "test"; String c = new String("test");
a、b和字面上的test都是指向JVM字符串常量池中的"test"對象,他們指向同一個對象。而new關鍵字一定會產生一個對象test,該對象存儲在堆中。所以new String("test")產生了兩個對象,保存在棧中的c和保存在堆中的test。而在java中根本就不存在兩個完全一模一樣的字符串對象,故在堆中的test應該是引用字符串常量池中的test。
例:
String str1 = "abc"; //棧中開辟一塊空間存放引用str1,str1指向池中String常量"abc"
String str2 = "def"; //棧中開辟一塊空間存放引用str2,str2指向池中String常量"def"
String str3 = str1 + str2;//棧中開辟一塊空間存放引用str3
//str1+str2通過StringBuilder的最后一步toString()方法返回一個新的String對象"abcdef"
//會在堆中開辟一塊空間存放此對象,引用str3指向堆中的(str1+str2)所返回的新String對象。
System.out.println(str3 == "abcdef");//返回false
因為str3指向堆中的"abcdef"對象,而"abcdef"是字符池中的對象,所以結果為false。JVM對String str="abc"對象放在常量池是在編譯時做的,而String str3=str1+str2是在運行時才知道的,new對象也是在運行時才做的。
1)字符串常量池需要String不可變。
因為String設計成不可變,當創建一個String對象時,若此字符串值已經存在于常量池中,則不會創建一個新的對象,而是引用已經存在的對象。如果字符串變量允許必變,會導致各種邏輯錯誤,如改變一個對象會影響到另一個獨立對象。
2)String對象可以緩存hashCode
。
字符串的不可變性保證了hash碼的唯一性,因此可以緩存String的hashCode
,這樣不用每次去重新計算哈希碼。在進行字符串比較時,可以直接比較hashCode
,提高了比較性能;
3)安全性。
String被許多java類用來當作參數,如url
地址,文件path路徑,反射機制所需的Strign
參數等,若String可變,將會引起各種安全隱患。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
(更多完整項目下載。未完待續。源碼。圖文知識后續上傳github。)
可以點擊關于我聯系我獲取完整PDF
(VX:×××)
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。