Java基礎面試題總結(題目來源JavaGuide)

問題1:Java 中有哪 8 種基本數據類型?它們的默認值和占用的空間大小知道不? 說說這 8 種基本數據類型對 應的包裝類型。

在 Java 中,有 8 種基本數據類型(Primitive Types):

基本數據類型關鍵字默認值占用空間對應的包裝類
整數類型
字節型 (byte)byte01 字節 (8 bit)Byte
短整型 (short)short02 字節 (16 bit)Short
整型 (int)int04 字節 (32 bit)Integer
長整型 (long)long0L8 字節 (64 bit)Long
浮點數類型
單精度浮點型 (float)float0.0f4 字節 (32 bit)Float
雙精度浮點型 (double)double0.0d8 字節 (64 bit)Double
字符類型
字符型 (char)char\u0000(空字符)2 字節 (16 bit)Character
布爾類型
布爾型 (boolean)booleanfalseJVM 規范未明確大小(通常 1 bit)Boolean

額外說明:

  1. boolean 的存儲大小依賴于 JVM 實現,通常使用 1 bit(但實際存儲可能會占據 1 字節)。
  2. char 采用 Unicode 編碼,所以它占用 2 字節
  3. 包裝類(Wrapper Classes)java.lang 包中,提供了基本類型的對象封裝,并支持自動裝箱(Autoboxing)和拆箱(Unboxing)。

問題2:包裝類型的常量池技術了解么?

1. 什么是包裝類型的常量池?

Java 的 ByteShortIntegerLongCharacterBoolean 類在一定范圍內會緩存對象,避免重復創建,提高性能


2. 包裝類常量池的示例

(1) Integer 緩存池

public class WrapperCacheTest {public static void main(String[] args) {Integer a = 127;Integer b = 127;System.out.println(a == b); // true,使用緩存Integer c = 128;Integer d = 128;System.out.println(c == d); // false,超出緩存范圍,創建新對象}
}

解析:

  • Integer a = 127;Integer b = 127; 指向同一個緩存對象,所以 a == btrue
  • Integer c = 128;Integer d = 128; 超出緩存范圍,創建不同對象,c == dfalse

(2) Boolean 常量池

Boolean bool1 = true;
Boolean bool2 = true;
System.out.println(bool1 == bool2); // true

Boolean 只有 TRUEFALSE 兩個緩存對象,所以 bool1 == bool2 始終為 true

(3) Character 緩存池

Character char1 = 127;
Character char2 = 127;
System.out.println(char1 == char2); // trueCharacter char3 = 128;
Character char4 = 128;
System.out.println(char3 == char4); // false

Character 只緩存 0 ~ 127,超出范圍會創建新對象。

3. 為什么 FloatDouble 沒有緩存池?

Float f1 = 1.0f;
Float f2 = 1.0f;
System.out.println(f1 == f2); // false,每次創建新對象Double d1 = 1.0;
Double d2 = 1.0;
System.out.println(d1 == d2); // false,每次創建新對象
原因
  • 浮點數范圍太大,緩存意義不大。
  • 浮點數計算常常涉及小數誤差,緩存可能會導致不穩定的行為。

4.?valueOf()new 的區別

(1) 使用 valueOf()

Integer x = Integer.valueOf(127);
Integer y = Integer.valueOf(127);
System.out.println(x == y); // true

valueOf() 方法使用緩存池,所以 x == ytrue

(2) 使用 new Integer()

Integer x = new Integer(127);
Integer y = new Integer(127);
System.out.println(x == y); // false

new Integer() 直接創建新對象,不使用緩存,所以 x == yfalse

最佳實踐:推薦使用 valueOf(),避免 new 關鍵字,以減少內存開銷。

5. equals() 比較推薦

由于 == 比較的是對象地址,而 equals() 比較的是,建議用 equals() 進行數值比較:

Integer a = 128;
Integer b = 128;
System.out.println(a.equals(b)); // true,比較值,結果正確System.out.println(a == b); // false,比較對象地址,超出緩存范圍

6. 總結

包裝類緩存范圍緩存機制
Byte-128 ~ 127使用緩存
Short-128 ~ 127使用緩存
Integer-128 ~ 127(可擴展)使用緩存,可調整 -XX:AutoBoxCacheMax
Long-128 ~ 127使用緩存
Character0 ~ 127使用緩存
Boolean只有 truefalse使用緩存
Float無緩存每次創建新對象
Double無緩存每次創建新對象

? 最佳實踐:

  1. 使用 valueOf() 代替 new 關鍵字。
  2. 使用 equals() 而不是 == 進行值比較。
  3. 了解緩存范圍,避免意外的 == 結果。

問題3:為什么要有包裝類型?

Java 之所以引入 包裝類型(Wrapper Classes),主要是為了讓基本數據類型(primitive types)具備對象的特性,方便在面向對象編程(OOP)中使用,同時增強泛型、集合框架等的兼容性。

1. 基本數據類型不是對象

Java 中有 8 種基本數據類型intcharbooleanfloat 等),它們的設計目標是提高性能,但它們不是對象:

int a = 10;
a.toString();  // ? 編譯錯誤,int 沒有方法
  • 不能直接調用方法。
  • 不能存儲在**集合(Collection)**中。
  • 不能作為泛型的類型參數。

2. 包裝類彌補了基本類型的不足

Java 提供了 對應的包裝類型IntegerDoubleBoolean 等),它們是,可以像對象一樣使用:

