Java面试题及详细答案120道之(081-100)

发布于:2025-07-28 ⋅ 阅读:(10) ⋅ 点赞:(0)

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

前后端面试题-专栏总目录

在这里插入图片描述

一、本文面试题目录

81. Java中的ConcurrentHashMapHashMap在并发场景下有什么区别?

原理

  • HashMap:非线程安全,并发修改可能导致ConcurrentModificationException
  • ConcurrentHashMap(JDK 8+):线程安全,采用“CAS + 同步块”实现,支持高并发读写,效率优于HashTable

核心区别

  • 锁粒度:ConcurrentHashMap锁桶(链表/红黑树),HashTable锁整个表;
  • 迭代器:ConcurrentHashMap的迭代器弱一致性(不抛异常),HashMap的迭代器快速失败。

代码示例

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):

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-finallyreturn的执行顺序是什么?

原理finally块始终执行(除非JVM退出),若trycatch中有return,会先执行finally,再返回结果(finally中的return会覆盖之前的返回值)。

代码示例

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),不能继承其他类(可实现接口)。

代码示例

// 记录类:自动生成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元素(针对基本类型数组的包装类)。

代码示例

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+引入锁升级优化,避免性能损耗:

  1. 偏向锁:单线程访问时,仅记录线程ID,无竞争时无需加锁;
  2. 轻量级锁:多线程交替访问,通过CAS尝试获取锁,避免重量级锁开销;
  3. 重量级锁:多线程竞争激烈时,使用操作系统互斥量,性能较差。

代码示例(同步方法与同步块):

public class SynchronizedDemo {
    // 同步方法(锁为当前对象)
    public synchronized void syncMethod() {
        // 同步代码
    }
    
    // 同步块(锁为指定对象)
    public void syncBlock() {
        Object lock = new Object();
        synchronized (lock) {
            // 同步代码
        }
    }
}

87. 什么是Java中的模式匹配(Pattern Matching)

原理:JDK 16+引入的语法糖,简化类型判断和转换,主要用于instanceofswitch
instanceof模式匹配:判断类型的同时完成转换,避免显式强转。

代码示例

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是标记注解,用于编译期检查接口是否为函数式接口(仅含一个抽象方法),若不符合则编译报错,增强代码可读性。

代码示例

@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()(线程池线程复用导致变量残留)。

代码示例(静态集合导致内存泄漏):

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包中的类(如ClassMethodField)实现。

原理
Java编译器将类编译为字节码(.class),运行时JVM加载类并生成Class对象,反射通过该对象访问类的结构。

应用场景

  • 框架开发(如Spring的IOC容器,通过反射实例化Bean);
  • 动态代理(如MyBatis的Mapper接口代理);
  • 注解解析(如JUnit的@Test注解检测)。

代码示例

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 ExceptionUnchecked Exception有何区别?

答案
异常体系结构

  • 顶层父类为Throwable,分为Error(错误,如OutOfMemoryError,程序无法处理)和Exception(异常,程序可处理)。
  • Exception分为RuntimeException(运行时异常,如NullPointerException)和非运行时异常(如IOException)。

区别

类型 特点 示例
Checked Exception 编译时检查,必须捕获或声明抛出 IOExceptionSQLException
Unchecked Exception 运行时抛出,编译不检查,无需强制处理 NullPointerExceptionIndexOutOfBoundsException

代码示例

// 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中,如果finallyreturntrycatch中的return会生效吗?

答案
不会。finally块始终执行(除非JVM退出),若finally包含return,会覆盖trycatch中的return值。

代码示例

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后,若finallyreturn,则直接返回该值,丢弃暂存值。

93. 什么是线程安全?如何实现线程安全?

答案
线程安全:多线程环境下,多个线程访问共享资源时,无需额外同步操作即可保证结果正确。

实现方式

  1. 同步机制
    • synchronized关键字(修饰方法或代码块,保证原子性、可见性、有序性)。
    • Lock接口(如ReentrantLock,更灵活的锁控制)。
  2. 使用线程安全的类:如ConcurrentHashMapAtomicInteger
  3. 无状态设计:避免共享变量(如Servlet默认线程不安全,需通过无状态实现安全)。

代码示例(synchronized)

class Counter {
    private int count = 0;
    // 同步方法,保证线程安全
    public synchronized void increment() {
        count++;
    }
    public int getCount() { return count; }
}

94. synchronizedLock的区别是什么?

