Java基础50道经典面试题(四)

Java基础50道经典面试题(四)

      • [💡 Java新特性与现代语法(11题)](#💡 Java新特性与现代语法(11题))
      • [💡 面向对象与设计进阶(9题)](#💡 面向对象与设计进阶(9题))
      • [💡 核心API与高级特性(9题)](#💡 核心API与高级特性(9题))
      • [💡 JVM与性能基础(10题)](#💡 JVM与性能基础(10题))
      • [💡 并发编程进阶(11题)](#💡 并发编程进阶(11题))
      • [💎 下一步的学习建议](#💎 下一步的学习建议)

这里为你准备了50道聚焦于Java新特性和深度原理的高质量基础问题,帮助你构建更完整的知识体系。

为了让您直观了解这50道题的侧重点分布,我将其按照考察维度进行了分类:
22% 18% 18% 20% 22% 50道Java基础问题分类 Java新特性与语法 面向对象与设计 核心API与特性 JVM与性能基础 并发编程进阶

💡 Java新特性与现代语法(11题)

这部分考察你对现代Java开发效率工具和编程范式的理解。

  1. Java 8的Stream API中,map()flatMap()有什么区别?

    • 核心分析map()将元素1:1映射为另一个元素,flatMap()将每个元素映射为一个Stream,然后将所有Stream扁平化 连接成一个Stream。例如,将List<List<String>>转为List<String>必须用flatMap()
  2. Optional类是用来解决什么问题的?滥用Optional可能会带来什么坏处?

    • 核心分析 :旨在更优雅地处理null,避免NPE。滥用如:在类字段、方法参数、集合中使用Optional会增加复杂度;毫无必要的链式调用(.get().get())违背其设计初衷。它应主要用于方法返回值
  3. Java 9的模块化(Project Jigsaw)中,requiresexportsopens指令分别是什么作用?

    • 核心分析requires声明模块依赖;exports将指定包导出给所有 模块;opens为了反射(如Spring框架)开放包,可以开放给所有模块(opens pkg)或指定模块(opens pkg to module)。
  4. 接口的私有方法(Java 9)和静态私有方法(Java 8/9)有什么用处?

    • 核心分析 :都是为了在接口内封装和复用公共代码。私有方法供接口内默认方法或其他私有方法调用,实现了接口内部实现的隐藏和重构,提高了代码的内聚性。
  5. 局部变量类型推断(var,Java 10)可以在哪些场景使用?有哪些限制?

    • 核心分析 :可用于局部变量初始化时、for循环索引、try-with-resources中。限制 :不能用于方法参数、返回值、字段、catch参数、lambda表达式参数(编译器无法推断)等。它不是动态类型,编译期即确定。
  6. switch表达式(Java 14成为标准)相比传统switch语句有哪些改进?

    • 核心分析 :① 可直接返回值(使用->箭头语法或yield关键字)。② 不需要break防止穿透。③ 可作为表达式嵌入其他语句。④ 覆盖所有可能值(穷举性)时不需要default。更安全、简洁。
  7. 文本块(Text Blocks,Java 15)有什么好处?如何表示?

    • 核心分析 :方便处理多行字符串(如JSON、SQL、HTML)。使用三引号 """ 作为定界符。会自动删除结尾 的公共缩进(以最短的非空行为基准),但开头换行符需注意处理。
  8. Record类(Java 16)是什么?它和Lombok的@Data注解有何异同?

    • 核心分析 :Record是不可变数据 的透明载体,编译器自动生成final字段、全参构造器、equals()hashCode()toString()等方法。与Lombok相比,它是语言特性,无需额外依赖和注解处理器,但功能更固定(不可变),定制能力较弱。
  9. Sealed类(密封类,Java 17)解决了什么问题?如何声明?

    • 核心分析 :用于限制一个类或接口只能被指定的子类继承或实现 ,在建模领域时提供更精确的层次控制。声明:public sealed class Shape permits Circle, Square, Rectangle {...}。子类必须是finalsealednon-sealed
  10. instanceof模式匹配(Java 16)如何简化代码?

    • 核心分析 :传统instanceof需要检查后显式强转。新模式:if (obj instanceof String s && s.length() > 5),在判断成功的同时,将obj自动转换为String并赋给变量s,省去了一行转换代码。
  11. 什么是var关键字与菱形运算符的组合类型推断增强(Java 11)?

    • 核心分析 :Java 11允许在Lambda表达式的形参中使用var,并与注解结合,如:(@NotNull var s) -> s.length()。这使得Lambda参数可以添加注解,增强了表达力。

💡 面向对象与设计进阶(9题)

这部分考察对设计原则、模式和复杂场景的把握。

  1. 什么是"组合优于继承"(Composition over Inheritance)原则?其优势是什么?

    • 核心分析 :通过包含(持有引用)而非扩展(继承)来复用功能。优势:① 更灵活 :运行时可以改变行为。② 更安全 :不会暴露父类内部细节。③ 避免继承层次爆炸脆弱的基类问题(父类修改可能破坏所有子类)。
  2. 如何实现一个不可变对象(Immutable Object)?需要特别注意什么?

    • 核心分析 :① 类final;② 字段private final;③ 无setter;④ 通过构造器深拷贝传入的可变对象;⑤ getter返回可变对象的副本。特别注意:如果字段是集合,应使用Collections.unmodifiableList()包装或返回新副本。
  3. 什么是建造者模式(Builder Pattern)?在什么场景下使用它比构造器或setter更好?

    • 核心分析 :将复杂对象的构造与其表示分离。适用于:① 对象有很多可选参数,且构造逻辑复杂。② 希望对象一旦创建就不可变(Immutable)。相比重叠构造器(telescoping constructor)更易读,相比setter能保证对象创建后的状态一致性。
  4. @Override注解的作用是什么?为什么推荐总是使用它?

    • 核心分析 :① 编译器检查 :确保该方法确实重写了父类方法,避免因拼写或签名错误导致的意外重载。② 提高可读性:明确标识出这是重写方法。这是一个低成本高收益的最佳实践。
  5. 什么时候该用抽象类?什么时候该用接口?从Java 8开始这个选择有变化吗?

    • 核心分析本质区别 是"是什么"(抽象类,is-a)和"能做什么"(接口,has-a/behaves-like)。Java 8后接口可以有默认实现,使得接口在行为复用 上能力增强,但状态(字段)仍只能由抽象类定义。优先考虑接口,除非需要定义非静态、非常量字段。
  6. 什么是回调(Callback)机制?在Java中通常如何实现?

    • 核心分析 :一种"你完成后通知我"的异步模式。实现方式:① 接口回调 (经典,如事件监听)。② Lambda/方法引用 (Java 8+,简洁)。③ Future/CompletableFuture` (更现代的异步回调)。
  7. 为什么说Object.clone()方法是"有问题"的?更好的替代方案是什么?

    • 核心分析 :① 是protected方法,需重写为public。② 浅拷贝,需手动实现深拷贝。③ 不调用构造器,破坏对象初始化逻辑。替代:复制构造器new MyClass(original))或复制工厂方法(更灵活、清晰)。
  8. 什么是空对象模式(Null Object Pattern)?它如何帮助消除null检查?

    • 核心分析 :提供一个实现接口、但行为为空或返回默认值的特殊对象,代替null引用。这样客户端代码可以无差别地调用方法,无需判空,代码更流畅。例如,返回空集合Collections.emptyList()而非null
  9. 如何防止一个类被实例化?(至少说出三种方式)

    • 核心分析 :① 将类声明为抽象类 (但仍可被继承实例化)。② 提供私有构造器 (最常用,工具类标准做法)。③ 在构造器中抛出异常 (如AssertionError)。④ 使用枚举单例(如果是单例需求)。

💡 核心API与高级特性(9题)

这部分深入语言核心机制和API的设计细节。

  1. Class.forName()ClassLoader.loadClass()在加载类时有什么区别?

    • 核心分析Class.forName(className)默认会初始化 类(执行静态块);ClassLoader.loadClass(className)默认只加载 类,不初始化。Class.forName可以指定是否初始化,且使用调用者的类加载器。
  2. Java动态代理(Dynamic Proxy)CGLIB代理有什么区别?

    • 核心分析JDK动态代理 :基于接口 ,使用ProxyInvocationHandler,生成实现接口的代理类。CGLIB代理 :基于继承,生成目标类的子类作为代理类,因此可以代理无接口的类。Spring AOP默认策略:有接口用JDK,无接口用CGLIB。
  3. 什么是服务加载器(ServiceLoader)?它如何工作?

    • 核心分析 :SPI机制的核心类。在META-INF/services/目录下,以接口全限定名 为文件名,文件内容为实现类全限定名 (每行一个)。ServiceLoader.load(Interface.class)会加载并实例化所有配置的实现类。
  4. Comparator接口的comparing()thenComparing()方法有什么用?它是如何实现链式比较的?

    • 核心分析 :用于构建复杂的比较器。comparing(Function keyExtractor)提取主排序键;thenComparing()用于在主键相等时指定次要排序键。这些方法返回一个新的Comparator,支持流畅的链式调用,极大简化了多字段排序。
  5. java.time.Instantjava.time.LocalDateTime有什么区别?

    • 核心分析Instant时间线上的一个瞬时点 (与GMT/UTC的偏移),绝对时间 ,适合机器时间戳、日志。LocalDateTime不带时区的本地日期时间 ,是挂钟时间,适合表示计划、生日等需要人类阅读但无需时区的情景。
  6. 如何安全地将一个受检异常(Checked Exception)StreamLambda中抛出?

    • 核心分析 :Lambda不允许直接抛出受检异常。解决方法:① 在Lambda内部try-catch并包装为RuntimeException。② 使用一个包装函数,在函数内部处理异常。③ 使用如ThrowingFunction这样的第三方工具接口。
  7. Files工具类中,readAllLines()lines()方法有什么区别?

    • 核心分析readAllLines()一次性将所有行读入内存的Listlines()返回一个Stream<String>惰性加载 ,适合处理大文件,不会一次性占用大量内存,可以用filtermap等流操作处理。
  8. 什么是try-with-resources语句的"抑制异常(Suppressed Exceptions)"?

    • 核心分析 :当try块和close()方法都抛出异常时,try块的异常被抛出,close()的异常会被抑制 (添加到主异常的suppressed数组),可以通过Throwable.getSuppressed()获取。这确保了最终抛出的是最有意义的业务异常。
  9. @SafeVarargs注解的作用是什么?在什么情况下需要使用它?

    • 核心分析 :用于抑制在不可具体化的参数(如泛型可变参数)上可能出现的"堆污染"警告。它告诉编译器,该方法会安全地处理可变参数,不会进行不安全的操作(如将错误类型的元素存入数组)。开发者需自己确保安全。

💡 JVM与性能基础(10题)

这部分考察程序运行时的底层机制和性能意识。

  1. 除了-Xms-Xmx,你还知道哪些常用的JVM参数?

    • 核心分析-XX:NewRatio(新生代老年代比例)、-XX:SurvivorRatio(Eden和Survivor区比例)、-XX:+UseG1GC(指定收集器)、-XX:MaxMetaspaceSize(元空间最大值)、-XX:+HeapDumpOnOutOfMemoryError(OOM时生成堆转储)、-XX:OnOutOfMemoryError(OOM时执行脚本)。
  2. 什么是逃逸分析(Escape Analysis)?JVM基于它可以做什么优化?

    • 核心分析 :分析对象动态作用域,判断对象是否"逃逸"出方法或线程。基于此可做:栈上分配 (对象在栈上创建销毁,减轻GC压力)、锁消除 (对线程局部对象移除同步锁)、标量替换(将对象分解为基本类型在栈上分配)。这些是JIT的优化手段。
  3. JDK自带的JVM监控和故障处理工具有哪些?你用过哪些?

    • 核心分析jps(进程状态)、jstat(统计信息)、jmap(内存映像)、jstack(线程快照)、jinfo(配置信息)、jcmd(多功能命令)、jconsoleVisualVMJMC(Java Mission Control)。jstack查死锁、jmap+MAT分析内存泄漏是常用组合。
  4. 什么是类加载器泄漏(ClassLoader Leak)?通常是如何引起的?

    • 核心分析 :当自定义ClassLoader加载的类被应用全局缓存(如静态Map)引用,导致ClassLoader及其加载的所有类无法被回收。典型场景:应用服务器(如Tomcat)热部署时,旧应用未完全卸载,内存逐渐累积,引发PermGen/Metaspace OOM。
  5. System.arraycopy()Arrays.copyOf(),以及对象.clone()在复制数组时性能上有何差异?

    • 核心分析System.arraycopy()本地方法 ,效率最高。Arrays.copyOf()内部调用arraycopy,并多了创建新数组的步骤。clone()对于数组是浅拷贝 ,性能与arraycopy相当。对于大批量复制,arraycopy是最佳选择。
  6. 解释一下JIT编译优化中的"内联(Inlining)"是什么?有什么好处?

    • 核心分析 :将方法调用处替换为方法体本身。好处:① 消除调用开销 (压栈、跳转等)。② 为其他优化创造更多机会 (如常量传播、死代码消除)。是JIT最重要优化之一,但对方法大小有限制(基于-XX:MaxInlineSize等参数)。
  7. 什么是卡表(Card Table)?它在垃圾收集中起什么作用?

    • 核心分析 :用于解决跨代引用 的GC问题。将老年代划分为大小为512字节的"卡",维护一个卡表。当老年代对象引用新生代对象时,JVM将对应卡标记为"脏"。Minor GC时,只需扫描脏卡,而非整个老年代,大大加快了Young GC的速度
  8. -XX:+DisableExplicitGC参数有什么作用?为什么生产环境有时会启用它?

    • 核心分析 :禁用System.gc()调用。生产环境启用是因为:① System.gc()会触发Full GC,造成不可预测的停顿。② 一些框架/库(如NIO的Direct Buffer清理)或RMI会隐式调用System.gc()。启用后需确保应用不依赖显式GC来管理内存(如堆外内存)。
  9. 如何理解元空间(Metaspace)永久代(PermGen)的区别?

    • 核心分析永久代 :在堆内,大小固定易OOM,存储类信息、字符串常量池等。元空间 :在本地内存(Native Memory)中,默认无上限(受系统内存限制),动态调整,将字符串常量池移至堆中。元空间减少了Full GC触发,降低了OOM风险。
  10. 什么是偏向锁(Biased Locking)?为什么Java 15开始默认禁用?

    • 核心分析 :假设锁总由同一线程获得,消除该线程后续的同步开销。但在高竞争或持有锁时间短的场景下,撤销偏向锁(需要安全点)的代价可能超过收益,且增加了JVM复杂性。随着并发模式和硬件变化,其收益已不明显,故被禁用。

💡 并发编程进阶(11题)

这部分深入到并发工具的实现原理和正确使用模式。

  1. ThreadLocal内存泄漏根本原因是什么?为什么WeakReference不能完全解决问题?

    • 核心分析ThreadLocalMapEntry继承WeakReference,其keyThreadLocal对象)是弱引用,但value强引用 。当key被GC回收变为null时,value仍被Entry强引用,且线程不结束就无法访问和清除这些Entry必须调用remove()
  2. 什么是happens-before原则?它和as-if-serial语义有什么关系?

    • 核心分析happens-before是Java内存模型(JMM)定义的内存可见性规则 ,保证前一个操作的结果对后一个操作可见。as-if-serial是单线程内的语义,保证单线程内执行结果不被重排序改变。happens-beforeas-if-serial在多线程下的扩展和保证。
  3. ConcurrentHashMapsize()方法返回值是精确的吗?为什么?

    • 核心分析不是绝对精确 。它采用分片计数 ,在并发更新时,为了性能,不会全局加锁来获取精确值,而是尝试无锁地累加各段的修改计数。在累加过程中可能有其他线程在修改,所以返回的是一个估计值,但大多数情况下足够用。
  4. ForkJoinPool和普通的ThreadPoolExecutor有什么区别?适合什么场景?

    • 核心分析ForkJoinPool 使用工作窃取(Work-Stealing)算法 ,适合处理大量可分解的递归型任务(如计算密集型)ThreadPoolExecutor 适合处理相互独立的任务(如I/O密集型)ForkJoinPool能更好地平衡线程负载。
  5. CompletableFutureFuture有什么区别?它如何实现回调?

    • 核心分析Future获取结果需要阻塞get()CompletableFuture实现了CompletionStage,支持异步回调thenApply/thenAccept)、组合thenCompose/thenCombine)、异常处理exceptionally/handle)等,是功能强大的异步编程工具。
  6. 什么是ABA问题AtomicStampedReference是如何解决的?

    • 核心分析 :CAS操作中,变量值从A变B再变回A,CAS会误认为没变。解决方法:AtomicStampedReference不仅比较值,还比较一个版本戳(Stamp),每次修改版本戳递增,从而避免了ABA问题。
  7. ReadWriteLock(读写锁)的锁降级是什么?为什么允许锁降级,而不允许锁升级?

    • 核心分析锁降级 :持有写锁 -> 获取读锁 -> 释放写锁。这个过程允许了数据在修改后,仍能被当前线程读取,且不会让其他写线程介入,保证了数据一致性。锁升级 (读锁 -> 写锁)容易引起死锁(多个读线程都想升级为写锁时相互等待),故通常不支持。
  8. BlockingQueue接口有哪些实现?ArrayBlockingQueueLinkedBlockingQueue有何区别?

    • 核心分析 :常见实现:ArrayBlockingQueue(有界数组)、LinkedBlockingQueue(可选有界的链表)、PriorityBlockingQueue(优先级)、SynchronousQueue(不存储元素)。区别:① 数据结构 :数组 vs 链表。② Array一把锁;Linked两把锁(putLock, takeLock),吞吐量通常更高。
  9. PhaserCyclicBarrier/CountDownLatch有什么区别?

    • 核心分析Phaser更灵活强大。① 动态调整 :可动态注册/注销参与方。② 多阶段 :可重复使用,每阶段可不同屏障动作。③ 分层 :支持树形结构降低竞争。CyclicBarrier固定参与方,可重复;CountDownLatch一次性的减数器。
  10. 什么是线程饥饿(Thread Starvation)?在Java中哪些情况可能导致?

    • 核心分析 :低优先级线程长期得不到执行。原因:① 高优先级线程抢占。② 同步区过长或等待条件不满足,使线程长期等待。③ 使用非公平锁,新来的线程可能"插队"成功,导致等待队列中线程饥饿。
  11. Java内存模型角度,解释final域的重排序规则及其能保证的"初始化安全性"。

    • 核心分析final域的重排序规则:① 在构造器内对final域的写入,与随后将构造对象的引用赋值给变量,这两个操作不能重排序 。② 初次读包含final域的对象的引用,与随后初次读这个final域,这两个操作不能重排序 。这保证了:只要对象正确构造(引用不逃逸),其他线程看到的final域一定是构造器初始化后的值,无需同步。

💎 下一步的学习建议

你已经积累了超过100个深度问题。为了将这些知识转化为真正的能力,我建议:

  1. 构建知识网络图:用思维导图工具,将JVM、并发、集合、新特性等模块的关键概念、类、原理、参数关联起来,形成你的"知识大脑"。
  2. 场景化学习 :针对"高性能缓存"、"百万级日志处理"、"秒杀系统"等具体场景,思考如何综合运用上述知识(如用ConcurrentHashMap+Future做缓存、用ForkJoinPool处理日志、用Phaser控制秒杀流程)来设计方案。
  3. 追踪源头 :对于核心机制(如AQS、ForkJoinPool工作窃取),直接阅读JDK源码中的关键注释和实现,这是最高质量的一手资料。

如果你希望针对 "微服务架构"、"系统设计"或"特定中间件(如Kafka、Redis)的原理与Java集成" 进行深入探讨,我可以为你提供新的学习路径和问题清单。

相关推荐
Slow菜鸟2 小时前
Java基础架构设计(三)| 通用响应与异常处理(分布式应用通用方案)
java·开发语言
吴佳浩2 小时前
Python入门指南(七) - YOLO检测API进阶实战
人工智能·后端·python
我是Superman丶2 小时前
《Spring WebFlux 实战:基于 SSE 实现多类型事件流(支持聊天消息、元数据与控制指令混合传输)》
java
廋到被风吹走2 小时前
【Spring】常用注解分类整理
java·后端·spring
是一个Bug2 小时前
Java基础20道经典面试题(二)
java·开发语言
Z_Easen2 小时前
Spring 之元编程
java·开发语言
liliangcsdn3 小时前
python下载并转存http文件链接的示例
开发语言·python
leoufung3 小时前
LeetCode 373. Find K Pairs with Smallest Sums:从暴力到堆优化的完整思路与踩坑
java·算法·leetcode
阿蒙Amon3 小时前
C#每日面试题-委托和事件的区别
java·开发语言·c#