Integer num = 10;
System.out.println(num.toString()); // ? 10
  • 允許基本類型調用方法(比如 toString())。
  • 能夠存入 泛型集合(如 ArrayList<Integer>)。
  • 支持 自動裝箱/拆箱,讓基本類型和對象能無縫轉換。

3. 適用于 Java 集合框架

Java 集合(如 ArrayListHashMap只能存儲對象,不能存儲基本類型:

ArrayList<int> list = new ArrayList<>(); // ? 編譯錯誤

必須使用包裝類

ArrayList<Integer> list = new ArrayList<>();
list.add(10); // ? 自動裝箱:int → Integer

原因:Java 泛型(Generics)不支持基本類型,但支持對象。

4. 支持泛型(Generics)

泛型不能直接使用基本類型:

public class Box<T> {private T value;public void set(T value) { this.value = value; }public T get() { return value; }
}Box<int> box = new Box<>(); // ? 編譯錯誤

必須使用包裝類型

Box<Integer> box = new Box<>();
box.set(100); // ? 自動裝箱:int → Integer
int num = box.get(); // ? 自動拆箱:Integer → int

泛型只能接受對象,所以 int 不能直接用,而 Integer 作為對象可以使用。

5. 具備更多功能

包裝類提供了豐富的方法,可以方便地進行類型轉換、數學運算等:

String str = "123";
int num = Integer.parseInt(str); // ? String → int
double d = Double.parseDouble("3.14"); // ? String → double

基本類型無法進行字符串解析,但包裝類可以。

6. 適用于多線程中的同步

基本類型是線程不安全的,而包裝類(如 AtomicInteger)可以在多線程環境下使用:

AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // ? 線程安全的自增

適用于高并發場景

7. 支持 null

基本類型不能存儲 null,但包裝類型可以:

Integer num = null; // ? 合法
int n = null;  // ? 編譯錯誤

數據庫操作時,某些字段可能為空,包裝類更合適。

總結

基本數據類型包裝類的作用
不是對象讓基本類型具備對象特性
不能存集合支持泛型和集合框架
無方法包裝類提供豐富的方法
不支持 null包裝類支持 null
非線程安全包裝類有線程安全實現

最佳實踐

  • 優先使用基本類型(性能更好),只在需要對象時才用包裝類。
  • 避免不必要的自動裝箱/拆箱,以提高性能。

問題4:什么是自動拆裝箱?原理?

自動裝箱(Autoboxing)自動拆箱(Unboxing)Java 5 引入的特性,使得基本數據類型(intcharboolean 等)和它們的包裝類IntegerCharacterBoolean 等)之間可以自動轉換,簡化代碼編寫。

1. 自動裝箱(Autoboxing)

把基本數據類型 自動轉換成 對應的包裝類對象

Integer num = 10;  // 相當于 Integer num = Integer.valueOf(10);
  • 10int 類型,自動轉換為 Integer 對象。
  • 底層調用 Integer.valueOf(int) 方法,如果在 -128 ~ 127 之間,會使用緩存池,否則創建新對象。

2. 自動拆箱(Unboxing)

把包裝類對象 自動轉換成 基本數據類型

Integer num = 10;  // 自動裝箱
int a = num;       // 自動拆箱,相當于 int a = num.intValue();
  • numInteger 對象,自動轉換成 int 類型。
  • 底層調用 num.intValue() 方法

3. 自動裝箱/拆箱的使用示例

public class AutoBoxingDemo {public static void main(String[] args) {// 自動裝箱:基本類型 → 包裝類Integer a = 100; // 相當于 Integer a = Integer.valueOf(100);// 自動拆箱:包裝類 → 基本類型int b = a; // 相當于 int b = a.intValue();// 自動裝箱 + 計算 + 自動拆箱Integer c = 200;int d = c + 300; // c 先自動拆箱,再加 300,最后結果賦值給 int 類型的 d// 直接存入集合ArrayList<Integer> list = new ArrayList<>();list.add(10); // 自動裝箱// 取出時自動拆箱int e = list.get(0);System.out.println("b = " + b); // 100System.out.println("d = " + d); // 500System.out.println("e = " + e); // 10}
}

問題5:遇到過自動拆箱引發的 NPE 問題嗎?

1. 自動拆箱導致 NullPointerException 的示例

(1) null 賦值給基本類型

public class UnboxingNPE {public static void main(String[] args) {Integer num = null; // num 為空int value = num;    // 自動拆箱:num.intValue(),導致 NPESystem.out.println(value);}
}

原因

