1. Java中的原始資料型別都有哪些,它們的大小及對應的封裝類別是什麼?
(1)boolean
boolean資料型態非true即false。這個資料型態表示1 bit的信息,但是它的大小並沒有精確定義。
《Java虛擬機規格》中如是說:「雖然定義了boolean這種資料類型,但是只對它提供了非常有限的支援。在Java虛擬機中沒有任何供boolean值專用的字節碼指令,Java語言表達式所操作的boolean值,在編譯之後都使用Java虛擬機中的int資料類型來代替,而boolean數組將會被編碼成Java虛擬機的byte數組,每個元素boolean元素佔8位」。這樣我們可以得到boolean型別單獨使用是4個位元組,在陣列中又是1個位元組。那虛擬機器為什麼要用int來取代boolean呢?為什麼不用byte或short,這樣不是更節省記憶體空間嗎?實際上,使用int的原因是,對於當下32位元的CPU來說,一次進行32位元的資料交換更有效率。
綜上,我們可以知道:官方文件對boolean類型沒有給出精確的定義,《Java虛擬機規範》給出了「單獨時使用4個字節,boolean數組時1個字節」的定義,具體還要看虛擬機器實作是否依照規範來,所以1個位元組、4個位元組都是有可能的。這其實是一種時空權衡。
boolean類型的封裝類別是Boolean。
(2)byte——1 byte——Byte
(3)short——2 bytes——Short
(4)int——4 bytes——Integer
(5)long——8 bytes— —Long
(6)float——4 bytes——Float
(7)double——8 bytes——Double
(8)char——2 bytes——Character
”==“與”equals()"的區別。
《Think in Java》中說:「關係運算子產生的是一個boolean結果,它們計算的是運算元的值之間的關係」。
"=="判斷的是兩個物件的記憶體位址是否一樣,適用於原始資料型別和枚舉型別(它們的變數儲存的是值本身,而引用型別變數所儲存的是引用);equals是Object類別的方法,Object對它的實作是比較記憶體位址,我們可以重寫這個方法來自訂「相等」這個概念。例如類別庫中的String、Date等類別就對這個方法進行了重寫。
綜上,對於枚舉型別和原始資料型別的相等性比較,應該使用"==";對於引用型別的相等性比較,應該使用equals法。
3. Java中的四個引用及其應用場景為何?
強引用: 通常我們使用new運算元建立物件時所傳回的參考即為強引用
軟引用: 若一個物件只能透過軟引用到達,那麼這個物件在記憶體不足時會被回收,可用於圖片快取中,記憶體不足時系統會自動回收不再使用的Bitmap
弱引用: 若物件只能透過弱引用到達,那麼它就會被回收(即使記憶體充足),同樣可用於圖片快取中,這時候只要Bitmap不再使用就會被回收
虛引用: 虛引用是Java中最「弱」的引用,透過它甚至無法取得被引用的對象,它存在的唯一作用就是當它指向的對象回收時,它本身會被加入到引用佇列中,這樣我們就可以知道它指向的物件何時被銷毀。
4. object中定義了哪些方法?
clone(), equals(), hashCode(), toString(), notify(), notifyAll(), wait(), finalize(), getClass()
作用是什麼?
🎜 請參閱大量清單的基本原則與實作🎜🎜 🎜6. ArrayList, LinkedList, Vector的差別是什麼?
ArrayList: 內部採用數組存儲元素,支援高效隨機訪問,支援動態調整大小
LinkedList: 內部採用鍊錶來存儲元素,支援快速插入/刪除元素,但不支援高效地隨機訪問
Vector: 可以看作線程安全版的ArrayList
7. String, StringBuilder, StringBuffer的差別是什麼?
String: 不可變的字元序列,若要在其中新增字元需要建立一個新的String物件
StringBuilder: 可變字元序列,支援在其中新增字元(無需建立新物件)
StringBuffer: 可以看作線程安全版的StringBuilder
8. Map, Set, List, Queue、Stack的特性及用法。
Map
Set
List
Queue
Stack
更詳細的說明請參考官方文檔,對相關資料結構較不熟悉的同學可以參考《演算法導論》或其他相關書籍。
9. HashMap和HashTable的區別
HashTable是線程安全的,而HashMap不是
HashMap中允許存在null鍵和null值,而HashMap不是
HashMap中允許存在null鍵和null值,而HashTable中不允許詳細參考
HashMap 、HashTable
10. HashMap的實作原理
簡單的說,HashMap的底層實作是「基於拉鍊法的散列表」。詳細分析請參考深入解析HashMap、HashTable
11. ConcurrentHashMap的實作原理
ConcurrentHashMap是支援並發讀寫的HashMap,它的特性是讀取資料時無需加鎖,寫資料時可確保加鎖盡可能的小。由於其內部採用“分段儲存”,只需對要進行寫入操作的資料所在的“段”進行加鎖。關於ConcurrentHashMap底層實作的詳細分析請參考Java並發程式設計:並發容器之ConcurrentHashMap
12. TreeMap, LinkedHashMap, HashMap的差別是什麼?
HashMap的底層實作是散列列表,因此它內部儲存的元素是無序的;
TreeMap的底層實作是紅黑樹,所以它內部的元素的有序的。排序的依據是自然序或是創建TreeMap時所提供的比較器(Comparator)物件。
LinkedHashMap能夠記住插入元素的順序。
更詳細的說明請參考HashMap,LinkedMap,TreeMap的差異
Collection
對Java集合框架還不太熟悉的小伙伴請參考Java核心技術點之集合框架
14. 對於“try-catch-finally”,若try語句區塊中包含“return”語句,finally語句區塊會執行嗎?
呼叫了System.exit()方法;
JVM「崩潰」了。
15. Java中的異常層次結構
Java中的異常層次結構如下圖:
🎜🎜我們可以看到Throwable類別是異常層級中的基底類別。 Error類別表示內部錯誤,這類錯誤使我們無法控制的;Exception表示異常,RuntimeException及其子類別屬於未檢查異常,這類異常包括ArrayIndexOutOfBoundsException、NullPointerException等,我們應該透過條件判斷等方式語句避免未檢查異常值的發生。 IOException及其子類別屬於已檢查異常,編譯器會檢查我們是否為所有可能拋出的已檢查異常提供了異常處理器,若沒有則會報錯。對於未檢查異常,我們無需捕獲(當然Java也允許我們捕獲,但我們應該做的事避免未檢查異常的發生)。
16. Java物件導向的三個特徵與意義
三大特徵:封裝、繼承、多態。詳細介紹請戳Java物件導向三大特性
17. Override, Overload的含義與區別
Override表示“重寫”,是子類別對父類別中同一方法的重新定義
Overload表示“重寫”,是子類別對父類別中同一方法的重新定義
18. 介面與抽象類別的區別
介面是一種約定,實現介面的類別要遵循這個約定;抽象化;抽象化;抽象類別本質上是一個類,使用抽象類別的代價要比介面大。介面與抽象類別的對比如下:
抽象類別中可以包含屬性,方法(包含抽象方法與有著具體實作的方法),常數;介面只能包含常數與方法宣告。
抽象類別中的方法和成員變數可以定義可見性(例如public、private等);而介面中的方法只能為public(缺省為public)。
一個子類別只能有一個父類別(具體類別或抽象類別);而一個介面可以繼承一個多個接口,一個類別也可以實作多個介面。
子類別中實作父類別中的抽象方法時,可見性可以大於等於父類別中的;而介面實作類別中的介面 方法的可見性只能與介面中相同(public)。
19. 靜態內部類別與非靜態內部類別的區別
靜態內部類別不會持有外圍類別的引用,而非靜態內部類別會隱式持有外圍類別的一個引用。
欲進一步了解內部類,請戳Java核心技術點之內部類
20. Java中多態的實作原理
所謂多態,指的就是父類引用方法會呼叫子類別的實作而不是父類別的實作。多態的實現的關鍵在於「動態綁定」。詳細介紹請戳Java動態綁定的內部實作機制
21. 簡述Java中創建新執行緒的兩種方法
繼承Thread類別(假設子類別為MyThread),並重寫run()方法,然後new一個MyThread物件並對其呼叫start()即可啟動新執行緒。
實作Runnable介面(假設實作類別為MyRunnable),而後將MyRunnable物件作為參數傳入Thread建構器,在得到的Thread物件上呼叫start()方法即可。
22. 簡述Java中進行線程同步的方法
volatile: Java Memory Model保證了對同一個volatile變量的寫happens before對它的讀;
synchronized: 塊可以來對一個代碼或塊是可以來對一個代碼來對一個代碼來是對一個代碼來對一個代碼
synchronized對一個方法上鎖,被「鎖住」的地方稱為臨界區,進入臨界區的執行緒會取得物件的monitor,這樣其他嘗試進入臨界區的執行緒會因無法取得monitor而被阻塞。由於等待另一個線程釋放monitor而被阻塞的線程無法被中斷。 ReentrantLock: 嘗試取得鎖定的執行緒可以中斷並且可以設定逾時參數。 更詳細的介紹請戳Java核心技術點之多執行緒 23. 簡述Java中具有哪幾種粒度的鎖定 Java中可以對類別、物件、方法或是程式碼區塊鎖定。更詳細的介紹請戳Java核心技術點之多執行緒🎜🎜 🎜🎜24. 給出「生產者-消費者」問題的解決方案🎜🎜 使用阻塞佇列:🎜public class BlockingQueueTest { private int size = 20; private ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(size); public static void main(String[] args) { BlockingQueueTest test = new BlockingQueueTest(); Producer producer = test.new Producer(); Consumer consumer = test.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { while(true){ try { //从阻塞队列中取出一个元素 queue.take(); System.out.println("队列剩余" + queue.size() + "个元素"); } catch (InterruptedException e) { } } } } class Producer extends Thread{ @Override public void run() { while (true) { try { //向阻塞队列中插入一个元素 queue.put(1); System.out.println("队列剩余空间:" + (size - queue.size())); } catch (InterruptedException e) { } } } } }
25. ThreadLocal的設計理念與作用
ThreadLocal的作用是提供線程內的局部變量,在多線程環境下訪問時能保證各個線程內的ThreadLocal變量各自獨立。也就是說,每個執行緒的ThreadLocal變數是自己專用的,其他執行緒是無法存取的。 ThreadLocal最常用於以下這個場景:多線程環境下存在對非線程安全物件的並發訪問,而且該物件不需要在線程間共享,但是我們不想加鎖,這時候可以使用ThreadLocal來使得每個線程都持有一個該物件的副本。
關於ThreadLocal的實作原理分析請戳深入剖析ThreadLocal
26. concurrent包的整體架構
27. ArrayBlockingQueue, CountDownLatch Count17.ACount18適用場景: 當一個或多個執行緒需要等待指定數目的事件發生後再繼續執行。
ArrayBlockingQueue: 一個基於陣列實作的阻塞佇列,它在建構時需要指定容量。當試圖向滿隊列中新增元素或從空隊列中移除元素時,當前執行緒會被阻塞。透過阻塞隊列,我們可以按以下模式來工作:工作者執行緒可以週期性的將中間結果放入阻塞隊列中,其它執行緒可以取出中間結果並進行進一步操作。若工作者執行緒的執行比較慢(還來不及向佇列插入元素),其他從佇列中取元素的執行緒會等待它(試圖從空佇列中取元素從而阻塞);若工作者執行緒執行較快(試圖向滿隊列中插入元素),則它會等待其它執行緒取出元素再繼續執行。
28. wait(),sleep() 的差異
wait(): Object類別中定義的實例方法。在指定物件上呼叫wait方法會讓目前執行緒進入等待狀態(前提是目前執行緒持有該物件的monitor),此時目前執行緒會釋放對應物件的monitor,這樣一來其它執行緒便有機會取得這個物件的monitor了。當其它執行緒取得了這個物件的monitor並進行了所需操作時,便可以呼叫notify方法喚醒之前進入等待狀態的執行緒。
sleep(): Thread類別中的靜態方法,作用是讓目前執行緒進入休眠狀態,以便讓其他執行緒有機會執行。進入休眠狀態的執行緒不會釋放它所持有的鎖。
29. 執行緒池的用法與優勢
優勢: 實現對執行緒的複用,避免了重複建立及銷毀執行緒的開銷;使用執行緒池統一管理執行緒可以減少並發執行緒的數目,而數數過多往往會在線程上下文切換上以及線程同步上浪費過多時間。
用法: 我們可以呼叫ThreadPoolExecutor的某個建構方法來自己建立一個執行緒池。但通常情況下我們可以使用Executors類別提供給我們的靜態工廠方法來更方便的建立一個線程池物件。創建了線程池物件後,我們就可以呼叫submit方法提交任務到線程池中去執行了;線程池使用完畢後我們要記得調用shutdown方法來關閉它。
關於線程池的詳細介紹以及實現原理分析請戳深入理解Java之線程池
30. for-each與常規for循環的效率對比
關於這個問題我們直接看《Effect Java》《我們直接看給我們做的解答:
for-each能够让代码更加清晰,并且减少了出错的机会。下面的惯用代码适用于集合与数组类型: for (Element e : elements) { doSomething(e); }使用for-each循环与常规的for循环相比,并不存在性能损失,即使对数组进行迭代也是如此。实际上,在有些场合下它还能带来微小的性能提升,因为它只计算一次数组索引的上限。
31. 簡述Java IO與NIO的區別
Java IO是面向流的,這意味著我們需要每次從流中讀取一個或多個字節,直到讀取完所有位元組;NIO是面向緩衝的,也就是說會把資料讀取到一個緩衝區中,然後對緩衝區中的資料進行相應處理。
Java IO是阻塞IO,而NIO是非阻塞IO。
Java NIO中存在一個稱為選擇器(selector)的東西,它允許你把多個通道(channel)註冊到一個選擇器上,然後使用一個線程來監視這些通道:如果這些通道裡有某個準備好可以開始進行讀取或寫入操作了,則開始對對應的通道進行讀寫。而在等待某通道變成可讀/寫期間,請求對通道進行讀寫操作的執行緒可以去幹別的事情。
更進一步的說明請戳Java NIO與IO
32. 反射的作用與原理
反射的作用概括地說是運作時獲取類別的各種定義資訊,例如定義了哪些屬性與方法。原理是透過類別的class物件來獲取它的各種資訊。
詳細介紹請參考Java核心技術點之反射
33. Java中的泛型機制
對泛型電腦7與Java 8的新功能
這裡有兩篇總結的非常好的:Java 7的新特性 Java 8的新功能
35. 常見設計模式,而是物件
程式設計中一些常用的軟體設計手法,並且經過實踐的檢驗,這些設計手法在各自的場景下能解決一些需求,因此它們就成為瞭如今廣為流傳的」設計模式「。也就是說,正式因為在某些場景下產生了一些棘手的問題,才催生了對應的設計模式。明確了這一點,我們在學習某種設計模式時要充分理解它產生的背景以及它所解決的主要矛盾是什麼。 常用的設計模式可分為以下三大類:創建型模式: 包含工廠模式(可進一步分為簡單工廠模式、工廠方法模式、抽象工廠模式)、建造者模式、單例模式。 結構型模式: 包含適配器模式、橋接模式、裝飾模式、外觀模式、享元模式、代理模式。 行為型模式: 包含指令模式、中介者模式、觀察者模式、狀態模式、策略模式。 關於每個模式具體的介紹請參考圖片說設計模式 36. JNI的基本用法 關於JNI,這裡有篇好文:Android中的JNI場景及原理
關於動態代理,請直接參見Java核心技術點之動態代理
38. 註解的基本概念與使用
註解可以看作是“增強版的註釋”,它可以向編譯器、虛擬機器說明一些事情。
註解是描述Java程式碼的程式碼,它能夠被編譯器解析,註解處理工具在執行時也能夠解析註解。註解本身就是「被動」的訊息,只有主動解析它才有意義。
除了向編譯器/虛擬機傳遞訊息,我們也可以使用註解來產生一些「模板化」的程式碼。
這些就夠了嗎?當然不夠。上面列出了面試中關於Java的常見問題,同時大多也是Java技術體系的核心技術點,透過這些問題而引發出的一系列問題正是為我們指出了完善自身知識體系的一條道路,我們要做的是順著這條道路堅持走下去:)
更多Java面試知識點總結相關文章請關注PHP中文網!