答案

特性 synchronized Lock
实现方式 JVM层面的隐式锁 API层面的显式锁(需手动加锁/释放)
灵活性 自动释放锁,无法中断等待线程 可手动释放(unlock()),支持中断、超时获取锁
公平性 非公平锁 可指定公平锁(构造方法参数)
绑定条件 不支持条件变量 支持(Condition),可实现复杂等待/唤醒
性能 JDK1.6后优化(偏向锁、轻量级锁),性能接近Lock 性能更优(高并发下)

代码示例(Lock)

class LockCounter {
    private int count = 0;
    private Lock lock = new ReentrantLock(); // 显式锁
    public void increment() {
        lock.lock(); // 加锁
        try {
            count++;
        } finally {
            lock.unlock(); // 必须手动释放
        }
    }
}

95. 什么是死锁?如何避免死锁?

答案
死锁:两个或多个线程相互持有对方所需的资源,且都不释放,导致无限等待的状态。

产生条件

  • 互斥:资源不可共享;
  • 持有并等待:线程持有部分资源,等待其他资源;
  • 不可剥夺:资源不可被强制剥夺;
  • 循环等待:线程间形成资源等待循环。

避免方式

  1. 按固定顺序获取资源(打破循环等待);
  2. 限时获取资源(如tryLock(timeout));
  3. 减少锁的持有时间。

死锁示例

// 线程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核心参数

  1. corePoolSize:核心线程数(始终存活,除非设置allowCoreThreadTimeOut)。
  2. maximumPoolSize:最大线程数(核心线程+临时线程)。
  3. keepAliveTime:临时线程空闲超时时间。
  4. workQueue:任务等待队列(如LinkedBlockingQueue)。
  5. threadFactory:线程创建工厂。
  6. handler:拒绝策略(如AbortPolicy:直接抛出异常)。

工作原理

  1. 提交任务时,若核心线程未满,创建核心线程执行任务;
  2. 核心线程满,任务加入等待队列;
  3. 队列满,若未达最大线程数,创建临时线程执行;
  4. 超过最大线程数,触发拒绝策略。

代码示例

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, // 核心线程数
    5, // 最大线程数
    60L, TimeUnit.SECONDS, // 临时线程超时时间
    new LinkedBlockingQueue<>(3), // 等待队列
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

97. Java中的volatile关键字有什么作用?它能保证原子性吗?

答案
作用

  1. 可见性:一个线程修改volatile变量后,其他线程能立即看到最新值(禁止CPU缓存)。
  2. 有序性:禁止指令重排序(通过内存屏障实现)。

不能保证原子性:例如i++操作(读取→修改→写入),多线程下可能出现丢失更新。

代码示例(可见性)

class Flag {
    volatile boolean stop = false; // 保证可见性
    public void run() {
        while (!stop) {
            // 循环执行,直到stop被修改为true
        }
    }
}

原子性问题示例

class VolatileTest {
    volatile int count = 0;
    public void increment() {
        count++; // 非原子操作,多线程下结果可能不正确
    }
}

98. 什么是Java内存模型(JMM)?它解决了什么问题?

答案
Java内存模型(JMM):是一种抽象模型,定义了线程和主内存之间的交互规则,解决多线程环境下的可见性、原子性、有序性问题。

核心问题及解决

  • 可见性:线程对共享变量的修改需立即同步到主内存,其他线程从主内存读取(通过volatilesynchronized实现)。
  • 原子性:保证操作不可分割(通过synchronizedLock、原子类实现)。
  • 有序性:禁止指令重排序(通过volatilesynchronizedhappens-before规则)。

JMM抽象结构

  • 线程操作本地内存(CPU缓存),通过主内存交换共享变量。

99. ArrayListLinkedList的区别是什么?各自的适用场景?

答案

特性 ArrayList LinkedList
数据结构 动态数组(连续内存) 双向链表(非连续内存)
随机访问 快(O(1)),通过索引直接访问 慢(O(n)),需遍历链表
增删操作(中间) 慢(O(n),需移动元素) 快(O(1),只需修改指针)
内存占用 较少(仅存储元素) 较多(需存储前后指针)

适用场景

  • ArrayList:频繁随机访问、少量增删(尾部)。
  • LinkedList:频繁增删(中间)、不频繁随机访问。

代码示例

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使用)

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)

网站公告

今日签到

点亮在社区的每一天
去签到