  • int value = num; 觸發自動拆箱,本質上調用了 num.intValue()
  • 由于 numnull,調用 intValue() 拋出 NullPointerException

2. 真實場景中的 NPE

(1) 集合取值時自動拆箱

import java.util.*;public class UnboxingNPE {public static void main(String[] args) {Map<String, Integer> scores = new HashMap<>();scores.put("Alice", 95);scores.put("Bob", null); // Bob 沒有分數int bobScore = scores.get("Bob"); // NPE: null 不能拆箱成 intSystem.out.println("Bob's score: " + bobScore);}
}

原因

  • scores.get("Bob") 返回 null,然后 int bobScore = null; 觸發自動拆箱,拋出 NullPointerException

解決方案

方式 1:手動檢查 null

Integer bobScore = scores.get("Bob");
int score = (bobScore != null) ? bobScore : 0; // 避免 NPE

方式 2:使用 getOrDefault()

int bobScore = scores.getOrDefault("Bob", 0); // 直接提供默認值

(2) 數據庫查詢結果可能為 null

public class UnboxingNPE {public static Integer getUserAgeFromDB() {return null; // 模擬數據庫查詢不到數據}public static void main(String[] args) {int age = getUserAgeFromDB(); // NPESystem.out.println("User age: " + age);}
}

解決方案

  • 使用 Optional 處理 null
Optional<Integer> ageOpt = Optional.ofNullable(getUserAgeFromDB());
int age = ageOpt.orElse(0); // 如果為空,默認值 0

3. 避免自動拆箱 NPE 的最佳實踐

方法示例優點
手動 null 檢查(num != null) ? num : 0直接避免 NPE
使用 getOrDefault()map.getOrDefault("key", 0)適用于 Map
使用 OptionalOptional.ofNullable(val).orElse(0)更優雅的 null 處理
避免包裝類用于計算int sum = 0; 代替 Integer sum = 0;避免不必要的拆裝箱

總結

  • 自動拆箱會導致 NullPointerException,如果變量可能為 null,一定要做 null 檢查!
  • 使用 getOrDefault()Optional 等方法來避免 NPE
  • 避免在計算時使用 Integer 等包裝類,盡量使用基本類型。

問題6:String、StringBuffer 和 StringBuilder 的區別是什么? String 為什么是不可變的?

1. StringStringBufferStringBuilder 的區別

在 Java 中,StringStringBufferStringBuilder 都是用于表示字符串的類,但它們的可變性、線程安全性和性能不同。

特性String (不可變)StringBuffer (可變 & 線程安全)StringBuilder (可變 & 非線程安全)
可變性不可變 (final char[])可變 (char[] 數組)可變 (char[] 數組)
線程安全性線程安全線程安全 (同步 synchronized)非線程安全
性能(每次修改都會創建新對象)較慢(線程安全的同步開銷)最快(無同步機制)
適用場景少量字符串處理(如字符串常量、少量拼接)多線程環境(字符串頻繁修改)單線程高性能需求(字符串頻繁修改)

2. 為什么 String 是不可變的?

String 在 Java 中是 不可變對象(Immutable),一旦創建就不能修改。這是由于以下幾個原因:

(1) String 內部使用 final char[] 存儲數據

查看 String 類的源碼:

public final class String implements java.io.Serializable, Comparable<String> {private final char value[];
}
  • valuefinal 類型的 字符數組 (char[]),所以它的引用不能被修改。
  • 不可變String 類不提供修改 char[] 內容的方法,如 setCharAt(),只能通過創建新對象改變值。

(2) 線程安全

由于 String 不可變,所以它天然是線程安全的,多個線程可以安全地共享同一個 String 對象,而不用加鎖

例如:

String str1 = "Hello";
String str2 = str1; // 共享同一個對象

由于 str1 是不可變的,str2 也不會因為 str1 的改變而受到影響。

(3) String 常量池優化

在 Java 中,String 對象會存儲在字符串常量池(String Pool)中,避免重復創建:

String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2); // true, 指向同一個對象
  • s1s2 指向的是同一個字符串常量池對象,而不會新建對象,減少內存占用。

如果 String 是可變的,這個優化就會導致數據混亂

s1.toUpperCase(); // 如果 String 可變,s2 也會被改變,破壞了安全性!

(4) hashCode() 設計

  • String 是不可變的,所以它的 hashCode() 在創建時就計算好并緩存,提高了 Hash 相關操作(如 HashMap)的性能:
public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {for (char val : value) {h = 31 * h + val;}hash = h;}return h;
}

由于 hashCode 不變,String 可以安全地作為 HashMapkey,不必擔心 key 被修改導致哈希值變化。

3. StringBufferStringBuilder 的區別

StringBufferStringBuilder 都是 可變的字符串類,但它們的主要區別是線程安全性

(1) StringBuffer 是線程安全的

  • StringBuffer 方法使用 synchronized 關鍵字,保證線程安全:
public synchronized StringBuffer append(String str) { ... }
  • 適用于多線程環境,但由于同步鎖的存在,性能比 StringBuilder 低。

示例:

StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");
System.out.println(sb); // Hello World

(2) StringBuilder 是非線程安全的

  • StringBuilder 沒有同步機制,所以性能更高,適用于單線程環境:
public StringBuilder append(String str) { ... } // 無 synchronized
  • 單線程環境推薦使用 StringBuilder,比 StringBuffer 更快。

示例:

StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb); // Hello World

4. 何時使用 StringStringBufferStringBuilder

需求推薦使用原因
少量字符串拼接String代碼簡潔,性能影響不大
大量字符串拼接(單線程)StringBuilder最高性能,無同步開銷
大量字符串拼接(多線程)StringBuffer線程安全,防止并發問題

5. 關鍵總結

