《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux... 。

文章目录
- 一、本文面试题目录
-
-
- [81. Java中的`ConcurrentHashMap`与`HashMap`在并发场景下有什么区别?](#81. Java中的
ConcurrentHashMap
与HashMap
在并发场景下有什么区别?) - [82. 解释Java中的`JIT`编译](#82. 解释Java中的
JIT
编译) - [83. Java中的`try-catch-finally`中`return`的执行顺序是什么?](#83. Java中的
try-catch-finally
中return
的执行顺序是什么?) - [84. 什么是Java中的`Record`(记录类)?](#84. 什么是Java中的
Record
(记录类)?) - [85. Java中的`Arrays.asList()`返回的列表有什么特点?](#85. Java中的
Arrays.asList()
返回的列表有什么特点?) - [86. Java中的`synchronized`关键字的实现原理(JDK 6+的优化)](#86. Java中的
synchronized
关键字的实现原理(JDK 6+的优化)) - [87. 什么是Java中的`模式匹配(Pattern Matching)`?](#87. 什么是Java中的
模式匹配(Pattern Matching)
?) - [88. Java中的`@FunctionalInterface`注解有什么作用?](#88. Java中的
@FunctionalInterface
注解有什么作用?) - [89. 解释Java中的`内存泄漏`及常见场景](#89. 解释Java中的
内存泄漏
及常见场景) - [90. 什么是Java的反射机制?有哪些常见应用场景?](#90. 什么是Java的反射机制?有哪些常见应用场景?)
- [91. Java中的异常体系结构是怎样的?`Checked Exception`和`Unchecked Exception`有何区别?](#91. Java中的异常体系结构是怎样的?
Checked Exception
和Unchecked Exception
有何区别?) - [92. `try-catch-finally`中,如果`finally`有`return`,`try`或`catch`中的`return`会生效吗?](#92.
try-catch-finally
中,如果finally
有return
,try
或catch
中的return
会生效吗?) - [93. 什么是线程安全?如何实现线程安全?](#93. 什么是线程安全?如何实现线程安全?)
- [94. `synchronized`和`Lock`的区别是什么?](#94.
synchronized
和Lock
的区别是什么?) - [95. 什么是死锁?如何避免死锁?](#95. 什么是死锁?如何避免死锁?)
- [96. Java中的线程池有哪些核心参数?`ThreadPoolExecutor`的工作原理是什么?](#96. Java中的线程池有哪些核心参数?
ThreadPoolExecutor
的工作原理是什么?) - [97. Java中的`volatile`关键字有什么作用?它能保证原子性吗?](#97. Java中的
volatile
关键字有什么作用?它能保证原子性吗?) - [98. 什么是Java内存模型(JMM)?它解决了什么问题?](#98. 什么是Java内存模型(JMM)?它解决了什么问题?)
- [99. `ArrayList`和`LinkedList`的区别是什么?各自的适用场景?](#99.
ArrayList
和LinkedList
的区别是什么?各自的适用场景?) - [100. `HashMap`的底层实现原理是什么?JDK1.7和JDK1.8有何区别?](#100.
HashMap
的底层实现原理是什么?JDK1.7和JDK1.8有何区别?)
- [81. Java中的`ConcurrentHashMap`与`HashMap`在并发场景下有什么区别?](#81. Java中的
-
- 二、120道面试题目录列表
一、本文面试题目录
81. Java中的ConcurrentHashMap
与HashMap
在并发场景下有什么区别?
原理:
HashMap
:非线程安全,并发修改可能导致ConcurrentModificationException
;ConcurrentHashMap
(JDK 8+):线程安全,采用"CAS + 同步块"实现,支持高并发读写,效率优于HashTable
。
核心区别:
- 锁粒度:
ConcurrentHashMap
锁桶(链表/红黑树),HashTable
锁整个表; - 迭代器:
ConcurrentHashMap
的迭代器弱一致性(不抛异常),HashMap
的迭代器快速失败。
代码示例:
java
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentMapDemo {
public static void main(String[] args) throws InterruptedException {
Map<String, Integer> map = new ConcurrentHashMap<>();
// 多线程并发写入
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
map.put(Thread.currentThread().getName() + i, i);
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("map大小:" + map.size()); // 输出2000(无并发问题)
}
}
82. 解释Java中的JIT
编译
原理 :JIT(即时编译)是JVM的优化技术,将热点代码(频繁执行的代码)从字节码编译为机器码,提高执行效率(字节码解释执行慢)。
关键概念:
- 解释执行:字节码逐条翻译为机器码,启动快但执行慢;
- 编译执行:提前将字节码编译为机器码,执行快但启动慢;
- 混合模式:JVM默认模式,结合两者优势(热点代码编译,其他解释)。
代码示例(热点代码触发JIT):
java
public class JITDemo {
// 热点方法(被频繁调用)
public static int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
// 频繁调用add(),触发JIT编译
for (int i = 0; i < 10_000_000; i++) {
add(i, i + 1);
}
}
}
83. Java中的try-catch-finally
中return
的执行顺序是什么?
原理 :finally
块始终执行(除非JVM退出),若try
或catch
中有return
,会先执行finally
,再返回结果(finally
中的return
会覆盖之前的返回值)。
代码示例:
java
public class TryCatchFinally {
public static int test() {
try {
System.out.println("try块");
return 1; // 先暂存返回值1,执行finally后返回
} catch (Exception e) {
return 2;
} finally {
System.out.println("finally块");
// return 3; // 若打开此行,最终返回3(覆盖try的return)
}
}
public static void main(String[] args) {
System.out.println("结果:" + test());
// 输出:try块 → finally块 → 结果:1
}
}
84. 什么是Java中的Record
(记录类)?
原理 :JDK 16引入的Record
,用于简化"数据载体"类(仅含字段和访问方法),自动生成equals()
、hashCode()
、toString()
和构造器。
特点 :不可变(字段final
),不能继承其他类(可实现接口)。
代码示例:
java
// 记录类:自动生成getter、equals、hashCode、toString
public record Point(int x, int y) {
// 可定义静态方法或构造器
public Point { // 紧凑构造器(无参数列表)
if (x < 0 || y < 0) {
throw new IllegalArgumentException("坐标不能为负");
}
}
public static Point origin() { // 静态方法
return new Point(0, 0);
}
}
public class RecordDemo {
public static void main(String[] args) {
Point p = new Point(3, 4);
System.out.println(p.x()); // 自动生成的getter(无get前缀)
System.out.println(p); // 输出Point[x=3, y=4](自动生成的toString)
}
}
85. Java中的Arrays.asList()
返回的列表有什么特点?
原理 :Arrays.asList()
返回java.util.Arrays.ArrayList
(内部类),而非java.util.ArrayList
,特点:
- 长度固定(不可增删元素,否则抛
UnsupportedOperationException
); - 底层是原数组的视图(修改列表元素会同步修改原数组);
- 不支持
null
元素(针对基本类型数组的包装类)。
代码示例:
java
import java.util.Arrays;
import java.util.List;
public class AsListDemo {
public static void main(String[] args) {
String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
// 修改列表元素 → 原数组同步修改
list.set(0, "x");
System.out.println(arr[0]); // 输出x
// list.add("d"); // 抛出UnsupportedOperationException(长度固定)
// 转换为可修改的ArrayList
List<String> mutableList = new java.util.ArrayList<>(list);
mutableList.add("d"); // 成功
}
}
86. Java中的synchronized
关键字的实现原理(JDK 6+的优化)
原理 :synchronized
通过锁机制保证同步,JDK 6+引入锁升级优化,避免性能损耗:
- 偏向锁:单线程访问时,仅记录线程ID,无竞争时无需加锁;
- 轻量级锁:多线程交替访问,通过CAS尝试获取锁,避免重量级锁开销;
- 重量级锁:多线程竞争激烈时,使用操作系统互斥量,性能较差。
代码示例(同步方法与同步块):
java
public class SynchronizedDemo {
// 同步方法(锁为当前对象)
public synchronized void syncMethod() {
// 同步代码
}
// 同步块(锁为指定对象)
public void syncBlock() {
Object lock = new Object();
synchronized (lock) {
// 同步代码
}
}
}
87. 什么是Java中的模式匹配(Pattern Matching)
?
原理 :JDK 16+引入的语法糖,简化类型判断和转换,主要用于instanceof
和switch
。
instanceof
模式匹配:判断类型的同时完成转换,避免显式强转。
代码示例:
java
public class PatternMatching {
public static void print(Object obj) {
// 传统方式:先判断再强转
if (obj instanceof String) {
String str = (String) obj;
System.out.println("字符串长度:" + str.length());
}
// 模式匹配:判断+转换一步完成
if (obj instanceof String str) { // 变量str仅在if块内有效
System.out.println("字符串长度:" + str.length());
}
// switch模式匹配(JDK 17+)
switch (obj) {
case Integer i -> System.out.println("整数:" + i);
case String s -> System.out.println("字符串:" + s);
default -> System.out.println("未知类型");
}
}
}
88. Java中的@FunctionalInterface
注解有什么作用?
原理 :@FunctionalInterface
是标记注解,用于编译期检查接口是否为函数式接口(仅含一个抽象方法),若不符合则编译报错,增强代码可读性。
代码示例:
java
@FunctionalInterface // 正确:仅一个抽象方法
interface MyFunction {
void doSomething();
default void defaultMethod() {} // 允许默认方法
static void staticMethod() {} // 允许静态方法
}
// @FunctionalInterface // 编译错误:两个抽象方法
interface InvalidFunction {
void method1();
void method2();
}
89. 解释Java中的内存泄漏
及常见场景
原理 :内存泄漏指对象不再使用但未被GC回收,导致内存占用持续增加,最终可能引发OutOfMemoryError
。
常见场景:
- 静态集合持有对象(如
static List
未清除元素); - 未关闭资源(如流、连接未调用
close()
); - 匿名内部类持有外部类引用(如线程未终止,外部类无法回收);
ThreadLocal
未调用remove()
(线程池线程复用导致变量残留)。
代码示例(静态集合导致内存泄漏):
java
import java.util.ArrayList;
import java.util.List;
public class MemoryLeak {
// 静态集合持有对象引用
private static List<Object> list = new ArrayList<>();
public static void addObject(Object obj) {
list.add(obj); // 对象被静态集合引用,无法回收
}
public static void main(String[] args) {
while (true) {
addObject(new Object()); // 持续添加对象,导致内存泄漏
}
}
}
90. 什么是Java的反射机制?有哪些常见应用场景?
答案 :
反射机制是指程序在运行时可以获取自身的信息(类、方法、字段等),并能动态操作这些信息的能力。通过java.lang.reflect
包中的类(如Class
、Method
、Field
)实现。
原理 :
Java编译器将类编译为字节码(.class),运行时JVM加载类并生成Class
对象,反射通过该对象访问类的结构。
应用场景:
- 框架开发(如Spring的IOC容器,通过反射实例化Bean);
- 动态代理(如MyBatis的Mapper接口代理);
- 注解解析(如JUnit的
@Test
注解检测)。
代码示例:
java
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// 获取Class对象
Class<?> clazz = User.class;
// 实例化对象
Object user = clazz.getDeclaredConstructor().newInstance();
// 获取并设置字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 访问私有字段
nameField.set(user, "Alice");
// 调用方法
Method getNameMethod = clazz.getMethod("getName");
String name = (String) getNameMethod.invoke(user);
System.out.println(name); // 输出:Alice
}
}
class User {
private String name;
public String getName() { return name; }
}
No. | 大剑师精品GIS教程推荐 |
---|---|
0 | 地图渲染基础- 【WebGL 教程】 - 【Canvas 教程】 - 【SVG 教程】 |
1 | Openlayers 【入门教程】 - 【源代码+示例 300+】 |
2 | Leaflet 【入门教程】 - 【源代码+图文示例 150+】 |
3 | MapboxGL 【入门教程】 - 【源代码+图文示例150+】 |
4 | Cesium 【入门教程】 - 【源代码+综合教程 200+】 |
5 | threejs 【中文API】 - 【源代码+图文示例200+】 |
91. Java中的异常体系结构是怎样的?Checked Exception
和Unchecked Exception
有何区别?
答案 :
异常体系结构:
- 顶层父类为
Throwable
,分为Error
(错误,如OutOfMemoryError
,程序无法处理)和Exception
(异常,程序可处理)。 Exception
分为RuntimeException
(运行时异常,如NullPointerException
)和非运行时异常(如IOException
)。
区别:
类型 | 特点 | 示例 |
---|---|---|
Checked Exception | 编译时检查,必须捕获或声明抛出 | IOException 、SQLException |
Unchecked Exception | 运行时抛出,编译不检查,无需强制处理 | NullPointerException 、IndexOutOfBoundsException |
代码示例:
java
// Checked Exception:必须处理(捕获或声明)
public void readFile() throws IOException { // 声明抛出
FileReader fr = new FileReader("test.txt");
fr.read();
}
// Unchecked Exception:无需强制处理
public void divide(int a, int b) {
if (b == 0) {
throw new ArithmeticException("除数不能为0"); // 运行时抛出
}
}
92. try-catch-finally
中,如果finally
有return
,try
或catch
中的return
会生效吗?
答案 :
不会。finally
块始终执行(除非JVM退出),若finally
包含return
,会覆盖try
或catch
中的return
值。
代码示例:
java
public static int test() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
return 3; // 覆盖try的return
}
}
public static void main(String[] args) {
System.out.println(test()); // 输出:3
}
原理 :
编译器会将try
/catch
中的return
值暂存,执行finally
后,若finally
有return
,则直接返回该值,丢弃暂存值。
93. 什么是线程安全?如何实现线程安全?
答案 :
线程安全:多线程环境下,多个线程访问共享资源时,无需额外同步操作即可保证结果正确。
实现方式:
- 同步机制 :
synchronized
关键字(修饰方法或代码块,保证原子性、可见性、有序性)。Lock
接口(如ReentrantLock
,更灵活的锁控制)。
- 使用线程安全的类 :如
ConcurrentHashMap
、AtomicInteger
。 - 无状态设计:避免共享变量(如Servlet默认线程不安全,需通过无状态实现安全)。
代码示例(synchronized):
java
class Counter {
private int count = 0;
// 同步方法,保证线程安全
public synchronized void increment() {
count++;
}
public int getCount() { return count; }
}
94. synchronized
和Lock
的区别是什么?
答案:
特性 | synchronized | Lock |
---|---|---|
实现方式 | JVM层面的隐式锁 | API层面的显式锁(需手动加锁/释放) |
灵活性 | 自动释放锁,无法中断等待线程 | 可手动释放(unlock() ),支持中断、超时获取锁 |
公平性 | 非公平锁 | 可指定公平锁(构造方法参数) |
绑定条件 | 不支持条件变量 | 支持(Condition ),可实现复杂等待/唤醒 |
性能 | JDK1.6后优化(偏向锁、轻量级锁),性能接近Lock | 性能更优(高并发下) |
代码示例(Lock):
java
class LockCounter {
private int count = 0;
private Lock lock = new ReentrantLock(); // 显式锁
public void increment() {
lock.lock(); // 加锁
try {
count++;
} finally {
lock.unlock(); // 必须手动释放
}
}
}
95. 什么是死锁?如何避免死锁?
答案 :
死锁:两个或多个线程相互持有对方所需的资源,且都不释放,导致无限等待的状态。
产生条件:
- 互斥:资源不可共享;
- 持有并等待:线程持有部分资源,等待其他资源;
- 不可剥夺:资源不可被强制剥夺;
- 循环等待:线程间形成资源等待循环。
避免方式:
- 按固定顺序获取资源(打破循环等待);
- 限时获取资源(如
tryLock(timeout)
); - 减少锁的持有时间。
死锁示例:
java
// 线程1持有lock1,等待lock2;线程2持有lock2,等待lock1 → 死锁
Object lock1 = new Object();
Object lock2 = new Object();
new Thread(() -> {
synchronized (lock1) {
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock2) { System.out.println("线程1获取lock2"); }
}
}).start();
new Thread(() -> {
synchronized (lock2) {
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized (lock1) { System.out.println("线程2获取lock1"); }
}
}).start();
96. Java中的线程池有哪些核心参数?ThreadPoolExecutor
的工作原理是什么?
答案 :
ThreadPoolExecutor核心参数:
corePoolSize
:核心线程数(始终存活,除非设置allowCoreThreadTimeOut
)。maximumPoolSize
:最大线程数(核心线程+临时线程)。keepAliveTime
:临时线程空闲超时时间。workQueue
:任务等待队列(如LinkedBlockingQueue
)。threadFactory
:线程创建工厂。handler
:拒绝策略(如AbortPolicy
:直接抛出异常)。
工作原理:
- 提交任务时,若核心线程未满,创建核心线程执行任务;
- 核心线程满,任务加入等待队列;
- 队列满,若未达最大线程数,创建临时线程执行;
- 超过最大线程数,触发拒绝策略。
代码示例:
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60L, TimeUnit.SECONDS, // 临时线程超时时间
new LinkedBlockingQueue<>(3), // 等待队列
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
97. Java中的volatile
关键字有什么作用?它能保证原子性吗?
答案 :
作用:
- 可见性 :一个线程修改
volatile
变量后,其他线程能立即看到最新值(禁止CPU缓存)。 - 有序性:禁止指令重排序(通过内存屏障实现)。
不能保证原子性 :例如i++
操作(读取→修改→写入),多线程下可能出现丢失更新。
代码示例(可见性):
java
class Flag {
volatile boolean stop = false; // 保证可见性
public void run() {
while (!stop) {
// 循环执行,直到stop被修改为true
}
}
}
原子性问题示例:
java
class VolatileTest {
volatile int count = 0;
public void increment() {
count++; // 非原子操作,多线程下结果可能不正确
}
}
98. 什么是Java内存模型(JMM)?它解决了什么问题?
答案 :
Java内存模型(JMM) :是一种抽象模型,定义了线程和主内存之间的交互规则,解决多线程环境下的可见性、原子性、有序性问题。
核心问题及解决:
- 可见性 :线程对共享变量的修改需立即同步到主内存,其他线程从主内存读取(通过
volatile
、synchronized
实现)。 - 原子性 :保证操作不可分割(通过
synchronized
、Lock
、原子类实现)。 - 有序性 :禁止指令重排序(通过
volatile
、synchronized
的happens-before
规则)。
JMM抽象结构:
- 线程操作本地内存(CPU缓存),通过主内存交换共享变量。
99. ArrayList
和LinkedList
的区别是什么?各自的适用场景?
答案:
特性 | ArrayList | LinkedList |
---|---|---|
数据结构 | 动态数组(连续内存) | 双向链表(非连续内存) |
随机访问 | 快(O(1) ),通过索引直接访问 |
慢(O(n) ),需遍历链表 |
增删操作(中间) | 慢(O(n) ,需移动元素) |
快(O(1) ,只需修改指针) |
内存占用 | 较少(仅存储元素) | 较多(需存储前后指针) |
适用场景:
ArrayList
:频繁随机访问、少量增删(尾部)。LinkedList
:频繁增删(中间)、不频繁随机访问。
代码示例:
java
List<String> arrayList = new ArrayList<>();
arrayList.add("A"); // 尾部添加快
arrayList.get(0); // 随机访问快
List<String> linkedList = new LinkedList<>();
linkedList.add(1, "B"); // 中间插入快
linkedList.remove(0); // 头部删除快
100. HashMap
的底层实现原理是什么?JDK1.7和JDK1.8有何区别?
答案 :
底层原理:基于数组+链表/红黑树实现,通过哈希函数计算键的索引,存储键值对。
JDK1.7 vs JDK1.8区别:
特性 | JDK1.7 | JDK1.8 |
---|---|---|
数据结构 | 数组+链表 | 数组+链表+红黑树(链表长度>8时转为树) |
哈希冲突解决 | 头插法(可能导致死锁) | 尾插法(避免死锁) |
扩容机制 | 需重新计算哈希值 | 高低位拆分(减少哈希计算) |
存储结构 | Entry<K,V> 数组 | Node<K,V> 数组(实现Entry接口) |
红黑树优化 | 无 | 有(提升查询效率,O(logn) ) |
代码示例(HashMap使用):
java
HashMap<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
int value = map.get("a"); // 获取值
二、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) |