java 高級面試部分合集


Android 面試 PDF 合集

1、說說你對 java 中反射的理解

java 中 的反射首先首先是能夠獲取到java 中要反射類的字節碼,獲取字節碼有三種方法,
1)Class.forName(className)
2)類名.class
3)this.class()
然後將字節碼中的方法,變量,構造函數等映射成相應的Method、Filed、Constructor 等 類,這些類提供豐富的方法被我們使用。

2、java 中的靜態代理,動態代理?

待續····

3、動態代理靜態代理的區別?

1)靜態代理通常只是代理一個類,動態代理是代理一個接口下的多個實現類。
2)靜態代理先知道要代理的是什麼,動態代理不知道需要代理的是什麼,只有在運行時才知道。
3)動態代理是實現JDK 裏的InvocationHandler 接口的invoke方法,但注意是代理的接口,也就是你的業務必須要實現接口,通過Proxy裏的new ProxyInstance 得到代理對象。
4)還有一種動態代理,CGLIB,代理的類,不需要業務繼承接口,通過派生的子類來實現代理,通過在運行時,動態修改字節碼到達修改類的目的。
5)AOP 編程就是基於動態代理實現的,比如著名的Spring 框架、Hibernate 框架等等,都是動態代理使用的例子。

4、Java 中常用的設計模式

Java 中一般認爲有 23 種設計模式,我們不需要所有的都會,但是其中常用的幾種設計模式應該去掌握。下面列 出了所有的設計模式。需要掌握的設計模式我單獨列出來了,當然能掌握的越多越好。

總體來說設計模式分爲三大類: 創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。

結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄 模式、狀態模式、訪問者模式、中介者模式、解釋器模式。

5、JVM垃圾回收機制和常見算法

理論上來講 Sun 公司只定義了垃圾回收機制規則而不侷限於其實現算法,因此不同廠商生產的虛擬機採用的算法 也不盡相同。
GC(Garbage Collector)在回收對象前首先必鬚髮現那些無用的對象,如何去發現定位這些無用的對象?

常用 的搜索算法如下:
1)引用計數器算法(廢棄)
引用計數器算法是給每個對象設置一個計數器,當有地方引用這個對象的時候,計數器+1,當引用失效的時候, 計數器-1,當計數器爲 0 的時候,JVM 就認爲對象不再被使用,是“垃圾”了。
引用計數器實現簡單,效率高;但是不能解決循環引用問問題(A 對象引用 B 對象,B 對象又引用 A 對象,但是 A,B 對象已不被任何其他對象引用),同時每次計數器的增加和減少都帶來了很多額外的開銷,所以在 JDK1.1 之後, 這個算法已經不再使用了。
2)根搜索算法(使用)
根搜索算法是通過一些“GC Roots”對象作爲起點,從這些節點開始往下搜索,搜索通過的路徑成爲引用鏈 (Reference Chain),當一個對象沒有被 GC Roots 的引用鏈連接的時候,說明這個對象是不可用的。
在這裏插入圖片描述
GC Roots 對象包括:
a) 虛擬機棧(棧幀中的本地變量表)中的引用的對象。
b) 方法區域中的類靜態屬性引用的對象。
c) 方法區域中常量引用的對象。
d) 本地方法棧中 JNI(Native 方法)的引用的對象。

通過上面的算法搜索到無用對象之後,就是回收過程,回收算法如下
1)標記—清除算法(Mark-Sweep)(DVM 使用的算法)
標記—清除算法包括兩個階段:“標記”和“清除”。在標記階段,確定所有要回收的對象,並做標記。清除階 段緊隨標記階段,將標記階段確定不可用的對象清除。標記—清除算法是基礎的收集算法,標記和清除階段的效率不 高,而且清除後回產生大量的不連續空間,這樣當程序需要分配大內存對象時,可能無法找到足夠的連續空間。
在這裏插入圖片描述
2)複製算法(Copying)
複製算法是把內存分成大小相等的兩塊,每次使用其中一塊,當垃圾回收的時候,把存活的對象複製到另一塊上, 然後把這塊內存整個清理掉。複製算法實現簡單,運行效率高,但是由於每次只能使用其中的一半,造成內存的利用 率不高。現在的 JVM 用複製方法收集新生代,由於新生代中大部分對象(98%)都是朝生夕死的,所以兩塊內存的比 例不是 1:1(大概是 8:1)。

在這裏插入圖片描述
3)標記—整理算法(Mark-Compact)
標記—整理算法和標記—清除算法一樣,但是標記—整理算法不是把存活對象複製到另一塊內存,而是把存活對 象往內存的一端移動,然後直接回收邊界以外的內存。標記—整理算法提高了內存的利用率,並且它適合在收集對象 存活時間較長的老年代。