  1. String 是不可變的,存儲在字符串常量池中,適用于少量字符串操作
  2. StringBuffer 是線程安全的,使用 synchronized,適用于多線程環境
  3. StringBuilder 是非線程安全的,但性能最好,適用于單線程高性能場景
  4. 推薦:
    • 少量拼接用 String(簡潔)。
    • 單線程高性能用 StringBuilder
    • 多線程環境用 StringBuffer

問題7:重載和重寫的區別?

重載(Overloading)重寫(Overriding) 是 Java 中**多態(Polymorphism)**的重要表現形式。它們的主要區別如下:

方法重載(Overloading)方法重寫(Overriding)
定義同一個類中,方法名相同,參數列表不同(參數個數或類型不同)父類和子類之間,方法名、參數列表都相同,子類對父類的方法進行重新實現
方法名必須相同必須相同
參數列表必須不同(參數類型、數量或順序)必須相同
返回值可以不同必須相同或是父類返回值的子類(協變返回類型)
訪問修飾符可以不同不能更嚴格,但可以更寬松
拋出異常可以不同不能拋出比父類更大的異常(可以拋出更小的或不拋出異常)
發生范圍同一個類內部子類繼承父類后
是否依賴繼承不需要繼承必須有繼承關系
調用方式通過方法簽名的不同,在編譯時決定調用哪個方法(靜態綁定,編譯期多態通過子類對象調用,運行時決定調用哪個方法(動態綁定,運行期多態

?問題8:== 和 equals() 的區別

在 Java 中,==equals() 都可以用來比較對象,但它們的本質、適用范圍和行為有所不同。

比較項==(引用/值比較)equals()(對象內容比較)
比較方式比較內存地址(引用)比較對象的內容(可重寫)
適用范圍基本數據類型引用類型只能用于對象
默認行為對于對象,默認比較地址Object 類的 equals() 方法)需要重寫 equals() 方法以比較內容
適用于基本數據類型的值比較引用是否相同判斷兩個對象是否邏輯相等

1. == 的行為

(1) 用于基本數據類型

對于 基本數據類型intdoublecharboolean 等),== 直接比較值

int a = 10;
int b = 10;
System.out.println(a == b); // true,值相等

(2) 用于引用類型

對于 引用類型(對象),== 比較的是 對象在內存中的地址(是否指向同一對象):

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false,不是同一個對象

雖然 s1s2 的內容相同,但它們指向不同的內存地址,所以 == 返回 false

(3) == 在字符串常量池中的行為

Java 的 字符串常量池 機制會讓相同的字符串共享內存

String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true,指向相同的字符串池對象

但如果用 new 關鍵字創建字符串:

String s1 = new String("hello");
String s2 = "hello";
System.out.println(s1 == s2); // false,s1 在堆中,s2 在字符串池

2. equals() 的行為

(1) Object 類的默認 equals()

Java 中所有類默認繼承 Object,其 equals() 方法默認也是比較內存地址

class Person {}
public class Test {public static void main(String[] args) {Person p1 = new Person();Person p2 = new Person();System.out.println(p1.equals(p2)); // false,不同對象}
}

== 行為相同。

2) String 類重寫了 equals()

String 類重寫了 equals(),改為比較字符串的內容

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2)); // true,比較的是內容

盡管 s1s2 指向不同的對象,但 equals() 比較的是字符內容,所以返回 true

3) 自定義類重寫 equals()

如果想讓 自定義類 按內容比較,需要重寫 equals()

class Person {String name;Person(String name) {this.name = name;}@Overridepublic boolean equals(Object obj) {if (this == obj) return true; // 判斷是否是同一對象if (obj == null || getClass() != obj.getClass()) return false;Person person = (Person) obj;return this.name.equals(person.name); // 按 name 比較}
}public class Test {public static void main(String[] args) {Person p1 = new Person("Alice");Person p2 = new Person("Alice");System.out.println(p1.equals(p2)); // true,內容相同}
}

這里 p1p2 是不同對象,但 equals() 被重寫為比較 name,所以返回 true

3. == vs equals() 總結

比較項==equals()
基本數據類型比較值不能用
對象引用比較地址默認比較地址,但可重寫
String

比較地址

比較內容(已重寫)
可否重寫不可重寫可重寫,按需求自定義邏輯
適用場景判斷是否為同一對象判斷對象內容是否相等

4. 推薦使用方式

1.基本數據類型用 ==

int a = 100;
int b = 100;
System.out.println(a == b); // true

2.引用類型判斷是否為同一個對象用 ==

String s1 = "hello";
String s2 = new String("hello");
System.out.println(s1 == s2); // false,不是同一個對象

3.判斷對象內容是否相等用 equals()

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2)); // true,內容相同

4.對于自定義對象,重寫 equals() 方法

class Person {String name;@Overridepublic boolean equals(Object obj) { ... }
}

?問題9:Java 反射?反射有什么優點/缺點?你是怎么理解反射的(為什么框架需要反射)?

Java 反射(Reflection)概述

Java 反射是 Java 提供的一種強大功能,它允許我們在運行時 動態地獲取類的信息(如類的方法、字段、構造方法等),并對它們進行操作。通過反射,我們可以 動態地創建對象、調用方法、訪問屬性,甚至可以在運行時加載類。

反射的基本概念

  1. Class:Java 中所有類的元數據都由 Class 類表示。通過 Class 類,你可以獲得類的構造方法、字段、方法等信息。
  2. Method:通過反射可以獲取類的所有方法并執行它們。
  3. Field:通過反射可以訪問類的字段。
  4. Constructor:通過反射可以創建類的實例。

常用反射操作示例

