Java基础面试高频八股文:从原理到实战的深度总结
Java基础面试八股文几乎是每一个Java岗位候选人绕不过去的一道坎。我写代码这几年也断断续续当过面试官最深的感触是很多人不是没看过八股而是只记住了名词没理解背后的逻辑。面试官稍微换一个角度追问就露怯了。这篇文章把我在真实面试里反复遇到的高频Java基础题梳理成一份超详细总结覆盖面向对象、集合框架、并发编程、JVM内存与垃圾回收、异常反射IO以及不少候选人栽跟头的追问式问题。适合三类人看马上要面Java岗位的候选人、带新人但不知道怎么系统考察的团队老手还有想自查基础有没有漏洞的在职开发。内容会有点密建议拿自己最薄弱的章节开刀别试图一口气全背下来。1. 面向对象与语言基础最容易被钻牛角尖的地方Java面试的开场基本上都从基础语法开始而面向对象这块几乎是必问的。你可能会觉得“封装、继承、多态”谁不会背但真正拉开差距的是你能不能解释清每个特性背后的机制。1.1 封装继承多态别只会背书封装的核心价值是隐藏内部实现对外只暴露稳定接口。面试官真正想听的不是“可以把属性设为private提供getter/setter”而是你能不能说出“高内聚、低耦合”在代码里怎么落地。比如你封装了一个队列工具类内部用数组实现对外只暴露offer和poll方法后面哪怕你把内部改成链表实现外部调用代码完全不用动。这就是封装带来的可维护性比背定义有说服力得多。继承相对简单但要小心这个追问为什么Java不支持多继承我通常会答两个层面一是菱形问题两个父类有同名方法时子类不知道该用哪个二是Java用接口加组合替代多继承接口只抽象方法契约不携带具体状态从设计上规避了歧义。多态是重头戏。运行时多态的实现依赖方法表和动态分派JVM在类加载阶段会把方法符号引用解析成实际入口放到方法表里invokevirtual指令执行时根据对象实际类型到对应的方法表找目标方法。所以Animal a new Dog(); a.sound()最终调的是Dog的sound而不是编译期看到的Animal。这里提一个生活类比帮助记忆同一个遥控器按钮按下去电视、空调、音响的反应完全不同触发哪个功能取决于接收信号的实际设备类型。还有一个高频考点成员变量的隐藏。子类和父类声明同名成员变量时通过父类引用访问到的是父类的变量通过子类引用访问到的是子类的变量这跟多态没有关系。静态方法也是隐藏而非重写所以不要再说“静态方法可以被重写”了。1.2 String、equals与hashCode的连环追问String是Java面试的固定节目几乎没有一次面试不问。String对象为什么不可变底层是final修饰的字符数组JDK9之后改成了byte数组加coder字段为了节省空间。不可变带来的好处要能说出来四个方向字符串常量池可以安全共享、作为HashMap的key时hashCode不会变、天然线程安全、避免恶意篡改。new String(abc)到底创建了几个对象这是经典陷阱题。如果abc这个字面量在常量池中还不存在那么是2个对象一个在常量池一个在堆如果常量池已经有abc那么只会在堆里创建1个对象。这个答案看起来简单但很多人栽在编译期和运行期的区别上字面量进入常量池是类加载到常量池解析阶段new是在运行期执行。equals与的区别、equals和hashCode的约定这两道题常常连在一起问。比较引用地址equals默认也是比较地址但String等类重写后比较内容。重写equals必须重写hashCode原因很简单hashCode决定对象在散列集合里的“桶位置”HashMap、HashSet都靠它定位。如果两个对象equals相等但hashCode不同它们会被分到不同的桶里HashMap里get就会拿不到值还会出现相同逻辑意义的对象重复存入的情况。反过来hashCode相等不代表equals相等因为哈希冲突是正常的。1.3 重载、重写与构造器细节重载是编译期多态要求方法名相同、参数列表不同和返回值、访问修饰符没有任何关系。为什么不能只靠返回类型区分重载JVM方法的签名只包含方法名加参数类型列表不包含返回值编译期根本无法决定该调用哪个版本所以Java的语法规范直接否了这条路。重写的规则容易记混我总结成四句话方法签名要一致、返回类型可以协变、访问权限不能更小、异常范围不能更大。面试时如果只说这三句还差点意思最好补一句被重写的方法不能是final的静态方法只能隐藏不能重写构造器也不能被重写。构造器这块有几个冷门考点值得准备子类构造器如果不显式super()编译器会默认加一个无参super()调用所以父类没有无参构造器时子类必须显式调用构造器可以用this()调用同类的另一个构造器但必须在第一行private构造器不是不能用单例模式就是靠它杜绝外部new。最后还有一个很常见的面试题“父类构造器里调用了一个被子类重写的方法会怎样”答案是会调用到子类的方法因为构造阶段虽然对象还没完全初始化但运行时的类型已经确定这也解释了为什么很多规范建议构造器里不要调用可重写方法。2. 集合框架HashMap几乎是必考必问基础语法聊完话题一定会切到集合。ArrayList、LinkedList、HashMap、ConcurrentHashMap这四个容器基本涵盖了集合模块80%的面试题。2.1 ArrayList与LinkedList不是记住区别就完事ArrayList底层是动态数组LinkedList底层是双向链表。很多人会把区别表背得很顺但面试官一追问就卡。比如ArrayList扩容一次扩大多少JDK8里是原来的1.5倍也就是oldCapacity加上右移一位的结果。为什么1.5倍是一个在空间浪费和频繁拷贝之间相对折中的系数扩容太频繁拷贝开销大扩容太大浪费内存。LinkedList虽然叫“链表”但真实业务里用得其实不多。面试官问你“什么时候用LinkedList”不要上来就说“频繁插入删除用LinkedList”因为插入删除的前提是你已经定位到了对应节点而LinkedList的随机访问是O(n)的。你光get(index)就要从头遍历这个代价往往比ArrayList的数组拷贝还高。实际项目中读多写少就用ArrayList如果确实有头尾操作频繁的场景再考虑LinkedList。顺便把CopyOnWriteArrayList也提一嘴读多写少的并发场景下它靠写时复制实现线程安全。这里还有个常考的坑ArrayList的toArray()返回的是Object[]为什么不能直接强转成String[]因为运行期真正的数组类型是Object[]强转会发生ClassCastException。正确做法是用toArray(new String[0])或者干脆toArray(new String[size])。2.2 HashMap的实现细节与面试追问HashMap是Java面试的压轴题级别每次面试几乎必问而且问法越来越细。先说JDK7和JDK8的差异数据结构从“数组链表”变成“数组链表红黑树”链表插入从头插法改成尾插法hash扰动函数从4次位运算优化成高16位异或低16位。头插法在并发扩容时会出现链表环导致get死循环这就是JDK7 HashMap在多线程下出事的经典原因。JDK8虽然用尾插法解决了死循环但线程安全问题依然存在数据覆盖、size统计错乱一个都跑不了。put流程要能一口气讲完整先对key的hashCode做扰动计算用(n - 1) hash定位桶索引桶为空直接放新节点桶非空就判断桶首节点的key是否相等相等直接覆盖否则看桶首节点是不是TreeNode是就走红黑树插入不是就遍历链表找相同key覆盖找不到就在尾部插入新节点链表长度达到8且数组长度达到64才树化如果数组长度没到64先扩容而不是直接转红黑树插入成功后再检查size是否超过threshold超过就resize。为什么数组容量必须是2的幂两个原因一是可以用(n - 1) hash代替取模运算位运算更快二是扩容时可以通过e.hash oldCap的结果是否为0直接判断节点留在原位置还是移动到“原位置oldCap”不需要重新计算所有节点的索引。指定初始容量不是2的幂也没关系HashMap内部会用tableSizeFor方法算出一个最近的2的幂。为什么链表转红黑树的阈值是8官方注释里给了泊松分布的计算结果在负载因子0.75且hash随机分布的前提下链表长度达到8的概率只有千万分之六基本可以视为不可能事件。所以长度8是一个基于概率模型的权衡点。为什么又要有6的退化阈值如果红黑树化之后又频繁删数据长度降到6左右红黑树相对链表没有明显优势而且红黑树节点比链表节点占内存退化回去更合理。2.3 ConcurrentHashMap并发场景的救星ConcurrentHashMap的发展史就是Java并发编程思路的缩影。JDK7用分段锁把整个map切成若干Segment每个Segment自己加锁锁粒度比HashTable的全表锁细很多。JDK8彻底抛弃了分段锁改用CAS加synchronized只对桶首节点加锁并发度更高而且锁粒度比Segment又细了一个层级。JDK8的put流程要能够复述计算hash定位桶桶为空就CAS插入桶不为空就用synchronized锁住桶首节点再检查是否有fwd节点如果有说明当前正在扩容当前线程就顺便帮忙迁移数据然后按链表或红黑树方式插入最后检查是否要树化或扩容。size()方法在JDK8的实现也和JDK7不同JDK7会先无锁尝试两次失败就锁全表JDK8是baseCount加CounterCell数组累加并发写多的情况下每个线程先往自己的CounterCell里加最后再汇总避免了全表锁。还有一个经典问题ConcurrentHashMap为什么不允许null的key和value因为并发场景下get返回null无法区分是“key不存在”还是“key对应的value本来就是null”如果用containsKey再查一次可能在查和用之间数据已经被其他线程修改造成语义歧义。HashMap允许null因为它本身不是线程安全的不需要承担这种并发语义。3. 并发编程面试里最容易拉开差距的模块并发模块是Java面试的分水岭。只背“synchronized是加锁volatile是可见性”已经远远不够面试官会往底层追。3.1 volatile和synchronized的底层语义先说JMMJava内存模型规定线程操作的是工作内存不是直接操作主内存。线程A修改变量后B线程不一定能立即看到因为B读的可能还是自己工作内存里的旧值。volatile做的事就是通过对变量读写插入内存屏障保证写操作立即刷回主内存读操作直接从主内存拿从而保证可见性。同时它禁止指令重排所以也保证有序性。但它不保证原子性这是最重要的坑点。i这个过程要经历读、加、写三步两个线程同时读同一个i各自加完写回去最后结果会丢更新。所以单靠volatile解决不了计数器这种需要复合操作的场景。synchronized底层依赖Monitor监视器锁JDK6之后有锁升级过程无锁、偏向锁、轻量级锁、重量级锁。偏向锁是同一个线程反复进入同步块时把锁偏向给该线程免去CAS操作轻量级锁靠自旋CAS抢锁适应锁竞争不激烈的情况竞争激烈或者自旋超过阈值就会膨胀成重量级锁阻塞等待线程。面试时一定要把锁升级讲出来如果说“synchronized是重量级锁”面试官会觉得你的知识停留在JDK5。双重检查锁单例为什么需要volatile这里涉及指令重排new Singleton()在字节码层面不是原子操作分配内存、初始化对象、把引用指向内存这三步可能被重排成“先指向内存后初始化对象”。另一个线程判断引用非空就直接返回拿到的可能是一个还没执行构造函数的半成品。volatile禁止了这个重排保证返回的一定是完整对象。3.2 线程池的核心参数与任务执行流程线程池七大参数corePoolSize核心线程数、maximumPoolSize最大线程数、keepAliveTime空闲存活时间、unit时间单位、workQueue任务队列、threadFactory线程工厂、handler拒绝策略。执行流程经常有人答反尤其容易说成“核心线程满了就加线程到最大再放队列”。真实流程是来一个任务先判断核心线程是否已满没满就创建核心线程执行满了就尝试放队列队列也满了才创建非核心线程执行直到达到最大线程数最大线程也满了就触发拒绝策略。这个设计的意图是尽量复用已有线程队列作为缓冲用来削峰而不是盲目地创建线程。四种拒绝策略AbortPolicy默认抛RejectedExecutionExceptionCallerRunsPolicy让提交任务的线程自己执行相当于变相限流DiscardPolicy静默丢弃DiscardOldestPolicy丢弃队列中最旧的任务再尝试提交。我比较喜欢CallerRunsPolicy至少不会无声无息丢任务。为什么规范建议不要用Executors创建线程池newFixedThreadPool用的是无界LinkedBlockingQueue任务堆积太多会内存溢出newCachedThreadPool最大线程数是Integer.MAX_VALUE极端情况下创建海量线程直接资源耗尽。用ThreadPoolExecutor自己传参数逼你思考队列和拒绝策略避免默认坑。线程数设置多少合适CPU密集任务大致是CPU核数1IO密集任务可以到2倍CPU核数甚至更高严谨一点按下述经验公式估算线程数 CPU核数 * (1 等待时间 / 计算时间)。但经验公式只是起点线上最终要压测。3.3 AQS、Lock与CAS的常见追问CAS是Compare And Swap用一条CPU原子指令实现“比较并交换”是乐观锁的底层实现。问题在于ABA线程A把值从1改成2又改回1线程B看到的值还是1以为没人动过。解决思路是用版本号或引用计数AtomicStampedReference正是干这个的。AQS是并发工具的半壁江山核心是volatile修饰的state状态量加双向等待队列。state等于0表示锁未被持有大于0表示重入次数。获取锁失败的线程会被封装成Node加入CLH变体队列前一个节点释放锁时唤醒后一个节点。ReentrantLock、CountDownLatch、Semaphore都是基于AQS实现的只是state的语义不同ReentrantLock的state是锁重入计数CountDownLatch的state是剩余计数Semaphore的state是剩余许可数。ReentrantLock和synchronized的区别是高频题至少要说三点ReentrantLock支持中断响应、可以设置超时时间、可以创建公平锁还支持多个Condition条件队列。synchronized则是语法层面自动释放锁写起来最简单。现在两者性能差距已经很小日常优先用synchronized需要超时或公平才用Lock。4. JVM与内存管理八股的重灾区也是区分度最大的地方JVM这块内容多也是各公司面试的硬骨头。不用怕把内存区域、垃圾回收、类加载三条线串起来就是一套完整的知识环。4.1 JVM内存区域与对象创建过程运行时数据区要分清哪些线程共享堆和方法区共享虚拟机栈、本地方法栈、程序计数器是线程私有。程序计数器是唯一不会OOM的区域虚拟机栈溢出会抛StackOverflowError递归没有终止条件最典型堆溢出抛OutOfMemoryError: Java heap space。堆内再细分为新生代和老年代新生代里又有Eden区和两个Survivor区。对象创建过程常考尤其是“分配内存”这一步在TLAB线程本地分配缓冲上分配TLAB不够再通过指针碰撞或空闲列表最后设置对象头。对象头包含Mark Word和Class PointerMark Word存哈希码、GC年龄、锁状态Class Pointer指向类元数据。32位还是64位JVM影响压缩指针这些细节不会都问到但对象头加实例数据加对齐填充的内存布局最好知道。为什么要说“对象主要分配在堆”而不说“一定分配在堆”因为JIT编译时如果做了逃逸分析确认对象不会逃逸出方法可能会进行栈上分配或标量替换对象就不一定在堆里了。这是一个加分项能说出来代表你真的研究过JVM优化。4.2 垃圾回收算法与常见收集器GC判断对象是否存活主流用的是可达性分析从GC Roots出发不能到达的对象判定为可回收。GC Roots包括栈帧中的局部变量引用的对象、静态变量引用的对象、常量池引用的对象、JNI引用的对象等。特别强调一点静态变量指向的对象算GC Root但被引用的对象内部再持有的字段引用不在GC Roots集合里而是从根出发向下遍历可达即可。三种基础算法要能对比标记-清除会产生内存碎片标记-复制没有碎片但浪费一半空间适合对象存活率低的年轻代标记-整理适合老年代对象存活率高移动成本相对可控。新生代之所以用复制算法可以结合8:1:1的Eden、Survivor分区来讲新对象大多朝生夕灭大部分对象一次Minor GC后就被回收一次复制只处理少部分幸存对象浪费的10%空间换来的是时间效率。主流收集器过一遍Serial单线程但简单高效CMS并发收集注重低停顿但会产生浮动垃圾、并发阶段占用CPU、最终还是会产生碎片G1把堆切成Region按回收价值排序能做到可预测停顿整体是标记-整理加局部复制的思路从JDK9开始成为默认ZGC染色指针加读屏障停顿可以控制在几毫秒以内。面试先答这里再往下深问就有得聊。晋升老年代的条件也要记牢大对象直接进老年代每熬过一次MinorGC年龄加1默认15岁进老年代Survivor里相同年龄对象超过一半取这个年龄作为动态晋升标准Survivor空间放不下时直接进老年代。什么时候触发FullGC老年代空间不足、元空间不足、调用System.gc、CMS并发失败等。4.3 类加载机制与双亲委派模型类加载分五步加载、验证、准备、解析、初始化。加载是把字节流转换成方法区里的Class对象验证做格式和语义校验准备阶段给静态变量分配内存并赋零值解析把符号引用换成直接引用初始化阶段执行静态代码块和静态字段赋值。真正触发初始化有几种主动引用场景new对象、访问静态字段、调用静态方法、反射、初始化子类时先初始化父类、包含main方法的类。双亲委派的过程一句话就能讲清AppClassLoader收到类加载请求先委托给PlatformClassLoaderPlatformClassLoader再委托给BootstrapClassLoader父加载器能加载就返回父加载不了才由自己加载。为什么这么设计两个原因防止同一个类被加载多次保护核心类库不被篡改你自己写个java.lang.String交给AppClassLoader也加载不了因为BootstrapClassLoader会先把它拦下来加载JDK里的String。什么时候打破双亲委派Tomcat的WebappClassLoader会在当前应用的类路径里优先加载因为每个Web应用需要自己隔离的类库版本JDBC驱动则通过线程上下文类加载器来加载因为SPI接口是核心库但实现是第三方提供的双亲委派模式下核心库加载器不认识第三方jar。这些例子记两三个就能证明你不是只会背结论。5. 异常、反射、IO看起来基础坑点不少这部分是很多人复习时容易跳过的但面试里出现频率不低而且特别喜欢在代码题里埋坑。5.1 异常体系与try-with-resources异常体系三个关键字先搞清楚Throwable是顶层下面分Error和Exception。Error是JVM层面的严重错误比如OutOfMemoryError、StackOverflowError不该捕获也不该处理。Exception分受检异常和运行时异常受检异常必须显式处理运行时异常如NullPointerException、ArithmeticException可以处理也可以不处理。实际项目中自定义异常一般建议继承RuntimeException避免调用方被一堆受检异常绑架。finally的使用有几个隐蔽坑。首先finally不是永远执行的System.exit、发生硬件层面中断时不会执行。其次如果在finally里写了return它会吞掉try或catch里的return值和异常。举个例子catch里return了一个结果finally又return了另一个值方法最终返回的是finally里的值这种代码逻辑混乱到几乎没法排查碰到一定要注意。再有catch里return的引用类型对象finally里可以修改这个对象的内容因为返回的是引用指向的对象但基本类型返回值在catch里被存进操作数栈finally里改了也没用。JDK7之后的try-with-resources写起来简洁底层其实会被编译成try-catch-finally自动调用close方法并且自动把关闭时抛出的异常作为被抑制异常附加到主异常上不会覆盖原始异常信息。面试官问你原理的时候能答出“自动close加Suppressed异常”就到位了。5.2 反射机制与动态代理反射让Java具备了运行时查看和操作类的能力。有三种方式拿Class对象Class.forName(完整类名)、类名.class、对象的getClass()方法。第一种会触发类初始化后两种不会这个细节常考。反射为什么慢运行时要做大量类型检查和方法查找本地方法调用也有开销JIT的内联等编译优化在反射场景下很难生效所以频繁反射性能会比较差。Spring框架里缓存反射调用元数据的机制就是为了缓解这个问题。JDK动态代理和CGLIB是要会的。JDK动态代理基于接口核心是Proxy.newProxyInstance加InvocationHandler生成的代理类继承了Proxy所以只能代理接口因为Java单继承不允许再继承其他类。CGLIB基于继承生成目标类的子类所以不能代理final类和final方法。Spring默认对接口用JDK代理对类用CGLIB如果是Spring Boot 2.x后默认强制CGLIB这个变化值得提一句。面试追问里最常见的场景是“为什么事务注解有时不生效”根因往往是方法不是public、方法调用发生在同一个类的内部this调用根本没走代理对象、方法被final修饰导致CGLIB无法生成子类。这套链路想通了反射和代理的知识就串成片了。5.3 IO/NIO 与序列化要点BIO、NIO、AIO的区别是传统考点BIO是同步阻塞每个连接一个线程NIO是同步非阻塞用多路复用器ChannelBuffer一个线程可以管理很多连接AIO是异步非阻塞回调机制实际生产中用得不多Netty实际上是基于NIO的不是AIO。零拷贝可以简单提一下通过sendfile等机制数据在操作系统内核里完成传输不经过用户态拷贝适用于大文件和高性能网络传输场景。序列化这块最常问的是serialVersionUID。它的作用是标识序列化版本类反序列化时如果发现序列化数据里的serialVersionUID和当前类的serialVersionUID不一致就会抛InvalidClassException。所以类结构变了必须显式维护serialVersionUID要么保持不变兼容旧版本要么主动改掉让旧数据失效。transient关键字修饰的字段不参与默认序列化静态字段也不参与序列化。还有一个高级题序列化会不会破坏单例默认情况下反序列化会绕过构造函数直接创建新对象单例就被破坏了。解决方法是定义一个readResolve方法返回已经存在的单例实例ObjectInputStream在读完后会调用它替换反序列化出来的对象。6. 面试追问实战遇到“怎么解决”类问题怎么答很多热词里都有“java面试中问到你遇到什么问题怎么解决”这正是转述经验的好机会。真正的面试不像题库那样单点提问考官会顺着一个场景连环追问考察你的定位和解决思路。6.1 数据一致性的几个典型场景“Java怎么保证数据一致性”这类问题第一反应千万别上来就答分布式事务面试官的意图往往更基础。最好先分层再进细节单机并发层面核心是解决可见性、原子性和线程隔离。volatile保证可见性synchronized和Lock保证原子性ThreadLocal实现线程内的变量隔离Atomic包靠CAS保证compare-and-set的原子性。如果问题面向数据库那就是事务隔离级别加锁加MVCCACID里的“一致性”最终依赖业务代码保证。如果明确谈到分布式再引出XA强一致和最终一致性本地消息表、MQ事务消息、TCC、Saga这些方案各说一句话即可。答题时刻意加一个分层结构既表明你知识体系完整也能避免答非所问。实际面试里这道题至少能聊出三四个分支覆盖得越全越加分。6.2 内存溢出与线程问题的定位思路这题的答法体现工程能力。先说排查工具的使用顺序jps查看进程IDjstat -gcutil观察GC频率jmap导出堆快照MAT分析大对象线程问题用jstack打印栈信息找死锁和长时间占用CPU的线程。命令很固定最好背熟jps -l jstat -gcutil pid 1000 jmap -dump:formatb,fileheap.hprof pid jstack pid我实际遇到过一次Metaspace溢出原因是代码里动态生成类太多类加载卸载没跟上最后通过限制生成数量和合理复用解决。还有一个常见的坑是ThreadLocal使用后没有调用removeThreadLocalMap的key是弱引用value是强引用线程池复用线程时就可能积累强引用链导致内存泄漏。这种场景不要只答名词要把“泄漏路径”讲清楚。线程问题里死锁是最容易在jstack里被抓到的会明确标记deadlock。解决思路是分析锁顺序然后用tryLock加超时避免永久阻塞。这些经验比单纯背诵概念更有说服力。7. 学习方法与面试心态八股文怎么背才不吃亏八股文既不能完全不背也不能只背不理解。怎样让复习效率更高我有几点实际体会。7.1 别死记硬背要会“翻译”八股文背得溜不代表你理解了。一个很有效的办法是把每条概念翻译成生活化的例子。比如HashMap的put过程可以理解成快递柜取件先算号码定位柜格柜格空就直接投递不空就看看柜子里是不是同一个人同一件快递不是就换个方式存放柜子满了就再拉一组柜子过来扩容。这样一翻译哪怕面试紧张忘了原话也能顺着场景把逻辑推出来。背题的时候我习惯按“30秒答案”的标准准备每一道题30秒内先说清楚结论再丢一句原理最后带一个反例或边界。比如“volatile能不能保证原子性”30秒答案就是不能它只保证可见性和有序性i这种读改写复合操作会丢更新需要AtomicInteger或加锁。够短也好记而且面试官点头的概率远高于你顺畅背完一整段课本。7.2 高频面试题的应答框架我一直用四步法组织技术回答适合大部分Java基础题第一步结论先行。如果问你HashMap是不是线程安全的先答“不是”再展开。第二步拆原理讲机制。第三步给边界或反例证明你真的理解。第四步落到项目经验。比如“为什么重写equals必须重写hashCode”结论是违反散列集合的存储约定原理是HashMap定位先算hash再比equals反例是两个业务上相等的对象放到了不同桶里get不到最后补一句“我之前处理过一个查询去重Bug就是对象equals重写但hashCode没重写导致的”。有层次有案例这才是面试官想听到的回答。8. 常见问题与避坑速查表最后整理一份实用的避坑清单我在面试里见过太多人栽在同样的地方列出来给大家提个醒。8.1 候选人高频翻车点下面是几个典型翻车现场和对应的调整建议翻车现象根因调整建议概念背得很顺被追问就卡壳只记结论没理解机制每个点强制自问“为什么”卡住就回补资料说synchronized就是重量级锁知识停留在JDK5之前补完锁升级全过程偏向锁、轻量级锁、重量级锁线程池执行流程说反对参数不理解用一句话记忆线程不够进队列队列不够再加线程加满就拒绝ConcurrentHashMap问null直接乱猜没考虑并发语义歧义记住核心原因get返回null无法区分不存在和值为null重写equals但hashCode没重写对散列原理不敏感回忆HashMap get流程对比两个对象桶位置不同的后果反射和代理分不清大部分是只背了接口手写一遍JDK动态代理Demo再用CGLIB写一遍对比区别8.2 面试回答对比表同一个问题好答案和差答案的区别非常明显。下面两个例子最典型题目差答好答说说HashMap底层是数组加链表JDK8之后是数组加链表加红黑树先答整体结构再讲put定位流程再说扩容时机和2的幂设计补1.7到1.8的变化最后点出并发不安全并引申到ConcurrentHashMapvolatile能保证原子性吗不能不能它只保证可见性和有序性i读改写三步会丢更新并发计数要用AtomicInteger或synchronized异常和Error的区别是什么Error是错误Exception是异常Error是JVM问题Exception是程序问题可以再补一句哪些Error不该捕获、运行时异常和受检异常使用时怎么选这类对比核心就一个八股文的答案宁可慢一点补充完整也不要一句话就交卷。我面过的人里最后拿到Offer的那批都不是背得最熟的而是能把一个知识点讲到“关我项目什么事”的人。Java基础八股文的价值是让你在紧张的真实面试里有肌肉记忆可用。我个人习惯是面试前三天每天两小时把这篇的顺序倒着刷先看避坑表摸清容易翻车的位置再过一遍实战场最后才回到集合和JVM这些大块头。比从第一章慢慢看到最后效果明显很多。最后分享一个小技巧拿手机录音给自己讲题重听一遍你会立刻发现哪些地方讲得不像人话把这些别扭的地方改掉面试状态会稳一个档次。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →