面試中java 知識點

StringBuilder、StringBuffer、+、String.concat 鏈接字符串:

  • StringBuffer 線程安全,StringBuilder 線程不安全
  • +實際上是用 StringBuilder 來實現的,所以非循環體可以直接用 +,循環體不行,因爲會頻繁創建 StringBuilder
  • String.concat 實質是 new String ,效率也低,耗時排序:StringBuilder < StringBuffer < concat < +

Java 泛型擦除

  • 修飾成員變量等類結構相關的泛型不會被擦除
  • 容器類泛型會被擦除

ArrayList、LinkedList

  • ArrayList
    * 基於數組實現,查找快:o(1),增刪慢:o(n)
    * 初始容量爲10,擴容通過 System.arrayCopy 方法
  • LinkedList
    * 基於雙向鏈表實現,查找慢:o(n),增刪快:o(1)
    * 封裝了隊列和棧的調用

HashMap 、HashTable

  • HashMap
    * 基於數組和鏈表實現,數組是 HashMap 的主體;鏈表是爲解決哈希衝突而存在的
    * 當發生哈希衝突且鏈表 size 大於閾值時會擴容,JAVA 8 會將鏈表轉爲紅黑樹提高性能
    * 允許 key/value 爲 null
  • HashTable
    * 數據結構和 HashMap 一樣
    * 不允許 value 爲 null
    * 線程安全

ArrayMap、SparseArray

  • ArrayMap
    * 基於兩個數組實現,一個存放 hash;一個存放鍵值對。擴容的時候只需要數組拷貝,不需要重建哈希表
    * 內存利用率高
    * 不適合存大量數據,因爲會對 key 進行二分法查找(1000以下)
  • SparseArray
    * 基於兩個數組實現,int 做 key
    * 內存利用率高
    * 不適合存大量數據,因爲會對 key 進行二分法查找(1000以下)

Volatile 關鍵字

  • 只能用來修飾變量,適用修飾可能被多線程同時訪問的變量
  • 相當於輕量級的 synchronized,volatitle 能保證有序性(禁用指令重排序)、可見性;後者還能保證原子性
  • 變量位於主內存中,每個線程還有自己的工作內存,變量在自己線程的工作內存中有份拷貝,線程直接操作的是這個拷貝
  • 被 volatile 修飾的變量改變後會立即同步到主內存,保持變量的可見性
  • 雙重檢查單例,爲什麼要加 violate?
    *volatile想要解決的問題是,在另一個線程中想要使用instance,發現instance!=null,但是實際上instance還未初始化完畢這個問題
    *將instance =newInstance();拆分爲3句話是。1.分配內存2.初始化3.將instance指向分配的內存空間
    *volatile可以禁止指令重排序,確保先執行2,後執行3
  • wait 和 sleep
    • sleep 是 Thread 的靜態方法,可以在任何地方調用
    • wait 是 Object 的成員方法,只能在 synchronized 代碼塊中調用,否則會報 IllegalMonitorStateException 非法監控狀態異常
    • sleep 不會釋放共享資源鎖,wait 會釋放共享資源鎖
  • lock 和 synchronized
    • synchronized 是 Java 關鍵字,內置特性;Lock 是一個接口
    • synchronized 會自動釋放鎖;lock 需要手動釋放,所以需要寫到 try catch 塊中並在 finally 中釋放鎖
    • synchronized 無法中斷等待鎖;lock 可以中斷
    • Lock 可以提高多個線程進行讀/寫操作的效率
    • 競爭資源激烈時,lock 的性能會明顯的優於 synchronized
  • 可重入鎖
    • 定義:已經獲取到鎖後,再次調用同步代碼塊/嘗試獲取鎖時不必重新去申請鎖,可以直接執行相關代碼
    • ReentrantLock 和 synchronized 都是可重入鎖
  • 公平鎖
    • 定義:等待時間最久的線程會優先獲得鎖
    • 非公平鎖無法保證哪個線程獲取到鎖,synchronized 就是非公平鎖
    • ReentrantLock 默認時非公平鎖,可以設置爲公平鎖
  • 樂觀鎖和悲觀鎖
    • 悲觀鎖:線程一旦得到鎖,其他線程就掛起等待,適用於寫入操作頻繁的場景;synchronized 就是悲觀鎖
    • 樂觀鎖:假設沒有衝突,不加鎖,更新數據時判斷該數據是否過期,過期的話則不進行數據更新,適用於讀取操作頻繁的場景
    • 樂觀鎖 CAS:Compare And Swap,更新數據時先比較原值是否相等,不相等則表示數據過去,不進行數據更新
    • 樂觀鎖實現:AtomicInteger、AtomicLong、AtomicBoolean
  • 死鎖 4 個必要條件
    • 互斥
    • 佔有且等待
    • 不可搶佔
    • 循環等待
  • Synchronized 原理
    • 每個對象都有一個監視器鎖:monitor,同步代碼塊會執行 monitorenter 開始,motnitorexit 結束
    • Wait/notify 就依賴 monitor 監視器,所以在非同步代碼塊中執行會報 IllegalMonitorStateException 異常

