《前后端面試題
》專欄集合了前后端各個知識模塊的面試題,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。
文章目錄
- 一、本文面試題目錄
- 41. 什么是工廠模式?簡單工廠、工廠方法、抽象工廠的區別?
- 42. Java中的泛型有什么作用?
- 43. Java中的`Comparable`和`Comparator`有何區別?
- 44. 什么是Java中的注解?
- 45. Java中的線程池有什么作用?
- 46. 解釋Java中的`雙親委派模型`
- 47. Java中的字符編碼有哪些常見類型,它們有什么區別?
- 48. 什么是Java中的函數式接口?如何使用Lambda表達式?
- 49. 簡述Java中的泛型擦除機制
- 50. 什么是Java中的線程局部變量(ThreadLocal)?如何使用?
- 51. 解釋Java中的動態代理及其實現方式
- 52. 什么是Java中的注解(Annotation)?如何自定義注解?
- 53. 解釋Java中的序列化與反序列化
- 54. Java中的`equals()`和`==`有什么區別?
- 55. 什么是Java中的Lambda表達式?適用于什么場景?
- 56. 解釋Java中的Stream API及其常用操作
- 57. Java中的`volatile`關鍵字有什么作用?
- 58. Java中的`try-with-resources`語法有什么作用?
- 59. 解釋Java中的深拷貝與淺拷貝
- 60. Java中的`String`、`StringBuilder`、`StringBuffer`有什么區別?
- 二、120道面試題目錄列表
一、本文面試題目錄
41. 什么是工廠模式?簡單工廠、工廠方法、抽象工廠的區別?
答案:
工廠模式:用于創建對象,隱藏實例化邏輯,解耦對象創建與使用。
區別:
-
簡單工廠:一個工廠類根據參數創建不同產品(違反開閉原則)。
class CarFactory {public static Car createCar(String type) {if ("Benz".equals(type)) return new Benz();else if ("BMW".equals(type)) return new BMW();return null;} }
-
工廠方法:每個產品對應一個工廠類,通過繼承擴展(符合開閉原則)。
interface CarFactory { Car createCar(); } class BenzFactory implements CarFactory {public Car createCar() { return new Benz(); } } class BMWFactory implements CarFactory {public Car createCar() { return new BMW(); } }
-
抽象工廠:創建一系列相關產品(產品族),如汽車工廠同時生產汽車和發動機。
42. Java中的泛型有什么作用?
- 答案:泛型用于在編譯時確保類型安全,可將類型作為參數傳遞,使代碼更通用。例如List表示該列表只能存儲String類型元素,避免了類型轉換錯誤,提高了代碼的可讀性和可維護性。
43. Java中的Comparable
和Comparator
有何區別?
答案:
特性 | Comparable | Comparator |
---|---|---|
定義位置 | 類內部(implements Comparable ) | 類外部(單獨實現Comparator 接口) |
方法 | int compareTo(T o) | int compare(T o1, T o2) |
排序邏輯 | 類自身的默認排序(自然排序) | 外部定義的定制排序 |
適用場景 | 固定排序邏輯 | 靈活切換排序邏輯(如升序/降序) |
代碼示例:
// Comparable:類內部實現
class Student implements Comparable<Student> {int age;@Overridepublic int compareTo(Student o) {return Integer.compare(this.age, o.age); // 按年齡升序}
}// Comparator:外部實現
class StudentNameComparator implements Comparator<Student> {@Overridepublic int compare(Student s1, Student s2) {return s1.name.compareTo(s2.name); // 按姓名排序}
}
44. 什么是Java中的注解?
- 答案:注解是Java 5.0引入的新特性,用于為代碼添加元數據。它可以用于類、方法、變量等上面,提供信息給編譯器、開發工具或運行時環境。例如@Override注解用于標識方法重寫,@Deprecated注解表示方法已過時等。
45. Java中的線程池有什么作用?
- 答案:線程池用于管理和復用線程,避免頻繁創建和銷毀線程帶來的開銷,提高系統性能和資源利用率。常見的線程池類有ThreadPoolExecutor,可通過它創建不同類型的線程池,如FixedThreadPool(固定大小線程池)、CachedThreadPool(緩存線程池)等。
46. 解釋Java中的雙親委派模型
原理:類加載器的委派機制,當加載類時,先委托父加載器加載,父加載器無法加載時才自己加載,避免類重復加載和安全問題(如自定義java.lang.String
不會被加載)。
類加載器層次:
- 啟動類加載器(
Bootstrap ClassLoader
):加載JAVA_HOME/lib
下的類; - 擴展類加載器(
Extension ClassLoader
):加載JAVA_HOME/lib/ext
下的類; - 應用類加載器(
Application ClassLoader
):加載類路徑下的類; - 自定義類加載器:繼承
ClassLoader
,重寫findClass()
。
代碼示例(類加載委派):
public class ClassLoaderDemo {public static void main(String[] args) {// 獲取當前類的類加載器(應用類加載器)ClassLoader appClassLoader = ClassLoaderDemo.class.getClassLoader();System.out.println(appClassLoader); // 輸出sun.misc.Launcher$AppClassLoader// 父加載器(擴展類加載器)ClassLoader extClassLoader = appClassLoader.getParent();System.out.println(extClassLoader); // 輸出sun.misc.Launcher$ExtClassLoader// 啟動類加載器(C++實現,返回null)ClassLoader bootstrapClassLoader = extClassLoader.getParent();System.out.println(bootstrapClassLoader); // 輸出null}
}
47. Java中的字符編碼有哪些常見類型,它們有什么區別?
- 答案:常見的字符編碼有ASCII、UTF - 8、UTF - 16等。ASCII編碼用7位二進制數表示一個字符,只能表示英文字母、數字和一些特殊字符;UTF - 8是一種可變長度編碼,可表示世界上幾乎所有字符,對英文字符用1個字節表示,對其他字符根據需要用2 - 4個字節表示;UTF - 16通常用2個字節表示一個字符,但對于一些特殊字符需用4個字節。
48. 什么是Java中的函數式接口?如何使用Lambda表達式?
- 原理:函數式接口是只包含一個抽象方法的接口(可以有默認方法和靜態方法),用
@FunctionalInterface
注解標識。Lambda表達式是函數式接口的實例化方式,用于簡化匿名內部類的寫法。 - 代碼示例:
import java.util.function.Predicate;// 自定義函數式接口
@FunctionalInterface
interface Calculator {int calculate(int a, int b);
}public class FunctionalInterfaceDemo {public static void main(String[] args) {// 使用Lambda表達式實現Calculator接口Calculator add = (a, b) -> a + b;System.out.println(add.calculate(2, 3)); // 輸出5// 內置函數式接口Predicate(判斷條件)Predicate<Integer> isEven = num -> num % 2 == 0;System.out.println(isEven.test(4)); // 輸出trueSystem.out.println(isEven.test(5)); // 輸出false}
}
49. 簡述Java中的泛型擦除機制
- 原理:Java泛型是“偽泛型”,在編譯階段會進行類型擦除:編譯器將泛型類型參數替換為其邊界類型(若無邊界則替換為
Object
),并在必要時插入類型轉換代碼。這意味著泛型信息在運行時不存在,無法通過反射獲取泛型參數的具體類型(除非通過匿名內部類等特殊方式)。 - 代碼示例:
import java.util.ArrayList;public class GenericErasureDemo {public static void main(String[] args) {ArrayList<String> strList = new ArrayList<>();ArrayList<Integer> intList = new ArrayList<>();// 類型擦除后,兩者的運行時類型相同System.out.println(strList.getClass() == intList.getClass()); // 輸出true// 編譯時泛型檢查,運行時無限制(可通過反射繞過)try {strList.getClass().getMethod("add", Object.class).invoke(strList, 10);System.out.println(strList); // 輸出[10],說明運行時無泛型限制} catch (Exception e) {e.printStackTrace();}}
}
50. 什么是Java中的線程局部變量(ThreadLocal)?如何使用?
- 原理:
ThreadLocal
用于為每個線程提供獨立的變量副本,確保線程間數據隔離。其內部通過Thread
類中的threadLocals
(類型為ThreadLocalMap
)存儲數據,每個ThreadLocal
對象作為key,對應的線程私有值作為value。 - 注意:使用后需手動調用
remove()
方法清理數據,否則可能導致內存泄漏(ThreadLocalMap
中的Entry是弱引用key,但value是強引用,若線程長期存活,value無法回收)。 - 代碼示例:
public class ThreadLocalDemo {// 定義ThreadLocal變量,指定泛型類型為Stringprivate static ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 線程1設置并獲取值new Thread(() -> {threadLocal.set("線程1的數據");System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());threadLocal.remove(); // 清理數據}, "線程1").start();// 線程2設置并獲取值new Thread(() -> {threadLocal.set("線程2的數據");System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());threadLocal.remove(); // 清理數據}, "線程2").start();}
}
// 輸出:
// 線程1:線程1的數據
// 線程2:線程2的數據
No. | 大劍師精品GIS教程推薦 |
---|---|
0 | 地圖渲染基礎- 【WebGL 教程】 - 【Canvas 教程】 - 【SVG 教程】 |
1 | Openlayers 【入門教程】 - 【源代碼+示例 300+】 |
2 | Leaflet 【入門教程】 - 【源代碼+圖文示例 150+】 |
3 | MapboxGL 【入門教程】 - 【源代碼+圖文示例150+】 |
4 | Cesium 【入門教程】 - 【源代碼+綜合教程 200+】 |
5 | threejs 【中文API】 - 【源代碼+圖文示例200+】 |
51. 解釋Java中的動態代理及其實現方式
原理:動態代理允許在運行時創建目標類的代理對象,增強目標方法(如日志、事務)而不修改源碼。
實現方式:
JDK動態代理
:基于接口,通過Proxy.newProxyInstance()
生成代理類;CGLIB動態代理
:基于繼承,通過生成目標類子類實現代理(需引入CGLIB庫)。
代碼示例(JDK動態代理):
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 目標接口
interface Target {void doSomething();
}// 目標實現類
class TargetImpl implements Target {@Overridepublic void doSomething() {System.out.println("執行目標方法");}
}// 代理處理器
class ProxyHandler implements InvocationHandler {private Object target;public ProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理:方法執行前"); // 增強邏輯Object result = method.invoke(target, args); // 調用目標方法System.out.println("代理:方法執行后");return result;}
}public class DynamicProxyDemo {public static void main(String[] args) {Target target = new TargetImpl();// 生成代理對象(基于接口)Target proxy = (Target) Proxy.newProxyInstance(Target.class.getClassLoader(),new Class[]{Target.class},new ProxyHandler(target));proxy.doSomething();}
}
// 輸出:代理:方法執行前 → 執行目標方法 → 代理:方法執行后
52. 什么是Java中的注解(Annotation)?如何自定義注解?
原理:注解是代碼的元數據,用于標記類、方法等,可通過反射解析。內置注解如@Override
、@Deprecated
,自定義注解需用@interface
聲明。
核心元注解:
@Retention
:指定注解保留階段(源碼、字節碼、運行時);@Target
:指定注解可修飾的元素(類、方法等)。
代碼示例:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 自定義注解
@Retention(RetentionPolicy.RUNTIME) // 運行時保留,可通過反射獲取
@Target(ElementType.METHOD) // 僅修飾方法
@interface Log {String value() default "執行方法"; // 注解屬性
}public class AnnotationDemo {@Log("測試方法") // 使用注解public void test() {System.out.println("測試方法執行");}public static void main(String[] args) throws Exception {// 反射獲取注解Log log = AnnotationDemo.class.getMethod("test").getAnnotation(Log.class);System.out.println(log.value()); // 輸出:測試方法}
}
53. 解釋Java中的序列化與反序列化
原理:序列化將對象轉換為字節流(便于存儲/傳輸),反序列化則將字節流恢復為對象。需實現Serializable
接口(標記接口,無方法),transient
關鍵字修飾的字段不參與序列化。
代碼示例:
import java.io.*;class User implements Serializable { // 實現Serializable接口private String name;private transient int age; // transient字段不序列化public User(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "User{name='" + name + "', age=" + age + "}";}
}public class SerializationDemo {public static void main(String[] args) throws IOException, ClassNotFoundException {// 序列化User user = new User("Alice", 20);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"));oos.writeObject(user);oos.close();// 反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"));User deserializedUser = (User) ois.readObject();ois.close();System.out.println(deserializedUser); // 輸出:User{name='Alice', age=0}(age被transient修飾,未序列化)}
}
54. Java中的equals()
和==
有什么區別?
原理:
==
:比較基本類型時為值比較;比較引用類型時為內存地址(對象是否為同一實例)比較。equals()
:Object類默認實現為==
,但可重寫(如String類重寫為值比較)。
代碼示例:
public class EqualsDemo {public static void main(String[] args) {// 基本類型比較int a = 10;int b = 10;System.out.println(a == b); // true(值相等)// 引用類型比較String s1 = new String("hello");String s2 = new String("hello");System.out.println(s1 == s2); // false(地址不同)System.out.println(s1.equals(s2)); // true(String重寫equals,比較值)}
}
55. 什么是Java中的Lambda表達式?適用于什么場景?
原理:Lambda是函數式接口的匿名實現,簡化代碼。格式:(參數) -> 表達式/代碼塊
。
適用場景:簡化集合遍歷(如forEach
)、線程創建、Stream API等。
代碼示例:
import java.util.Arrays;
import java.util.List;public class LambdaDemo {public static void main(String[] args) {// 1. 簡化線程創建(Runnable是函數式接口)new Thread(() -> System.out.println("Lambda線程運行")).start();// 2. 集合遍歷List<String> list = Arrays.asList("a", "b", "c");list.forEach(str -> System.out.println(str)); // 輸出a、b、c// 3. 簡化Comparatorlist.sort((s1, s2) -> s2.compareTo(s1)); // 倒序排序System.out.println(list); // 輸出[c, b, a]}
}
56. 解釋Java中的Stream API及其常用操作
原理:Stream API用于處理集合數據,支持鏈式操作(過濾、映射、聚合等),類似SQL查詢,分為中間操作(返回Stream)和終端操作(返回結果)。
常用操作:
filter()
:過濾元素;map()
:轉換元素;collect()
:收集結果(如轉List);count()
:統計元素數。
代碼示例:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;public class StreamDemo {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);// 過濾偶數 → 乘以2 → 收集為ListList<Integer> result = numbers.stream().filter(n -> n % 2 == 0) // 中間操作:保留偶數.map(n -> n * 2) // 中間操作:乘以2.collect(Collectors.toList()); // 終端操作:轉ListSystem.out.println(result); // 輸出[4, 8, 12]}
}
57. Java中的volatile
關鍵字有什么作用?
原理:volatile
保證變量的可見性和禁止指令重排序,但不保證原子性。
- 可見性:線程修改
volatile
變量后,立即刷新到主內存,其他線程讀取時從主內存加載; - 有序性:禁止編譯器和CPU對
volatile
變量相關指令重排序(如單例模式中的雙重檢查鎖)。
代碼示例(禁止重排序):
public class VolatileDemo {private static volatile VolatileDemo instance; // 禁止重排序private VolatileDemo() {}public static VolatileDemo getInstance() {if (instance == null) {synchronized (VolatileDemo.class) {if (instance == null) {// 若不加volatile,可能發生指令重排序:// 1. 分配內存 → 2. 實例化對象 → 3. 賦值給instance// 重排序后可能為1→3→2,導致其他線程獲取到未初始化的instanceinstance = new VolatileDemo();}}}return instance;}
}
58. Java中的try-with-resources
語法有什么作用?
原理:try-with-resources
用于自動關閉實現AutoCloseable
接口的資源(如流、連接),替代傳統的try-finally
,避免資源泄漏。
代碼示例:
import java.io.FileInputStream;
import java.io.IOException;public class TryWithResources {public static void main(String[] args) {// 資源在try塊結束后自動關閉try (FileInputStream fis = new FileInputStream("test.txt")) {int data = fis.read();while (data != -1) {System.out.print((char) data);data = fis.read();}} catch (IOException e) { // 統一處理異常e.printStackTrace();}// 無需手動調用fis.close()}
}
59. 解釋Java中的深拷貝與淺拷貝
原理:
- 淺拷貝:復制對象時,僅復制基本類型字段,引用類型字段仍指向原對象(如
Object.clone()
默認實現); - 深拷貝:復制對象及引用類型字段指向的所有對象,完全獨立于原對象(需手動實現,如序列化/反序列化或遞歸克隆)。
代碼示例:
import java.util.Arrays;class Person implements Cloneable {private String name;private int[] ages; // 引用類型public Person(String name, int[] ages) {this.name = name;this.ages = ages;}// 淺拷貝(默認clone)@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}// 深拷貝(手動復制引用類型)public Person deepClone() throws CloneNotSupportedException {Person clone = (Person) super.clone();clone.ages = Arrays.copyOf(this.ages, this.ages.length); // 復制數組return clone;}// getter/setter省略
}public class CopyDemo {public static void main(String[] args) throws CloneNotSupportedException {int[] ages = {20, 30};Person original = new Person("Alice", ages);// 淺拷貝Person shallowClone = (Person) original.clone();shallowClone.ages[0] = 25;System.out.println(original.ages[0]); // 輸出25(原對象被修改)// 深拷貝Person deepClone = original.deepClone();deepClone.ages[0] = 30;System.out.println(original.ages[0]); // 輸出25(原對象未被修改)}
}
60. Java中的String
、StringBuilder
、StringBuffer
有什么區別?
原理:
String
:不可變字符序列(底層char[]
被final
修飾),每次修改創建新對象,效率低;StringBuilder
:可變字符序列,非線程安全,效率高(單線程推薦);StringBuffer
:可變字符序列,線程安全(方法加synchronized
),效率低(多線程推薦)。
代碼示例:
public class StringCompare {public static void main(String[] args) {String str = "a";str += "b"; // 創建新String對象(原對象不變)StringBuilder sb = new StringBuilder("a");sb.append("b"); // 直接修改內部數組,無新對象StringBuffer sbf = new StringBuffer("a");sbf.append("b"); // 線程安全的append}
}
二、120道面試題目錄列表
文章序號 | Java面試題120道 |
---|---|
1 | Java面試題及詳細答案120道(01-20) |
2 | Java面試題及詳細答案120道(21-40) |
3 | Java面試題及詳細答案120道(41-60) |
4 | Java面試題及詳細答案120道(61-80) |
5 | Java面試題及詳細答案120道(81-100) |
6 | Java面試題及詳細答案120道(5101-120) |