Java面試題(一)
2、訪問修飾符 public,private,protected,以及不寫(預設)時的區別?
5、short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎?
9、解釋記憶體中的棧(stack)、堆(heap)和方法區(method area)的用法。
10、Math.round(11.5) 等於多少?Math.round(-11.5)等於多少?
11、switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上?
13、陣列有沒有 length()方法?String 有沒有 length()方法?
14、在 Java 中,如何跳出當前的多重巢狀迴圈?
15、構造器(constructor)是否可被重寫(override)?
16、兩個物件值相同(x.equals(y) == true),但卻可有不同的 hashcode,這句話對不對?
18、當一個物件被當作引數傳遞到一個方法後,此方法可改變這個物件的屬性,並可返回變化後的結果,那麼這裡到底是值傳遞還是引用傳遞?
19、String 和 StringBuilder、StringBuffer 的區別?
20、過載(Overload)和重寫(Override)的區別。過載的方法能否根據返回型別進行區分?
21、描述一下 JVM 載入 class 檔案的原理機制?
22、char 型變數中能不能存貯一箇中文漢字,為什麼?
23、抽象類(abstract class)和介面(interface)有什麼異同?
24、靜態巢狀類(Static Nested Class)和內部類(Inner Class)的不同?
25、Java 中會存在記憶體洩漏嗎,請簡單描述。
26、抽象的(abstract)方法是否可同時是靜態的(static),是否可同時是本地方法(native),是否可同時被 synchronized 修飾?
28、是否可以從一個靜態(static)方法內部發出對非靜態(non-static)方法的呼叫?
31、String s = new String(“xyz”);建立了幾個字串物件?
32 、 接 口 是 否 可 繼 承 ( extends ) 接 口 ? 抽 象 類 是 否 可 實 現(implements)介面?抽象類是否可繼承具體類(concrete class)?
33、一個”.java”原始檔中是否可以包含多個類(不是內部類)?有什麼限制?
34、Anonymous Inner Class(匿名內部類)是否可以繼承其它類?是否可以實現介面?
35、內部類可以引用它的包含類(外部類)的成員嗎?有沒有什麼限制?
36、Java 中的 final 關鍵字有哪些用法?
40、怎樣將 GB2312 編碼的字串轉換為 ISO-8859-1 編碼的字串?
43、比較一下 Java 和 JavaSciprt。
45、Error 和 Exception 有什麼區別?
46、try{}裡有一個 return 語句,那麼緊跟在這個 try 後的 finally{}裡的程式碼會不會被執行,什麼時候被執行,在 return 前還是後?
47、Java 語言如何進行異常處理,關鍵字:throws、throw、try、catch、finally 分別如何使用?
50、闡述 final、finally、finalize 的區別。
51、類 ExampleA 繼承 Exception,類 ExampleB 繼承 ExampleA。
52、List、Set、Map 是否繼承自 Collection 介面?
53、闡述 ArrayList、Vector、LinkedList 的儲存效能和特性。
54、Collection 和 Collections 的區別?
55、List、Map、Set 三個介面存取元素時,各有什麼特點?
56、TreeMap 和 TreeSet 在排序時如何比較元素?Collections 工具類中的 sort()方法如何比較元素?
57、Thread 類的 sleep()方法和物件的 wait()方法都可以讓執行緒暫停執行,它們有什麼區別?
58、執行緒的 sleep()方法和 yield()方法有什麼區別?
59、當一個執行緒進入一個物件的 synchronized 方法 A 之後,其它執行緒是否可進入此物件 synchronized 方法 B?
60、請說出與執行緒同步以及執行緒排程相關的方法。
64、啟動一個執行緒是呼叫 run()還是 start()方法?
67、簡述 synchronized 和 java.util.concurrent.locks.Lock 的異同?
70、寫一個方法,輸入一個檔名和一個字串,統計這個字串在這個檔案中出現的次數。
71、如何用 Java 程式碼列出一個目錄下所有的檔案?
72、用 Java 的套接字程式設計實現一個多執行緒的回顯(echo)伺服器。
73、XML 文件定義有幾種形式?它們之間有何本質區別?解析 XML文件有哪幾種方式?
76、Statement 和 PreparedStatement 有什麼區別?哪個效能更好?
77、使用 JDBC 運算元據庫時,如何提升讀取資料的效能?如何提升更新資料的效能?
82、JDBC 能否處理 Blob 和 Clob?
Java 面試題(二)
1、Java 中能建立 volatile 陣列嗎?
2、volatile 能使得一個非原子操作變成原子操作嗎?
5、10 個執行緒和 2 個執行緒的同步程式碼,哪個更容易寫?
6、你是如何呼叫 wait()方法的?使用 if 塊還是迴圈?為什麼?
8、什麼是 Busy spin?我們為什麼要使用它?
9、Java 中怎麼獲取一份執行緒 dump 檔案?
12、用 wait-notify 寫一段程式碼來解決生產者-消費者問題?
13、用 Java 寫一個執行緒安全的單例模式(Singleton)?
14、Java 中 sleep 方法和 wait 方法的區別?
15、什麼是不可變物件(immutable object)?Java 中怎麼建立一個不可變物件?
17、Java 中應該使用什麼資料型別來代表價格?
19、Java 中怎樣將 bytes 轉換為 long 型別?
20、我們能將 int 強制轉換為 byte 型別的變數嗎?如果該值大於byte 型別的範圍,將會出現什麼現象?
21、存在兩個類,B 繼承 A ,C 繼承 B,我們能將 B 轉換為 C 麼?如 C = (C) B;
22、哪個類包含 clone 方法?是 Cloneable 還是 Object?
23、Java 中 ++ 操作符是執行緒安全的嗎?
23、不是執行緒安全的操作。它涉及到多個指令,如讀取變數值,增加,然後儲存回記憶體,這個過程可能會出現多個執行緒交差。
24、a = a + b 與 a += b 的區別
25、我能在不進行強制轉換的情況下將一個 double 值賦值給 long型別的變數嗎?
26、3*0.1 == 0.3 將會返回什麼?true 還是 false?
27、int 和 Integer 哪個會佔用更多的記憶體?
28、為什麼 Java 中的 String 是不可變的(Immutable)?
31、64 位 JVM 中,int 的長度是多數?
32、Serial 與 Parallel GC 之間的不同之處?
33、32 位和 64 位的 JVM,int 型別變數的長度是多數?
34、Java 中 WeakReference 與 SoftReference 的區別?
36、JVM 選項 -XX:+UseCompressedOops 有什麼作用?為什麼要使用?
37、怎樣通過 Java 程式來判斷 JVM 是 32 位 還是 64 位?
38、32 位 JVM 和 64 位 JVM 的最大堆記憶體分別是多數?
39、JRE、JDK、JVM 及 JIT 之間有什麼不同?
42、怎麼獲取 Java 程式使用的記憶體?堆使用的百分比?
44、“a==b”和”a.equals(b)”有什麼區別?
45、a.hashCode() 有什麼用?與 a.equals(b) 有什麼關係?
46、final、finalize 和 finally 的不同之處?
47、Java 中的編譯期常量是什麼?使用它又什麼風險?
48、List、Set、Map 和 Queue 之間的區別(答案)
49、poll() 方法和 remove() 方法的區別?
50、Java 中 LinkedHashMap 和 PriorityQueue 的區別是什麼?
51、ArrayList 與 LinkedList 的不區別?
54、Java 中的 LinkedList 是單向連結串列還是雙向連結串列?
55、Java 中的 TreeMap 是採用什麼樹實現的?(答案)
56、Hashtable 與 HashMap 有什麼不同之處?
57、Java 中的 HashSet,內部是如何工作的?
58、寫一段程式碼在遍歷 ArrayList 時移除一個元素?
59、我們能自己寫一個容器類,然後使用 for-each 迴圈碼?
60、ArrayList 和 HashMap 的預設大小是多數?
61、有沒有可能兩個不相等的物件有有相同的 hashcode?
62、兩個相同的物件會有不同的的 hash code 嗎?
63、我們可以在 hashcode() 中使用隨機數字嗎?
64、Java 中,Comparator 與 Comparable 有什麼不同?
66、在我 Java 程式中,我有三個 socket,我需要多少個執行緒來處理?
67、Java 中怎麼建立 ByteBuffer?
68、Java 中,怎麼讀寫 ByteBuffer ?
71、Java 中,直接緩衝區與非直接緩衝器有什麼區別?
73、socket 選項 TCP NO DELAY 是指什麼?
75、Java 中,ByteBuffer 與 StringBuffer 有什麼區別?(答案)
76、Java 中,編寫多執行緒程式的時候你會遵循哪些最佳實踐?
77、說出幾點 Java 中使用 Collections 的最佳實踐
78、說出至少 5 點在 Java 中使用執行緒的最佳實踐。
82、在多執行緒環境下,SimpleDateFormat 是執行緒安全的嗎?
83、Java 中如何格式化一個日期?如格式化為 ddMMyyyy 的形式?
84、Java 中,怎麼在格式化的日期中顯示時區?
85、Java 中 java.util.Date 與 java.sql.Date 有什麼區別?
87、Java 中,如何將字串 YYYYMMDD 轉換為日期?
90、怎麼利用 JUnit 來測試一個方法的異常?
91、你使用過哪個單元測試庫來測試你的 Java 程式?
92、@Before 和 @BeforeClass 有什麼區別?
94、Java 中如何利用泛型寫一個 LRU 快取?
95、寫一段 Java 程式將 byte 轉換為 long?
96、在不使用 StringBuffer 的前提下,怎麼反轉一個字串?
97、Java 中,怎麼獲取一個檔案中單詞出現的最高頻率?
99、Java 中,怎麼列印出一個字串的所有排列?
100、Java 中,怎樣才能列印出陣列中的重複元素?
102、在沒有使用臨時變數的情況如何交換兩個整數變數的值?
103、介面是什麼?為什麼要使用介面而不是直接使用具體類?
104、Java 中,抽象類與介面之間有什麼不同?
105、除了單例模式,你在生產環境中還用過什麼設計模式?
107、什麼情況下會違反迪米特法則?為什麼會有這個問題?
109、什麼是“依賴注入”和“控制反轉”?為什麼有人使用?
110、抽象類是什麼?它與介面有什麼區別?你為什麼要使用過抽象類?
111、構造器注入和 setter 依賴注入,那種方式更好?
120、Java 中,巢狀公共靜態類與頂級類有什麼不同?
121、 OOP 中的 組合、聚合和關聯有什麼區別?
126、你能寫出一個正規表示式來判斷一個字串是否是一個數字嗎?
127、Java 中,受檢查異常 和 不受檢查異常的區別?
128、Java 中,throw 和 throws 有什麼區別
129、Java 中,Serializable 與 Externalizable 的區別?
130、Java 中,DOM 和 SAX 解析器有什麼不同?
132、說出 5 個 JDK 1.8 引入的新特性?
133、Java 中,Maven 和 ANT 有什麼區別?
1、物件導向的特徵有哪些方面?
抽象:抽象是將一類物件的共同特徵總結出來構造類的過程,包括資料抽象和行為抽象兩方面。抽象只關注物件有哪些屬性和行為,並不關注這些行為的細節是什麼。
繼承:繼承是從已有類得到繼承資訊建立新類的過程。提供繼承資訊的類被稱為父類(超類、基類);得到繼承資訊的類被稱為子類(派生類)。繼承讓變化中的軟體系統有了一定的延續性,同時繼承也是封裝程式中可變因素的重要手段(如果不能理解請閱讀閻巨集博士的《Java 與模式》或《設計模式精解》中關於橋樑模式的部分)。
封裝:通常認為封裝是把資料和運算元據的方法繫結起來,對資料的訪問只能通過已定義的介面。物件導向的本質就是將現實世界描繪成一系列完全自治、封閉的物件。我們在類中編寫的方法就是對實現細節的一種封裝;我們編寫一個類就是對資料和資料操作的封裝。可以說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的程式設計介面(可以想想普通洗衣機和全自動洗衣機的差別,明顯全自動洗衣機封裝更好因此操作起來更簡單;我們現在使用的智慧手機也是封裝得足夠好的,因為幾個按鍵就搞定了所有的事情)。
多型性:多型性是指允許不同子型別的物件對同一訊息作出不同的響應。簡單的說就是用同樣的物件引用呼叫同樣的方法但是做了不同的事情。多型性分為編譯時的多型性和執行時的多型性。如果將物件的方法視為物件向外界提供的服務,那麼執行時的多型性可以解釋為:當 A 系統訪問 B 系統提供的服務時,B系統有多種提供服務的方式,但一切對 A 系統來說都是透明的(就像電動剃鬚刀是 A 系統,它的供電系統是 B 系統,B 系統可以使用電池供電或者用交流電,甚至還有可能是太陽能,A 系統只會通過 B 類物件呼叫供電的方法,但並不知道供電系統的底層實現是什麼,究竟通過何種方式獲得了動力)。方法過載(overload)實現的是編譯時的多型性(也稱為前繫結),而方法重寫(override)實現的是執行時的多型性(也稱為後繫結)。執行時的多型是物件導向最精髓的東西,要實現多型需要做兩件事:
1). 方法重寫(子類繼承父類並重寫父類中已有的或抽象的方法);
2). 物件造型(用父型別引用引用子型別物件,這樣同樣的引用呼叫同樣的方法就會根據子類物件的不同而表現出不同的行為)。
2、訪問修飾符 public,private,protected,以及不寫(預設)時的區別?
類的成員不寫訪問修飾時預設為 default。預設對於同一個包中的其他類相當於公開(public),對於不是同一個包中的其他類相當於私有(private)。受保護(protected)對子類相當於公開,對不是同一包中的沒有父子關係的類相當於私有。Java 中,外部類的修飾符只能是 public 或預設,類的成員(包括內部類)的修飾符可以是以上四種。
3、String 是最基本的資料型別嗎?
不是。Java 中的基本資料型別只有 8 個 :byte、short、int、long、float、double、char、boolean;除了基本型別(primitive type),剩下的都是引用型別(referencetype),Java 5 以後引入的列舉型別也算是一種比較特殊的引用型別。
4、float f=3.4;是否正確?
不正確。3.4 是雙精度數,將雙精度型(double)賦值給浮點型(float)屬於下轉型(down-casting,也稱為窄化)會造成精度損失,因此需要強制型別轉換float f =(float)3.4; 或者寫成 float f =3.4F;。
5、short s1 = 1; s1 = s1 + 1;有錯嗎?short s1 = 1; s1 += 1;有錯嗎?
對於 short s1 = 1; s1 = s1 + 1;由於 1 是 int 型別,因此 s1+1 運算結果也是 int型,需要強制轉換型別才能賦值給 short 型。而 short s1 = 1; s1 += 1;可以正確編譯,因為 s1+= 1;相當於 s1 = (short(s1 + 1);其中有隱含的強制型別轉換。
6、Java 有沒有 goto?
goto 是 Java 中的保留字,在目前版本的 Java 中沒有使用。(根據 James Gosling(Java 之父)編寫的《The Java Programming Language》一書的附錄中給出了一個 Java 關鍵字列表,其中有goto 和 const,但是這兩個是目前無法使用的關鍵字,因此有些地方將其稱之為保留字,其實保留字這個詞應該有更廣泛的意義,因為熟悉 C 語言的程式設計師都知道,在系統類庫中使用過的有特殊意義的但詞或單詞的組合都被視為保留字)
7、int 和 Integer 有什麼區別?
Java 是一個近乎純潔的物件導向程式語言,但是為了程式設計的方便還是引入了基本資料型別,但是為了能夠將這些基本資料型別當成物件操作,Java 為每一個基本資料型別都引入了對應的包裝型別(wrapper class),int 的包裝類就是 Integer,從 Java 5 開始引入了自動裝箱/拆箱機制,使得二者可以相互轉換。
原始型別: boolean,char,byte,short,int,long,float,double
包裝型別:Boolean,Character,Byte,Short,Integer,Long,Float,Double
class AutoUnboxingTest {
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3;
int c = 3;
System.out.println(a == b);
象
System.out.println(a == c);
比較
}
}複製程式碼
最近還遇到一個面試題,也是和自動裝箱和拆箱有點關係的,程式碼如下所示:
public class Test03 {
public static void main(String[] args) {
Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
System.out.println(f1 == f2);
System.out.println(f3 == f4);
}
}複製程式碼
如果不明就裡很容易認為兩個輸出要麼都是 true 要麼都是 false。首先需要注意的是 f1、f2、f3、f4 四個變數都是 Integer 物件引用,所以下面的==運算比較的不是值而是引用。裝箱的本質是什麼呢?當我們給一個 Integer 物件賦一個 int 值的時候,會呼叫 Integer 類的靜態方法 valueOf,如果看 valueOf 的原始碼就知道發生了什麼。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}複製程式碼
IntegerCache 是 Integer 的內部類,其程式碼如下所示:
/**
* Cache to support the object identity semantics of autoboxing for
values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>}
option.
* During VM initialization, java.lang.Integer.IntegerCache.high
property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseint(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int,
ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for (int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {
}
}複製程式碼
簡單的說,如果整型字面量的值在-128 到 127 之間,那麼不會 new 新的 Integer物件,而是直接引用常量池中的 Integer 物件,所以上面的面試題中 f1f4 的結果是 false。
提醒:越是貌似簡單的面試題其中的玄機就越多,需要面試者有相當深厚的功力。
8、&和&&的區別?
&運算子有兩種用法:(1)按位與;(2)邏輯與。&&運算子是短路與運算。邏輯與跟短路與的差別是非常巨大的,雖然二者都要求運算子左右兩端的布林值都是true 整個表示式的值才是 true。&&之所以稱為短路運算是因為,如果&&左邊的表示式的值是 false,右邊的表示式會被直接短路掉,不會進行運算。很多時候我們可能都需要用&&而不是&,例如在驗證使用者登入時判定使用者名稱不是 null 而且不是空字串,應當寫為:username != null &&!username.equals(“”),二者的順序不能交換,更不能用&運算子,因為第一個條件如果不成立,根本不能進行字串的 equals 比較,否則會生 NullPointerException 異常。注意:邏輯或運算子(|)和短路或運算子(||)的差別也是如此。
9、解釋記憶體中的棧(stack)、堆(heap)和方法區(method area)的用法。
通常我們定義一個基本資料型別的變數,一個物件的引用,還有就是函式呼叫的現場儲存都使用 JVM 中的棧空間;而通過 new 關鍵字和構造器建立的物件則放在堆空間,堆是垃圾收集器管理的主要區域,由於現在的垃圾收集器都採用分代收集演算法,所以堆空間還可以細分為新生代和老生代,再具體一點可以分為 Eden、Survivor(又可分為 From Survivor 和 To Survivor)、Tenured;方法區和堆都是各個執行緒共享的記憶體區域,用於儲存已經被 JVM 載入的類資訊、常量、靜態變數、JIT 編譯器編譯後的程式碼等資料;程式中的字面量(literal)如直接書寫的 100、”hello”和常量都是放在常量池中,常量池是方法區的一部分,。棧空間操作起來最快但是棧很小,通常大量的物件都是放在堆空間,棧和堆的大小都可以通過 JVM的啟動引數來進行調整,棧空間用光了會引發 StackOverflowError,而堆和常量池空間不足則會引發 OutOfMemoryError。
String str = new String("hello");複製程式碼
上面的語句中變數 str 放在棧上,用 new 建立出來的字串物件放在堆上,而”hello”這個字面量是放在方法區的。
補充 1:較新版本的 Java(從 Java 6 的某個更新開始)中,由於 JIT 編譯器的發展和”逃逸分析”技術的逐漸成熟,棧上分配、標量替換等優化技術使得物件一定分配在堆上這件事情已經變得不那麼絕對了。
補充 2:執行時常量池相當於 Class 檔案常量池具有動態性,Java 語言並不要求常量一定只有編譯期間才能產生,執行期間也可以將新的常量放入池中,String類的 intern()方法就是這樣的。
看看下面程式碼的執行結果是什麼並且比較一下 Java 7 以前和以後的執行結果是否一致。
String s1 = new StringBuilder("go")
.append("od").toString();
System.out.println(s1.intern() == s1);
String s2 = new StringBuilder("ja")
.append("va").toString();
System.out.println(s2.intern() == s2);複製程式碼
10、Math.round(11.5) 等於多少?Math.round(-11.5)等於多少?
Math.round(11.5)的返回值是 12,Math.round(-11.5)的返回值是-11。四捨五入的原理是在引數上加 0.5 然後進行下取整。
11、switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上?
在 Java 5 以前,switch(expr)中,expr 只能是 byte、short、char、int。從 Java5 開始,Java 中引入了列舉型別,expr 也可以是 enum 型別,從 Java 7 開始,expr 還可以是字串(String),但是長整型(long)在目前所有的版本中都是不可以的。
12、用最有效率的方法計算 2 乘以 8?
2 << 3(左移 3 位相當於乘以 2 的 3 次方,右移 3 位相當於除以 2 的 3 次方)。
補充:我們為編寫的類重寫 hashCode 方法時,可能會看到如下所示的程式碼,其實我們不太理解為什麼要使用這樣的乘法運算來產生雜湊碼(雜湊碼),而且為什麼這個數是個素數,為什麼通常選擇 31 這個數?前兩個問題的答案你可以自己百度一下,選擇 31 是因為可以用移位和減法運算來代替乘法,從而得到更好的效能。說到這裡你可能已經想到了:31 * num 等價於(num << 5) - num,左移 5位相當於乘以 2 的 5 次方再減去自身就相當於乘以 31,現在的 VM 都能自動完成這個優化。
public class PhoneNumber {
private int areaCode;
private String prefix;
private String lineNumber;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + areaCode;
result = prime * result
+ ((lineNumber == null) ? 0 : lineNumber.hashCode());
result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
return result;
}
@Override
public Boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PhoneNumber other = (PhoneNumber) obj;
if (areaCode != other.areaCode)
return false;
if (lineNumber == null) {
if (other.lineNumber != null)
return false;
} else if (!lineNumber.equals(other.lineNumber))
return false;
if (prefix == null) {
if (other.prefix != null)
return false;
} else if (!prefix.equals(other.prefix))
return false;
return true;
}
}複製程式碼
13、陣列有沒有 length()方法?String 有沒有 length()方法?
陣列沒有 length()方法 ,有 length 的屬性。String 有 length()方法。JavaScript中,獲得字串的長度是通過 length 屬性得到的,這一點容易和 Java 混淆。
14、在 Java 中,如何跳出當前的多重巢狀迴圈?
在最外層迴圈前加一個標記如 A,然後用 break A;可以跳出多重迴圈。(Java 中支援帶標籤的 break 和 continue 語句,作用有點類似於 C 和 C++中的 goto 語句,但是就像要避免使用 goto 一樣,應該避免使用帶標籤的 break 和 continue,因為它不會讓你的程式變得更優雅,很多時候甚至有相反的作用,所以這種語法其實不知道更好)
15、構造器(constructor)是否可被重寫(override)?
16、兩個物件值相同(x.equals(y) == true),但卻可有不同的hash code,這句話對不對?
不對,如果兩個物件 x 和 y 滿足 x.equals(y) == true,它們的雜湊碼(hash code)應當相同。Java 對於 eqauls 方法和 hashCode 方法是這樣規定的:
(1)如果兩個物件相同(equals 方法返回 true),那麼它們的 hashCode 值一定要相同;
(2)如果兩個物件的 hashCode 相同,它們並不一定相同。當然,你未必要按照要求去做,但是如果你違背了上述原則就會發現在使用容器時,相同的物件可以出現在 Set 集合中,同時增加新元素的效率會大大下降(對於使用雜湊儲存的系統,如果雜湊碼頻繁的衝突將會造成存取效能急劇下降)。
補充:關於 equals 和 hashCode 方法,很多 Java 程式都知道,但很多人也就是僅僅知道而已,在 Joshua Bloch 的大作《Effective Java》(很多軟體公司,《Effective Java》、《Java 程式設計思想》以及《重構:改善既有程式碼質量》是 Java程式設計師必看書籍,如果你還沒看過,那就趕緊去亞馬遜買一本吧)中是這樣介紹equals 方法的:首先 equals 方法必須滿足自反性(x.equals(x)必須返回 true)、
對稱性(x.equals(y)返回 true 時,y.equals(x)也必須返回 true)、傳遞性(x.equals(y)和 y.equals(z)都返回 true 時,x.equals(z)也必須返回 true)和一致性(當 x 和 y 引用的物件資訊沒有被修改時,多次呼叫 x.equals(y)應該得到同樣的返回值),而且對於任何非 null 值的引用 x,x.equals(null)必須返回 false。
(1) 使用==操作符檢查”引數是否為這個物件的引用”;
(2) 使用 instanceof 操作符檢查”引數是否為正確的型別”;
(3) 對於類中的關鍵屬性,檢查引數傳入物件的屬性是否與之相匹配;
(4) 編寫完 equals方法後,問自己它是否滿足對稱性、傳遞性、一致性;
(5) 重寫 equals 時總是要重寫 hashCode;
(6) 不要將 equals 方法引數中的 Object 物件替換為其他的型別,在重寫時不要忘掉@Override 註解。
17、是否可以繼承 String 類?
String 類是 final 類,不可以被繼承。
補充:繼承 String 本身就是一個錯誤的行為,對 String 型別最好的重用方式是關聯關係(Has-A)和依賴關係(Use-A)而不是繼承關係(Is-A)。
18、當一個物件被當作引數傳遞到一個方法後,此方法可改變這個物件的屬性,並可返回變化後的結果,那麼這裡到底是值傳遞還是引用傳遞?
是值傳遞。Java 語言的方法呼叫只支援引數的值傳遞。當一個物件例項作為一個引數被傳遞到方法中時,引數的值就是對該物件的引用。物件的屬性可以在被呼叫過程中被改變,但對物件引用的改變是不會影響到呼叫者的。C++和 C#中可以通過傳引用或傳輸出引數來改變傳入的引數的值。在 C#中可以編寫如下所示的程式碼,但是在 Java 中卻做不到。
using System;
namespace CS01 {
class Program {
public static void swap(ref int x, ref int y) {
int temp = x;
x = y;
y = temp;
}
public static void Main (string[] args) {
int a = 5, b = 10;
swap (ref a, ref b);
// a = 10, b = 5;
第 225 頁 共 485 頁
Console.WriteLine ("a = {0}, b = {1}", a, b);
}
}
}複製程式碼
說明:Java 中沒有傳引用實在是非常的不方便,這一點在 Java 8 中仍然沒有得到改進,正是如此在 Java 編寫的程式碼中才會出現大量的 Wrapper 類(將需要通過方法呼叫修改的引用置於一個 Wrapper 類中,再將 Wrapper 物件傳入方法),這樣的做法只會讓程式碼變得臃腫,尤其是讓從 C 和 C++轉型為 Java 程式設計師的開發者無法容忍。
19、String 和 StringBuilder、StringBuffer 的區別?
Java 平臺提供了兩種型別的字串:String 和 StringBuffer/StringBuilder,它們可以儲存和操作字串。其中 String 是隻讀字串,也就意味著 String 引用的字串內容是不能被改變的。而 StringBuffer/StringBuilder 類表示的字串物件可以直接進行修改。StringBuilder 是 Java 5 中引入的,它和 StringBuffer 的方法完全相同,區別在於它是在單執行緒環境下使用的,因為它的所有方面都沒有被synchronized 修飾,因此它的效率也比 StringBuffer 要高。
面試題 1 - 什麼情況下用+運算子進行字串連線比呼叫
StringBuffer/StringBuilder 物件的 append 方法連線字串效能更好?
class StringEqualTest {
public static void main(String[] args) {
String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program";
String s4 = "ming";
String s5 = "Program" + "ming";
String s6 = s3 + s4;
System.out.println(s1 == s2);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s1 == s6.intern());
System.out.println(s2 == s2.intern());
}
}複製程式碼
(1)String 物件的 intern 方法會得到字串物件在常量池中對應的版本的引用(如果常量池中有一個字串與 String 物件的 equals 結果是 true),如果常量池中沒有對應的字串,則該字串將被新增到常量池中,然後返回常量池中字串的引用;
(2)字串的+操作其本質是建立了 StringBuilder 物件進行 append 操作,然後將拼接後的 StringBuilder 物件用toString 方法處理成 String 物件,這一點可以用 javap -c StringEqualTest.class命令獲得 class 檔案對應的 JVM 位元組碼指令就可以看出來。
20、過載(Overload)和重寫(Override)的區別。過載的方法能否根據返回型別進行區分?
方法的過載和重寫都是實現多型的方式,區別在於前者實現的是編譯時的多型性,而後者實現的是執行時的多型性。過載發生在一個類中,同名的方法如果有不同的引數列表(引數型別不同、引數個數不同或者二者都不同)則視為過載;重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的返回型別,比父類被重寫方法更好訪問,不能比父類被重寫方法宣告更多的異常(里氏代換原則)。過載對返回型別沒有特殊的要求。
面試題:華為的面試題中曾經問過這樣一個問題 - “為什麼不能根據返回型別來區分過載”,快說出你的答案吧!
最後
歡迎大家關注我的公種浩【程式設計師追風】,整理了1000道2019年多家公司java面試題400多頁pdf文件,文章都會在裡面更新,整理的資料也會放在裡面。喜歡文章記得關注我點個贊喲,感謝支援!