3.Java 虛擬機&內存結構&GC&類加載&四種引用&動態代理

JVM

  • 定義:可以理解成一個虛構的計算機,解釋自己的字節碼指令集映射到本地 CPU 或 OS 的指令集,上層只需關注 Class 文件,與操作系統無關,實現跨平臺
  • Kotlin 就是能解釋成 Class 文件,所以可以跑在 JVM 上

JVM 內存模型

  • Java 多線程之間是通過共享內存來通信的,每個線程都有自己的本地內存
  • 共享變量存放於主內存中,線程會拷貝一份共享變量到本地內存
  • volatile 關鍵字就是給內存模型服務的,用來保證內存可見性和順序性

JVM 內存結構

  • 線程私有:
    * 1.程序計數器:記錄正在執行的字節碼指令地址,若正在執行 Native 方法則爲空
    * 2.虛擬機棧:執行方法時把方法所需數據存爲一個棧幀入棧,執行完後出棧
    * 3.本地方法棧:同虛擬機棧,但是針對的是 Native 方法
  • 線程共享:
    * 1.堆:存儲 Java 實例,GC 主要區域,分代收集 GC 方法會吧堆劃分爲新生代、老年代
    * 2.方法區:存儲類信息,常量池,靜態變量等數據

GC

  • 回收區域:只針對堆、方法區;線程私有區域數據會隨線程結束銷燬,不用回收
  • 回收類型:
    1.堆中的對象
    * 分代收集 GC 方法會吧堆劃分爲新生代、老年代
    * 新生代:新建小對象會進入新生代;通過複製算法回收對象
    * 老年代:新建大對象及老對象會進入老年代;通過標記-清除算法回收對象
    2.方法區中的類信息、常量池
  • 判斷一個對象是否可被回收:
    1.引用計數法
    * 缺點:循環引用
    2.可達性分析法
    * 定義:從 GC ROOT 開始搜索,不可達的對象都是可以被回收的
    * GC ROOT :
    * 1.虛擬機棧/本地方法棧中引用的對象
    * 2.方法區中常量/靜態變量引用的對象

四種引用

  • 強引用:不會被回收
  • 軟引用:內存不足時會被回收
  • 弱引用:gc 時會被回收
  • 虛引用:無法通過虛引用得到對象,可以監聽對象的回收

ClassLoader

  • 類的生命週期:
    1.加載;2.驗證;3.準備;4.解析;5.初始化;6.使用;7.卸載
  • 類加載過程:
    1.加載:獲取類的二進制字節流;生成方法區的運行時存儲結構;在內存中生成 Class 對象
    2.驗證:確保該 Class 字節流符合虛擬機要求
    3.準備:初始化靜態變量
    4.解析:將常量池的符號引用替換爲直接引用
    5.初始化:執行靜態塊代碼、類變量賦值
  • 類加載時機:
    1.實例化對象
    2.調用類的靜態方法
    3.調用類的靜態變量(放入常量池的常量除外)
  • 類加載器:負責加載 class 文件
    * 分類:
    1.引導類加載器 - 沒有父類加載器
    2.拓展類加載器 - 繼承自引導類加載器
    3.系統類加載器 - 繼承自拓展類加載器
  • 雙親委託模型:
    * 當要加載一個 class 時,會先逐層向上讓父加載器先加載,加載失敗纔會自己加載
    * 爲什麼叫雙親?不考慮自定義加載器,系統類加載器需要網上詢問兩層,所以叫雙親
    * 判斷是否是同一個類時,除了類信息,還必須時同一個類加載器
    * 優點:
    * 防止重複加載,父加載器加載過了就沒必要加載了
    * 安全,防止篡改核心庫類

動態代理原理及實現

  • InvocationHandler 接口,動態代理類需要實現這個接口
  • Proxy.newProxyInstance,用於動態創建代理對象
  • Retrofit 應用: Retrofit 通過動態代理,爲我們定義的請求接口都生成一個動態代理對象,實現請求



作者:王英豪
鏈接:https://www.jianshu.com/p/f3e98dbfb918
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。

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