在這裏插入圖片描述
4)分代收集(Generational Collection)
分代收集是根據對象的存活時間把內存分爲新生代和老年代,根據各個代對象的存活特點,每個代採用不同的垃 圾回收算法。新生代採用複製算法,老年代採用標記—整理算法。垃圾算法的實現涉及大量的程序細節,而且不同的 虛擬機平臺實現的方法也各不相同。

6、談談JVM 的內存結構和內存分配

  1. Java 內存模型
    Java 虛擬機將其管轄的內存大致分三個邏輯部分:方法區(Method Area)、Java 棧和 Java 堆。
    (1)、方法區是靜態分配的,編譯器將變量綁定在某個存儲位置上,而且這些綁定不會在運行時改變。 常數池,源代碼中的命名常量、String 常量和 static 變量保存在方法區。
    (2)、Java Stack 是一個邏輯概念,特點是後進先出。一個棧的空間可能是連續的,也可能是不連續的。 最典型的 Stack 應用是方法的調用,Java 虛擬機每調用一次方法就創建一個方法幀(frame),退出該 方法則對應的 方法幀被彈出(pop)。棧中存儲的數據也是運行時確定的。
    (3)、Java 堆分配(heap allocation)意味着以隨意的順序,在運行時進行存儲空間分配和收回的內存管理模型。 堆中存儲的數據常常是大小、數量和生命期在編譯時無法確定的。Java 對象的內存總是在 heap 中分配。 我們每天都在寫代碼,每天都在使用 JVM 的內存。
    2)、java 內存分配
    (1)、基礎數據類型直接在棧空間分配;
    (2)、方法的形式參數,直接在棧空間分配,當方法調用完成後從棧空間回收;
    (3)、引用數據類型,需要用 new 來創建,既在棧空間分配一個地址空間,又在堆空間分配對象的類變量;
    (4)、方法的引用參數,在棧空間分配一個地址空間,並指向堆空間的對象區,當方法調用完後從棧空間回收;
    (5)、局部變量 new 出來時,在棧空間和堆空間中分配空間,當局部變量生命週期結束後,棧空間立刻被回收, 堆空間區域等待 GC 回收;
    (6)、方法調用時傳入的實際參數,先在棧空間分配,在方法調用完成後從棧空間釋放;
    (7)、字符串常量在 DATA 區域分配 ,this 在堆空間分配;
    (8)、數組既在棧空間分配數組名稱, 又在堆空間分配數組實際的大小!

7、java 中引用類型都有哪些

Java 中對象的引用分爲四種級別,這四種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。

8、 stack (棧) 和 heap (堆) 有什麼區別

1) 申請方式不同
stack:由系統自動分配。例如,聲明在函數中一個局部變量 int b; 系統自動在棧中爲 b 開闢空間 heap:需要程序員自己申請,並指明大小,在 c 中 malloc 函數,對於 Java 需要手動 new Object()的形式開闢

2)申請後系統的響應
stack:只要棧的剩餘空間大於所申請空間,系統將爲程序提供內存,否則將報異常提示棧溢出。 heap:首先應該知道操作系統有一個記錄空閒內存地址的鏈表,當系統收到程序的申請時, 會遍歷該鏈表,尋找第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點鏈表中刪除,並將該結點的空間 分配給程序。另外,由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多餘的那部分重新放入空 閒鏈表中。

3)申請大小的限制
stack:棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS 下,棧的大小是 2M(也有的說是 1M,總之是一個編譯時就確定的常數),如果 申請的空間超過棧的剩餘空間時,將提示 overflow。因此,能從棧獲得的空間較小。

heap:堆是向高地址擴展的數據結構,是不連續的內存區域。這是由於系統是用鏈表來存儲的空閒內存地址的, 自然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。由此可見, 堆獲得的空間比較靈活,也比較大。

4)申請效率的比較:
stack:由系統自動分配,速度較快。但程序員是無法控制的。
heap:由 new 分配的內存,一般速度比較慢,而且容易產生內存碎片,不過用起來最方便。

5)heap 和 stack 中的存儲內容
stack: 在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的地址, 然後是函數的各個參數,在大多數的 C 編譯器中,參數是由右往左入棧的,然後是函數中的局部變量。注意靜態變量 是不入棧的。 當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中的 下一條指令,程序由該點繼續運行。
heap:一般是在堆的頭部用一個字節存放堆的大小。堆中的具體內容有程序員安排。

6) 數據結構層面的區別
還有就是數據結構方面的堆和棧,這些都是不同的概念。這裏的堆實際上指的就是(滿足堆性質的)優先隊列的 一種數據結構,第 1 個元素有最高的優先權;棧實際上就是滿足先進後出的性質的數學或數據結構。 雖然堆棧,堆棧的說法是連起來叫,但是他們還是有很大區別的,連着叫只是由於歷史的原因。

