通過參考網上諸多大佬的博客,歸納整理的一部分Java 面試資料,僅供大家參考
Java概述
1. JVM、JRE和JDK的關係
- JVM
Java Virtual Machine是Java虛擬機,Java程序需要運行在Java虛擬機上,不同開發平臺由不同的指令集構成,在各個不同的開發平臺上安裝有不同的JVM,來達到在不同的平臺上運行相同代碼的原理。因此Java語言可以實現跨平臺特性。 - JRE
Java Runtime Environment包括Java虛擬機和Java程序所需的核心類庫等。核心類庫主要包括java.lang包:包含了運行Java程序必不可少的系統類,如基本數據類型、基本數學函數、字符串處理、線程、異常處理類等。 - JDK
Java Development Kit 是用於支持Java程序開發的最小環境,其中包括JRE。JRE是支持Java程序運行的最小環境。
2. Java 語言的特點有哪些
- 簡單性:語法簡單,無需深奧訓練即可掌握
- 面向對象:封裝、繼承、多態
- 跨平臺:由於Java虛擬機的存在,可實現平臺無關性
- 健壯性:Java語言的強類型機制,異常處理等機制,使得開發人員能夠快速定位bug
- 支持網絡編程
- 支持多線程
- 安全性
3. Java和C++的區別
- Java是解釋型語言,所謂的解釋型語言,就是源碼會先經過一次編譯,成爲中間碼,中間碼再被解釋器解釋成機器碼。對於Java而言,中間碼就是字節碼(.class),而解釋器在JVM中內置了。
- C++是編譯型語言,所謂編譯型語言,就是源碼一次編譯,直接在編譯的過程中鏈接了,形成了機器碼。
- C++比Java執行速度快,但是Java可以利用JVM跨平臺。
- Java是純面向對象的語言,所有代碼(包括函數、變量)都必須在類中定義。而C++中還有面向過程的東西,比如是全局變量和全局函數。
- C++中有指針,Java中沒有,但是有引用。
- C++支持多繼承,Java中類都是單繼承的。但是繼承都有傳遞性,同時Java中的接口是多繼承,類對接口的實現也是多實現。
- C++中,開發需要自己去管理內存,但是Java中JVM有自己的GC機制,雖然有自己的GC機制,但是也會出現OOM和內存泄漏的問題。C++中有析構函數,Java中Object的finalize方法。
- C++運算符可以重載,但是Java中不可以。同時C++中支持強制自動轉型,Java中不行,會出現ClassCastException(類型不匹配)。
Java基礎語法
1. 九種基本數據類型
自動裝箱拆箱:
一般我們要創建一個類的對象實例的時候,我們會這樣:
Class a = new Class(parameter); 而當我們創建一個Integer對象時,卻可以這樣:int i = 100; (注意:不是 Integer i = new Integer(100); )
實際上,執行上面那句代碼的時候,系統爲我們執行了:Integer i = Integer.valueOf(100);
此即基本數據類型的自動裝箱功能。
而當我們執行 Integer i = 1; int t = i; 時,便把對象中的數據從對象中取出,即實現了自動拆箱。
Integer 的自動拆箱:
//在-128~127 之外的數
Integer i1 =200;
Integer i2 =200;
System.out.println("i1==i2: "+(i1==i2));
// 在-128~127 之內的數
Integer i3 =100;
Integer i4 =100;
System.out.println("i3==i4: "+(i3==i4));
輸出的結果是:
i1==i2: false
i3==i4: true
在Java 5後,在Integer的操作上引入了一個新功能來節省內存和提高性能。整型對象通過使用相同的對象引用實現了緩存和重用。當Integer需要自動裝箱時,如果在整數區間-128 ~ 127 之中,會直接引用緩存中的對象,避免了新建對象。如此以來,即可避免裝箱或拆箱操作頻繁的創建對象。
具體實現源碼如下:
@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
return i >= -128 && i <= Integer.IntegerCache.high ? Integer.IntegerCache.cache[i + 128] : new Integer(i);
}
2. Object 類的公用方法
@HotSpotIntrinsicCandidate
public Object() {
}
@HotSpotIntrinsicCandidate
public final native Class<?> getClass();
@HotSpotIntrinsicCandidate
public native int hashCode();
public boolean equals(Object obj) {
return this == obj;
}
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
public String toString() {
return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
}
@HotSpotIntrinsicCandidate
public final native void notify();
@HotSpotIntrinsicCandidate
public final native void notifyAll();
public final void wait() throws InterruptedException {
this.wait(0L);
}
public final native void wait(long var1) throws InterruptedException;
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
if (timeoutMillis < 0L) {
throw new IllegalArgumentException("timeoutMillis value is negative");
} else if (nanos >= 0 && nanos <= 999999) {
if (nanos > 0) {
++timeoutMillis;
}
this.wait(timeoutMillis);
} else {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
}
- clone()方法 :返回一個獨立的對象,和原對象地址不同,屬性相同。並且只有實現
Cloneable
接口才能調用clone方法。返回的對象爲Object類型,可通過強轉轉回原對象類型。 - equals()方法:判斷兩個對象是否相等,和“==”作用相同,一般在子類中被重寫。
- hashCode()方法:該方法時爲了配合基於散列的集合可以正常運行(
例如,當向集合中插入對象時,如何判別在集合中是否已經存在該對象?(集合中不允許重複的元素存在)
),重寫了equals方法一般都要重寫hashCode方法。這個方法在一些具有哈希功能的Collection中用到。 - getClass()方法:final方法,返回此Object的運行時類。
- wait() 方法:
使當前線程等待該對象的鎖,當前線程必須是該對象的擁有者,也就是具有該對象的鎖。
wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設定一個超時間隔,如果在規定時間內沒有獲得鎖就返回。
調用該方法後當前線程進入睡眠狀態,直到以下事件發生:- 其他線程調用了該對象的notify方法
- 其他線程調用了該對象的notifyAll方法
- 其他線程調用了interrupt中斷該線程
- 時間間隔到了
此時該線程就可以被調度了,如果是被中斷的話就拋出一個InterruptedException異常
- notify() 方法:喚醒該對象某個處於等待狀態的線程。
- notifyAll()方法:喚醒在該對象上等待的所有線程
- toString()方法:把類轉換成字符串,一般子類都會進行重寫。
3. final 關鍵字的用法
- 項目被final修飾的類不可以被繼承
- 被final修飾的方法不可以被重寫
- 被final修飾的變量不可以被改變.如果修飾引用,那麼表示引用不可變,引用指向的內容可變.
- 被final修飾的方法,JVM會嘗試將其內聯,以提高運行效率
- 被final修飾的常量,在編譯階段會存入常量池中.
4. String,StringBuffer和StringBuilder區別
String | StringBuffer | StringBuilder |
---|---|---|
String的值是不可變的,這就導致每次對String的操作都會生成新的String對象,不僅效率低下,而且浪費大量優先的內存空間 | StringBuffer是可變類,和線程安全的字符串操作類,任何對它指向的字符串的操作都不會產生新的對象。 | 可變類,速度更快 |
不可變 | 可變類 | 可變類 |
線程安全 | 線程不安全 | |
多線程操作字符串 | 單線程操作字符串 |
5. 接口和抽象類的區別是什麼
- 抽象類可以提供成員方法的實現體並且成員方法可以是各種類型的,而接口中只能存在public abstract final static方法;
- 接口中不能含有靜態代碼塊以及靜態方法,而抽象類可以有靜態代碼塊和靜態方法;
- 一個類只能繼承一個抽象類,而一個類卻可以實現多個接口。
- 設計層面不同,抽象類作爲很多子類的父類,它是一種模板式設計。而接口是一種行爲規範,它是一種輻射式設計。
- 抽象類是對一種事物的抽象,即對類抽象,而接口是對行爲的抽象。抽象類是對整個類整體進行抽象,包括屬性、行爲,但是接口卻是對類局部(行爲)進行抽象。
6. 反射機制:框架設計的靈魂
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
反射的應用:
在日常業務開發中很少會用到反射機制。但實際上有很多設計、開發都與反射機制有關,例如模塊化的開發,通過反射去調用對應的字節碼;動態代理設計模式也採用了反射機制,還有我們日常使用的 Spring/mybatis 等框架也大量使用到了反射機制。
- 我們在使用JDBC連接數據庫時使用Class.forName()通過反射加載數據庫的驅動程序;
- Spring框架也用到很多反射機制,最經典的就是xml的配置模式。Spring 通過 XML 配置模式裝載 Bean 的過程:
1) 將程序內所有 XML 或 Properties 配置文件加載入內存中;
2)Java類裏面解析xml或properties裏面的內容,得到對應實體類的字節碼字符串以及相關的屬性信息;
3)使用反射機制,根據這個字符串獲得某個類的Class實例; 4)動態配置實例的屬性
7.集合知識點總結:
7.1 體系框架
Collection
- List
- Arraylist:Object數組
- Vector:Object數組
- LinkedList:雙向鏈表
- Set
- HashSet(無序,唯一): 基於 HashMap 實現的,底層採用 HashMap 來保存元素
- LinkedHashSet: LinkedHashSet 繼承於 HashSet,並且其內部是通過 LinkedHashMap 來實現的。有點類似於我們之前說的LinkedHashMap 其內部是基於 HashMap 實現一樣,不過還是有一點點區別的
- TreeSet(有序,唯一): 紅黑樹(自平衡的排序二叉樹)
Map
- HashMap:JDK1.8之前HashMap由數組+鏈表組成的,數組是HashMap的主體,鏈表則是主要爲了解決哈希衝突而存在的(“拉鍊法”解決衝突)。JDK1.8以後在解決哈希衝突時有了較大的變化,當鏈表長度大於閾值(默認爲8)時,將鏈表轉化爲紅黑樹,以減少搜索時間
- LinkedHashMap :繼承自 HashMap,所以它的底層仍然是基於拉鍊式散列結構即由數組和鏈表或紅黑樹組成。另外,LinkedHashMap 在上面結構的基礎上,增加了一條雙向鏈表,使得上面的結構可以保持鍵值對的插入順序。同時通過對鏈表進行相應的操作,實現了訪問順序相關邏輯。
- Hashtable: 數組+鏈表組成的,數組是 HashMap 的主體,鏈表則是主要爲了解決哈希衝突而存在的
- TreeMap:紅黑樹(自平衡的排序二叉樹)
7.2 ArrayList、LinkedList、Vector的區別和實現原理
ArrayList | ArrayList | Vector | |
---|---|---|---|
存儲結構 | 基於數組實現的 | 基於雙向鏈表實現 | 基於數組實現的 |
線程安全性 | 不具有有線程安全性,多線程環境下需使用synchronizedList | 不具有有線程安全性,多線程環境下需使用synchronizedList | Vector實現線程安全的,即它大部分的方法都包含關鍵字synchronized,但是Vector的效率沒2有ArraykList和LinkedList高。: |
擴容機制 | Object的數組形式來存儲的,元素不夠時,擴容至原來的1.5倍 | Object的數組形式來存儲的,元素不夠時,擴容至原來的2倍 |
7.3 HashMap與Hashtable的區別
- HaspMap是非線程安全的,HashTable是線程安全的;HashTable內部的方法基本都經過synchronized修飾。
- 因爲線程安全問題,HashMap要比HashTable效率高一點,HashTable基本被淘汰。
- HashMap允許有空值存在,而在HashTable中put進的健值只要有一個爲空,直接拋出NullPointerException。
所以在單線程開發中,因爲速度問題,一般推薦用HashMap,而需要完全線程安全時,使用HashTable。不過Java5以上的話,推薦用ConcurrentHashMap,HashTable已經是馬上要淘汰的遺留類。