1.Java中的原始數據類型都有哪些,它們的大小及對應的封裝類是什么?
數據類型 | 大小(字節) | 內存空間(8位等于1字節) | 包裝類 |
---|---|---|---|
byte | 1 | 8位 | Byte |
char | 2 | 16位 | Character |
short | 2 | 16位 | Short |
int | 4 | 32位 | Integer |
long | 8 | 64位 | Long |
float | 4 | 32位 | Float |
double | 8 | 64位 | Double |
boolean | - | - | 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個字節都是有可能的。這其實是一種時空權衡。
2.談一談”==“與”equals()"的區別。
《Think in Java》中說:“關系操作符生成的是一個boolean結果,它們計算的是操作數的值之間的關系”。
- "=="判斷的是兩個對象的內存地址是否一樣,適用于原始數據類型和枚舉類型(它們的變量存儲的是值本身,而引用類型變量存儲的是引用);
- equals是Object類的方法,Object對它的實現是比較內存地址,我們可以重寫這個方法來自定義“相等”這個概念。比如類庫中的String、Date等類就對這個方法進行了重寫。
綜上,對于枚舉類型和原始數據類型的相等性比較,應該使用"==";對于引用類型的相等性比較,應該使用equals方法。
3.Java中的四種引用及其應用場景是什么?
強引用: 通常我們使用new操作符創建一個對象時所返回的引用即為強引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內存空間不足,JVM寧愿拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。
軟引用: 若一個對象只能通過軟引用到達,那么這個對象在內存不足時會被回收,可用于圖片緩存中,內存不足時系統會自動回收不再使用的Bitmap
弱引用: 若一個對象只能通過弱引用到達,那么它就會被回收(即使內存充足),同樣可用于圖片緩存中,這時候只要Bitmap不再使用就會被回收
虛引用: 虛引用是Java中最“弱”的引用,通過它甚至無法獲取被引用的對象,它存在的唯一作用就是當它指向的對象回收時,它本身會被加入到引用隊列中,這樣我們可以知道它指向的對象何時被銷毀。
4.Object中定義了哪些方法?
Object是所有類的父類,任何類都默認繼承Object。
clone
保護方法,實現對象的淺復制,只有實現了Cloneable接口才可以調用該方法,否則拋出CloneNotSupportedException異常equals
在Object中與==是一樣的,子類一般需要重寫該方法hashCode
該方法用于哈希查找,重寫了equals方法一般都要重寫hashCode方法。這個方法在一些具有哈希功能的Collection中用到getClass
final方法,獲得運行時類型wait
使當前線程等待該對象的鎖,當前線程必須是該對象的擁有者,也就是具有該對象的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設定一個超時間隔,如果在規定時間內沒有獲得鎖就返回。
調用該方法后當前線程進入睡眠狀態,直到以下事件發生:
- 其他線程調用了該對象的notify方法
- 其他線程調用了該對象的notifyAll方法
- 其他線程調用了interrupt中斷該線程
- 時間間隔到了
此時該線程就可以被調度了,如果是被中斷的話就拋出一個InterruptedException異常
notify
喚醒在該對象上等待的某個線程notifyAll
喚醒在該對象上等待的所有線程toString
轉換成字符串,一般子類都有重寫,否則打印句柄
5.hashCode的作用是什么?
什么是HashCode,總結幾個關鍵點:
- HashCode的存在主要是為了查找的快捷性,HashCode是用來在散列存儲結構中確定對象的存儲地址的
- 如果兩個對象equals相等,那么這兩個對象的HashCode一定也相同
- 如果對象的equals方法被重寫,那么對象的HashCode方法也盡量重寫
- 如果兩個對象的HashCode相同,不代表兩個對象就相同,只能說明這兩個對象在散列存儲結構中,存放于同一個位置
HashCode有什么用?舉個例子:
- 假設內存中有0 1 2 3 4 5 6 7 8這8個位置,如果我有個字段叫做ID,那么我要把這個字段存放在以上8個位置之一,如果不用HashCode而任意存放,那么當查找時就需要到8個位置中去挨個查找
- 使用HashCode則效率會快很多,把ID的HashCode%8,然后把ID存放在取得余數的那個位置,然后每次查找該類的時候都可以通過ID的HashCode%8求余數直接找到存放的位置了
- 如果ID的HashCode%8算出來的位置上本身已經有數據了怎么辦?這就取決于算法的實現了,比如ThreadLocal中的做法就是從算出來的位置向后查找第一個為空的位置,放置數據;HashMap的做法就是通過鏈式結構連起來。反正,只要保證放的時候和取的時候的算法一致就行了。
- 如果ID的HashCode%8相等怎么辦(這種對應的是第三點說的鏈式結構的場景)?這時候就需要定義equals了。先通過HashCode%8來判斷類在哪一個位置,再通過equals來在這個位置上尋找需要的類。對比兩個類的時候也差不多,先通過HashCode比較,假如HashCode相等再判斷equals。如果兩個類的HashCode都不相同,那么這兩個類必定是不同的。
具體可以看以下文章:
談談HashCode的作用
散列表的原理與實現
6.ArrayList, LinkedList, Vector的區別是什么?
ArrayList: 內部采用數組存儲元素,支持高效隨機訪問,支持動態調整大小
LinkedList: 內部采用鏈表來存儲元素,支持快速插入/刪除元素,但不支持高效地隨機訪問
Vector: 可以看作線程安全版的ArrayList
7.String, StringBuilder, StringBuffer的區別是什么?
String: 不可變的字符序列,若要向其中添加新字符需要創建一個新的String對象
StringBuilder: 可變字符序列,支持向其中添加新字符(無需創建新對象)
StringBuffer: 可以看作線程安全版的StringBuilder
8.Map, Set, List, Queue、Stack的特點及用法。
Map<K, V>: Java中存儲鍵值對的數據類型都實現了這個接口,表示“映射表”。支持的兩個核心操作是get(Object key)以及put(K key, V value),分別用來獲取鍵對應的值以及向映射表中插入鍵值對。
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()等操作。
9.HashMap和HashTable的區別。
HashMap是非線程安全的,HashTable是線程安全的。
HashMap的鍵和值都允許有null值存在,而HashTable則不行。
因為線程安全的問題,HashMap效率比HashTable的要高。
詳細分析請參考深入解析HashMap、HashTable
10.HashMap的實現原理。
簡單說,HashMap就是將key做hash算法,然后將hash所對應的數據映射到內存地址,直接取得key所對應的數據。在HashMap中。底層數據結構使用的是數組,所謂的內存地址即數組的下標索引。HashMap的高性能需要保證以下幾點:
- hash算法必須高效
- hash值到內存地址(數組索引)的算法是快速的
- 根據內存地址(數組索引)可以直接取得對應的值
具體可參考從源碼分析HashMap
11.ConcurrentHashMap的實現原理
ConcurrentHashMap是支持并發讀寫的HashMap,它的特點是讀取數據時無需加鎖,寫數據時可以保證加鎖粒度盡可能的小。由于其內部采用“分段存儲”,只需對要進行寫操作的數據所在的“段”進行加鎖。關于ConcurrentHashMap底層實現的詳細分析請參考Java并發編程:并發容器之ConcurrentHashMap
12.TreeMap, LinkedHashMap, HashMap的區別是什么?
HashMap的底層實現是散列表,因此它內部存儲的元素是無序的;
TreeMap的底層實現是紅黑樹,所以它內部的元素的有序的。排序的依據是自然序或者是創建TreeMap時所提供的比較器(Comparator)對象。
LinkedHashMap可以看作能夠記住插入元素的順序的HashMap。
13.Collection與Collections的區別是什么?
Collection是一個接口,它是Set、List等容器的父接口;Collections是一個工具類,提供了一系列的靜態方法來輔助容器操作,這些方法包括對容器的搜索、排序、線程安全化等等。
14.Set、List、Map之間的區別是什么?
Collection是最基本的集合接口,Set 和List 都繼承了Conllection,Map沒有。
- Set是最簡單的一種集合。集合中的對象不按特定的方式排序,并且沒有重復對象。
- List的特征是其元素以線性方式存儲,集合中可以存放重復對象。
- Map 是一種把鍵對象和值對象映射的集合,它的每一個元素都包含一對鍵對象和值對象。
詳細可參考java 中的Set,List,Map
15.特殊關鍵字:final
- 可以修飾類、函數、變量;
- 被final修飾的類不可以被繼承。為了避免被繼承,被子類復寫。final class Demo { }
- 被final修飾的方法不可以被復寫。final void show () { }
- 被final 修飾的變量是一個常量,只能賦值一次。
- 內部類定義在類中的局部位置上時,只能訪問該局部被final修飾的局部變量。
16.Switch能否用string做參數?
在Java7之前,switch只支持byte、short、char、int及其對應的封裝類,以及Enum類型,在Java 7中,String類型被加上。
17.對于“try-catch-finally”,若try語句塊中包含“return”語句,finally語句塊會執行嗎?
答案是會執行。只有兩種情況finally塊中的語句不會被執行:
調用了System.exit()方法;
JVM“崩潰”了。
18.泛型的優缺點
優點:
使用泛型類型可以最大限度地重用代碼、保護類型的安全以及提高性能。
泛型最常見的用途是創建集合類。
缺點:
在性能上不如數組快。
19.Java中的異常層次結構
Java中的異常層次結構如下圖所示:
我們可以看到Throwable類是異常層級中的基類。Error類表示內部錯誤,這類錯誤使我們無法控制的;Exception表示異常,RuntimeException及其子類屬于未檢查異常,這類異常包括ArrayIndexOutOfBoundsException、NullPointerException等,我們應該通過條件判斷等方式語句避免未檢查異常的發生。IOException及其子類屬于已檢查異常,編譯器會檢查我們是否為所有可能拋出的已檢查異常提供了異常處理器,若沒有則會報錯。對于未檢查異常,我們無需捕獲(當然Java也允許我們捕獲,但我們應該做的事避免未檢查異常的發生)。
20.Interface與abstract類的區別。
抽象類和接口都不能夠實例化,但可以定義抽象類和接口類型的引用。一個類如果繼承了某個抽象類或者實現了某個接口都需要對其中的抽象方法全部進行實現,否則該類仍然需要被聲明為抽象類。接口比抽象類更加抽象,因為抽象類中可以定義構造器,可以有抽象方法和具體方法,而接口中不能定義構造器而且其中的方法全部都是抽象方法。抽象類中的成員可以是private、默認、protected、public的,而接口中的成員全都是public的。抽象類中可以定義成員變量,而接口中定義的成員變量實際上都是常量。有抽象方法的類必須被聲明為抽象類,而抽象類未必要有抽象方法。
21.Java面向對象的三個特征與含義。
三大特征:封裝、繼承、多態。
- 繼承
(1)繼承是一種聯結類的層次模型,并且允許和鼓勵類的重用,它提供了一種明確表述共性的方法。
(2)對象的一個新類可以從現有的類中派生,這個過程稱為類繼承,新類繼承了原始類的特性,新類稱為原始類的派生類(子類),而原始類稱為新類的基類(父類)。
(3)派生類可以從它的基類那里繼承方法和實例變量,并且類可以修改或增加新的方法使之更適合特殊的需要。 - 封裝
封裝是把過程和數據包圍起來,對數據的訪問只能通過已定義的界面。面向對象計算始于這個基本概念,即現實世界可以被描繪成一系列完全自治、封裝的對象,這些對象通過一個受保護的接口訪問其他對象。 - 多態性
(1)多態性是指允許不同類的對象對同一消息作出響應。
(2)多態性包括參數化多態性和包含多態性。
(3)多態性語言具有靈活、抽象、行為共享、代碼共享的優勢,很好的解決了應用程序函數同名問題。
22.Override, Overload的含義與區別。
Override表示“重寫”,是子類對父類中同一方法的重新定義
Overload表示“重載”,也就是定義一個與已定義方法名稱相同但簽名不同的新方法
23.接口與抽象類的區別
接口是一種約定,實現接口的類要遵循這個約定;抽象類本質上是一個類,使用抽象類的代價要比接口大。接口與抽象類的對比如下:
抽象類中可以包含屬性,方法(包含抽象方法與有著具體實現的方法),常量;接口只能包含常量和方法聲明。
抽象類中的方法和成員變量可以定義可見性(比如public、private等);而接口中的方法只能為public(缺省為public)。
一個子類只能有一個父類(具體類或抽象類);而一個接口可以繼承一個多個接口,一個類也可以實現多個接口。
子類中實現父類中的抽象方法時,可見性可以大于等于父類中的;而接口實現類中的接口 方法的可見性只能與接口中相同(public)。
24.靜態內部類與非靜態內部類的區別。
內部靜態類不需要有指向外部類的引用。但非靜態內部類需要持有對外部類的引用。非靜態內部類能夠訪問外部類的靜態和非靜態成員。靜態類不能訪問外部類的非靜態成員。他只能訪問外部類的靜態成員。一個非靜態內部類不能脫離外部類實體被創建,一個非靜態內部類可以訪問外部類的數據和方法,因為他就在外部類里面。
25.Java中多態的實現原理。
所謂多態,指的就是父類引用指向子類對象,調用方法時會調用子類的實現而不是父類的實現。多態的實現的關鍵在于“動態綁定”。詳細介紹請戳Java動態綁定的內部實現機制
-
Java方法的存儲機制
當JVM執行Java字節碼時,類型信息會存儲在方法區中,為了優化對象的調用方法的速度,方法區的類型信息會增加一個指針,該指針指向一個記錄該類方法的方法表,方法表中的每一個項都是對應方法的指針。方法區:方法區和JAVA堆一樣,是各個線程共享的內存區域,用于存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。 運行時常量池:它是方法區的一部分,Class文件中除了有類的版本、方法、字段等描述信息外,還有一項信息是常量池,用于存放編譯器生成的各種符號引用,這部分信息在類加載時進入方法區的運行時常量池中。 方法區的內存回收目標是針對常量池的回收及對類型的卸載。
-
方法表的構造
由于java的單繼承機制,一個類只能繼承一個父類,而所有的類又都繼承Object類,方法表中最先存放的是Object的方法,接下來是父類的方法,最后是該類本身的方法。如果子類改寫了父類的方法,那么子類和父類的那些同名的方法共享一個方法表項。由于這樣的特性,使得方法表的偏移量總是固定的,例如,對于任何類來說,其方法表的equals方法的偏移量總是一個定值,所有繼承父類的子類的方法表中,其父類所定義的方法的偏移量也總是一個定值。
-
實例
假設Class A是Class B的子類,并且A改寫了B的方法的method(),那么B來說,method方法的指針指向B的method方法入口;對于A來說,A的方法表的method項指向自身的method而非父類的。流程:調用方法時,虛擬機通過對象引用得到方法區中類型信息的方法表的指針入口,查詢類的方法表 ,根據實例方法的符號引用解析出該方法在方法表的偏移量,子類對象聲明為父類類型時,形式上調用的是父類的方法,此時虛擬機會從實際的方法表中找到方法地址,從而定位到實際類的方法。 注:所有引用為父類,但方法區的類型信息中存放的是子類的信息,所以調用的是子類的方法表。
詳細可查看JAVA多態的實現原理(JVM實現方式)
26.簡述Java中創建新線程的兩種方法。
繼承Thread類(假設子類為MyThread),并重寫run()方法,然后new一個MyThread對象并對其調用start()即可啟動新線程。
實現Runnable接口(假設實現類為MyRunnable),而后將MyRunnable對象作為參數傳入Thread構造器,在得到的Thread對象上調用start()方法即可。
實現方式和繼承方式有什么區別?
- 實現方式相比繼承方式的好處: 避免了單繼承的局限性(單繼承只能繼承一個父類)。在定義線程時,建議使用實現方式。
- 存放代碼的位置不一樣:
繼承Thread: 線程代碼存放Thread子類的run方法中。
實現Runnable: 線程代碼存在接口的子類的run方法。
27.簡述Java中進行線程同步的方法。
volatile: Java Memory Model保證了對同一個volatile變量的寫happens before對它的讀;
synchronized: 可以來對一個代碼塊或是對一個方法上鎖,被“鎖住”的地方稱為臨界區,進入臨界區的線程會獲取對象的monitor,這樣其他嘗試進入臨界區的線程會因無法獲取monitor而被阻塞。由于等待另一個線程釋放monitor而被阻塞的線程無法被中斷。
ReentrantLock: 嘗試獲取鎖的線程可以被中斷并可以設置超時參數。
28.簡述Java中具有哪幾種粒度的鎖
Java中可以對類、對象、方法或是代碼塊上鎖。
更加詳細的介紹請查看:Java核心技術點之多線程
29.給出“生產者-消費者”問題的一種解決方案。
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) {
}
}
}
}
}
30.ThreadLocal的設計理念與作用。
ThreadLocal的作用是提供線程內的局部變量,在多線程環境下訪問時能保證各個線程內的ThreadLocal變量各自獨立。也就是說,每個線程的ThreadLocal變量是自己專用的,其他線程是訪問不到的。ThreadLocal最常用于以下這個場景:多線程環境下存在對非線程安全對象的并發訪問,而且該對象不需要在線程間共享,但是我們不想加鎖,這時候可以使用ThreadLocal來使得每個線程都持有一個該對象的副本。
關于ThreadLocal的實現原理分析請戳深入剖析ThreadLocal
31.concurrent包的整體架構。
32.ArrayBlockingQueue, CountDownLatch類的作用.
CountDownLatch: 允許線程集等待直到計數器為0。適用場景: 當一個或多個線程需要等待指定數目的事件發生后再繼續執行。
ArrayBlockingQueue: 一個基于數組實現的阻塞隊列,它在構造時需要指定容量。當試圖向滿隊列中添加元素或者從空隊列中移除元素時,當前線程會被阻塞。通過阻塞隊列,我們可以按以下模式來工作:工作者線程可以周期性的將中間結果放入阻塞隊列中,其它線程可以取出中間結果并進行進一步操作。若工作者線程的執行比較慢(還沒來得及向隊列中插入元素),其他從隊列中取元素的線程會等待它(試圖從空隊列中取元素從而阻塞);若工作者線程執行較快(試圖向滿隊列中插入元素),則它會等待其它線程取出元素再繼續執行。
33.wait(),sleep() 的區別
wait(): Object類中定義的實例方法。在指定對象上調用wait方法會讓當前線程進入等待狀態(前提是當前線程持有該對象的monitor),此時當前線程會釋放相應對象的monitor,這樣一來其它線程便有機會獲取這個對象的monitor了。當其它線程獲取了這個對象的monitor并進行了所需操作時,便可以調用notify方法喚醒之前進入等待狀態的線程。
sleep(): Thread類中的靜態方法,作用是讓當前線程進入休眠狀態,以便讓其他線程有機會執行。進入休眠狀態的線程不會釋放它所持有的鎖。
34.線程池的用法與優勢
優勢: 實現對線程的復用,避免了反復創建及銷毀線程的開銷;使用線程池統一管理線程可以減少并發線程的數目,而線程數過多往往會在線程上下文切換上以及線程同步上浪費過多時間。
用法: 我們可以調用ThreadPoolExecutor的某個構造方法來自己創建一個線程池。但通常情況下我們可以使用Executors類提供給我們的靜態工廠方法來更方便的創建一個線程池對象。創建了線程池對象后,我們就可以調用submit方法提交任務到線程池中去執行了;線程池使用完畢后我們要記得調用shutdown方法來關閉它。
關于線程池的詳細介紹以及實現原理分析請查看:深入理解java線程池—ThreadPoolExecutor
35.for-each與常規for循環的效率對比
關于這個問題我們直接看《Effective Java》給我們做的解答:
for-each能夠讓代碼更加清晰,并且減少了出錯的機會。下面的慣用代碼適用于集合與數組類型:
for (Element e : elements) {
doSomething(e);
}
使用for-each循環與常規的for循環相比,并不存在性能損失,即使對數組進行迭代也是如此。實際上,在有些場合下它還能帶來微小的性能提升,因為它只計算一次數組索引的上限。
36.簡述Java IO與NIO的區別
Java IO是面向流的,這意味著我們需要每次從流中讀取一個或多個字節,直到讀取完所有字節;NIO是面向緩沖的,也就是說會把數據讀取到一個緩沖區中,然后對緩沖區中的數據進行相應處理。
Java IO是阻塞IO,而NIO是非阻塞IO。
Java NIO中存在一個稱為選擇器(selector)的東西,它允許你把多個通道(channel)注冊到一個選擇器上,然后使用一個線程來監視這些通道:若這些通道里有某個準備好可以開始進行讀或寫操作了,則開始對相應的通道進行讀寫。而在等待某通道變為可讀/寫期間,請求對通道進行讀寫操作的線程可以去干別的事情。
更進一步的說明請查看:Java NIO與IO
37.父類的靜態方法能否被子類重寫,為什么?
父類的靜態方法可以被子類繼承,也就是說子類可以調用父類的靜態方法,但是不能重寫。
可以寫個例子進行驗證:
父類代碼:
public class Fu {
public static void show() {
System.out.println("父類的靜態方法");
}
public void method() {
System.out.println("父類的普通方法");
}
}
子類代碼:
public class Zi extends Fu {
public static void main(String[] args) {
Fu fu = new Zi();
fu.show();
fu.method();
}
public static void show() {
System.out.println("子類的靜態方法");
}
public void method() {
System.out.println("子類的普通方法");
}
}
輸出的結果是:
父類的靜態方法
子類的普通方法
38.反射的作用與原理
JAVA反射機制是在運行狀態中, 對于任意一個類, 都能夠知道這個類的所有屬性和方法; 對于任意一個對象, 都能夠調用它的任意一個方法和屬性; 這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制.
主要作用有三:
- 運行時取得類的方法和字段的相關信息。
- 創建某個類的新實例(.newInstance())
- 取得字段引用直接獲取和設置對象字段,無論訪問修飾符是什么。
用處如下:
- 觀察或操作應用程序的運行時行為。
- 調試或測試程序,因為可以直接訪問方法、構造函數和成員字段。
- 通過名字調用不知道的方法并使用該信息來創建對象和調用方法。
39.Java中的泛型機制
泛型:JDK1.5版本以后出現的新特性,用于解決安全問題,是一個類型安全機制。
其好處是:
- 將運行時期出現的問題CLassCastException(類型匹配異常),轉移到了編譯時期,方便與程序員解決問題,讓運行時期問題減少,安全了。
- 避免了強制類型轉換,且運行時不會提示程序類型沒檢查的不安全提示。
泛型技術是給編譯器使用的技術,用于編譯時期。確保了類型的安全。 運行時,會將泛型去掉,生成的class文件中是不帶泛型的,這個稱為泛型的擦除。 為什么擦除呢?因為為了兼容運行的類加載器。 泛型的補償:在類加載器原有基礎上,編寫一個補償程序。在運行時,通過反射, 獲取元素的類型進行轉換動作。不用使用者在強制轉換了。
具體可查看Java核心技術點之泛型
40.內部類的作用
- 完善多重繼承
- C++作為比較早期的面向對象編程語言,摸著石頭過河,不幸的當了炮灰。比如多重繼承,Java是不太歡迎繼承的。因為繼承耦合度太高。比如你是一個人,你想會飛,于是就繼承了鳥這個類,然后你順便擁有了一對翅膀和厚厚的羽毛,可這些玩意你并不需要。所以Java發明了接口,以契約的方式向你提供功能。想想看,你的程序里成員變量會比函數多嗎?況且多重繼承會遇到死亡菱形問題,就是兩個父類有同樣名字的函數,你繼承誰的呢?其實C++也可以做到這些,那就是定義沒有成員變量的純虛類,而且所有函數都是純虛函數??墒沁@些都是要靠程序員自己把握,并沒有把這些功能集成到類似Interface這樣的語法里。
- 所以Java只支持單重繼承,想擴展功能,去實現接口吧。很快Java的設計者就發現了他們犯了矯枉過正的錯誤,多重繼承還是有一定用處的。比如每一個人都是同時繼承父親和母親兩個類,要不然你的身體里怎么能留著父母的血呢?Java內部類應運而生。
實現事件驅動系統
用來開發GUI的Java Swing使用了大量內部類,主要用來響應各種事件。Swing的工作就是在事件就緒的時候執行事件,至于事件具體怎么做,這由事件決定。這里面有兩個問題:1.事件必須要用到繼承2.事件必須能訪問到Swing。所以必須把事件寫成內部類。閉包。
內部類是面向對象的閉包,因為它不僅包含創建內部類的作用域的信息,還自動擁有一個指向此外圍類對象的引用,在此作用域內,內部類有權操作所有的成員,包括private成員。一般使用一個庫或類時,是你主動調用人家的API,這個叫Call,有的時候這樣不能滿足需要,需要你注冊(注入)你自己的程序(比如一個對象),然后讓人家在合適的時候來調用你,這叫Callback。
當父類和實現的接口出現同名函數時,你又不想父類的函數被覆蓋,回調可以幫你解決這個問題。
摘取至知乎中Java 中引入內部類的意義一文的回答
41.if和switch的區別:
if :
1.對具體的值進行判斷
2.對區間判斷
3.對運算結果是boolean類型的表達式進行判斷switch :
1.對具體的值進行判斷;
2.值的個數通常是固定的。
對于幾個固定值的判斷,建議使用switch語句,因為switch語句會將具體的答案加載進內存,相對高效一點。
42.進程和線程的區別
進程就是一個應用程在處理機上的一次執行過程, 它是一個動態的概念。而線程是進程的一部分,一個進程可以包含多個線程. 進程是一個具有獨立功能的程序關于某個數據集合的一次運行活動。它可以申請和擁有系統資源,是一個動態的概念,是一個活動的實體。它不只是程序的代碼,還包括當前的活動,通過程序計數器的值和處理寄存器的內容來表示。
進程是一個“執行中的程序”。程序是一個沒有生命的實體,只有處理器賦予程序生命時,它才能成為一個活動的實體,我們稱其為進程。
通常在一個進程中可以包含若干個線程,它們可以利用進程所擁有的資源。在引入線程的操作系統中,通常都是把進程作為分配資源的基本單位,而把線程作為獨立運行和獨立調度的基本單位。由于線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提高系統內多個程序間并發執行的程度。
線程和進程的區別在于,子進程和父進程有不同的代碼和數據空間,而多個線程則共享數據空間,每個線程有自己的執行堆棧和程序計數器為其執行上下文。多線程主要是為了節約CPU時間,發揮利用,根據具體情況而定。線程的運行中需要使用計算機的內存資源和CPU。
線程與進程的區別歸納:
- 地址空間和其它資源**:進程間相互獨立,同一進程的各線程間共享。某進程內的線程在其它進程不可見。
- 通信:進程間通信IPC,線程間可以直接讀寫進程數據段(如全局變量)來進行通信——需要進程同步和互斥手段的輔助,以保證數據的一致性。
- 調度和切換:線程上下文切換比進程上下文切換要快得多。
- 在多線程OS中,進程不是一個可執行的實體。
進程是具有一定獨立功能的程序關于某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位。線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。
來源于文章面試題二(談談進程與線程)
43.Java 7與Java 8的新特性
Java 7:
-
對集合的支持
創建List / Set / Map 時寫法更簡單了。 - 對資源的自動回收管理
- 泛型實例創建過程中類型引用的簡化
- 在數字中使用下劃線
- 對字符串進行switch case
- 二進制符號
- 一個catch里捕捉多個異常類型
Java 8:
- Lambdas表達式與Functional接口
- 接口的默認與靜態方法
- 方法引用
- 重復注解
- 更好的類型推測機制
- 擴展注解的支持
這里有兩篇總結的非常好的:
Java 7的新特性
Java 8的新特性
44.常見設計模式
所謂“設計模式”,不過是面向對象編程中一些常用的軟件設計手法,并且經過實踐的檢驗,這些設計手法在各自的場景下能解決一些需求,因此它們就成為了如今廣為流傳的”設計模式“。也就是說,正式因為在某些場景下產生了一些棘手的問題,才催生了相應的設計模式。明確了這一點,我們在學習某種設計模式時要充分理解它產生的背景以及它所解決的主要矛盾是什么。
常用的設計模式可以分為以下三大類:
創建型模式: 包括工廠模式(又可進一步分為簡單工廠模式、工廠方法模式、抽象工廠模式)、建造者模式、單例模式。
結構型模式: 包括適配器模式、橋接模式、裝飾模式、外觀模式、享元模式、代理模式。
行為型模式: 包括命令模式、中介者模式、觀察者模式、狀態模式、策略模式。
關于每個模式具體的介紹請參考圖說設計模式
45.動態代理的定義、應用場景及原理
46.注解的基本概念與使用
注解可以看作是“增強版的注釋”,它可以向編譯器、虛擬機說明一些事情。
注解是描述Java代碼的代碼,它能夠被編譯器解析,注解處理工具在運行時也能夠解析注解。注解本身是“被動”的信息,只有主動解析它才有意義。
除了向編譯器/虛擬機傳遞信息,我們也可以使用注解來生成一些“模板化”的代碼。
本文主要根據以下文章整理:
后續會持續添加我認為重要的東西