尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Java面试八股文核心知识点全梳理:集合、并发、JVM、Spring、MySQL、Redis

Java面试八股文核心知识点全梳理:集合、并发、JVM、Spring、MySQL、Redis 最近后台收到不少读者留言都在问同一个问题“Java面试八股到底还要不要背”。我的回答一直很明确八股不是万能的但不看八股是万万不能的。关键在于你怎么看它——它不是死记硬背的题库而是一张帮你快速定位“面试官到底在考什么”的认知地图。尤其是2023年这个时间点Java岗位的竞争已经卷到“基础不牢地动山摇”的地步几乎所有大厂面试的第一轮都在疯狂考察Java核心基础也就是大家口中的“Java面试八股文”。这份内容我整理了很久把Java集合、并发、JVM、Spring、MySQL、Redis这些核心面试知识点全部串在了一起适合正在准备校招、社招或者想系统自查Java基础的朋友建议先收藏再慢慢消化。1. 集合框架面试官最爱连环追问的重灾区如果你只准备一个模块那一定是集合。Java集合是面试中出场率最高的知识点没有之一。面试官几乎不会只问“ArrayList和LinkedList有什么区别”这种送分题他们会顺着你的回答一直往下挖挖到你说不出来为止。所以这一章我按“底层原理 → 高频追问 → 易错点”三层来拆帮你把集合这块彻底吃透。1.1 ArrayList和LinkedList别只背“数组 vs 链表”ArrayList的底层是Object数组LinkedList的底层是双向链表这是基础中的基础。但真正拉开差距的是你对扩容机制的理解。ArrayList默认容量是10当元素个数超过容量时会进行扩容扩容后的新容量是原容量的1.5倍也就是oldCapacity (oldCapacity 1)。扩容时会创建一个新数组然后把旧数组里的元素用Arrays.copyOf拷贝过去这个过程的时间复杂度是O(n)。如果你能预估数据量最好在构造时直接指定初始容量避免多次扩容带来的性能损耗。LinkedList则不同它不需要扩容新增元素只需要创建一个节点把前后指针指好就行。但它的内存开销更大每个节点除了存储数据还要存储前驱和后继两个引用。在Java 8之后LinkedList的节点还有针对性的优化比如部分场景下会使用Collections.unmodifiableList做安全封装但那是另一个话题。实操心得很多人以为LinkedList插入快ArrayList插入慢这个结论太绝对。如果在ArrayList的末尾插入由于有ensureCapacityInternal的扩容优化效率并不低但如果是在头部插入每次都要移动所有元素那才是真的慢。判断用哪个看的是“增删的位置”和“访问的方式”而不是无脑套结论。1.2 HashMap的put流程从hash到红黑树的完整链路HashMap是Java面试中的“必考点之王”。我建议你把它当做一个“故事”来记而不是死记硬背步骤。整个put流程可以这样讲第一步对key的hashCode做一次扰动计算。(h key.hashCode()) ^ (h 16)把高16位和低16位做异或目的是让hash值的高位也参与数组下标的计算减少碰撞。第二步根据(n - 1) hash计算出数组下标其中n是数组长度。这里有一个隐藏考点为什么HashMap的容量必须是2的幂因为n - 1的二进制低位全是1可以让hash值均匀分布同时运算比%取模高效得多。第三步如果数组下标位置为null直接放入一个新Node如果不为null说明发生了hash碰撞此时分三种情况key完全相同就覆盖valuekey不相同就判断当前节点是不是红黑树节点如果是就按红黑树方式插入否则按链表方式遍历找到尾部插入尾插法Java 8后不再用头插法主要是为了解决并发扩容时的死循环问题。链表长度超过8且数组容量达到64时链表会转成红黑树树化的目的是把查询时间从O(n)降到O(logn)。第四步插入完成后检查size thresholdthreshold等于容量乘加载因子0.75如果超过就触发resize扩容。扩容时元素会被重新分配到新数组原来的链表会分裂成两部分一部分留在原下标一部分移动到原下标加旧容量的位置这个设计很巧妙利用了数组长度是2的幂这个特性不用重算hash。1.3 ConcurrentHashMap锁粒度背后的演进逻辑面试官问你ConcurrentHashMap主要考察的是你对“并发进化史”的理解。Java 7的ConcurrentHashMap用Segment分段锁默认16个Segment锁粒度是Segment级别Java 8抛弃了Segment改用CAS synchronized锁粒度细化到每个数组桶Node。put时先用CAS尝试插入如果失败说明有竞争就对当前桶的头节点加synchronized锁。这种设计在大多数场景下并发性能更好因为不同线程操作不同桶时完全互不干扰。还有一个容易忽略的细节ConcurrentHashMap不允许key或value为null。很多人不知道为什么我的理解是在多线程环境下map.get(key)返回null无法区分是“key不存在”还是“value就是null”而HashMap因为不允许并发访问所以不存在这个歧义。这个问题面试官也很爱问答上来会加分。2. 并发编程从synchronized到AQS把“背题”变成“讲题”并发编程是Java面试的“深水区”也是区分初级和中级程序员的分水岭。这一章的知识点环环相扣如果你能把这些概念串成一条线讲清楚面试官会觉得你的底层功底非常扎实。2.1 synchronized的锁升级从偏向锁到重量级锁synchronized在Java 6之后做了大量优化锁一共有四种状态无锁、偏向锁、轻量级锁、重量级锁。锁的升级方向是单向的只能从低到高升级不能降级。偏向锁的核心思想是大多数情况下锁不仅不存在多线程竞争而且总是由同一线程多次获得。所以当第一次有线程访问同步块时会在对象头的Mark Word中记录该线程的ID如果后续没有其他线程竞争持有偏向锁的线程每次进入同步块都无需CAS操作。如果出现竞争偏向锁会升级为轻量级锁。轻量级锁通过CAS来获取锁如果CAS失败说明有多个线程在竞争锁会自旋一段时间避免线程上下文切换如果自旋超过阈值就会升级为重量级锁此时未拿到锁的线程会被阻塞进入操作系统内核态。面试官可能会追问“为什么锁要升级”回答时可以聚焦在“性能”上无竞争时用偏向锁几乎零开销低竞争时用CAS和自旋避免线程切换高竞争时才不得不让线程阻塞。这个设计体现了“因地制宜”的优化思想。2.2 volatile的关键字可见性、原子性与内存屏障volatile是老生常谈但能答全的人真不多。它有两个作用保证变量的可见性禁止指令重排序。很多人忘了第三个隐含知识点——它不能保证复合操作的原子性。可见性的底层原理是MESI缓存一致性协议线程修改volatile变量时会立即将缓存行写回主内存并使其他CPU核心中的缓存行失效。禁止重排序的原理则涉及内存屏障volatile写操作前会插入StoreStore屏障写操作后会插入StoreLoad屏障这样就能确保写操作前的普通写操作不会被重排到写操作之后。易错点volatile int count然后count这个操作不是线程安全的。因为count实际上是“读取 → 修改 → 写入”三步volatile只能保证每一步的可见性但无法保证这三步的原子性。要解决原子性问题得用AtomicInteger或者synchronized。2.3 AQS与ReentrantLock看懂一个类就看懂了半个并发包AQSAbstractQueuedSynchronizer是Java并发包的基础ReentrantLock、CountDownLatch、Semaphore、ReentrantReadWriteLock底层都用到了它。AQS的核心是一个volatile int state变量和一个CLH变体的FIFO双端队列。以ReentrantLock加锁为例线程尝试用CAS把state从0改为1如果成功就拿到锁如果失败就把当前线程封装成Node放入同步队列尾部然后循环尝试获取锁或者通过LockSupport.park挂起。释放锁时把state减1如果减到0说明锁完全释放就唤醒队首线程。这里的“可重入”体现在同一个线程每次加锁state就加1释放时减1所以必须配对调用才能完全释放。实话说AQS这套设计面试官一般不会让你从零手写但你要能讲清楚state的语义和队列的入队出队流程以及独占锁和共享锁的区别ReentrantLock是独占Semaphore是共享CountDownLatch也利用了共享锁的状态计数。把AQS讲透了后面答线程池、答各种并发工具类都会顺很多。2.4 线程池的七大参数与四种拒绝策略线程池是并发编程的“应用题”面试官很喜欢结合项目问你为什么用线程池核心参数怎么调线程数设置多少合理ThreadPoolExecutor一共有七个核心参数核心线程数corePoolSize、最大线程数maximumPoolSize、空闲存活时间keepAliveTime、时间单位unit、阻塞队列workQueue、线程工厂threadFactory、拒绝策略handler。任务提交时流程是这样的如果当前线程数小于核心线程数创建新线程执行任务如果线程数达到核心线程数任务进入阻塞队列如果队列满了线程数小于最大线程数继续创建新线程如果线程数已经达到最大值就触发拒绝策略。四种拒绝策略分别是AbortPolicy直接抛异常、CallerRunsPolicy用调用者线程执行、DiscardPolicy静默丢弃、DiscardOldestPolicy丢弃队首任务后重新提交。我个人推荐在业务中用CallerRunsPolicy因为它至少能保证任务不丢同时还能通过让调用线程自己执行来反向压测系统瓶颈。线程数的设置没有标准答案如果是CPU密集型任务推荐CPU核心数 1如果是IO密集型任务推荐CPU核心数 * 2或者用公式“CPU核心数 / (1 - 阻塞系数)”来估算。3. JVM与内存从字符串常量池到GC的完整地图JVM是Java面试八股文里“含金量”最高的部分也是很多自学Java的同学最容易放弃的部分。实际上JVM没有传说中那么玄乎你只需要从“内存布局 → 对象创建 → 垃圾回收”这条主线路去学就能建立完整的知识框架。3.1 运行时数据区哪些是线程私有哪些是线程共享JVM运行时数据区一共分五块虚拟机栈、本地方法栈、程序计数器是线程私有的堆、方法区是线程共享的。这里最容易混淆的是“Java 8后方法区去哪了”这个问题。Java 8后方法区被元空间Metaspace取代元空间使用的是本地内存而不是JVM堆内存。为什么要换因为永久代的大小很难确定Full GC时容易产生内存溢出换成本地内存后元空间只受物理内存限制OutOfMemoryError的概率大大降低。虚拟机栈是面试的高频考点因为它和“栈帧”深度绑定。每个方法调用都会创建一个栈帧栈帧里包含局部变量表、操作数栈、动态链接、方法出口。当方法递归调用过深时就会抛出StackOverflowError。而堆内存不足时会抛出OutOfMemoryError: Java heap space这两者一个是栈溢出一个是堆溢出概念完全不一样。3.2 字符串常量池与intern()面试官眼中“真正的理解”字符串是Java里最常用的对象也是面试最容易翻车的地方。经典的题目是String s1 new String(abc)创建了几个对象答案是两个前提是字符串常量池中没有“abc”一个是常量池中的对象一个是堆中的对象。如果常量池中已经有“abc”那就只创建一个堆对象。intern()方法的作用是如果字符串常量池中没有该字符串的值就把它加入常量池并返回常量池引用如果已经存在直接返回常量池引用。这里有一个细节Java 6及之前intern()把字符串放入永久代如果大量使用会造成永久代内存溢出Java 7之后常量池移到堆中intern()的行为也发生了变化。面试官很喜欢让你判断s1.intern() s1的结果本质就是考察你有没有搞清楚“引用比较”和“值比较”的区别以及不同JDK版本下intern的实现差异。实操建议不要在生产代码里大量使用intern()虽然它能节省内存但常量池的字符串查找本身也有开销而且会引入内存泄漏的隐患。代码里尽量用常量字符串拼接JVM编译器会自动优化成常量池中的值。3.3 类加载机制与双亲委派模型类加载机制考的是“加载、验证、准备、解析、初始化”五个阶段。这里要重点区分“准备”和“初始化”准备阶段会为静态变量分配内存并设置默认零值比如static int num 10在准备阶段num的值是0到了初始化阶段才会赋值为10。双亲委派模型是必考题。当一个类加载器收到类加载请求时它不会自己先去加载而是先委派给父类加载器一直递到最顶层的Bootstrap ClassLoader只有当父类加载器加载不了时子加载器才会尝试自己加载。这样做的核心目的是保证Java核心类库的安全比如你写一个java.lang.String类由于双亲委派最终会由Bootstrap ClassLoader加载JDK自带的String而不是你写的那个这就防止了核心API被篡改。面试进阶题“怎么打破双亲委派”答案是重写ClassLoader的loadClass方法典型的例子是Tomcat的WebAppClassLoader它为每个Web应用创建独立的类加载器这样可以实现应用间的类隔离也支持热部署。3.4 GC算法与垃圾收集器别再只会说“复制算法”和“标记清除”垃圾回收这块基础要掌握三种GC算法标记-清除、标记-复制、标记-整理。标记-清除会产生内存碎片标记-复制解决了碎片问题但浪费空间标记-整理兼顾了空间和碎片但移动对象有开销。现代JVM的垃圾收集器基本都是分代收集的新生代用复制算法因为对象存活率低老年代用标记-整理或标记-清除因为对象存活率高。收集器方面至少要了解CMS和G1。CMSConcurrent Mark Sweep是第一款真正意义上的并发收集器它解决了老年代Full GC时STWStop The World过长的问题但CMS有两个著名缺陷一是使用标记-清除会产生碎片二是并发模式失败时退化为Serial Old反而更慢。G1Garbage First是JDK 9之后的默认收集器G1把堆划分为多个Region通过维护可预测的停顿时间模型每次回收价值最大的Region。JDK 11引入的ZGC更进一步把STW时间压缩到10ms以下。面试时你不需要把每个收集器的参数背得滚瓜烂熟但至少要能说出CMS和G1的适用场景和核心区别。4. Spring与Spring BootIOC/AOP的两种问法和高分回答姿势现在的Java后端岗位基本离不开Spring生态。面试官不会直接问“IOC是什么”而是会结合你写的项目问“你项目里为什么用Service标注的类可以自动注入”或者“一个接口有两个实现类时Spring怎么处理”这些问题表面是项目细节底层全是Spring八股。4.1 IOC与Bean生命周期从“Class”到“可用的Bean”经历了什么IOC控制反转的核心思想是对象不是由使用者new出来的而是交给容器创建和管理使用者只需要声明依赖容器负责注入。这样带来的好处是解耦和可测试性。Bean的生命周期是个完整链路可以浓缩成一句话实例化 → 属性填充 → 初始化Aware回调 → PostConstruct → InitializingBean.afterPropertiesSet → 自定义init方法→ 使用 → 销毁。面试官最常挖的是初始化和销毁的顺序。我的记忆技巧是把“初始化”想象成一套流水线Bean工厂先给你把对象new出来实例化然后给你打标签属性填充最后做体检Aware检查、注解初始化、接口初始化、自定义初始化体检做完才能上岗。销毁顺序和初始化是反过来的先执行自定义destroy方法再是DisposableBean.destroy最后是PreDestroy。补充一个高频追问Bean的默认作用域是singleton单例它在Spring容器中只创建一次。注意Spring的单例和设计模式里的单例不完全一样设计模式单例保证一个JVM内只有一个实例Spring单例则保证一个Spring容器内只有一个实例。如果你在多个Spring容器中都加载了同一个类那就会有多个实例。4.2 AOP与动态代理JDK代理和CGLIB怎么选AOP面向切面编程的底层实现是动态代理。Spring AOP默认在目标类实现了接口时使用JDK动态代理通过Proxy.newProxyInstance在运行时生成代理类代理类实现了目标类的所有接口如果目标类没有实现接口Spring会使用CGLIB通过继承目标类生成子类作为代理。这里有一个大坑CGLIB代理是通过继承实现的所以被final修饰的方法无法被CGLIB代理。面试易错点Transactional注解失效的场景。最经典的就是“同类内方法自调用”比如A方法调用同类里的B方法B上有Transactional但事务不生效。原因很简单this调用不走代理对象Transactional是通过代理对象拦截的。解决办法是注入自身代理或者把B方法移到另一个Bean中。这类问题在简历里写“用过Spring事务”时一定会被追问建议提前准备好说辞。4.3 循环依赖与三级缓存Spring为什么能解决setter注入的循环依赖循环依赖是指A依赖BB也依赖A。Spring解决循环依赖的核心是“提前暴露对象的早期引用”依赖的是三级缓存singletonObjects一级缓存存完整Bean、earlySingletonObjects二级缓存存早期暴露的Bean即还没完成属性填充的Bean、singletonFactories三级缓存存ObjectFactory工厂对象。流程大概是这样创建A时发现A依赖B于是先去创建B创建B时发现B依赖A此时从三级缓存中找到A的ObjectFactory通过getEarlyBeanReference拿到A的早期引用放入二级缓存然后B完成创建B创建完成后A拿到B的引用继续完成自己的属性填充和初始化。很多人以为二级缓存就够了为什么要三级缓存因为只有三级缓存中的ObjectFactory才能支持在循环依赖时执行AOP代理对象的提前创建。如果是普通Bean二级缓存其实也能解决但如果有切面逻辑就必须通过三级缓存。特别注意Spring解决不了构造器注入的循环依赖因为构造器创建对象时必须先传入依赖没有“提前暴露”的机会。所以在实际项目中如果遇到循环依赖Spring Boot 2.6之后默认会直接启动报错我之前就踩过这个坑后来查了半天发现是项目里用了Lazy注解临时绕过但根因还是设计问题最好通过重构依赖关系来解决。4.4 Spring Boot自动配置从SpringBootApplication到内嵌TomcatSpring Boot的出现让Spring配置变得极其简单但很多人只会用不会讲。自动配置的核心是EnableAutoConfiguration它通过Import(AutoConfigurationImportSelector.class)引入一系列自动配置类。这些自动配置类都在spring.factories或AutoConfiguration.imports文件中注册条件注解ConditionalOnClass、ConditionalOnMissingBean、ConditionalOnProperty等控制着配置何时生效。举个例子Spring Boot为什么能自动内嵌Tomcat因为ServletWebServerFactoryAutoConfiguration类上标注了ConditionalOnClass(ServletRequest.class)只要classpath里有Servlet API就会自动注册Tomcat的工厂Bean。如果你想把内嵌服务器从Tomcat换成Undertow只需要在pom里排除Tomcat依赖引入UndertowSpring Boot会自动检测到并切换。这也是我在实际项目里优化启动速度时常用的一个手段。5. MySQL与Redis企业面试的体系化作答搞Java后端数据库是避不开的。面试官考察数据库核心就两件事你写的SQL为什么快/慢你的数据在并发下安不安全这两个问题牵扯出的知识点非常多我挑几个最常考的给你捋一遍并附上我这几年应对面试的“体系化答题框架”。5.1 MySQL索引B树、覆盖索引与最左前缀MySQL索引默认使用B树而不是B树或红黑树。原因可以这样答B树所有数据都存在叶子节点并且叶子节点之间通过双向链表连接这让范围查询BETWEEN、、非常高效同时B树的非叶子节点不存储数据所以树的高度更低一般3层就能存2000万行左右的数据磁盘IO次数更少。覆盖索引是优化慢SQL的一大利器。所谓覆盖索引就是查询的列恰好都在索引里这样就不需要回表。比如你有一个联合索引(a, b)执行SELECT a, b FROM table WHERE a 1直接在索引上就能拿到结果无需回主键表。最左前缀原则也基于联合索引MySQL会从索引的最左侧列开始匹配如果查询条件里没有最左列索引就失效。比如联合索引(a, b, c)可以用a、ab、abc的查询条件但只用b或c查询时无法走这个索引。实操经验给表建索引时不要贪多索引不是越多越好每次插入和更新都要维护索引。很多项目里慢SQL的根本原因不是没建索引而是建了太多没用的索引导致写入变慢。我的习惯是用SHOW INDEX FROM table先看已有索引再用EXPLAIN分析SQL的执行计划最后才决定是否新增索引。5.2 事务隔离级别与MVCCInnoDB为什么默认是可重复读事务的四大特性ACID大家都会背真正难的是隔离级别。MySQLInnoDB支持四种隔离级别读未提交、读已提交、可重复读、串行化。其中可重复读是默认级别。面试官常问为什么MySQL不学Oracle把默认级别设为读已提交原因和主从复制有关。在MySQL早期主从复制基于binlog的statement格式记录SQL语句。如果使用读已提交会出现“一个事务中两次SELECT结果不同”的情况导致主从数据库的数据不一致。而可重复读配合MVCC能让同一个事务中的快照保持一致从而避免主从不一致。虽然现在binlog可以用row格式解决这个问题但作为历史原因MySQL默认级别依然是可重复读。MVCC多版本并发控制是可重复读的底层实现。它通过隐藏的版本号字段row trx_id、roll_pointer构建undo log版本链再配合ReadView实现快照读。面试时重点回答可重复读的ReadView是事务第一次执行SELECT时生成的后续所有SELECT都复用这个ReadView而读已提交每次SELECT都会生成新的ReadView。这就是两者“一个能看到别的事务新提交的数据一个看不到”的本质区别。5.3 Redis缓存三大问题与缓存一致性Redis在Java面试中的地位不用多说但大多数人的回答都浮于表面。缓存穿透是指查询一个必然不存在的数据每次都会打到数据库上。解决办法是缓存空值null也缓存或使用布隆过滤器拦截。缓存击穿是指某个热点key在过期的一瞬间大量请求打到了数据库。解决办法是互斥锁用SETNX重试获取锁或逻辑过期key不设真实过期时间在value里存过期时间。缓存雪崩是指大量key在同一时间过期或者Redis宕机导致数据库压力瞬间飙升。解决办法是过期时间加随机值、多级缓存、限流降级。缓存和数据库的一致性是另一个重灾区。最常用的是“Cache Aside Pattern”读的时候先读缓存读不到就读数据库再回写缓存写的时候先更新数据库再删除缓存。但删除缓存失败怎么办业界有几种方案延迟双删更新数据库后等待几百毫秒再删一次缓存、消息队列异步重试、订阅binlogCanal异步删除缓存。我用得比较多的是“先更新数据库然后发消息到MQ消费者再删缓存”虽然引入中间件增加复杂度但能保证最终一致性适合对一致性要求较高的业务。6. 用“面试官视角”刷题一套我自己用的三轮复习法把上面这些知识点完整过一遍之后很多人会陷入一种“什么都看了但又好像什么都没记住”的状态。这很正常因为八股文的本质是信息密度很高的知识点集合单靠“看”根本记不住。我建议你按照下面这套三轮复习法来安排时间亲测有效适合一个月左右的突击周期。6.1 第一轮广撒网扫盲所有高频考点花10天左右把集合、并发、JVM、Spring、MySQL、Redis、网络TCP三次握手、HTTP与HTTPS、操作系统进程线程、死锁这些高频模块全部过一遍。这一轮不要追求“背下来”只需要做到看到题目能快速回忆起“这道题考的是哪个模块的哪个知识点”并且能用自己的话解释一遍核心原理。我习惯用思维导图记录每个知识点分支只写关键词后面复习时看到关键词能还原出完整答案就算过关。6.2 第二轮深挖洞横向对比相似概念第二轮的关键是“横向对比”。比如把HashMap、Hashtable、ConcurrentHashMap放在一张表里对比它们的线程安全性、锁机制、初始容量、扩容规则把synchronized、ReentrantLock、volatile放在一起对比它们的实现方式、优缺点和使用场景把Spring的setter注入循环依赖和构造器注入循环依赖放在一起对比为什么一个能解决一个不能。面试官很喜欢连续追问“你刚才说的这个和XXX有什么区别”如果你只是孤立地背每个知识点很容易在这一环崩掉。6.3 第三轮模拟考限时口答训练思路最后一轮是模拟面试。你可以找别人帮你抽题也可以自己拿手机录下来答给自己听。最重要的是“限时”每道题只给自己3到5分钟时间组织回答。这一轮暴露的问题往往不是“不会”而是“知道但讲不清楚”。口答的关键是结构化先说结论再拆原因最后举例子。举个例子面试官问“HashMap线程安全吗”你可以这样答“不安全。因为多线程下put操作可能导致数据覆盖Java 8前还可能因为头插法扩容导致死循环。所以并发场景建议用ConcurrentHashMap它在Java 8后使用CAS和synchronized保证线程安全。”这个回答既有结论又有原因还有对比逻辑清晰面试官一听就知道你有底子。重要提醒刷八股时一定要动手写代码。JVM参数调优、线程池参数、Spring Bean的作用域这些光靠背是背不出感觉的。我见过太多候选人把线程池参数背得滚瓜烂熟但问他“你项目里线程池核心线程数设置的多少为什么”就卡壳了。八股是骨架项目经验和动手能力才是血肉两者缺一不可。6.4 易错的“陷阱题”清单这些坑我当年都踩过最后分享几个我在面试和实际项目中容易栽跟头的点帮你避坑。第一个是字符串比较。Integer a 127, b 127; a b为true但Integer c 128, d 128; c d为false。原因是Integer在-128到127之间有缓存直接用缓存对象超出范围则new新对象。如果你用比较对象就是在比较引用而不是值。真正应该做的是用equals。第二个是数组转列表。Arrays.asList()返回的是一个长度不可变的内部ArrayList不是java.util.ArrayList所以调用add会抛UnsupportedOperationException。想要真正的可变列表要写成new ArrayList(Arrays.asList(...))。第三个是Java 17的“源发行版”报错。很多人在升级JDK 17后发现编译时报“警告: 源发行版 17 需要目标发行版 17”或者相关报错。这通常是因为项目的maven.compiler.source和maven.compiler.target设置不一致或者IDE中Java版本没切到项目对应的版本。最直接的解决办法是打开Maven的pom.xml加上java.version17/java.version或者在IDE里把Project Structure中的Project SDK和Modules的Language Level都统一到17。第四个是数组越界异常。ArrayIndexOutOfBoundsException是每个Java程序员都遇到过的但它背后考的是数组长度为0时的循环条件。比如for (int i 0; i arr.length; i)当i等于arr.length时就越界了应该写i arr.length。这个错误在真实项目里经常出现尤其是从C转Java的人一定要警惕。7. 关于“立刻收藏”之后的正确打开方式这篇文章我尽量把Java面试核心知识点串成体系来讲但八股文的本质是“地图”不是“目的地”。你收藏了还得真正打开它沿着地图去查漏补缺。我自己的经验是把这份内容当成“索引”而不是“答案”每次面试前快速过一遍章节标题找出自己答不上来的部分然后针对性地去查官方文档、看源码、写Demo验证。我曾经在面试前一周把HashMap的源码从头到尾读了一遍包括resize的详细逻辑结果面试官正好追问到扩容时链表分裂的细节那一刻真的会有一种“这个offer稳了”的感觉。最后再提一个建议文章里我提到了不少“面试官喜欢怎么问”和“怎么答加分”但你一定要把这些答案转化成自己的语言。每个人的表达习惯不一样背得太流畅反而显得刻意。真正好的面试状态是你对每个知识点都能像聊天一样讲出来带着自己的理解和实验数据。这样不管你遇到什么变体问题都能以不变应万变。祝大家在求职路上都能拿到心仪的offer也希望这份Java面试八股能成为你复试路上的一块安稳的垫脚石。
返回列表