面试官与水货程序员的戏剧性面试之旅场景某互联网大厂Java开发岗位面试室面试官严肃、专业的技术面试官老王求职者自称水货程序员谢飞机简历写得花里胡哨实际水平…第一轮基础入门面试官推了推眼镜面前摆着谢飞机的简历眉头微皱。面试官「谢飞机你好咱们先聊聊基础问题。你在工作中有使用过ArrayList和LinkedList吧」谢飞机端坐身体露出自信的微笑谢飞机「报告面试官这个我会ArrayList底层是数组LinkedList底层是链表数组查询快链表增删快」面试官微微点头面试官「不错那你说说HashMap的put过程」谢飞机摸了摸鼻子谢飞机「这个…先计算hash值然后…然后…那个…找到对应位置如果有值就…就覆盖」面试官「那JDK1.8之后HashMap有什么优化」谢飞机开始冒汗谢飞机「好像…好像加了红黑树当链表长度超过8就转成红黑树」面试官「回答得还可以。那ArrayList在多线程环境下会有什么问题」谢飞机想了想谢飞机「会…会并发修改异常因为ArrayList不是线程安全的」面试官「很好看来基础还行。那我们继续深入。」第二轮进阶挑战面试官换了个坐姿眼神变得犀利起来。面试官「现在聊聊并发编程。你知道synchronized和ReentrantLock的区别吗」谢飞机咽了咽口水谢飞机「synchronized是关键字自动加锁解锁。ReentrantLock是类需要手动lock和unlock还要放在finally里。」面试官「那为什么还需要ReentrantLock」谢飞机开始胡扯谢飞机「因为…因为ReentrantLock可以绑定多个条件变量还可以中断等待还可以尝试获取锁」面试官有些惊讶面试官「哟这些你也知道。那你说说什么是CAS」谢飞机眼睛一亮谢飞机「CAS就是Compare And Swap比较并交换三个参数内存位置、预期值、新值乐观锁」面试官「CAS有什么问题」谢飞机尴尬地笑了笑谢飞机「会…会有ABA问题还有自旋开销大还有只能保证单个变量的原子性…那个…大概就这些」面试官「那聊聊JVM运行时数据区有哪些」谢飞机开始掰手指谢飞机「程序计数器、虚拟机栈、本地方法堆、堆、方法区还有运行时常量池」面试官「对象创建的过程了解吗」谢飞机已经有点懵了谢飞机「就是…就是new一个对象嘛…先检查类加载…然后分配内存…然后设置对象头…最后执行构造函数…」面试官记录着什么然后继续提问。面试官「线程池的核心参数说说看」谢飞机如数家珍谢飞机「核心线程数、最大线程数、存活时间、时间单位、阻塞队列、线程工厂、拒绝策略」面试官「拒绝策略有哪些」谢飞机开始瞎编谢飞机「有DiscardOldestPolicy、AbortPolicy…那个…还有CallerRunsPolicy还有…还有什么Policy來着…」第三轮高级挑战面试官放下笔眼神深邃。面试官「好咱们聊聊微服务。你用过Dubbo吗 dubbo的架构是什么样的」谢飞机额头开始冒汗谢飞机「Dubbo啊…就是那个…那个RPC框架有提供者、消费者、注册中心…还有监控中心」面试官「负载均衡策略有哪些」谢飞机硬着头皮谢飞机「随机、轮询、最少活跃调用、一致性哈希」面试官「了解。那RabbitMQ的消息模型有哪几种」谢飞机开始抓耳挠腮谢飞机「有…有简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式」面试官「消息确认机制呢」谢飞机已经眼神涣散谢飞机「就是…生产者确认、消费者确认…那个…还有事务」面试官摇了摇头继续问。面试官「Redis的数据类型有哪些」谢飞机仿佛抓住了救命稻草谢飞机「String、List、Set、Zset、Hash还有HyperLogLog、Bitmap、Geo」面试官「Redis的持久化机制呢」谢飞机支支吾吾谢飞机「RDB和AOFRDB是定时的AOF是记录命令的…那个…各有利弊…」面试官「MyBatis中#和$的区别」谢飞机灵机一动谢飞机「#会预编译防止SQL注入$是直接拼接有SQL注入风险」面试官点了点头。面试官「最后问你一个DDD领域驱动设计了解吗」谢飞机彻底懵了谢飞机「DDD…就是…就是领域驱动设计有实体、值对象、聚合根、领域服务、仓储…那个…领域事件」面试官合上简历站起身来。面试官「好的谢飞机今天面试就到这里。你先回去等通知吧。」谢飞机站起身握手道别谢飞机「谢谢面试官那…那个…我什么时候能得到通知」面试官「等HR联系你。」谢飞机灰溜溜地走了出去… 技术答案详解第一轮答案1. ArrayList vs LinkedList特性ArrayListLinkedList底层数据结构动态数组双向链表随机访问O(1)支持索引直接访问O(n)需要遍历头部插入/删除O(n)需要移动元素O(1)尾部插入/删除均摊O(1)O(1)内存占用连续内存空间利用率高每个节点需要额外存储指针适用场景频繁随机访问频繁插入删除关键点ArrayList查询快是因为数组在内存中是连续存储的可以通过数组首地址偏移量直接计算元素位置。LinkedList每个节点都有前后指针插入删除只需要改变指针指向。2. HashMap的put过程JDK1.8publicVput(Kkey,Vvalue){returnputVal(hash(key),key,value,false,true);}finalVputVal(inthash,Kkey,Vvalue,booleanonlyIfAbsent,booleanevict){NodeK,V[]tab;NodeK,Vp;intn,i;// 1. 判断数组是否为空空则扩容if((tabtable)null||(ntab.length)0)n(tabresize()).length;// 2. 计算索引位置判断是否为空if((ptab[i(n-1)hash])null)// 3. 直接创建新节点tab[i]newNode(hash,key,value,null);else{NodeK,Ve;Kk;// 4. 如果key已存在替换valueif(p.hashhash((kp.key)key||key.equals(k)))ep;// 5. 如果是红黑树插入树节点elseif(pinstanceofTreeNode)e((TreeNodeK,V)p).putTreeVal(this,tab,hash,key,value);else{// 6. 遍历链表for(intbinCount0;;binCount){if((ep.next)null){p.nextnewNode(hash,key,value,null);// 7. 链表转红黑树if(binCountTREEIFY_THRESHOLD-1)treeifyBin(tab,hash);break;}if(e.hashhash((ke.key)key||key.equals(k)))break;pe;}}if(e!null){VoldValuee.value;if(!onlyIfAbsent||oldValuenull)e.valuevalue;afterNodeAccess(e);returnoldValue;}}modCount;// 8. 超过阈值则扩容if(sizethreshold)resize();afterNodeInsertion(evict);returnnull;}put过程关键点计算key的hash值高16位异或低16位通过(n-1)hash计算数组索引如果数组为空先扩容如果当前位置为空直接创建新节点如果key已存在替换value如果是红黑树调用树的插入方法如果是链表遍历到尾部插入链表长度超过8且数组容量64时转为红黑树插入后检查是否需要扩容3. HashMap在JDK1.8的优化红黑树优化当链表长度8且数组容量64时链表转为红黑树将O(n)查询优化为O(logn)高位运算使用(n-1)hash代替hash%n且采用扰动函数hash高16位异或低16位减少哈希碰撞扩容优化使用e.hash oldCap判断节点新位置不需要重新计算hash插入优化新元素插入链表头部1.7是尾部减少尾部遍历4. ArrayList多线程问题ArrayList线程不安全主要问题并发修改异常多个线程同时add时可能导致elementData数组越界或元素丢失数据不一致size变量和实际数组元素数量不一致解决方案使用Collections.synchronizedList()包装使用CopyOnWriteArrayList使用Vector已过时// 错误示例 - 并发add会导致异常ListStringlistnewArrayList();for(inti0;i1000;i){newThread(()-list.add(test)).start();}// 正确示例ListStringlistnewCopyOnWriteArrayList();// 或ListStringlistCollections.synchronizedList(newArrayList());第二轮答案1. synchronized vs ReentrantLock特性synchronizedReentrantLock类型关键字类获取/释放自动手动必须在finally中释放锁类型非公平锁公平锁/非公平锁可选等待可中断不可lockInterruptibly()可中断条件变量无多个Condition尝试获取锁无tryLock()性能JDK6优化后性能提升稍好但差异不大ReentrantLock示例privatefinalReentrantLocklocknewReentrantLock();publicvoidmethod(){lock.lock();try{// 业务逻辑}finally{lock.unlock();// 必须放在finally}}// 尝试获取锁if(lock.tryLock()){try{// 业务逻辑}finally{lock.unlock();}}else{// 处理获取锁失败}// 条件变量privateConditionconditionlock.newCondition();condition.await();// 等待condition.signal();// 唤醒2. CASCompare And Swap概念CAS是一种无锁算法通过三个参数内存位置V、预期值A、新值B实现原子操作。// 伪代码booleancompareAndSwap(V,A,B){if(VA){VB;returntrue;}returnfalse;}CAS的问题ABA问题线程1读取值A线程2将值改为B再改回A线程1 CAS成功但实际值已被修改解决方案使用版本号AtomicStampedReference自旋开销大CAS失败会循环重试高竞争下CPU开销大解决方案限制自旋次数结合synchronized只能保证单个变量对于多个变量的原子操作无能为力解决方案使用AtomicReference封装多个变量Java中的CAS应用AtomicInteger/AtomicLong/AtomicReferenceConcurrentHashMapLongAdderJDK83. JVM运行时数据区┌─────────────────────────────────────────────────┐ │ JVM 进程 │ ├─────────────────────────────────────────────────┤ │ 程序计数器PC Register │ │ - 记录当前线程执行的字节码行号 │ │ - 线程私有每个线程独立 │ ├─────────────────────────────────────────────────┤ │ 虚拟机栈VM Stack │ │ - 每个线程一个栈 │ │ - 栈帧局部变量表、操作数栈、动态链接、方法返回地址 │ │ - -Xss设置大小 │ ├─────────────────────────────────────────────────┤ │ 本地方法栈Native Method Stack │ │ - 为Native方法服务 │ │ -_hotspot合并到虚拟机栈 │ ├─────────────────────────────────────────────────┤\n│ 堆Heap │ │ - 所有对象实例和数组分配内存 │ │ - 垃圾回收主要区域 │ │ - -Xmx -Xms设置大小 │ │ ┌─────────────┬─────────────┐ │ │ │ 新生代 │ 老年代 │ │ │ │ Eden S0 S1 │ │ │ │ └─────────────┴─────────────┘ │ ├─────────────────────────────────────────────────┤ │ 方法区Method Area │ │ - 类信息、常量、静态变量、JIT编译后的代码 │ │ - -XX:MetaspaceSize设置 │ │ - 运行时常量池 │ └─────────────────────────────────────────────────┘4. 对象创建过程类加载检查检查常量池中是否存在该类的符号引用检查类是否已加载、解析、初始化分配内存从堆中分配对象所需内存空间分配方式指针碰撞规整内存/空闲列表不规整并发问题CAS重试/TLA本地线程分配缓冲初始化零值将分配到的内存空间初始化为零值不包括对象头设置对象头Mark Word哈希码、分代年龄、锁状态标志等类型指针指向类元数据的指针数组长度如果是数组执行init方法执行构造函数初始化对象5. 线程池核心参数publicThreadPoolExecutor(intcorePoolSize,// 核心线程数intmaximumPoolSize,// 最大线程数longkeepAliveTime,// 空闲线程存活时间TimeUnitunit,// 时间单位BlockingQueueRunnableworkQueue,// 阻塞队列ThreadFactorythreadFactory,// 线程工厂RejectedExecutionHandlerhandler// 拒绝策略){// ...}参数详解corePoolSize核心线程数即使空闲也不会被销毁maximumPoolSize最大线程数包含核心线程临时线程keepAliveTime临时线程空闲后的存活时间workQueue任务缓冲队列常用ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueuethreadFactory创建线程的工厂可设置线程名称、守护属性等handler拒绝策略拒绝策略AbortPolicy默认抛出RejectedExecutionExceptionCallerRunsPolicy调用者线程执行任务DiscardOldestPolicy丢弃队列最老的任务DiscardPolicy直接丢弃任务第三轮答案1. Dubbo架构┌─────────────────────────────────────────────────────────┐ │ Dubbo 架构 │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ 服务消费者 │ │ 服务提供者 │ │ │ │ (Consumer) │ │ (Provider) │ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ └────────┬───────────┘ │ │ │ RPC │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ Dubbo 框架层 │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │ │ │ │ 监控中心 │ │ 注册中心 │ │ 集群容错 │ │ │ │ │ │(Monitor)│ │(Registry)│ │ (Cluster) │ │ │ │ │ └─────────┘ └─────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘工作流程Provider启动时向Registry注册自己Consumer启动时向Registry订阅ProviderRegistry返回Provider列表给ConsumerConsumer根据负载均衡选择Provider调用Consumer和Provider建立长连接Monitor监控调用情况2. Dubbo负载均衡策略Random LoadBalance随机按权重随机选择RoundRobin LoadBalance轮询按权重轮询LeastActive LoadBalance最少活跃调用选择活跃数最少的ConsistentHash LoadBalance一致性哈希相同参数的请求发送到同一节点3. RabbitMQ消息模型简单模式生产者→队列→消费者工作队列模式生产者→队列→多个消费者竞争消费发布订阅模式生产者→交换机→队列→消费者fanout路由模式生产者→交换机→队列→消费者direct根据routing key匹配通配符模式生产者→交换机→队列→消费者topic支持*和#通配符4. RabbitMQ消息确认机制生产者确认Publisher Confirm异步确认broker收到消息后回调Publisher Transactions事务模式性能较差消费者确认手动ACK消费者处理完成后手动确认消息才会被删除自动ACK消息投递即确认可能丢失消息// 手动确认示例channel.basicConsume(queueName,false,deliverCallback,cancelCallback);// 确认消息channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);// 拒绝消息channel.basicNack(delivery.getEnvelope().getDeliveryTag(),false,true);// true: 重新入队 false: 丢弃5. Redis数据类型数据类型应用场景命令示例String缓存、计数器、分布式锁SET/GET/INCRList消息队列、列表LPUSH/RPOP/LRANGESet标签、好友、抽奖SADD/SMEMBERS/SINTERZset排行榜、延迟队列ZADD/ZRANGE/ZRANKHash对象存储、购物车HSET/HGET/HGETALLHyperLogLogUV统计PFADD/PFCOUNTBitmap用户签到、布隆过滤器SETBIT/GETBITGeo附近的人、地理位置GEOADD/GEORADIUS6. Redis持久化机制RDBRedis Database定时生成数据快照保存为RDB文件优点恢复快适合备份缺点可能丢失最近数据# 触发方式SAVE# 同步阻塞BGSAVE# 异步AOFAppend Only File记录所有写命令可配置同步策略优点数据完整性高缺点文件较大恢复慢# appendfsync 配置 always # 每条命令都同步 everysec # 每秒同步推荐 no # 由操作系统决定混合持久化Redis 4.0AOF重写时使用RDB格式开头后续增量用AOF格式7. MyBatis # vs $特性#{}${}原理占位符预编译字符串拼接SQL注入安全危险类型处理自动添加引号原样输出使用场景推荐使用动态表名/列名!-- #{} - 推荐 --selectidfindUserparameterTypeintresultTypeUserSELECT * FROM user WHERE id #{id}/select!-- 生成的SQL: SELECT * FROM user WHERE id ? --!-- ${} - 谨慎使用 --selectidfindByColumnresultTypeUserSELECT * FROM user ORDER BY ${columnName}/select!-- 生成的SQL: SELECT * FROM user ORDER BY name --8. SpringBoot自动装配原理核心注解SpringBootApplication SpringBootConfiguration EnableAutoConfiguration ComponentScan自动装配流程EnableAutoConfiguration启用自动装配AutoConfigurationImportSelector实现ImportSelector接口getAutoConfigurationEntry()获取自动配置类loadFactoryNames()从META-INF/spring.factories加载配置spring.boot.enableautoconfiguration排除不需要的自动配置// AutoConfigurationImportSelector核心方法protectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadataautoConfigurationMetadata,AnnotationMetadataannotationMetadata){// 1. 获取启用配置ListStringconfigurationsgetCandidateConfigurations(...);// 2. 去除重复configurationsremoveDuplicates(configurations);// 3. 排除指定的配置SetStringexclusionsgetExclusions(annotationMetadata);configurations.removeAll(exclusions);// 4. 过滤不满足条件的配置configurationsfilter(configurations,autoConfigurationMetadata);// 5. 触发AutoConfigurationImportEventfireAutoConfigurationImportEvents(configurations,exclusions);returnnewAutoConfigurationEntry(configurations,exclusions);}自定义Starter创建autoconfigure模块在META-INF/spring.factories配置AutoConfiguration创建starter模块依赖autoconfigure9. DDD领域驱动设计核心概念实体Entity有唯一标识的对象如User、Order值对象Value Object无唯一标识属性相同的对象可互换如Money、Address聚合根Aggregate Root聚合的根实体负责协调聚合内对象如Order领域服务Domain Service无法归类到实体或值对象的业务逻辑仓储Repository领域对象的持久化抽象领域事件Domain Event领域内发生的事件// 示例订单聚合publicclassOrder{// 聚合根privateOrderIdid;privateListOrderItemitems;// 实体privateMoneytotalAmount;// 值对象publicvoidaddItem(Productproduct,intquantity){// 业务规则校验// ...items.add(newOrderItem(product,quantity));}}// 值对象publicclassMoney{privateBigDecimalamount;privateCurrencycurrency;// 不可变}// 领域服务publicclassOrderService{publicvoidplaceOrder(Orderorder){// 跨聚合的业务逻辑}}后记谢飞机走出公司大楼收到一条短信“您好您投递的Java开发岗位已结束招聘感谢您的参与。” 谢飞机叹了口气“看来又要继续准备了…”
面试官与“水货“程序员的戏剧性面试之旅 - 互联网大厂Java岗
面试官与水货程序员的戏剧性面试之旅场景某互联网大厂Java开发岗位面试室面试官严肃、专业的技术面试官老王求职者自称水货程序员谢飞机简历写得花里胡哨实际水平…第一轮基础入门面试官推了推眼镜面前摆着谢飞机的简历眉头微皱。面试官「谢飞机你好咱们先聊聊基础问题。你在工作中有使用过ArrayList和LinkedList吧」谢飞机端坐身体露出自信的微笑谢飞机「报告面试官这个我会ArrayList底层是数组LinkedList底层是链表数组查询快链表增删快」面试官微微点头面试官「不错那你说说HashMap的put过程」谢飞机摸了摸鼻子谢飞机「这个…先计算hash值然后…然后…那个…找到对应位置如果有值就…就覆盖」面试官「那JDK1.8之后HashMap有什么优化」谢飞机开始冒汗谢飞机「好像…好像加了红黑树当链表长度超过8就转成红黑树」面试官「回答得还可以。那ArrayList在多线程环境下会有什么问题」谢飞机想了想谢飞机「会…会并发修改异常因为ArrayList不是线程安全的」面试官「很好看来基础还行。那我们继续深入。」第二轮进阶挑战面试官换了个坐姿眼神变得犀利起来。面试官「现在聊聊并发编程。你知道synchronized和ReentrantLock的区别吗」谢飞机咽了咽口水谢飞机「synchronized是关键字自动加锁解锁。ReentrantLock是类需要手动lock和unlock还要放在finally里。」面试官「那为什么还需要ReentrantLock」谢飞机开始胡扯谢飞机「因为…因为ReentrantLock可以绑定多个条件变量还可以中断等待还可以尝试获取锁」面试官有些惊讶面试官「哟这些你也知道。那你说说什么是CAS」谢飞机眼睛一亮谢飞机「CAS就是Compare And Swap比较并交换三个参数内存位置、预期值、新值乐观锁」面试官「CAS有什么问题」谢飞机尴尬地笑了笑谢飞机「会…会有ABA问题还有自旋开销大还有只能保证单个变量的原子性…那个…大概就这些」面试官「那聊聊JVM运行时数据区有哪些」谢飞机开始掰手指谢飞机「程序计数器、虚拟机栈、本地方法堆、堆、方法区还有运行时常量池」面试官「对象创建的过程了解吗」谢飞机已经有点懵了谢飞机「就是…就是new一个对象嘛…先检查类加载…然后分配内存…然后设置对象头…最后执行构造函数…」面试官记录着什么然后继续提问。面试官「线程池的核心参数说说看」谢飞机如数家珍谢飞机「核心线程数、最大线程数、存活时间、时间单位、阻塞队列、线程工厂、拒绝策略」面试官「拒绝策略有哪些」谢飞机开始瞎编谢飞机「有DiscardOldestPolicy、AbortPolicy…那个…还有CallerRunsPolicy还有…还有什么Policy來着…」第三轮高级挑战面试官放下笔眼神深邃。面试官「好咱们聊聊微服务。你用过Dubbo吗 dubbo的架构是什么样的」谢飞机额头开始冒汗谢飞机「Dubbo啊…就是那个…那个RPC框架有提供者、消费者、注册中心…还有监控中心」面试官「负载均衡策略有哪些」谢飞机硬着头皮谢飞机「随机、轮询、最少活跃调用、一致性哈希」面试官「了解。那RabbitMQ的消息模型有哪几种」谢飞机开始抓耳挠腮谢飞机「有…有简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式」面试官「消息确认机制呢」谢飞机已经眼神涣散谢飞机「就是…生产者确认、消费者确认…那个…还有事务」面试官摇了摇头继续问。面试官「Redis的数据类型有哪些」谢飞机仿佛抓住了救命稻草谢飞机「String、List、Set、Zset、Hash还有HyperLogLog、Bitmap、Geo」面试官「Redis的持久化机制呢」谢飞机支支吾吾谢飞机「RDB和AOFRDB是定时的AOF是记录命令的…那个…各有利弊…」面试官「MyBatis中#和$的区别」谢飞机灵机一动谢飞机「#会预编译防止SQL注入$是直接拼接有SQL注入风险」面试官点了点头。面试官「最后问你一个DDD领域驱动设计了解吗」谢飞机彻底懵了谢飞机「DDD…就是…就是领域驱动设计有实体、值对象、聚合根、领域服务、仓储…那个…领域事件」面试官合上简历站起身来。面试官「好的谢飞机今天面试就到这里。你先回去等通知吧。」谢飞机站起身握手道别谢飞机「谢谢面试官那…那个…我什么时候能得到通知」面试官「等HR联系你。」谢飞机灰溜溜地走了出去… 技术答案详解第一轮答案1. ArrayList vs LinkedList特性ArrayListLinkedList底层数据结构动态数组双向链表随机访问O(1)支持索引直接访问O(n)需要遍历头部插入/删除O(n)需要移动元素O(1)尾部插入/删除均摊O(1)O(1)内存占用连续内存空间利用率高每个节点需要额外存储指针适用场景频繁随机访问频繁插入删除关键点ArrayList查询快是因为数组在内存中是连续存储的可以通过数组首地址偏移量直接计算元素位置。LinkedList每个节点都有前后指针插入删除只需要改变指针指向。2. HashMap的put过程JDK1.8publicVput(Kkey,Vvalue){returnputVal(hash(key),key,value,false,true);}finalVputVal(inthash,Kkey,Vvalue,booleanonlyIfAbsent,booleanevict){NodeK,V[]tab;NodeK,Vp;intn,i;// 1. 判断数组是否为空空则扩容if((tabtable)null||(ntab.length)0)n(tabresize()).length;// 2. 计算索引位置判断是否为空if((ptab[i(n-1)hash])null)// 3. 直接创建新节点tab[i]newNode(hash,key,value,null);else{NodeK,Ve;Kk;// 4. 如果key已存在替换valueif(p.hashhash((kp.key)key||key.equals(k)))ep;// 5. 如果是红黑树插入树节点elseif(pinstanceofTreeNode)e((TreeNodeK,V)p).putTreeVal(this,tab,hash,key,value);else{// 6. 遍历链表for(intbinCount0;;binCount){if((ep.next)null){p.nextnewNode(hash,key,value,null);// 7. 链表转红黑树if(binCountTREEIFY_THRESHOLD-1)treeifyBin(tab,hash);break;}if(e.hashhash((ke.key)key||key.equals(k)))break;pe;}}if(e!null){VoldValuee.value;if(!onlyIfAbsent||oldValuenull)e.valuevalue;afterNodeAccess(e);returnoldValue;}}modCount;// 8. 超过阈值则扩容if(sizethreshold)resize();afterNodeInsertion(evict);returnnull;}put过程关键点计算key的hash值高16位异或低16位通过(n-1)hash计算数组索引如果数组为空先扩容如果当前位置为空直接创建新节点如果key已存在替换value如果是红黑树调用树的插入方法如果是链表遍历到尾部插入链表长度超过8且数组容量64时转为红黑树插入后检查是否需要扩容3. HashMap在JDK1.8的优化红黑树优化当链表长度8且数组容量64时链表转为红黑树将O(n)查询优化为O(logn)高位运算使用(n-1)hash代替hash%n且采用扰动函数hash高16位异或低16位减少哈希碰撞扩容优化使用e.hash oldCap判断节点新位置不需要重新计算hash插入优化新元素插入链表头部1.7是尾部减少尾部遍历4. ArrayList多线程问题ArrayList线程不安全主要问题并发修改异常多个线程同时add时可能导致elementData数组越界或元素丢失数据不一致size变量和实际数组元素数量不一致解决方案使用Collections.synchronizedList()包装使用CopyOnWriteArrayList使用Vector已过时// 错误示例 - 并发add会导致异常ListStringlistnewArrayList();for(inti0;i1000;i){newThread(()-list.add(test)).start();}// 正确示例ListStringlistnewCopyOnWriteArrayList();// 或ListStringlistCollections.synchronizedList(newArrayList());第二轮答案1. synchronized vs ReentrantLock特性synchronizedReentrantLock类型关键字类获取/释放自动手动必须在finally中释放锁类型非公平锁公平锁/非公平锁可选等待可中断不可lockInterruptibly()可中断条件变量无多个Condition尝试获取锁无tryLock()性能JDK6优化后性能提升稍好但差异不大ReentrantLock示例privatefinalReentrantLocklocknewReentrantLock();publicvoidmethod(){lock.lock();try{// 业务逻辑}finally{lock.unlock();// 必须放在finally}}// 尝试获取锁if(lock.tryLock()){try{// 业务逻辑}finally{lock.unlock();}}else{// 处理获取锁失败}// 条件变量privateConditionconditionlock.newCondition();condition.await();// 等待condition.signal();// 唤醒2. CASCompare And Swap概念CAS是一种无锁算法通过三个参数内存位置V、预期值A、新值B实现原子操作。// 伪代码booleancompareAndSwap(V,A,B){if(VA){VB;returntrue;}returnfalse;}CAS的问题ABA问题线程1读取值A线程2将值改为B再改回A线程1 CAS成功但实际值已被修改解决方案使用版本号AtomicStampedReference自旋开销大CAS失败会循环重试高竞争下CPU开销大解决方案限制自旋次数结合synchronized只能保证单个变量对于多个变量的原子操作无能为力解决方案使用AtomicReference封装多个变量Java中的CAS应用AtomicInteger/AtomicLong/AtomicReferenceConcurrentHashMapLongAdderJDK83. JVM运行时数据区┌─────────────────────────────────────────────────┐ │ JVM 进程 │ ├─────────────────────────────────────────────────┤ │ 程序计数器PC Register │ │ - 记录当前线程执行的字节码行号 │ │ - 线程私有每个线程独立 │ ├─────────────────────────────────────────────────┤ │ 虚拟机栈VM Stack │ │ - 每个线程一个栈 │ │ - 栈帧局部变量表、操作数栈、动态链接、方法返回地址 │ │ - -Xss设置大小 │ ├─────────────────────────────────────────────────┤ │ 本地方法栈Native Method Stack │ │ - 为Native方法服务 │ │ -_hotspot合并到虚拟机栈 │ ├─────────────────────────────────────────────────┤\n│ 堆Heap │ │ - 所有对象实例和数组分配内存 │ │ - 垃圾回收主要区域 │ │ - -Xmx -Xms设置大小 │ │ ┌─────────────┬─────────────┐ │ │ │ 新生代 │ 老年代 │ │ │ │ Eden S0 S1 │ │ │ │ └─────────────┴─────────────┘ │ ├─────────────────────────────────────────────────┤ │ 方法区Method Area │ │ - 类信息、常量、静态变量、JIT编译后的代码 │ │ - -XX:MetaspaceSize设置 │ │ - 运行时常量池 │ └─────────────────────────────────────────────────┘4. 对象创建过程类加载检查检查常量池中是否存在该类的符号引用检查类是否已加载、解析、初始化分配内存从堆中分配对象所需内存空间分配方式指针碰撞规整内存/空闲列表不规整并发问题CAS重试/TLA本地线程分配缓冲初始化零值将分配到的内存空间初始化为零值不包括对象头设置对象头Mark Word哈希码、分代年龄、锁状态标志等类型指针指向类元数据的指针数组长度如果是数组执行init方法执行构造函数初始化对象5. 线程池核心参数publicThreadPoolExecutor(intcorePoolSize,// 核心线程数intmaximumPoolSize,// 最大线程数longkeepAliveTime,// 空闲线程存活时间TimeUnitunit,// 时间单位BlockingQueueRunnableworkQueue,// 阻塞队列ThreadFactorythreadFactory,// 线程工厂RejectedExecutionHandlerhandler// 拒绝策略){// ...}参数详解corePoolSize核心线程数即使空闲也不会被销毁maximumPoolSize最大线程数包含核心线程临时线程keepAliveTime临时线程空闲后的存活时间workQueue任务缓冲队列常用ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueuethreadFactory创建线程的工厂可设置线程名称、守护属性等handler拒绝策略拒绝策略AbortPolicy默认抛出RejectedExecutionExceptionCallerRunsPolicy调用者线程执行任务DiscardOldestPolicy丢弃队列最老的任务DiscardPolicy直接丢弃任务第三轮答案1. Dubbo架构┌─────────────────────────────────────────────────────────┐ │ Dubbo 架构 │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ 服务消费者 │ │ 服务提供者 │ │ │ │ (Consumer) │ │ (Provider) │ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ └────────┬───────────┘ │ │ │ RPC │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ Dubbo 框架层 │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │ │ │ │ 监控中心 │ │ 注册中心 │ │ 集群容错 │ │ │ │ │ │(Monitor)│ │(Registry)│ │ (Cluster) │ │ │ │ │ └─────────┘ └─────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘工作流程Provider启动时向Registry注册自己Consumer启动时向Registry订阅ProviderRegistry返回Provider列表给ConsumerConsumer根据负载均衡选择Provider调用Consumer和Provider建立长连接Monitor监控调用情况2. Dubbo负载均衡策略Random LoadBalance随机按权重随机选择RoundRobin LoadBalance轮询按权重轮询LeastActive LoadBalance最少活跃调用选择活跃数最少的ConsistentHash LoadBalance一致性哈希相同参数的请求发送到同一节点3. RabbitMQ消息模型简单模式生产者→队列→消费者工作队列模式生产者→队列→多个消费者竞争消费发布订阅模式生产者→交换机→队列→消费者fanout路由模式生产者→交换机→队列→消费者direct根据routing key匹配通配符模式生产者→交换机→队列→消费者topic支持*和#通配符4. RabbitMQ消息确认机制生产者确认Publisher Confirm异步确认broker收到消息后回调Publisher Transactions事务模式性能较差消费者确认手动ACK消费者处理完成后手动确认消息才会被删除自动ACK消息投递即确认可能丢失消息// 手动确认示例channel.basicConsume(queueName,false,deliverCallback,cancelCallback);// 确认消息channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);// 拒绝消息channel.basicNack(delivery.getEnvelope().getDeliveryTag(),false,true);// true: 重新入队 false: 丢弃5. Redis数据类型数据类型应用场景命令示例String缓存、计数器、分布式锁SET/GET/INCRList消息队列、列表LPUSH/RPOP/LRANGESet标签、好友、抽奖SADD/SMEMBERS/SINTERZset排行榜、延迟队列ZADD/ZRANGE/ZRANKHash对象存储、购物车HSET/HGET/HGETALLHyperLogLogUV统计PFADD/PFCOUNTBitmap用户签到、布隆过滤器SETBIT/GETBITGeo附近的人、地理位置GEOADD/GEORADIUS6. Redis持久化机制RDBRedis Database定时生成数据快照保存为RDB文件优点恢复快适合备份缺点可能丢失最近数据# 触发方式SAVE# 同步阻塞BGSAVE# 异步AOFAppend Only File记录所有写命令可配置同步策略优点数据完整性高缺点文件较大恢复慢# appendfsync 配置 always # 每条命令都同步 everysec # 每秒同步推荐 no # 由操作系统决定混合持久化Redis 4.0AOF重写时使用RDB格式开头后续增量用AOF格式7. MyBatis # vs $特性#{}${}原理占位符预编译字符串拼接SQL注入安全危险类型处理自动添加引号原样输出使用场景推荐使用动态表名/列名!-- #{} - 推荐 --selectidfindUserparameterTypeintresultTypeUserSELECT * FROM user WHERE id #{id}/select!-- 生成的SQL: SELECT * FROM user WHERE id ? --!-- ${} - 谨慎使用 --selectidfindByColumnresultTypeUserSELECT * FROM user ORDER BY ${columnName}/select!-- 生成的SQL: SELECT * FROM user ORDER BY name --8. SpringBoot自动装配原理核心注解SpringBootApplication SpringBootConfiguration EnableAutoConfiguration ComponentScan自动装配流程EnableAutoConfiguration启用自动装配AutoConfigurationImportSelector实现ImportSelector接口getAutoConfigurationEntry()获取自动配置类loadFactoryNames()从META-INF/spring.factories加载配置spring.boot.enableautoconfiguration排除不需要的自动配置// AutoConfigurationImportSelector核心方法protectedAutoConfigurationEntrygetAutoConfigurationEntry(AutoConfigurationMetadataautoConfigurationMetadata,AnnotationMetadataannotationMetadata){// 1. 获取启用配置ListStringconfigurationsgetCandidateConfigurations(...);// 2. 去除重复configurationsremoveDuplicates(configurations);// 3. 排除指定的配置SetStringexclusionsgetExclusions(annotationMetadata);configurations.removeAll(exclusions);// 4. 过滤不满足条件的配置configurationsfilter(configurations,autoConfigurationMetadata);// 5. 触发AutoConfigurationImportEventfireAutoConfigurationImportEvents(configurations,exclusions);returnnewAutoConfigurationEntry(configurations,exclusions);}自定义Starter创建autoconfigure模块在META-INF/spring.factories配置AutoConfiguration创建starter模块依赖autoconfigure9. DDD领域驱动设计核心概念实体Entity有唯一标识的对象如User、Order值对象Value Object无唯一标识属性相同的对象可互换如Money、Address聚合根Aggregate Root聚合的根实体负责协调聚合内对象如Order领域服务Domain Service无法归类到实体或值对象的业务逻辑仓储Repository领域对象的持久化抽象领域事件Domain Event领域内发生的事件// 示例订单聚合publicclassOrder{// 聚合根privateOrderIdid;privateListOrderItemitems;// 实体privateMoneytotalAmount;// 值对象publicvoidaddItem(Productproduct,intquantity){// 业务规则校验// ...items.add(newOrderItem(product,quantity));}}// 值对象publicclassMoney{privateBigDecimalamount;privateCurrencycurrency;// 不可变}// 领域服务publicclassOrderService{publicvoidplaceOrder(Orderorder){// 跨聚合的业务逻辑}}后记谢飞机走出公司大楼收到一条短信“您好您投递的Java开发岗位已结束招聘感谢您的参与。” 谢飞机叹了口气“看来又要继续准备了…”