引言记录自己5月份以来的面试真题以及感受会持续更新开科唯识面试感受总体面试情况是预定的30分钟聊满还给了反问环节。目前结果不知道大概率凉。说实话面完出来我人是懵的。去之前我其实心里有底觉得自己两年半经验项目也能讲面个普通开发岗应该问题不大。结果坐下来之后节奏完全不对。他问的第一个项目问题就直接往死里挖——为什么迁MongoDB三读一写怎么定的批次为什么是2万不是1万队列积压了多少还好我早有准备用精准的压测数据应对了。然后开始问基础。基本数据类型我竟然漏了byte包装类场景我也没答好double底层精度问题我只说了一半。这些问题我平时觉得自己知道但他一问“从底层讲”我就卡住了。还有自定义异常我竟然完全忘了怎么说——这东西我写过一百遍但被盯着问的时候大脑一片空白。最让我难受的是他问的很多方向是我之前模拟面试从来没遇到过的。比如为什么只用时间戳不能做防重复而是要随机字符串我模拟的时候那个AI从来不问这种问题。还有验签他追问了一句“是不是在拦截器做的”我当时就意识到他是在考我架构分层而我只能在接口层手动调答得特别虚。面到一半的时候我已经有点慌了因为每一次追问都像在揭开我的盲区。但我咬着牙没崩能答的就答答不上的就说没涉及到。最后竟然撑满了30分钟没有提前结束。我现在回过味来了——我不是不行是这面试本身就不是普通难度。他问的那些东西已经不是“知不知道”的问题了而是要你“经历过、踩过坑、有自己设计思路”才能答好。但说实话这次面的值。它把我所有的盲区一次性全炸出来了包装类、自定义异常、多线程完整实现、容器切换、double底层、防重放攻击……这些东西要是不面这次我可能再过半年也不会主动去补。痛是真痛值也是真值。接下来我知道该干什么了——对着这些错题一个一个啃下来。下次再遇到这种难度的我得让他知道我已经不是今天这个我了。八股统计含错题修改1、基本数据类型有哪些回答漏掉了一个byte。Byte short char int float double long boolean2、那为什么还需要包装类型没太答上回答了泛型场景必须使用对象类型并举例成员变量用基本类型而方法参数多用包装类。标准简单来说包装类型让基本数据类型能融入Java的面向对象体系并提供了基本类型不具备的实用功能。核心原因主要有四点容器需要对象像 ArrayList、HashMap 这样的集合类只能存储对象无法存储 int、double 等基本类型。包装类型解决了这个问题。处理“空值”基本类型如 int总有默认值无法表达“缺失”状态。而包装类型如 Integer可以是 null这在数据库字段可选或查找不到结果时非常有用。提供实用方法包装类型内置了类型转换、进制转换、比较等常用方法操作更方便。例如Integer.parseInt(123)字符串转整数。Integer.toHexString(255)转为十六进制字符串“ff”。支持泛型泛型中的类型参数必须是类不能是基本类型。比如定义 ListInteger 是合法的但 Listint 会编译报错。3、包装类和基本数据类型使用场景大概是哪些没太答上就回答了泛型和接口参数是包装类常量、静态变量是基本类型标准包装类场景1、集合类。集合只能存放对象。2、泛型参数泛型的类型参数不支持基本类型。3、表示“空值”当需要明确区分变量有值和没值时只能用包装类。4、需要调用对象方法例如Integer.parseInt()、String.valueOf()。基本类型场景1、局部变量和方法内的简单计算无对象头开销。2、大型循环和数值数组基本类型存储在栈上或专门的连续内存中能极大提升性能。3、类的成员变量有默认值需求用基本类型更方便。4、高频方法调用涉及大量数值计算用基本类型避免频繁装箱拆箱。4、double处理金额的隐患问题从底层角度回答没太答对它会有精度丢失也就是double在处理数据如果超出显示范围会将转换的二进制数从中截断导致精度丢失。标准第一精度问题。double 采用二进制浮点数存储像 0.1 这样的小数在二进制中无限循环而 double的位数有上限只能截断存近似值。计算时的误差会累积导致 0.1 0.2 不等于 0.3在金额计算中无法接受。第二舍入问题。double无法精确控制舍入行为例如1.015 这样的数字实际存的近似值四舍五入直接变1.02。第三有业务风险。金额误差看似微小但在高频交易、账务核算中会累积成实际损失且跨系统对账时会因为精度不一致导致失败。正确的做法是日常开发使用 BigDecimal且必须用String参数的构造器高性能或数据库存储以分为单位用 long 存储避免小数运算数据库字段使用 DECIMAL 类型不要用 float 或 double5、Java中String为什么不可变回答漏了一个。类通过final修饰禁止继承避免子类新增方法破坏不可变性。底层的char数组用final修饰避免它的引用被更改。补充string类本身不提供任何修改字符串方法所有方法例如substringreplace都会创建并返回新字符串。6、多线程实现方式。回答漏不少并被打断继承thread类重写run方法、实现runnable接口实现run方法。后面卡壳被打断。标准继承 Thread 类并重写 run 方法实现 Runnable 接口并重写 run 方法实现 Callable 接口并重写 call 方法配合 FutureTask 获取返回值使用线程池ExecutorService7、讲一下对死锁的理解。系统阐述了死锁四大条件互斥、不剥夺、请求保持和循环等待并强调避免死锁需从后两个条件入手。标准死锁四个必要条件互斥条件资源一次只能被一个线程占用请求和保持线程持有资源的同时还在等待其他资源不可剥夺资源不能被强行抢占循环等待多个线程形成等待环路。要预防死锁可以破坏其中一个条件比如破坏请求和保持就是一次申请所有资源、破坏不剥夺就是一段时间无法获取锁就释放手里其他锁、破坏循环等待就是规定所有线程按相同顺序申请资源。但是互斥条件不可破坏。8、多线程中start和run方法的区别run方法存放线程具体逻辑start方法触发线程就绪状态也就是通知该线程就绪进入Runable状态、准备进入线程的调度。标准start() 用于启动一个新线程调用后线程进入就绪态JVM会选择时机调用该线程的 run() 方法而 run() 只是一个普通方法在当前调用线程中同步执行不会创建新线程。如果直接调 run()就失去了异步执行的意义。另外start() 只能调用一次重复调用会抛异常。9、ArrayList和LinkedList的底层在增加数据有什么不同ArrayList底层是动态数组插入操作如果没有超过数组扩容阈值就直接插否则需要扩容尾部增删高效但中间插入要移动后续所有数据效率低LinkedList底层是链表头尾操作快但中间增删需要遍历增删慢。10、jdk8的新特性你了解哪些第一个是接口中default方法的引入之前接口如果加新方法那么如果版本更新老系统所有实现该接口的地方都要实现而引入default提供一种默认实现让其他地方可以按需实现接口。第二个是新增的static方法特性之前写例如排序的工具方法需要额外创建工具类java8就可以直接在接口写通用工具方法并通过接口名.方法名直接调用。第三个是JDK7和JDK8的HashMap优化JDK7底层是数组加链表哈希冲突采用头插法扩容顺序会反转并发扩容死循环。Jdk8底层是数组链表红黑树链表长度8和数组长度64会触发扩容链表转红黑树链表查找效率从n变成logn链表长度6红黑树转回链表。哈希冲突使用尾插法避免死循环。标准1、Lambda 表达式简化匿名内部类支持函数式编程语法变成(参数) - { 方法体 }。2、Stream API对集合进行链式的流式操作支持并行处理。写法类似于list.stream().filter().map().collect(Collectors.toList());3、函数式接口只有一个抽象方法的接口配合Lambda使用如 Predicate、Consumer。4、方法引用Lambda 的简化写法双冒号:: 让代码更简洁。5、Optional类用于优雅处理 null避免空指针。6、新的日期时间 APIjava.time线程安全。11、自定义异常类是怎么做的没答上来想了半天忘记了。标准自定义异常类需要继承 Exception受检异常或 RuntimeException非受检异常然后提供多个构造方法通常包括无参构造、带 message 参数的构造、带 message 和 cause 参数的构造。如果需要传递更多信息可以添加自定义字段如错误码。命名上以 Exception 结尾语义清晰12、SpringBoot默认集成的Web容器Tomcat13、怎么修改集成的容器把 Spring Boot 默认的 Tomcat 换成其他容器例如jetty只需要在 pom.xml 中排除tomcat依赖然后引入 spring-boot-starter-jetty即可。Spring Boot 的自动配置会根据 classpath 中存在的容器类型自动切换没答上来这个我确实还没了解到14、Redis数据库一致性的保障措施详细解释了三种方案先更新数据库再删除缓存会存在短暂脏数据问题也就是事务提交前读取的数据会被写入缓存只能等它自己超时删除延迟双删就是更新数据库前先删除一次删除完后通过异步二次清理解决确保事务提交之后清理脏数据第三种利用binlog日志监听实现事务提交后精准删除。(canal中间件漏了)