import java.lang.reflect.*;class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public void sayHello() {System.out.println("Hello, my name is " + name);}private void privateMethod() {System.out.println("This is a private method.");}
}public class ReflectionExample {public static void main(String[] args) throws Exception {// 獲取類的 Class 對象Class<?> clazz = Class.forName("Person");// 獲取構造方法并創建實例Constructor<?> constructor = clazz.getConstructor(String.class, int.class);Object person = constructor.newInstance("Alice", 25);// 調用方法Method method = clazz.getMethod("sayHello");method.invoke(person);// 獲取私有方法并調用Method privateMethod = clazz.getDeclaredMethod("privateMethod");privateMethod.setAccessible(true); // 設置可訪問privateMethod.invoke(person);}
}

輸出

Hello, my name is Alice
This is a private method.

在這個例子中,我們使用反射:

  • 獲取類的 Class 對象
  • 通過構造方法創建對象
  • 調用公開方法 sayHello
  • 調用私有方法 privateMethod,并通過 setAccessible(true) 讓私有方法可以被訪問。

反射的優點

  1. 動態性

    • 反射允許你在運行時 動態地加載類動態地創建對象,以及 動態地調用方法,這讓程序可以非常靈活地應對不同的情況。
    • 例如,Spring 框架使用反射來根據配置文件 自動注入依賴,而不需要在代碼中硬編碼類。
  2. 靈活性

    • 通過反射,你可以訪問類的私有方法和字段,甚至是 訪問不存在的類成員,這使得在某些場景下,開發者可以靈活處理一些特殊情況。
  3. 框架和庫的開發

    • 框架和庫(例如 Hibernate、Spring、JUnit)通過反射來實現靈活的功能。通過反射,框架可以在運行時了解類的信息并做出相應的處理,而無需顯式地了解每個類。
  4. 與遺留代碼的兼容性

    • 使用反射可以訪問沒有源代碼的類,例如,在 Java 庫中使用的第三方庫或組件,反射可以幫助在運行時動態地調用和修改類成員。

反射的缺點

  1. 性能開銷

    • 反射操作通常比直接調用方法慢得多,因為它會繞過編譯時的類型檢查。每次反射都會涉及到一些額外的計算(如查找方法、創建實例等),因此 性能開銷較大
    • 對于需要頻繁調用的代碼,反射可能會導致性能瓶頸。
  2. 安全性問題

    • 反射可以訪問類的私有成員,這可能會暴露 敏感數據 或者 破壞類的封裝性,帶來 安全隱患。因此,反射有時會被禁用,尤其是在安全敏感的應用中
  3. 代碼可讀性和可維護性差

    • 使用反射的代碼不如普通的面向對象代碼清晰和易于理解。因為你不能通過直接查看代碼或接口來確定一個類的行為,反射代碼可能會變得難以調試和維護
  4. 錯誤較難發現

    • 反射的代碼通常在編譯時無法捕獲錯誤,錯誤通常會在運行時出現,這使得 調試變得困難
    • 例如,反射可能會嘗試調用不存在的方法,或者訪問不存在的字段,這些問題通常只有在程序運行時才能被發現。

為什么框架需要反射

許多框架(如 Spring、Hibernate)依賴反射來實現靈活的配置和動態行為。反射為框架提供了以下幾方面的優勢:

  1. 依賴注入

    • Spring 框架通過反射來實現 依賴注入。當應用啟動時,Spring 容器會通過反射獲取各個類的構造方法、屬性等信息,然后根據配置自動為類注入所需的依賴。
  2. 動態代理

    • 在 AOP(面向切面編程)中,Spring 使用反射技術生成 動態代理類,通過代理對象的反射,攔截目標方法的執行,實現諸如日志記錄、事務控制等功能。
  3. ORM(對象關系映射)

    • Hibernate 等 ORM 框架通過反射來將 數據庫表映射成 Java 對象,并實現自動的持久化操作。通過反射,Hibernate 可以動態地從類中獲取字段信息,將數據持久化到數據庫。
  4. 配置和擴展性

    • 反射為框架提供了 高度的擴展性,使得框架可以在運行時動態地加載不同的類或組件,而不需要在編譯時知道所有的細節。比如,插件式框架可以通過反射動態加載和調用外部插件。

總結

  • 反射是 Java 提供的一種強大機制,可以在運行時動態地獲取類的信息并操作它們。
  • 反射的優點包括 動態性、靈活性,尤其適用于框架開發和與遺留代碼的兼容。
  • 然而,反射也有一些缺點,主要是 性能開銷、代碼可維護性差、潛在的安全隱患
  • 框架需要反射,主要是為了提供 靈活的依賴注入、動態代理、對象關系映射 等功能,以便在運行時根據需求靈活調整。

問題10:談談對 Java 注解的理解,解決了什么問題?

Java 注解概述

Java 注解是一種提供元數據的機制,用于向代碼中添加額外的信息,通常通過反射等方式進行處理。它本身不直接影響程序執行,但可以提供對代碼的附加信息,用于編譯檢查、代碼生成、運行時處理等。

注解解決的問題

  1. 簡化代碼和配置: 注解幫助減少配置文件或硬編碼,提升開發效率。比如在 Spring 中使用 @Autowired 注解自動注入依賴。

  2. 提高可讀性: 注解使得代碼自文檔化,開發者能通過注解清晰地知道代碼的意圖。例如,@Override 注解標明方法是覆蓋父類方法。

  3. 自動化處理: 通過注解和反射,框架能夠自動化處理某些功能,如 Spring 框架通過 @RequestMapping 處理 HTTP 請求。

  4. 驗證和編譯時檢查: 使用注解可以進行數據驗證或編譯時檢查,比如 @NotNull 注解確保字段或參數不為 null

注解的常見用途

  • 依賴注入(Spring 中使用 @Autowired 自動注入)。
  • ORM 映射(Hibernate 使用 @Entity 注解映射類到數據庫表)。
  • Web 請求映射(Spring MVC 使用 @RequestMapping 映射 URL)。
  • 驗證(Hibernate Validator 使用 @NotNull@Size 等注解)。

優缺點

優點

  • 簡化配置和代碼,減少硬編碼。
  • 提高代碼可讀性和維護性。
  • 自動化處理,減少重復代碼。

缺點

  • 性能開銷:反射和注解處理可能影響性能。
  • 調試困難:注解的實際作用通常由框架處理,調試較為復雜。

問題11:內部類了解嗎?匿名內部類了解嗎?

內部類(Inner Class)概述

Java 中的 內部類 是指在一個類的內部定義的類。內部類能夠訪問外部類的成員(包括私有成員),并且可以通過外部類的實例創建。

內部類的類型

1.成員內部類: 定義在外部類的成員位置,可以訪問外部類的所有成員(包括私有成員)。

class Outer {private String name = "Outer class";class Inner {public void display() {System.out.println(name); // 可以訪問外部類的私有成員}}
}

2.靜態內部類: 使用 static 修飾的內部類,它不能訪問外部類的非靜態成員,必須通過外部類的類名來訪問。靜態內部類的實例可以獨立于外部類的實例存在。

class Outer {private static String message = "Static Inner Class";static class StaticInner {public void show() {System.out.println(message); // 只能訪問外部類的靜態成員}}
}

3.局部內部類: 定義在方法內部的類,通常是局部變量的一部分。它只能在方法內部使用。

class Outer {public void outerMethod() {class LocalInner {public void display() {System.out.println("Local inner class");}}LocalInner local = new LocalInner();local.display();}
}

4.匿名內部類: 是沒有名字的內部類,通常用于簡化代碼,特別是在事件監聽器和回調中常用。匿名內部類的語法通常是直接在創建對象的同時定義類,省去了定義內部類的步驟。

匿名內部類

匿名內部類是 沒有類名 的內部類,它通過繼承一個類或實現一個接口來創建一個新的類實例。通常,匿名內部類用于需要創建類的實例并立即使用的場景,尤其是在接口的回調方法、事件監聽器等情況下。

匿名內部類的語法

ClassName obj = new ClassName() {// 重寫類的方法@Overridepublic void method() {System.out.println("Method implemented in anonymous class");}
};

使用匿名內部類的例子

1.實現接口

interface Greeting {void greet(String name);
}public class AnonymousInnerClassExample {public static void main(String[] args) {// 匿名內部類實現接口Greeting greeting = new Greeting() {@Overridepublic void greet(String name) {System.out.println("Hello, " + name);}};greeting.greet("Alice");}
}

2.繼承類

class Animal {void sound() {System.out.println("Animal makes sound");}
}public class AnonymousInnerClassExample {public static void main(String[] args) {// 匿名內部類繼承類Animal animal = new Animal() {@Overridevoid sound() {System.out.println("Dog barks");}};animal.sound();}
}

匿名內部類的特點

  1. 簡潔性:它可以讓你在創建對象的同時定義類,而不需要顯式地定義一個新類。
  2. 不能有構造器:匿名內部類沒有名稱,因此不能定義構造器。
  3. 只能繼承一個類或實現一個接口:匿名內部類必須繼承一個類或者實現一個接口,不能多重繼承。
  4. 常用于事件監聽:在 GUI 編程中,匿名內部類常用來實現事件監聽器等。

問題12:BIO,NIO,AIO 有什么區別??

BIO(Blocking I/O)、NIO(Non-blocking I/O)和 AIO(Asynchronous I/O)是 Java 中三種不同的 I/O 模型,它們主要的區別在于 I/O 操作的阻塞特性和異步處理的能力。下面是它們的詳細對比:


1. BIO(Blocking I/O)

特點:

  • 阻塞式 I/O:每次 I/O 操作(讀取或寫入)都會阻塞當前線程,直到操作完成。
  • 每個 I/O 操作都需要一個線程來完成,當請求很多時,可能會創建大量線程,造成性能瓶頸。

流程:

  1. 客戶端發起連接請求。
  2. 服務器接受連接請求,分配一個線程進行處理。
  3. 該線程在 I/O 操作時會被阻塞,直到完成操作(讀或寫)。

優缺點:

  • 優點:實現簡單、直觀,適合小規模并發或單線程應用。
  • 缺點:性能較差,線程過多時會導致高開銷,限制了系統的并發處理能力。

適用場景:適用于連接數較少、并發量不高的傳統應用。


2. NIO(Non-blocking I/O)

特點:

  • 非阻塞 I/O:引入了 SelectorChannel 等概念,允許多個 I/O 操作共享一個或多個線程,避免每個連接占用一個線程。線程不會因 I/O 操作而阻塞,線程可以在等待 I/O 完成的同時做其他事情。
  • 事件驅動:NIO 使用非阻塞模式,線程可以輪詢 (polling) 檢查 I/O 操作是否完成,通過 Selector 來監聽多個通道(Channel)的 I/O 狀態。

流程:

  1. 客戶端發起連接請求。
  2. 服務器通過 Selector 監聽多個通道(Channel)上的 I/O 事件,線程不會被阻塞,而是輪詢所有通道。
  3. 一旦某個通道的 I/O 操作準備好,線程就會處理相應的操作。

優缺點:

  • 優點:支持高并發,使用少量線程就能處理大量連接。
  • 缺點:編程復雜,處理多個連接時需要編寫較為復雜的代碼(如 SelectorChannel)。

適用場景:適用于高并發應用,如 Web 服務器、聊天服務器等。


3. AIO(Asynchronous I/O)

特點:

  • 異步 I/O:在 AIO 中,I/O 操作的執行完全是異步的,線程不需要等待 I/O 完成。I/O 請求會通過操作系統內核來處理,操作系統會在完成 I/O 操作時通知應用程序。
  • 線程發出 I/O 請求后,立即返回,I/O 操作在后臺完成。當 I/O 完成時,操作系統會通過回調函數通知應用程序。

流程:

  1. 客戶端發起連接請求。
  2. 服務器通過異步接口發出 I/O 請求。
  3. 當 I/O 操作完成時,操作系統通過回調函數通知服務器。

優缺點:

  • 優點:高效,能夠利用操作系統的異步 I/O 支持,減少了應用層的線程等待時間,極大提高了并發處理能力。
  • 缺點:實現較為復雜,底層需要支持異步 I/O,且需要操作系統的支持(如 Linux 的 epoll 或 Windows 的 IOCP)。

適用場景:適用于大規模、高并發、低延遲的應用,特別是需要大量并發連接而不希望使用過多線程的場景。


總結對比

特性BIO(阻塞 I/O)NIO(非阻塞 I/O)AIO(異步 I/O)
阻塞方式阻塞式操作非阻塞操作完全異步,不阻塞線程
線程模型每個連接一個線程一個線程處理多個連接,通過輪詢(Selector通過操作系統異步處理,通知回調
性能性能較差,連接數多時會消耗大量線程性能較好,支持高并發性能最好,幾乎不依賴線程阻塞
編程復雜度簡單易懂,代碼直觀編程復雜,需要使用 SelectorChannel編程復雜,操作系統支持,通常通過回調處理
適用場景低并發、傳統應用高并發、大量連接的場景超高并發、低延遲、大規模并發連接的應用

總結

  • BIO 適用于低并發場景,簡單易懂,但性能較差。
  • NIO 適用于中到高并發場景,能高效利用少量線程處理大量連接,但編程復雜。
  • AIO 提供最好的性能,適用于極高并發的場景,但實現復雜并依賴操作系統的異步支持。

不同的 I/O 模型適用于不同的應用需求,選擇合適的模型能有效提升程序性能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/67570.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/67570.shtml
英文地址,請注明出處:http://en.pswp.cn/web/67570.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

人工智能|基本概念|人工智能相關重要概念---AI定義以及模型相關知識

一、 前言&#xff1a; 最近deepseek&#xff08;深度求索&#xff09;公司的開源自然語言處理模型非常火爆。 本人很早就對人工智能比較感興趣&#xff0c;但由于種種原因沒有過多的深入此領域&#xff0c;僅僅是做了一點初步的了解&#xff0c;借著這個deepseek&#xff0…

Python GIL(全局解釋器鎖)機制對多線程性能影響的深度分析

在Python開發領域&#xff0c;GIL&#xff08;Global Interpreter Lock&#xff09;一直是一個廣受關注的技術話題。在3.13已經默認將GIL去除&#xff0c;在詳細介紹3.13的更親前&#xff0c;我們先要留了解GIL的技術本質、其對Python程序性能的影響。本文將主要基于CPython&am…

從0開始使用面對對象C語言搭建一個基于OLED的圖形顯示框架(繪圖設備封裝)

目錄 圖像層的底層抽象——繪圖設備抽象 如何抽象一個繪圖設備&#xff1f; 橋接繪圖設備&#xff0c;特化為OLED設備 題外話&#xff1a;設備的屬性&#xff0c;與設計一個相似函數化簡的通用辦法 使用函數指針來操作設備 總結一下 圖像層的底層抽象——繪圖設備抽象 在…

Git 版本控制:基礎介紹與常用操作

目錄 Git 的基本概念 Git 安裝與配置 Git 常用命令與操作 1. 初始化本地倉庫 2. 版本控制工作流程 3. 分支管理 4. 解決沖突 5. 回退和撤銷 6. 查看提交日志 前言 在軟件開發過程中&#xff0c;開發者常常需要在現有程序的基礎上進行修改和擴展。但如果不加以管理&am…

(筆記+作業)書生大模型實戰營春節卷王班---L0G2000 Python 基礎知識

學員闖關手冊&#xff1a;https://aicarrier.feishu.cn/wiki/QtJnweAW1iFl8LkoMKGcsUS9nld 課程視頻&#xff1a;https://www.bilibili.com/video/BV13U1VYmEUr/ 課程文檔&#xff1a;https://github.com/InternLM/Tutorial/tree/camp4/docs/L0/Python 關卡作業&#xff1a;htt…

仿真設計|基于51單片機的高速路口貨車稱重系統仿真

目錄 具體實現功能 設計介紹 51單片機簡介 資料內容 仿真實現&#xff08;protues8.7&#xff09; 程序&#xff08;Keil5&#xff09; 全部內容 資料獲取 具體實現功能 &#xff08;1&#xff09;LCD1602液晶第一行顯示當前的車輛重量&#xff0c;第二行顯示車輛重量…

Ubuntu Server 安裝 XFCE4桌面

Ubuntu Server沒有桌面環境&#xff0c;一些軟件有桌面環境使用起來才更加方便&#xff0c;所以我嘗試安裝桌面環境。常用的桌面環境有&#xff1a;GNOME、KDE Plasma、XFCE4等。這里我選擇安裝XFCE4桌面環境&#xff0c;主要因為它是一個極輕量級的桌面環境&#xff0c;適合內…

2025:影刀RPA使用新實踐--CSDN博客下載

文章目錄 一鍵CSDN博客下載器程序說明指導說明使用步驟 獲取方法 一鍵CSDN博客下載器 程序說明 配置信息&#xff1a;CSDN賬號&#xff08;手機號/郵箱/用戶名&#xff09;、密碼、博客文件類型支持markdown格式、html格式&#xff08;默認值markdown格式&#xff09;、博客保…

深度學習的應用

目錄 一、機器視覺 1.1 應用場景 1.2 常見的計算機視覺任務 1.2.1 圖像分類 1.2.2 目標檢測 1.2.3 圖像分割 二、自然語言處理 三、推薦系統 3.1 常用的推薦系統算法實現方案 四、圖像分類實驗補充 4.1 CIFAR-100 數據集實驗 實驗代碼 4.2 CIFAR-10 實驗代碼 深…

前端js高級25.1.30

原型&#xff1a;函數的組成結構 通過這個圖我們需要知道。 假設我們創建了一個Foo函數。 規則&#xff1a;Function.protoType是函數顯示原型。__proto__是隱式對象。 Function、Object、Foo函數的__proto__指向了Function.protoType說明。這三個都依托function函數來創建。…

android 音視頻系列引導

音視頻這塊的知識點自己工作中有用到&#xff0c;一直沒有好好做一個總結&#xff0c;原因有客觀和主觀的。 客觀是工作太忙&#xff0c;沒有成段時間做總結。 主觀自己懶。 趁著這次主動離職拿了n1的錢&#xff0c;休息一下&#xff0c;對自己的人生做一下總結&#xff0c;…

為AI聊天工具添加一個知識系統 之80 詳細設計之21 符號邏輯 之1

本文要點 要點 前面我們討論了本項目中的正則表達式。現在我們將前面討論的正則表達式視為狹義的符號文本及其符號規則rule&#xff08;認識的原則--認識上認識對象的約束&#xff09;&#xff0c;進而在更廣泛的視角下將其視為符號邏輯及其符號原則principle&#xff08;知識…

.NET Core緩存

目錄 緩存的概念 客戶端響應緩存 cache-control 服務器端響應緩存 內存緩存&#xff08;In-memory cache&#xff09; 用法 GetOrCreateAsync 緩存過期時間策略 緩存的過期時間 解決方法&#xff1a; 兩種過期時間策略&#xff1a; 絕對過期時間 滑動過期時間 兩…

自動駕駛---蘇箐對智駕產品的思考

1 前言 對于更高級別的自動駕駛&#xff0c;很多人都有不同的思考&#xff0c;方案也好&#xff0c;產品也罷。最近在圈內一位知名的自動駕駛專家蘇箐發表了他自己對于自動駕駛未來的思考。 蘇箐是地平線的副總裁兼首席架構師&#xff0c;同時也是高階智能駕駛解決方案SuperDri…

Sklearn 中的邏輯回歸

邏輯回歸的數學模型 基本模型 邏輯回歸主要用于處理二分類問題。二分類問題對于模型的輸出包含 0 和 1&#xff0c;是一個不連續的值。分類問題的結果一般不能由線性函數求出。這里就需要一個特別的函數來求解&#xff0c;這里引入一個新的函數 Sigmoid 函數&#xff0c;也成…

FPGA|使用quartus II通過AS下載POF固件

1、將開發板設置到AS下載擋位&#xff0c;或者把下載線插入到AS端口 2、打開quartus II&#xff0c;選擇Tools→Programmer→ Mode選擇Active Serial Programming 3、點擊左側Add file…&#xff0c;選擇 .pof 文件 →start 4、勾選program和verify&#xff08;可選&#xff0…

.Net / C# 分析文件編碼 并將 各種編碼格式 轉為 另一個編碼格式 ( 比如: GB2312→UTF-8, UTF-8→GB2312)

相關庫 .Net 8 編碼識別: github.com/CharsetDetector/UTF-unknown <PackageReference Include"UTF.Unknown" Version"2.5.1" />代碼 using UtfUnknown;var dir_path "D:\\Desktop\\新建文件夾2\\新建文件夾"; var dir_new_path &quo…

32. C 語言 安全函數( _s 尾綴)

本章目錄 前言什么是安全函數&#xff1f;安全函數的特點主要的安全函數1. 字符串操作安全函數2. 格式化輸出安全函數3. 內存操作安全函數4. 其他常用安全函數 安全函數實例示例 1&#xff1a;strcpy_s 和 strcat_s示例 2&#xff1a;memcpy_s示例 3&#xff1a;strtok_s 總結 …

淺談網絡 | 容器網絡之Flannel

目錄 云原生網絡架構深度解構&#xff1a;Flannel的設計哲學與實現機制Flannel架構解析&#xff1a;三層核心設計原則UDP模式&#xff08;用戶態隧道&#xff09;VXLAN模式&#xff08;內核態隧道&#xff09;Host-GW模式&#xff08;直連路由&#xff09; 生產環境架構選型與調…

autosar bsw 的關鍵模塊

AUTOSAR&#xff08;AUTomotive Open System ARchitecture&#xff09;的**基礎軟件層&#xff08;BSW&#xff0c;Basic Software&#xff09;**是汽車電子系統標準化的核心&#xff0c;負責提供硬件抽象、通信、診斷、安全等基礎服務。以下是BSW的關鍵模塊及其功能分類&#…