9、Java 的類加載器的種類都有哪些?

1)、根類加載器(Bootstrap) --C++寫的 ,看不到源碼
2)、擴展類加載器(Extension) --加載位置 :jre\lib\ext 中
3)、系統(應用)類加載器(System\App) --加載位置 :classpath 中
4)、自定義加載器(必須繼承 ClassLoader)

10、類什麼時候被初始化?

1)創建類的實例,也就是 new 一個對象
2)訪問某個類或接口的靜態變量,或者對該靜態變量賦值
3)調用類的靜態方法
4)反射(Class.forName(“com.lyj.load”))
5)初始化一個類的子類(會首先初始化子類的父類
6)JVM 啓動時標明的啓動類,即文件名和類名相同的那個類
只有這 6 中情況纔會導致類的類的初始化。
類的初始化步驟:
1)如果這個類還沒有被加載和鏈接,那先進行加載和鏈接
2)假如這個類存在直接父類,並且這個類還沒有被初始化(注意:在一個類加載器中,類只能初始化一 次),那就初始化直接的父類(不適用於接口)
3)加入類中存在初始化語句(如 static 變量和 static 塊),那就依次執行這些初始化語句。

11、簡述Java 類加載體系之 ClassLoader 雙親委託機制

java 是一種類型安全的語言,它有四類稱爲安全沙箱機制的安全機制來保證語言的安全性,這四類安全 沙箱分別是:
1) 類加載體系
2) .class 文件檢驗器
3) 內置於 Java 虛擬機(及語言)的安全特性
4) 安全管理器及 Java API

主要講解類的加載體系: java 程序中的 .java 文件編譯完會生成 .class 文件,而 .class 文件就是通過被稱爲類加載器的 ClassLoader 加載的,而 ClassLoder 在加載過程中會使用“雙親委派機制”來加載 .class 文件,先上圖:
在這裏插入圖片描述

BootStrapClassLoader : 啓 動 類 加 載 器 , 該 ClassLoader 是 jvm 在 啓 動 時 創 建 的 , 用 於 加 載 JAVAHOMEJAVA_HOME/jre/lib 下面的類庫(或者通過參數-Xbootclasspath 指定)。由於啓動類加載器涉及到虛擬 機本地實現細節,開發者無法直接獲取到啓動類加載器的引用,所以不能直接通過引用進行操作。

ExtClassLoader:擴展類加載器,該ClassLoader是在sun.misc.Launcher裏作爲一個內部類ExtClassLoader 定義的(即 sun.misc.Launcher$ExtClassLoader),ExtClassLoader 會加載 $JAVA_HOME/jre/lib/ext 下的類 庫(或者通過參數-Djava.ext.dirs 指定)。

AppClassLoader:應用程序類加載器,該 ClassLoader 同樣是在 sun.misc.Launcher 裏作爲一個內部類 AppClassLoader 定義的(即 sun.misc.Launcher$AppClassLoader),AppClassLoader 會加載 java 環境變量 CLASSPATH 所 指 定 的 路 徑 下 的 類 庫 , 而 CLASSPATH 所 指 定 的 路 徑 可 以 通 過 System.getProperty(“java.class.path”)獲取;當然,該變量也可以覆蓋,可以使用參數-cp,例如:java -cp 路 徑 (可以指定要執行的 class 目錄)。

CustomClassLoader:自定義類加載器,該 ClassLoader 是指我們自定義的 ClassLoader,比如 tomcat 的 StandardClassLoader 屬於這一類;當然,大部分情況下使用 AppClassLoader 就足夠了。

前面談到了 ClassLoader 的幾類加載器,而 ClassLoader 使用雙親委派機制來加載 class 文件的。ClassLoader 的雙親委派機制是這樣的(這裏先忽略掉自定義類加載器 CustomClassLoader):

1)當 AppClassLoader 加載一個 class 時,它首先不會自己去嘗試加載這個類,而是把類加載請求委派給父 類加載器 ExtClassLoader 去完成。

2)當 ExtClassLoader 加載一個 class 時,它首先也不會自己去嘗試加載這個類,而是把類加載請求委派給 BootStrapClassLoader 去完成。

3)如果 BootStrapClassLoader 加載失敗(例如在JAVAHOMEJAVA_HOME/jre/lib 裏未查找到該 class),會使用 ExtClassLoader 來嘗試加載;

4)若 ExtClassLoader 也加載失敗,則會使用 AppClassLoader 來加載,如果 AppClassLoader 也加載失敗, 則會報出異常 ClassNotFoundException。

待續····

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章