Java學習---面試基礎知識點總結
Java中sleep和wait的區別
① 這兩個方法來自不同的類分別是,sleep來自Thread類,和wait來自Object類。 sleep是Thread的靜態類方法,誰調用的誰去睡覺,即使在a線程裏調用b的sleep方法,實際上還是a去睡覺,要讓b線程睡覺要在b的代碼中調用sleep。 ② 鎖: 最主要是sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法。 sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其他線程可以占用CPU。一般wait不會加時間限制,因為如果wait線程的運行資源不夠,再出來也沒用,要等待其他線程調用notify/notifyAll喚醒等待池中的所有線程,才會進入就緒隊列等待OS分配系統資源。sleep(milliseconds)可以用時間指定使它自動喚醒過來,如果時間不到只能調用interrupt()強行打斷。 Thread.sleep(0)的作用是“觸發操作系統立刻重新進行一次CPU競爭”。 ③ 使用範圍:wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep可以在任何地方使用。 synchronized(x){ x.notify() //或者wait() }
Java中HashMap和HashTable的區別
① 歷史原因: Hashtable是給予陳舊的Dictonary類的, HashMap是Java1.2引進的Map接口的一個實現 ② HashMap允許空的鍵值對, 而HashTable不允許 ③ HashTable同步,而HashMap非同步,效率上比HashTable要高
請簡述在異常當中,throw和throws有什麽區別
① throw代表動作,表示拋出一個異常的動作;throws代表一種狀態,代表方法可能有異常拋出 ② throw用在方法實現中,而throws用在方法聲明中 ③ throw只能用於拋出一種異常,而throws可以拋出多個異常
forward和redirect的區別
forward: A -> B -> C redirect: A -> B A -> C 1.從地址欄顯示來說 forward是服務器請求資源,服務器直接訪問目標地址的URL,把那個URL的響應內容讀取過來,然後把這些內容再發給瀏覽器.瀏覽器根本不知道服務器發送的內容從哪裏來的,所以它的地址欄還是原來的地址. redirect是服務端根據邏輯,發送一個狀態碼,告訴瀏覽器重新去請求那個地址.所以地址欄顯示的是新的URL. 2.從數據共享來說 forward:轉發頁面和轉發到的頁面可以共享request裏面的數據. redirect:不能共享數據. 3.從運用地方來說 forward:一般用於用戶登陸的時候,根據角色轉發到相應的模塊. redirect:一般用於用戶註銷登陸時返回主頁面和跳轉到其它的網站等. 4.從效率來說 forward:高. redirect:低.
關於內存溢出的小結
內存溢出 out of memory,是指程序在申請內存時,沒有足夠的內存空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是內存溢出。 內存泄露 memory leak,是指程序在申請內存後,無法釋放已申請的內存空間,一次內存泄露危害可以忽略,但內存泄露堆積後果很嚴重,無論多少內存,遲早會被占光。 memory leak會最終會導致out of memory!內存泄漏是指你向系統申請分配內存進行使用(new),可是使用完了以後卻不歸還(delete),結果你申請到的那塊內存你自己也不能再訪問(也許你把它的地址給弄丟了),而系統也不能再次將它分配給需要的程序。
關於枚舉的小結
枚舉類型的那些事:https://www.cnblogs.com/hyl8218/p/5088287.html
枚舉類型符合通用模式 Class Enum<E extends Enum<E>>,而 E 表示枚舉類型的名稱。枚舉類型的每一個值都將映射到 protected Enum(String name, int ordinal) 構造函數中,在這裏,每個值的名稱都被轉換成一個字符串,並且序數設置表示了此設置被創建的順序。
HTTP返回碼分析
301:- 永久移動。請求的資源已被永久的移動到新URI,返回信息會包括新的URI,瀏覽器會自動定向到新URI。今後任何新的請求都應使用新的URI代替
302:- 臨時移動。與301類似。但資源只是臨時被移動。客戶端應繼續使用原有URI
304:- 如果客戶端發送了一個帶條件的GET 請求且該請求已被允許,而文檔的內容(自上次訪問以來或者根據請求的條件)並沒有改變,則服務器應當返回這個304狀態碼..
403:- 代表客戶端錯誤,指的是服務器端有能力處理該請求,但是拒絕授權訪問。
404:- 服務器無法根據客戶端的請求找到資源(網頁)。通過此代碼,網站設計人員可設置"您所請求的資源無法找到"的個性頁面
500:- 服務器內部錯誤,無法完成請求
Java中的原始數據類型都有哪些,它們的大小及對應的封裝類是什麽?
- byte——1 byte——Byte
- short——2 bytes——Short
- int——4 bytes——Integer
- long——8 bytes——Long
- float——4 bytes——Float
- double——8 bytes——Double
- char——2 bytes——Character
- boolean——————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。
談一談”==“與”equals()"的區別。
《Think in Java》中說:“關系操作符生成的是一個boolean結果,它們計算的是操作數的值之間的關系”。 "=="判斷的是兩個對象的內存地址是否一樣,適用於原始數據類型和枚舉類型(它們的變量存儲的是值本身,而引用類型變量存儲的是引用);equals是Object類的方法,Object對它的實現是比較內存地址,我們可以重寫這個方法來自定義“相等”這個概念。比如類庫中的String、Date等類就對這個方法進行了重寫。 綜上,對於枚舉類型和原始數據類型的相等性比較,應該使用"==";對於引用類型的相等性比較,應該使用equals方法。
Java中的四種引用及其應用場景是什麽?
- 強引用: 通常我們使用new操作符創建一個對象時所返回的引用即為強引用
- 軟引用: 若一個對象只能通過軟引用到達,那麽這個對象在內存不足時會被回收,可用於圖片緩存中,內存不足時系統會自動回收不再使用的Bitmap
- 弱引用: 若一個對象只能通過弱引用到達,那麽它就會被回收(即使內存充足),同樣可用於圖片緩存中,這時候只要Bitmap不再使用就會被回收
- 虛引用: 虛引用是Java中最“弱”的引用,通過它甚至無法獲取被引用的對象,它存在的唯一作用就是當它指向的對象回收時,它本身會被加入到引用隊列中,這樣我們可以知道它指向的對象何時被銷毀。
object中定義了哪些方法?
- clone()
- equals()
- hashCode()
- toString()
- notify()
- notifyAll()
- wait()
- finalize()
- getClass()
hashCode的作用是什麽?
請參考散列表的基本原理與實現
ArrayList, LinkedList, Vector的區別是什麽?
- ArrayList:內部采用數組存儲元素,支持高效隨機訪問,支持動態調整大小
- LinkedList:內部采用鏈表來存儲元素,支持快速插入/刪除元素,但不支持高效地隨機訪問
- Vector: 可以看作線程安全版的ArrayList
String, StringBuilder, StringBuffer的區別是什麽?
- String: 不可變的字符序列,若要向其中添加新字符需要創建一個新的String對象
- StringBuilder: 可變字符序列,支持向其中添加新字符(無需創建新對象)
- StringBuffer: 可以看作線程安全版的StringBuilder
Map, Set, List, Queue、Stack的特點及用法。
- Map<k, v="">:Java中存儲鍵值對的數據類型都實現了這個接口,表示“映射表”。支持的兩個核心操作是get(Object key)以及put(K key, V value),分別用來獲取鍵對應的值以及向映射表中插入鍵值對。</k,>
- Set<e>:實現了這個接口的集合類型中不允許存在重復的元素,代表數學意義上的“集合”。它所支持的核心操作有add(E e), remove(Object o), contains(Object o),分別用於添加元素,刪除元素以及判斷給定元素是否存在於集中。
- List<e>: Java中集合框架中的列表類型都實現了這個接口,表示一種有序序列。支持get(int index), add(E e)等操作。
- Queue<e>:Java集合框架中的隊列接口,代表了“先進先出”隊列。支持add(E element),remove()等操作。
- Stack<e>:Java集合框架中表示堆棧的數據類型,堆棧是一種“後進先出”的數據結構。支持push(E item), pop()等操作。
更詳細的說明請參考官方文檔,對相關數據結構不太熟悉的同學可以參考《算法導論》或其他相關書籍。
HashMap和HashTable的區別
- HashTable是線程安全的,而HashMap不是
- HashMap中允許存在null鍵和null值,而HashTable中不允許
HashMap的實現原理
簡單的說,HashMap的底層實現是“基於拉鏈法的散列表”。
詳細分析請參考 Map源碼解析之HashMap源碼分析
ConcurrentHashMap的實現原理
ConcurrentHashMap是支持並發讀寫的HashMap,它的特點是讀取數據時無需加鎖,寫數據時可以保證加鎖粒度盡可能的小。由於其內部采用“分段存儲”,只需對要進行寫操作的數據所在的“段”進行加鎖。關於ConcurrentHashMap底層實現的詳細分析請參考 Java並發編程:並發容器之ConcurrentHashMap
TreeMap, LinkedHashMap, HashMap的區別是什麽?
- HashMap的底層實現是散列表,因此它內部存儲的元素是無序的;
- TreeMap的底層實現是紅黑樹,所以它內部的元素的有序的。排序的依據是自然序或者是創建TreeMap時所提供的比較器(Comparator)對象。
- LinkedHashMap可以看作能夠記住插入元素的順序的HashMap。
Collection與Collections的區別是什麽?
- Collection<e>是Java集合框架中的基本接口;
- Collections是Java集合框架提供的一個工具類,其中包含了大量用於操作或返回集合的靜態方法。
對於“try-catch-finally”,若try語句塊中包含“return”語句,finally語句塊會執行嗎?
會執行。只有兩種情況finally塊中的語句不會被執行:
- 調用了System.exit()方法;
- JVM“崩潰”了。
Java中的異常層次結構
Java中的異常層次結構如下圖所示:
我們可以看到Throwable類是異常層級中的基類。
Error類表示內部錯誤,這類錯誤使我們無法控制的;Exception表示異常,RuntimeException及其子類屬於未檢查異常,這類異常包括ArrayIndexOutOfBoundsException、NullPointerException等,我們應該通過條件判斷等方式語句避免未檢查異常的發生。IOException及其子類屬於已檢查異常,編譯器會檢查我們是否為所有可能拋出的已檢查異常提供了異常處理器,若沒有則會報錯。對於未檢查異常,我們無需捕獲(當然Java也允許我們捕獲,但我們應該做的事避免未檢查異常的發生)。
Java面向對象的三個特征與含義
三大特征:封裝、繼承、多態。
Override, Overload的含義與區別
- Override表示“重寫”,是子類對父類中同一方法的重新定義
- Overload表示“重載”,也就是定義一個與已定義方法名稱相同但簽名不同的新方法
接口與抽象類的區別
- 接口是一種約定,實現接口的類要遵循這個約定;
- 抽象類本質上是一個類,使用抽象類的代價要比接口大。
- 接口與抽象類的對比如下:
抽象類中可以包含屬性,方法(包含抽象方法與有著具體實現的方法),常量;接口只能包含常量和方法聲明。
抽象類中的方法和成員變量可以定義可見性(比如 public、private等);而接口中的方法只能為public(缺省為public)。
一個子類只能有一個父類(具體類或抽象類);而一個接口可以繼承一個多個接口,一個類也可以實現多個接口。
子類中實現父類中的抽象方法時,可見性可以大於等於父類中的;而接口實現類中的接口 方法的可見性只能與接口中相同(public)。
靜態內部類與非靜態內部類的區別
靜態內部類不會持有外圍類的引用,而非靜態內部類會隱式持有外圍類的一個引用。
Java中多態的實現原理
所謂多態,指的就是父類引用指向子類對象,調用方法時會調用子類的實現而不是父類的實現。多態的實現的關鍵在於“動態綁定”。詳細介紹請戳 Java動態綁定的內部實現機制
簡述Java中創建新線程的兩種方法
- 繼承Thread類(假設子類為MyThread),並重寫run()方法,然後new一個MyThread對象並對其調用start()即可啟動新線程。
- 實現Runnable接口(假設實現類為MyRunnable),而後將MyRunnable對象作為參數傳入Thread構造器,在得到的Thread對象上調用start()方法即可。
簡述Java中進行線程同步的方法
- volatile: Java Memory Model保證了對同一個volatile變量的寫happens before對它的讀;
- synchronized:可以來對一個代碼塊或是對一個方法上鎖,被“鎖住”的地方稱為臨界區,進入臨界區的線程會獲取對象的monitor,這樣其他嘗試進入臨界區的線程會因無法獲取monitor而被阻塞。由於等待另一個線程釋放monitor而被阻塞的線程無法被中斷。
- ReentrantLock: 嘗試獲取鎖的線程可以被中斷並可以設置超時參數。
簡述Java中具有哪幾種粒度的鎖
Java中可以對類、對象、方法或是代碼塊上鎖。
給出“生產者-消費者”問題的一種解決方案
使用阻塞隊列:
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) { } } } } }
ThreadLocal的設計理念與作用
ThreadLocal的作用是提供線程內的局部變量,在多線程環境下訪問時能保證各個線程內的ThreadLocal變量各自獨立。也就是說,每個線程的ThreadLocal變量是自己專用的,其他線程是訪問不到的。ThreadLocal最常用於以下這個場景:多線程環境下存在對非線程安全對象的並發訪問,而且該對象不需要在線程間共享,但是我們不想加鎖,這時候可以使用ThreadLocal來使得每個線程都持有一個該對象的副本。
concurrent包的整體架構
ArrayBlockingQueue, CountDownLatch類的作用
- CountDownLatch:允許線程集等待直到計數器為0。適用場景: 當一個或多個線程需要等待指定數目的事件發生後再繼續執行。
- ArrayBlockingQueue: 一個基於數組實現的阻塞隊列,它在構造時需要指定容量。當試圖向滿隊列中添加元素或者從空隊列中移除元素時,當前線程會被阻塞。通過阻塞隊列,我們可以按以下模式來工作:工作者線程可以周期性的將中間結果放入阻塞隊列中,其它線程可以取出中間結果並進行進一步操作。若工作者線程的執行比較慢(還沒來得及向隊列中插入元素),其他從隊列中取元素的線程會等待它(試圖從空隊列中取元素從而阻塞);若工作者線程執行較快(試圖向滿隊列中插入元素),則它會等待其它線程取出元素再繼續執行。
wait(),sleep() 的區別
- wait():Object類中定義的實例方法。在指定對象上調用wait方法會讓當前線程進入等待狀態(前提是當前線程持有該對象的monitor),此時當前線程會釋放相應對象的monitor,這樣一來其它線程便有機會獲取這個對象的monitor了。當其它線程獲取了這個對象的monitor並進行了所需操作時,便可以調用notify方法喚醒之前進入等待狀態的線程。
- sleep():Thread類中的靜態方法,作用是讓當前線程進入休眠狀態,以便讓其他線程有機會執行。進入休眠狀態的線程不會釋放它所持有的鎖。
線程池的用法與優勢
- 優勢: 實現對線程的復用,避免了反復創建及銷毀線程的開銷;使用線程池統一管理線程可以減少並發線程的數目,而線程數過多往往會在線程上下文切換上以及線程同步上浪費過多時間。
- 用法: 我們可以調用ThreadPoolExecutor的某個構造方法來自己創建一個線程池。但通常情況下我們可以使用Executors類提供給我們的靜態工廠方法來更方便的創建一個線程池對象。創建了線程池對象後,我們就可以調用submit方法提交任務到線程池中去執行了;線程池使用完畢後我們要記得調用shutdown方法來關閉它。
for-each與常規for循環的效率對比
關於這個問題我們直接看《Effective Java》給我們做的解答:
for-each能夠讓代碼更加清晰,並且減少了出錯的機會。 下面的慣用代碼適用於集合與數組類型:
for (Element e : elements) {
doSomething(e);
}
使用for-each循環與常規的for循環相比,並不存在性能損失,即使對數組進行叠代也是如此。實際上,在有些場合下它還能帶來微小的性能提升,因為它只計算一次數組索引的上限。
簡述Java IO與NIO的區別
Java IO是面向流的,這意味著我們需要每次從流中讀取一個或多個字節,直到讀取完所有字節;NIO是面向緩沖的,也就是說會把數據讀取到一個緩沖區中,然後對緩沖區中的數據進行相應處理。
Java IO是阻塞IO,而NIO是非阻塞IO。
Java NIO中存在一個稱為選擇器(selector)的東西,它允許你把多個通道(channel)註冊到一個選擇器上,然後使用一個線程來監視這些通道:若這些通道裏有某個準備好可以開始進行讀或寫操作了,則開始對相應的通道進行讀寫。而在等待某通道變為可讀/寫期間,請求對通道進行讀寫操作的線程可以去幹別的事情。
反射的作用與原理
反射的作用概括地說是運行時獲取類的各種定義信息,比如定義了哪些屬性與方法。原理是通過類的class對象來獲取它的各種信息。
Java中的泛型機制
關於泛型機制的詳細介紹請直接戳 Java核心技術點之泛型
常見設計模式
所謂“設計模式”,不過是面向對象編程中一些常用的軟件設計手法,並且經過實踐的檢驗,這些設計手法在各自的場景下能解決一些需求,因此它們就成為了如今廣為流傳的”設計模式“。也就是說,正式因為在某些場景下產生了一些棘手的問題,才催生了相應的設計模式。明確了這一點,我們在學習某種設計模式時要充分理解它產生的背景以及它所解決的主要矛盾是什麽。
常用的設計模式可以分為以下三大類:
- 創建型模式: 包括工廠模式(又可進一步分為簡單工廠模式、工廠方法模式、抽象工廠模式)、建造者模式、單例模式。
- 結構型模式: 包括適配器模式、橋接模式、裝飾模式、外觀模式、享元模式、代理模式。
- 行為型模式: 包括命令模式、中介者模式、觀察者模式、狀態模式、策略模式。
註解的基本概念與使用
註解可以看作是“增強版的註釋”,它可以向編譯器、虛擬機說明一些事情。 註解是描述Java代碼的代碼,它能夠被編譯器解析,註解處理工具在運行時也能夠解析註解。註解本身是“被動”的信息,只有主動解析它才有意義。 除了向編譯器/虛擬機傳遞信息,我們也可以使用註解來生成一些“模板化”的代碼。
Java學習---面試基礎知識點總結