引用:(代碼隨想錄的八股轉免費了)以下為網址
卡碼筆記
本文為學習以上文章的筆記,如果有時間推薦直接去原網址
Java中的數據類型有哪些?分為哪兩大類? (考點:Java數據類型及其分類) 【簡單】
基本回答
分為 基本數據類型 和 引用數據類型
基本數據類型
基本數據類型在內存中直接存儲值,有八種
-
整數:byte(8位),short(16位),int(32位),long(64位)
-
浮點數:float(32位),double(64位)
-
字符:char(16位)
-
布爾:boolean(1位)
引用數據類型
對對象的引用,而不是對象本身。存儲的是對象在內存中的地址。
- 類
- 接口
- 數組
基本數據類型(
整數:byte(8位),short(16位),int(32位),long(64位)
浮點數:float(32位),double(64位)
字符:char(16位)
布爾:boolean(1位))
和 引用數據類型(類? 接口? 數組)
隱式類型轉換(小轉大),顯式類型轉換(大轉小)。
表達式結果的類型自動提升為所操作數據中最大的類型。
Java中的final關鍵字可以修飾什么?被final修飾后有什么特點? (考點:final關鍵字的用法) 【中等】
簡要回答
- final作為Java中的一個關鍵字可以用來修飾 類 , 方法 ,和 變量。(但final不能修飾構造器)
- 修飾類: 被final修飾的類不能被繼承,但該類可以去繼承別的 (沒有被final修飾的)類,例如String類和System類,它們被final修飾,是不可以被繼承的,但是它們有自己的父類——即頂層父類Object類。 被final修飾的類雖然不能被繼承,但?可以被實例化。
- 修飾方法: 被final修飾的方法不能被子類重寫,但可以被子類繼承并使用(在滿足訪問權限規則的前提下)。當修飾方法時,?final關鍵字不能與abstract關鍵字共存;因為abstract修飾的方法是必須被非抽象子類重寫的。
- 修飾變量: 被final修飾的變量稱為最終變量,即常量——根據被修飾變量的定義位置又可分為成員常量和局部常量。常量只能賦值一次,不能被二次更改。
- final作為Java中的一個關鍵字可以用來修飾 類 , 方法 ,和 變量。(但final不能修飾構造器)
- 修飾類: 不能被繼承,但該類可以去繼承別的 (沒有被final修飾的)類,可以被實例化。
- 修飾方法: 不能被子類重寫,但可以被子類繼承并使用(在滿足訪問權限規則的前提下)。final關鍵字不能與abstract關鍵字共存
- 修飾變量: 最終變量,即常量又可分為成員常量和局部常量。常量只能賦值一次,不能被二次更改。
解釋一下Java中的自動裝箱和拆箱。 (考點:自動裝箱與拆箱機制) 【簡單】
概念
自動裝箱:編譯器自動將基本數據類型轉換為對應的包裝類型
自動拆箱:編譯器自動將包裝類型轉換為對應的基本數據類型
Integer a = Integer.valueOf(10); // 自動裝箱
int b = a.intValue(); // 自動拆箱
Java中的==和equals()方法有什么區別? (考點:==與equals()的區別) 【簡單】
== 運算符
- 用途:
==
?運算符用于比較兩個引用是否指向同一個對象實例。 - 比較對象:當用于對象時,
==
?比較的是對象的內存地址,即它們是否是同一個對象。 - 基本類型:對于基本數據類型(如?
int
、char
?等),==
?比較的是它們的值。
equals()
equals()
?方法在?Object
?類中定義,默認情況下,它與?==
?等價(比較內存地址)。但是,許多類(如?String
?和?Integer
)重寫了此方法,以提供基于內容的比較。
== 運算符
- 用途:是否指向同一個對象實例。
- 比較對象:比較的是內存地址
- 基本類型:比較的是值。
equals()
equals()
?方法在?Object
?類中定義,默認情況下,它與?==
?等價(比較內存地址)。許多類(如?
String
?和?Integer
)重寫了此方法,以提供基于內容的比較。
Java中的訪問修飾符有哪些?各自的訪問范圍是什么? (考點:Java訪問修飾符) 【簡單】
Java中有四種訪問修飾符:
一、public(公共的)
- 訪問范圍 ? 可以被任何類訪問,無論是在同一個包內還是不同包內的類。
二、protected(受保護的)
- 訪問范圍 ? 在同一個包內的類可以訪問。 ? 不同包中的子類可以訪問(這里的子類是通過繼承得到的)。
三、默認(無修飾符,也叫包級私有)
- 訪問范圍 ? 只能在同一個包內的類訪問。
四、private(私有的)
- 訪問范圍 ? 只能在定義它的類內部訪問,類外部的任何類(包括同一個包內的其他類和不同包中的子類等)都不能訪問。
public(公共的)>protected(受保護的)>默認(無修飾符,也叫包級私有)>private(私有的)
任何類-> 同一個包內的類,不同包中的子類->同一個包內的類->只在定義它的類內部訪問
Java中的靜態變量(static)和非靜態變量有什么區別? (考點:靜態變量與非靜態變量的區別) 【中等】
靜態變量
- 靜態變量存儲在方法區的靜態存儲區,在類加載時分配內存
- 靜態變量是全局共享的,所有的類的實例和靜態方法都可以訪問,且修改靜態變量會影響所有實例。
- 可以通過類名.變量名的方式來訪問
非靜態變量
- 非靜態變量存儲在堆內存中
- 隨實例的創建而創建,隨實例的銷毀而銷毀
- 必須通過實例對象訪問,不能直接通過類名訪問
靜態變量
- 存儲:靜態存儲區,類加載時分配內存
- 全局共享:所有的類的實例和靜態方法都可以訪問,且修改靜態變量會影響所有實例。
- 訪問方式:類名.變量名
非靜態變量
- 存儲:堆內存中
- 隨實例的創建而創建,隨實例的銷毀而銷毀
- 訪問方式:必須通過實例對象訪問,不能直接通過類名訪問
Java中的方法重載(Overloading)和方法覆蓋(Overriding)(重寫與方法覆蓋是同一概念的不同表述))有什么區別? (考點:方法重載與覆蓋) 【中等】
方法重載指的是在同一個類中,多個方法具有相同的名字,但參數列表不同。方法重載是編譯時多態的一種表現,它通過方法參數的不同來區分方法。
特點:
- 方法名相同,但參數列表不同(參數個數、類型、順序)。
- 返回值類型可以相同,也可以不同,但如果只有返回值不同,不能作為重載的依據。
- 方法重載發生在同一個類中。
方法覆蓋是指子類重新實現父類中已經存在的方法,并且方法簽名(方法名、參數列表)保持一致。 方法覆蓋發生在繼承關系中,是運行時多態的一種體現,允許子類通過自己的實現覆蓋父類的方法。
特點:
- 子類重寫父類的方法,方法名、參數列表、返回類型必須一致。
- 只能發生在繼承關系中(子類繼承父類)。
- 可以改變方法的實現,但不能改變方法簽名。
- 運行時多態:方法的調用決定于對象的實際類型,而不是引用類型。
重載和重寫的主要區別:
特性 | 方法重載(Overloading) | 方法覆蓋(Overriding) |
---|---|---|
定義 | 在同一個類中,方法名相同,參數不同 | 子類重新實現父類的已存在的方法 |
發生地點 | 同一類中 | 子類繼承父類時發生 |
方法簽名 | 方法名相同,參數不同 | 方法名相同,參數相同 |
返回類型 | 返回類型可以不同(不作為重載的依據) | 返回類型必須相同(或是子類類型的子類) |
訪問修飾符 | 子類的訪問修飾符可以更寬松(但不能更嚴格) | 子類的訪問修飾符不能更嚴格,必須是相同或更寬松 |
多態類型 | 編譯時多態 | 運行時多態 |
調用方式 | 通過方法參數來區分調用哪個方法 | 通過對象的實際類型來調用方法 |
Java中的異常處理機制是怎樣的? (考點:Java異常處理機制) 【中等】
- 編寫代碼時,將可能拋出異常的代碼放在
try
塊中。 - 在
try
塊后面添加一個或多個catch
塊來捕獲并處理可能發生的不同類型的異常。 - 可選地,在
try
塊和catch
塊之后添加一個finally
塊來執行必要的清理代碼。 - 在方法簽名中使用
throws
子句聲明方法可能拋出的異常。 - 在代碼中,可以使用
throw
語句顯式地拋出異常。
- 編寫代碼時,將可能拋出異常的代碼放在
try
塊中。- 在
try
塊后面添加一個或多個catch
塊來捕獲并處理可能發生的不同類型的異常。- 可選地,在
try
塊和catch
塊之后添加一個finally
塊來執行必要的清理代碼。- 在方法簽名中使用
throws
子句聲明方法可能拋出的異常。- 在代碼中,可以使用
throw
語句顯式地拋出異常。
解釋一下Java中的泛型(Generics)及其作用。 (考點:Java泛型) 【中等】
Java中的泛型(Generics)是一種通過參數化類型來增強代碼重用性和類型安全性的機制。它使得類、接口、方法能夠操作指定類型的對象,而不需要指定具體的類型。通過使用泛型,程序員可以編寫類型安全的代碼,同時避免強制類型轉換,增加了代碼的可讀性和維護性。
1. 泛型的基本概念
泛型允許你在編寫類、接口和方法時,不確定它們將操作的對象的類型,而是在實例化時指定具體的類型。你可以將泛型看作一個**“模板”**,它能在運行時確定操作的數據類型。
2. 泛型的作用
-
類型安全:使用泛型可以讓代碼在編譯時檢查類型,避免了運行時錯誤。
-
代碼復用:泛型允許你編寫更具通用性的代碼,可以處理不同的數據類型,而不需要為每個數據類型編寫不同的類或方法。
-
避免強制類型轉換:使用泛型,編譯器會自動推斷類型,避免了手動進行類型轉換的麻煩。
3. 泛型的基本語法
泛型通常通過尖括號<>
來指定,<>
內是占位符,用來表示類型。例如:
-
泛型類:
class ClassName<T>
,T
代表一種類型。 -
泛型方法:
<T> void method(T param)
,T
是傳入方法的類型。
比喻:
假設你在開一個果汁店,你提供不同類型的果汁(橙汁、蘋果汁、葡萄汁等)。如果你不使用泛型,每次做一個新類型的果汁時,你都需要創建一個新的方法(例如做橙汁、做蘋果汁、做葡萄汁的獨立方法)。但是,如果你使用了泛型,你只需要一個通用的“果汁制作方法”,然后根據不同的果實類型來制作相應的果汁。
4. 泛型類示例
假設我們有一個容器類(比如Box
),它可以存儲任何類型的對象。如果沒有泛型,我們就只能存儲特定類型的對象,且取出時需要做類型轉換。使用泛型后,容器可以存儲任何類型的對象,同時避免了類型轉換的問題。
沒有泛型的例子:
public class Box { private Object value; public void setValue(Object value) { this.value = value; } public Object getValue() { return value; } public static void main(String[] args) { Box box = new Box(); box.setValue("Hello"); String message = (String) box.getValue(); // 強制類型轉換 System.out.println(message); } }
這種方式雖然能存儲任何類型,但每次取出數據時都需要強制類型轉換,這容易出錯。
使用泛型的例子:
public class Box<T> { // 使用泛型 T private T value; public void setValue(T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args) { Box<String> box = new Box<>(); // 創建一個存儲 String 類型的 Box box.setValue("Hello"); String message = box.getValue(); // 不需要強制類型轉換 System.out.println(message); } }
在這個例子中,Box
類是一個泛型類,它可以存儲任何類型的數據(通過類型參數T
來指定)。通過在創建Box
實例時指定類型(例如Box<String>
),我們可以確保類型安全,而且不需要手動進行類型轉換。
5. 泛型方法
泛型不僅可以用于類,還可以用于方法。泛型方法允許你在方法中使用泛型參數,使得方法在不同的類型之間共享。
泛型方法示例:
public class GenericMethodExample { public static <T> void printArray(T[] array) { for (T element : array) { System.out.println(element); } } public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4}; String[] strArray = {"Apple", "Banana", "Cherry"}; printArray(intArray); // 輸出整數數組 printArray(strArray); // 輸出字符串數組 } }
這里的<T>
表示該方法是一個泛型方法,可以接收任何類型的數組作為參數。無論是Integer
類型的數組,還是String
類型的數組,都可以通過這個方法進行打印。
6. 泛型的邊界(Bounded Type Parameters)
泛型類型參數可以加上邊界(Bound),限制它可以接受的類型范圍。使用extends
關鍵字,你可以指定泛型的上邊界,這樣泛型參數就只能是指定類或其子類的類型。
上邊界(Upper Bounded Wildcards):
public class BoundedTypeExample { public static <T extends Number> void printNumbers(T[] numbers) { for (T num : numbers) { System.out.println(num); } } public static void main(String[] args) { Integer[] intArray = {1, 2, 3}; Double[] doubleArray = {1.1, 2.2, 3.3}; printNumbers(intArray); // 適用于 Integer 類型 printNumbers(doubleArray); // 適用于 Double 類型 } }
在這個例子中,<T extends Number>
表示T
只能是Number
類或它的子類(如Integer
、Double
等)。這確保了T
的類型是數字類型。
7. 通配符(Wildcard)
泛型中的**通配符(Wildcard)**可以用來表示不確定的類型。例如,?
表示任何類型。常見的通配符有:
-
? extends T
:表示類型是T
或T
的子類。 -
? super T
:表示類型是T
或T
的父類。
示例:
public class WildcardExample { public static void printList(List<? extends Number> list) { for (Number num : list) { System.out.println(num); } } public static void main(String[] args) { List<Integer> intList = List.of(1, 2, 3); List<Double> doubleList = List.of(1.1, 2.2, 3.3); printList(intList); // 適用于 Integer 類型 printList(doubleList); // 適用于 Double 類型 } }
這里的List<? extends Number>
表示可以接受任何Number
及其子類的List
(如Integer
、Double
等)。
8. 泛型的類型擦除
Java中的泛型是在編譯時通過類型擦除來實現的。在運行時,泛型的類型參數會被擦除,轉換為原始類型(例如,T
被替換為Object
,List<T>
變成了List
)。因此,泛型的類型信息是不會保留到運行時的。
9. 總結
-
泛型的作用:
- 提供類型安全的代碼。
- 增強代碼的可重用性。
- 避免強制類型轉換和運行時錯誤。
-
泛型語法:
- 泛型類:
class ClassName<T>
。 - 泛型方法:
<T> void method(T param)
。 - 邊界(Bounded Type):
<T extends Number>
。
- 泛型類:
-
通配符:
? extends T
表示類型是T
的子類,? super T
表示類型是T
的父類。
泛型(Generics)是一種通過參數化類型來增強代碼重用性和類型安全性的機制。它使得
類、接口、方法能夠操作指定類型的對象,而不需要指定具體的類型。
通過使用泛型,程序員可以編寫類型安全的代碼,同時避免強制類型轉換,增加了代碼的可讀性和維護性。
1. 泛型的基本概念
泛型允許你在編寫類、接口和方法時,不確定它們將操作的對象的類型,而是在實例化時指定具體的類型。你可以將泛型看作一個**“模板”**,它能在運行時確定操作的數據類型。
2. 泛型的作用
類型安全:使用泛型可以讓代碼在編譯時檢查類型,避免了運行時錯誤。
代碼復用:泛型允許你編寫更具通用性的代碼,可以處理不同的數據類型,而不需要為每個數據類型編寫不同的類或方法。
避免強制類型轉換:使用泛型,編譯器會自動推斷類型,避免了手動進行類型轉換的麻煩。
3. 泛型的基本語法
泛型通常通過尖括號
<>
來指定,<>
內是占位符,用來表示類型。例如:
泛型類:
class ClassName<T>
,T
代表一種類型。泛型方法:
<T> void method(T param)
,T
是傳入方法的類型。4. 泛型類示例
假設我們有一個容器類(比如
Box
),它可以存儲任何類型的對象。如果沒有泛型,我們就只能存儲特定類型的對象,且取出時需要做類型轉換。使用泛型后,容器可以存儲任何類型的對象,同時避免了類型轉換的問題
泛型的作用:
- 提供類型安全的代碼。
- 增強代碼的可重用性。
- 避免強制類型轉換和運行時錯誤。
泛型語法:
- 泛型類:
class ClassName<T>
。- 泛型方法:
<T> void method(T param)
。- 邊界(Bounded Type):
<T extends Number>
。通配符:
? extends T
表示類型是T
的子類,? super T
表示類型是T
的父類。
Java中的String類為什么是不可變的? (考點:String類的不可變性) 【中等】
Java中的String
類是不可變的(immutable),這意味著一旦創建了一個String
對象,它的值就不能被改變。這個設計帶來了很多好處,包括線程安全、性能優化和安全性等。
1. 為什么要設計成不可變?
1.1 線程安全
不可變的String
類是天然線程安全的,因為它的值不能被修改。這就意味著多個線程可以共享同一個String
對象,而不必擔心并發修改帶來的問題。
- 比喻:想象你和幾個朋友一起共享一部旅行指南(
String
對象)。每個人都可以自由地閱讀這本書,但沒有人能在別人看書的時候悄悄改動里面的內容。所以,不會發生因為修改內容而導致爭議或錯誤的情況。
1.2 安全性
不可變對象的另一個好處是安全性。例如,String
常常用于存儲用戶名、密碼、URL等敏感信息。如果String
是可變的,那在某些情況下,其他代碼就有可能不小心或故意地修改它的值。而不可變的String
確保了它的內容不會被改變,保障了數據的安全性。
- 比喻:假設你在圖書館借書(
String
對象)。一旦借走,你就不能隨意修改書的內容,這樣其他借書的人才能放心使用。
1.3 優化性能(常量池)
Java通過字符串常量池來優化內存使用。因為String
對象不可變,當你創建多個相同內容的String
時,JVM會使用同一個對象而不是創建多個相同的String
對象,從而節省內存。
- 比喻:假設你在一家餐館吃飯,每次你點了一樣的菜,餐館都會直接給你一份已經做好的,而不是每次都重新做一份,這樣就避免了重復勞動和資源浪費。
1.4 提高效率
不可變的String
對象可以共享,并且JVM可以對其進行各種優化(如緩存)。例如,String
的intern()
方法會檢查常量池中是否已經存在相同的字符串,如果存在就返回相同的引用,這減少了內存的使用。
- 比喻:想象你在購物時,商店有很多相同的商品,商店會在庫存中有一份相同的商品,而不是每次都去生產新商品,這樣節省了時間和資源。
2. 如何保證String不可變?
Java的String
類通過以下幾個方面來實現不可變:
2.1?String
類的字段是final
String
類中存儲字符串內容的字段是private final char[] value
。final
關鍵字意味著這個字段只能被賦值一次,賦值后無法更改。因此,String
的內容一旦設置好就無法修改。
2.2?String
類沒有提供修改內容的方法
String
類沒有提供可以直接修改其內容的方法,例如沒有setCharAt()
、append()
等方法。所有對String
的“修改”操作實際上都會生成一個新的String
對象。例如,String
類的concat()
方法不會改變原始String
對象,而是返回一個新的String
對象。
- 比喻:如果你正在寫一個日記(
String
),你不能直接在原來頁面上修改內容。每次你想修改內容時,必須在新的日記本上記錄,這樣舊的日記本內容不受影響。
2.3?String
的不可變設計
每當對String
進行修改時,都會生成一個新的String
對象,而不是修改原來的對象。例如,調用+
操作符或concat()
方法連接字符串時,實際上會創建一個新的String
對象。
- 比喻:假設你想加裝飾畫到墻上(
String
)。雖然你可以在原有墻面上畫畫,但實際是把畫作掛在了新墻面上(新的String
對象),原墻面沒有任何變化。
3.?String
不可變的實現示例
public class StringExample { public static void main(String[] args) { String str1 = "Hello"; String str2 = str1; str1 = str1 + " World"; // 創建了一個新的String對象 System.out.println("str1: " + str1); // 輸出 "Hello World" System.out.println("str2: " + str2); // 輸出 "Hello" } }
在上面的例子中,雖然我們通過+
操作符給str1
增加了內容,但實際上它指向的是一個新的String
對象,原始的str1
對象內容沒有改變。因此,str2
仍然指向原始的"Hello"
。
4. String的不可變性帶來的影響
4.1 性能考慮
雖然不可變性帶來了很多好處,但它也有一些缺點,尤其是在字符串頻繁修改的場景下,因為每次修改都會創建一個新的String
對象。為了優化性能,通常可以使用StringBuilder
或StringBuffer
類,它們是可變的,適合做頻繁的字符串拼接操作。
4.2 對比String和StringBuilder
public class StringBuilderExample { public static void main(String[] args) { StringBuilder sb = new StringBuilder("Hello"); sb.append(" World"); // 不會創建新對象,而是修改原有對象 System.out.println(sb.toString()); // 輸出 "Hello World" } }
在上面的代碼中,StringBuilder
是可變的,它不會每次修改時創建新的對象,而是在原對象上進行操作,這樣可以提高效率。
String
類是不可變的,意味著它一旦被創建就不能改變。不可變性帶來了線程安全、安全性、性能優化等優點。
String
的不可變設計是通過final
字段、沒有修改內容的方法、以及每次操作都會創建新的String
對象來實現的。如果需要頻繁修改字符串,可以考慮使用
StringBuilder
或StringBuffer
,它們是可變的。不可變的設計是
String
類的一大優勢,幫助Java程序在多線程和內存管理上變得更加高效和安全。
String、StringBuffer和StringBuilder有什么區別? (考點:String、StringBuffer與StringBuilder的區別) 【簡單】
String
、StringBuffer
?和?StringBuilder
?是 Java 中用于處理字符串的三種類。它們之間有許多不同的特點,適用于不同的場景。
1.?String
- 定義:
String
?是不可變的字符序列。它的值一旦被賦值就不能改變,任何對?String
?對象的操作(如連接、修改等)都會生成一個新的?String
?對象。 - 特點:
- 不可變性:一旦創建,字符串的值不能改變。任何修改字符串的操作都會生成一個新的對象,而原有的字符串對象保持不變。
- 線程安全:由于是不可變的,不需要額外的同步機制,因此是線程安全的。
- 內存效率問題:由于?
String
?是不可變的,所以它的字符串值會被多次復制,頻繁操作時可能會造成內存浪費(特別是字符串連接操作)。
- 使用場景:
- 當字符串內容不需要頻繁改變時(例如固定內容、常量池中的字符串等),使用?
String
?是合適的。 - 適合用于不需要改變的、較小的字符串處理操作。
- 當字符串內容不需要頻繁改變時(例如固定內容、常量池中的字符串等),使用?
2.?StringBuffer
- 定義:
StringBuffer
?是一個可以變更的字符序列,允許在原有字符序列上進行修改(比如追加、刪除字符等)。它是可變的,并且線程安全的。 - 特點:
- 可變性:
StringBuffer
?對象中的內容是可以改變的(例如:添加、刪除字符等)。它會直接修改原有字符數組,而不創建新的對象。 - 線程安全:
StringBuffer
?的方法是同步的,因此它是線程安全的,適用于多線程環境。 - 性能:由于其線程安全性,
StringBuffer
?的性能相對較低,特別是在不涉及多線程的情況下,可能會比?StringBuilder
?慢。
- 可變性:
- 使用場景:
- 當需要在多線程環境下進行字符串拼接或者修改時使用?
StringBuffer
。 - 在保證線程安全的情況下,需要進行字符串頻繁修改的場景(比如構建動態的字符串)。
- 當需要在多線程環境下進行字符串拼接或者修改時使用?
3.?StringBuilder
- 定義:
StringBuilder
?與?StringBuffer
?類似,也是一個可以變更的字符序列。與?StringBuffer
?不同的是,StringBuilder
?并不是線程安全的,適合單線程環境下使用。 - 特點:
- 可變性:
StringBuilder
?也可以修改其內部的字符序列,直接在原有字符數組上進行修改。 - 線程不安全:
StringBuilder
?的方法沒有同步,因此在多線程環境下,它不保證線程安全。 - 性能:因為它沒有同步機制,相比?
StringBuffer
?來說,StringBuilder
?在單線程下的性能更好,適合單線程環境中的高效字符串操作。
- 可變性:
- 使用場景:
- 在單線程環境下頻繁修改字符串時,使用?
StringBuilder
?可以獲得更好的性能。 - 當線程安全不是問題時,并且操作頻繁時,推薦使用?
StringBuilder
。
- 在單線程環境下頻繁修改字符串時,使用?
總結:
特性 | String | StringBuffer | StringBuilder |
---|---|---|---|
是否可變 | 不可變 | 可變 | 可變 |
線程安全 | 是 | 是 (通過同步實現線程安全) | 否 |
性能 | 性能較低(頻繁修改時不推薦) | 性能較低(同步操作影響性能) | 性能較高(適合單線程環境) |
適用場景 | 字符串不變時,常量池等使用 | 多線程環境下需要修改字符串時使用 | 單線程環境下頻繁修改字符串時使用 |
結論:
- 如果你不需要修改字符串,使用?
String
。 - 如果在多線程環境下需要修改字符串,使用?
StringBuffer
。 - 如果在單線程環境下需要頻繁修改字符串,使用?
StringBuilder
。
總結:
特性 String StringBuffer StringBuilder 是否可變 不可變 可變 可變 線程安全 是 是 (通過同步實現線程安全) 否 性能 性能較低(頻繁修改時不推薦) 性能較低(同步操作影響性能) 性能較高(適合單線程環境) 適用場景 字符串不變時,常量池等使用 多線程環境下需要修改字符串時使用 單線程環境下頻繁修改字符串時使用 結論:
- 如果你不需要修改字符串,使用?
String
。- 如果在多線程環境下需要修改字符串,使用?
StringBuffer
。- 如果在單線程環境下需要頻繁修改字符串,使用?
StringBuilder
。
Java中的lambda表達式是什么?它帶來了哪些便利? (考點:Java lambda表達式) 【中等】
想象一下,你去咖啡店點了一杯咖啡。原來你得等服務員跑去廚房,然后帶著咖啡回來給你。這個過程就像傳統的代碼,你要去寫一個方法,然后傳遞給某個地方去執行。你看,整個過程慢得像烏龜。
但是,lambda 表達式就像是你告訴咖啡師:“嘿,我不想等,你直接在這兒做完就行了。” 這樣,你自己只需要告訴咖啡師“做一杯咖啡”就行,省去了不必要的繁瑣步驟。
Lambda表達式的好處就是讓代碼變得簡潔,功能也變得更靈活。
看個例子:
如果你要做一個加法運算:
傳統寫法:
// 使用匿名類來寫 public int add(int a, int b) { return a + b; }
Lambda表達式寫法:
// 使用Lambda表達式來寫 BinaryOperator<Integer> add = (a, b) -> a + b;
1.?Lambda表達式的基本結構:
(參數列表) -> {方法體}
參數列表:就是你要傳給這個函數的值,比如?(a, b)
。?方法體:就是具體的操作,像?a + b
,它決定了這個表達式要做什么。
2.?Lambda表達式帶來的便利:
- 簡潔性:減少了冗長的代碼,像上面的加法例子,傳統方法需要很多代碼,而Lambda表達式只需要一行。
- 可讀性:代碼變得簡潔,其他人閱讀起來更容易理解。
- 函數式編程:讓你可以像使用“工具”一樣去操作代碼,提高了代碼的靈活性。
3.?實際應用場景:
比如,Lambda 在?Java 8 的流式操作中,允許你通過一種更加直觀的方式來處理集合數據,像是篩選、映射等。
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); names.stream() .filter(name -> name.startsWith("A")) // 用Lambda表達式過濾 .forEach(System.out::println); // 輸出符合條件的元素
看!簡單的幾行代碼就搞定了原本復雜的過濾和遍歷操作,Lambda大大提高了代碼的簡潔度和效率。
Lambda表達式是Java 8引入的一種新的語法特性,它主要目的是實現函數式編程風格,使得代碼更加簡潔、可讀性更高,并且能夠輕松地處理一些常見的操作,特別是在集合框架和并行處理方面。
Lambda表達式的基本語法形式如下:
(參數) -> {表達式}
核心特點:
- 簡潔性:Lambda允許我們直接在需要的地方傳遞行為(函數),無需冗長的匿名類或方法定義。
- 函數式接口:Lambda表達式的目標是簡化實現函數式接口(僅有一個抽象方法的接口)的方法體,通常用于回調、事件監聽等場景。
- 延遲計算與更強大的集合操作:與?
Stream API
?配合使用時,Lambda表達式能夠高效地進行集合操作,比如篩選、排序、映射等,且具有支持并行流的優勢。應用場景:
- 集合操作:Java 8 引入的?
Stream API
,使得使用Lambda表達式可以進行過濾、映射、排序等操作,大大提高了代碼的簡潔性。- 事件監聽:Lambda常用于回調或事件處理中,通過簡化代碼提高可維護性。
總的來說,Lambda表達式不僅僅是為了簡化代碼,它還提升了Java編程語言的表達能力,增強了函數式編程的特性,尤其在處理集合和并行處理時表現得尤為突出。
Java中的Optional類是什么?為什么需要它? (考點:Java Optional類及其必要性) 【中等】
簡要回答
- Optional類的定義:
- 包裝類:將可能為?
null
?的值封裝成?Optional
?對象(如?Optional<String>
)。 - 明確空值意圖:通過方法名(如?
isPresent()
、orElse()
)顯式表達“值可能不存在”的邏輯。
- 包裝類:將可能為?
- Optional類的作用:
- 避免?
NullPointerException
:強制開發者主動處理空值,減少運行時錯誤。 - 代碼更簡潔:鏈式調用替代多層?
if (obj != null)
?判空。 - API 設計更清晰:方法簽名中明確表示返回值可能為空(如?
Optional<User> findUser()
)。
- 避免?
談談JDK8新特性。(考點:JDK8新特性)【困難】
JDK 8 是 Java 的一次重要升級,引入了多個新特性,主要用于提升語言的表達能力、增強代碼的簡潔性和提高開發效率。
1.?Lambda表達式:
Lambda 表達式允許以簡潔的方式傳遞行為,它使得 Java 編程更具函數式編程風格。Lambda 表達式的基本語法是?(parameter) -> expression
,它為 Java 提供了匿名方法的替代,常常用于簡化集合操作和并行流處理等。
List<String> list = Arrays.asList("apple", "banana", "cherry"); list.forEach(s -> System.out.println(s)); // 使用Lambda表達式簡化代碼
2.?Stream API:
Stream API 是對集合的增強,支持聲明式地進行數據處理,包括排序、過濾、映射等操作。Stream 的操作支持鏈式調用,并且可以通過?parallelStream()
?輕松實現并行處理,提高大數據量處理的效率。
List<String> list = Arrays.asList("apple", "banana", "cherry"); long count = list.stream() .filter(s -> s.startsWith("a")) .count();
3.?Optional類:
Optional 類是為了避免 NullPointerException 的發生,作為一種容器來表示可能存在或者不存在的值。它為值存在與否提供了明確的語義,并提供了一些方法,如?orElse
?和?ifPresent
?來處理空值問題。
Optional<String> optional = Optional.ofNullable("Hello"); optional.ifPresent(s -> System.out.println(s)); // 如果值存在,打印
4.?新的日期時間API:
JDK 8 引入了新的?java.time
?包(包括?LocalDate
,?LocalTime
,?LocalDateTime
?等類),大大簡化了日期和時間的處理。新的 API 解決了老舊的?Date
?類和?Calendar
?類存在的線程不安全、復雜性等問題。
LocalDate date = LocalDate.of(2023, 10, 1); LocalDate today = LocalDate.now();
5.?默認方法(Default Methods):
默認方法是接口的一個新特性,允許在接口中定義有實現的方法,這樣接口新增方法時不會影響到實現該接口的類,減少了代碼的侵入性。
interface MyInterface { default void show() { System.out.println("Default method"); } } class MyClass implements MyInterface { // 無需實現show方法 }
6.?函數式接口和@FunctionalInterface
:
JDK 8 引入了?函數式接口?的概念,旨在支持 Lambda 表達式。函數式接口是只包含一個抽象方法的接口,可以使用?@FunctionalInterface
?注解來標識,常用于與 Lambda 表達式結合使用。
@FunctionalInterface interface MyFunction { void apply(); }
7.?方法引用(Method References):
方法引用是 Lambda 表達式的簡化形式,允許直接引用類中的方法,增強了代碼的可讀性和簡潔性。
List<String> list = Arrays.asList("apple", "banana", "cherry"); list.forEach(System.out::println); // 方法引用,簡化代碼