java知識點總結(一)

  1. 談談你對Java平臺的理解?“Java是解釋執行”,這句話正確嗎?

    Java本身是一種面向對象的語言,最顯著的特性有兩個方面,一是所謂的“書寫一次,到處運行”(Write once, run anywhere),能夠非常容易地獲得跨平臺能力;另外就是垃圾收
    集(GC, Garbage Collection),Java通過垃圾收集器(Garbage Collector)回收分配內存,大部分情況下,程序員不需要自己操心內存的分配和回收。
    我們日常會接觸到JRE(Java Runtime Environment)或者JDK(Java Development Kit)。 JRE,也就是Java運行環境,包含了JVM和Java類庫,以及一些模塊等。
    而JDK可以看作是JRE的一個超集,提供了更多工具,比如編譯器、各種診斷工具等。
    對於“Java是解釋執行”這句話,這個說法不太準確。我們開發的Java的源代碼,首先通過Javac編譯成爲字節碼(bytecode),然後,在運行時,通過 Java虛擬機(JVM)內嵌的
    解釋器將字節碼轉換成爲最終的機器碼。但是常見的JVM,比如我們大多數情況使用的Oracle JDK提供的Hotspot JVM,都提供了JIT(Just-In-Time)編譯器,也就是通常所說的
    動態編譯器,JIT能夠在運行時將熱點代碼編譯成機器碼,這種情況下部分熱點代碼就屬於編譯執行,而不是解釋執行了。

  2. Exception和Error有什麼區別?

    Exception和Error都是繼承了Throwable類,在Java中只有Throwable類型的實例纔可以被拋出(throw)或者捕獲(catch),它是異常處理機制的基本組成類型。

    Exception和Error體現了Java平臺設計者對不同異常情況的分類。Exception是程序正常運行中,可以預料的意外情況,可能並且應該被捕獲,進行相應處理。

    Error是指在正常情況下,不大可能出現的情況,絕大部分的Error都會導致程序(比如JVM自身)處於非正常的、不可恢復狀態。既然是非正常情況,所以不便於也不需要捕獲,常見的比如OutOfMemoryError之類,都是Error的子類。

    Exception又分爲可檢查(checked)異常和不檢查(unchecked)異常,可檢查異常在源代碼裏必須顯式地進行捕獲處理,這是編譯期檢查的一部分。前面我介紹的不可查的Error,是Throwable不是Exception。

    不檢查異常就是所謂的運行時異常,類似 NullPointerException、ArrayIndexOutOfBoundsException之類,通常是可以編碼避免的邏輯錯誤,具體根據需要來判斷是否需要捕獲,並不會在編譯期強制要求。

  3. 談談fnal、fnally、 fnalize有什麼不同?

    fnal可以用來修飾類、方法、變量,分別有不同的意義,fnal修飾的class代表不可以繼承擴展,fnal的變量是不可以修改的,而fnal的方法也是不可以重寫的(override)。

    fnally則是Java保證重點代碼一定要被執行的一種機制。我們可以使用try-fnally或者try-catch-fnally來進行類似關閉JDBC連接、保證unlock鎖等動作。

    fnalize是基礎類java.lang.Object的一個方法,它的設計目的是保證對象在被垃圾收集前完成特定資源的回收。fnalize機制現在已經不推薦使用,並且在JDK 9開始被標記爲deprecated。

  4. 強引用、軟引用、弱引用、幻象引用有什麼區別?具體使用場景是什麼?

    不同的引用類型,主要體現的是對象不同的可達性(reachable)狀態和對垃圾收集的影響。

    所謂強引用(“Strong” Reference),就是我們最常見的普通對象引用,只要還有強引用指向一個對象,就能表明對象還“活着”,垃圾收集器不會碰這種對象。對於一個普通的對象,如果沒有其他的引用關係,只要超過了引用的作用域或者顯式地將相應(強)引用賦值爲null,就是可以被垃圾收集的了,當然具體回收時機還是要看垃圾收集策略。

    軟引用(SoftReference),是一種相對強引用弱化一些的引用,可以讓對象豁免一些垃圾收集,只有當JVM認爲內存不足時,纔會去試圖回收軟引用指向的對象。JVM會確保在拋出OutOfMemoryError之前,清理軟引用指向的對象。軟引用通常用來實現內存敏感的緩存,如果還有空閒內存,就可以暫時保留緩存,當內存不足時清理掉,這樣就保證了使用緩存的同時,不會耗盡內存。

    弱引用(WeakReference)並不能使對象豁免垃圾收集,僅僅是提供一種訪問在弱引用狀態下對象的途徑。這就可以用來構建一種沒有特定約束的關係,比如,維護一種非強制性的映射關係,如果試圖獲取時對象還在,就使用它,否則重現實例化。它同樣是很多緩存實現的選擇。

    對於幻象引用,有時候也翻譯成虛引用,你不能通過它訪問對象。幻象引用僅僅是提供了一種確保對象被fnalize以後,做某些事情的機制,比如,通常用來做所謂的PostMortem清理機制,我在專欄上一講中介紹的Java平臺自身Cleaner機制等,也有人利用幻象引用監控對象的創建和銷燬。

  5. 理解Java的字符串,String、StringBufer、StringBuilder有什麼區別?

    String是Java語言非常基礎和重要的類,提供了構造和管理字符串的各種基本邏輯。它是典型的Immutable類,被聲明成爲fnal class,所有屬性也都是fnal的。也由於它的不可變性,類似拼接、裁剪字符串等動作,都會產生新的String對象。由於字符串操作的普遍性,所以相關操作的效率往往對應用性能有明顯影響。

    StringBufer是爲解決上面提到拼接產生太多中間對象的問題而提供的一個類,我們可以用append或者add方法,把字符串添加到已有序列的末尾或者指定位置。StringBufer本質是一個線程安全的可修改字符序列,它保證了線程安全,也隨之帶來了額外的性能開銷,所以除非有線程安全的需要,不然還是推薦使用它的後繼者,也就是StringBuilder。

    StringBuilder是Java 1.5中新增的,在能力上和StringBufer沒有本質區別,但是它去掉了線程安全的部分,有效減小了開銷,是絕大部分情況下進行字符串拼接的首選。

  6. 談談Java反射機制,動態代理是基於什麼原理?

    反射機制是Java語言提供的一種基礎功能,賦予程序在運行時自省(introspect,官方用語)的能力。通過反射我們可以直接操作類或者對象,比如獲取某個對象的類定義,獲取類聲明的屬性和方法,調用方法或者構造對象,甚至可以運行時修改類定義。

    動態代理是一種方便運行時動態構建代理、動態處理代理方法調用的機制,很多場景都是利用類似機制做到的,比如用來包裝RPC調用、面向切面的編程(AOP)。
    實現動態代理的方式很多,比如JDK自身提供的動態代理,就是主要利用了上面提到的反射機制。還有其他的實現方式,比如利用傳說中更高性能的字節碼操作機制,類似ASM、cglib(基於ASM)、Javassist等。

  7. int和Integer有什麼區別?談談Integer的值緩存範圍

    int是我們常說的整形數字,是Java的8個原始數據類型(Primitive Types,boolean、byte 、short、char、int、foat、double、long)之一。Java語言雖然號稱一切都是對象,但原始數據類型是例外。

    Integer是int對應的包裝類,它有一個int類型的字段存儲數據,並且提供了基本操作,比如數學運算、int和字符串之間轉換等。在Java 5中,引入了自動裝箱和自動拆箱功能(boxing/unboxing),Java可以根據上下文,自動進行轉換,極大地簡化了相關編程。

    關於Integer的值緩存,這涉及Java 5中另一個改進。構建Integer對象的傳統方式是直接調用構造器,直接new一個對象。但是根據實踐,我們發現大部分數據操作都是集中在有限的、較小的數值範圍,因而,在Java 5中新增了靜態工廠方法valueOf,在調用它的時候會利用一個緩存機制,帶來了明顯的性能改進。按照Javadoc,這個值默認緩存
    是-128到127之間。

  8. 對比Vector、ArrayList、LinkedList有何區別?

    這三者都是實現集合框架中的List,也就是所謂的有序集合,因此具體功能也比較近似,比如都提供按照位置進行定位、添加或者刪除的操作,都提供迭代器以遍歷其內容等。但因爲具體的設計區別,在行爲、性能、線程安全等方面,表現又有很大不同。

    Vector是Java早期提供的線程安全的動態數組,如果不需要線程安全,並不建議選擇,畢竟同步是有額外開銷的。Vector內部是使用對象數組來保存數據,可以根據需要自動的增加容量,當數組已滿時,會創建新的數組,並拷貝原有數組數據。

    ArrayList是應用更加廣泛的動態數組實現,它本身不是線程安全的,所以性能要好很多。與Vector近似,ArrayList也是可以根據需要調整容量,不過兩者的調整邏輯有所區別,Vector在擴容時會提高1倍,而ArrayList則是增加50%。

    LinkedList顧名思義是Java提供的雙向鏈表,所以它不需要像上面兩種那樣調整容量,它也不是線程安全的。

  9. 對比Hashtable、HashMap、TreeMap有什麼不同?談談你對HashMap的掌握。

    Hashtable、HashMap、TreeMap都是最常見的一些Map實現,是以鍵值對的形式存儲和操作數據的容器類型。

    Hashtable是早期Java類庫提供的一個哈希表實現,本身是同步的,不支持null鍵和值,由於同步導致的性能開銷,所以已經很少被推薦使用。

    HashMap是應用更加廣泛的哈希表實現,行爲上大致上與HashTable一致,主要區別在於HashMap不是同步的,支持null鍵和值等。通常情況下,HashMap進行put或者get操作,可以達到常數時間的性能,所以它是絕大部分利用鍵值對存取場景的首選,比如,實現一個用戶ID和用戶信息對應的運行時存儲結構。

    TreeMap則是基於紅黑樹的一種提供順序訪問的Map,和HashMap不同,它的get、put、remove之類操作都是O(log(n))的時間複雜度,具體順序可以由指定的Comparator來決定,或者根據鍵的自然順序來判斷。

  10. 如何保證容器是線程安全的?ConcurrentHashMap如何實現高效地線程安全?

    Java提供了不同層面的線程安全支持。在傳統集合框架內部,除了Hashtable等同步容器,還提供了所謂的同步包裝器(Synchronized Wrapper),我們可以調用Collections工具類提供的包裝方法,來獲取一個同步的包裝容器(如Collections.synchronizedMap),但是它們都是利用非常粗粒度的同步方式,在高併發情況下,性能比較低下。

    另外,更加普遍的選擇是利用併發包提供的線程安全容器類,它提供了:

    • 各種併發容器,比如ConcurrentHashMap、CopyOnWriteArrayList。
    • 各種線程安全隊列(Queue/Deque),如ArrayBlockingQueue、SynchronousQueue。
    • 各種有序容器的線程安全版本等。

    具體保證線程安全的方式,包括有從簡單的synchronize方式,到基於更加精細化的,比如基於分離鎖實現的ConcurrentHashMap等併發實現等。具體選擇要看開發的場景需求,
    總體來說,併發包內提供的容器通用場景,遠優於早期的簡單同步實現。

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