Spots

浩鲸科技 秋招一面_牛客网

String 字面量直接赋值和直接 new 创建对象有什么区别? StringBuilder 跟 StringBuffer 有什么区别? Java 序列化里面的 serialVersionUID(序列化版本 ID)是什么?它的作用是什么? MySQL 有哪些隔离级别,默认的隔离级别是什么? MySQL 的 redo log、undo log、binlog 分别有什么作用? 用过 Redis 实现分布式锁吗?或者说有了解过吗? @SpringBootApplication 组合了哪些注解?这些注解各起什么作用? BeanPostProcessor 这个扩展点有了解过吗? Spring Boot 支持哪些外部化配置?它们的优先级大致是什么样的? 【追问】它有一个 Advisor(增强器)机制,有了解过吗? 【追问】简历中两段经历都有用 AI Agent,用的框架应该是 Spring AI 吧? 平时用 AI 写代码的场景多吗?使用度到了什么程度? 拿到一个需求用 AI 生成代码后,接下来会怎么处理? 简历提到用 Redis 实现令牌桶限流,简单说一下令牌桶算法的实现原理 简历提到自研 SDK 通过 @ReachCapability 注解声明式注册了 50 多个 Agent 业务能力,讲一下注解扫描、注册及调度总体的流程 智能商家点评平台里的秒杀是先 Redis 预减库存、再 MQ 异步下单,对吧? 【追问】如果有一部分订单下单失败了,库存怎么返还?(因为已经预减了) 【追问】秒杀发生时间很短,如果半小时才检查一次,下单失败但库存没返还,会造成什么问题? 面试官:String字面量直接赋值和直接new创建对象有什么区别? 字面量直接赋值的话,它会用那个字符串常量池中的。然后 new 的话,你每 new 一个对象,它的地址都是不同的。嗯,就是我们使用 equals 去比较它里面的内容是否一致。 字面量直接赋值的话,它会用那个字符串常量池中的。然后 new 的话,你每 new 一个对象,它的地址都是不同的。嗯,就是我们使用 equals 去比较它里面的内容是否一致。

优化后的回答:"String字面量和new创建有三个核心区别。 第一,内存位置不同。字面量存放在字符串常量池,new创建存放在堆内存,常量池中也会有一份副本。…

优化后的回答:"String字面量和new创建有三个核心区别。 第一,内存位置不同。字面量存放在字符串常量池,new创建存放在堆内存,常量池中也会有一份副本。 第二,对象数量不同。字面量如果常量池已存在则直接引用,不创建新对象。new每次都创建新对象,如果常量池不存在还会在常量池创建一份。 第三,比较结果不同。相同内容的字面量比较结果为true,因为指向同一常量池引用。字面量和new创建的对象比较结果为false,因为内存位置不同。使用equals方法比较内容则都为true。 底层原理方面,JDK 6字符串常量池在方法区,JDK 7及以后移到堆内存,避免永久代OOM。" intern() 方法:手动把字符串放入常量池,返回常量池引用。 编译期优化:"a" + "b" 会直接优化成 "ab",但 s1 + s2 不会。 StringBuilder vs StringBuffer 面试官:StringBuilder跟StringBuffer有什么区别? StringBuilder 它是线程不安全的,然后 StringBuffer 它是线程安全的 StringBuilder 它是线程不安全的,然后 StringBuffer 它是线程安全的 优化后的回答:"StringBuilder和StringBuffer有四个核心区别。 第一,线程安全。StringBuilder线程不安全,StringBuffer线程安全,使用synchronized修饰方法。 第二,性能。StringBuilder性能高,StringBuffer性能低,因为有锁开销。 第三,出现版本。StringBuilder是JDK 1.5新增,StringBuffer是JDK 1.0就有。 第四,使用场景。局部变量、单线程环境优先StringBuilder。共享变量、多线程环境必须用StringBuffer。 性能对比方面,单线程环境StringBuilder快15%到20%。多线程环境StringBuilder会出现线程安全问题,StringBuffer能保证正确性。

使用建议:方法内的字符串拼接用StringBuilder,类的成员变量且会被多线程访问用StringBuffer。"

使用建议:方法内的字符串拼接用StringBuilder,类的成员变量且会被多线程访问用StringBuffer。"

先是计算哈希值,然后定位桶,就是嗯先计算它的哈希值与上这个数组长度减 1,然后找到桶的位置,也就是在数组中的位置。如果该数组的位置为空的话,那直接插入。如果不为…

先是计算哈希值,然后定位桶,就是嗯先计算它的哈希值与上这个数组长度减 1,然后找到桶的位置,也就是在数组中的位置。如果该数组的位置为空的话,那直接插入。如果不为空的话,遍历旗下的链表或者红黑树,呃如果已存在该 key 值,那么就覆盖,然后如果不存在的话就进行插入。嗯,如果插入之后,它的数组总长度大于等于 64,且链表长度大于等于 8 的时候,会把这个链表转换成红黑树。哦我回答完了。

先是计算哈希值,然后定位桶,就是嗯先计算它的哈希值与上这个数组长度减 1,然后找到桶的位置,也就是在数组中的位置。如果该数组的位置为空的话,那直接插入。如果不为…

先是计算哈希值,然后定位桶,就是嗯先计算它的哈希值与上这个数组长度减 1,然后找到桶的位置,也就是在数组中的位置。如果该数组的位置为空的话,那直接插入。如果不为空的话,遍历旗下的链表或者红黑树,呃如果已存在该 key 值,那么就覆盖,然后如果不存在的话就进行插入。嗯,如果插入之后,它的数组总长度大于等于 64,且链表长度大于等于 8 的时候,会把这个链表转换成红黑树。哦我回答完了。 优化后的回答:"HashMap的put过程分为七步。 第一步,计算哈希值。调用key的hashCode方法,然后高16位异或低16位,减少哈希冲突。 第二步,定位桶位置。用哈希值与数组长度减1做与运算,得到数组下标,等同于取模运算但更快。 第四步,处理非空桶。遍历链表或红黑树,如果key已存在则覆盖value,如果不存在则插入新节点。 第五步,链表转红黑树。如果链表长度达到8且数组长度达到64,将链表转为红黑树。如果数组长度不足64则先扩容。 第六步,检查扩容。如果元素数量超过阈值,触发扩容,容量翻倍,重新哈希。 第七步,返回结果。如果是覆盖返回旧value,如果是插入返回null。" 优化后的回答:"HashMap扩容有两个触发条件。 第一个条件,元素数量超过阈值。阈值等于容量乘以负载因子,默认是16乘以0.75等于12。当元素数量超过12时触发扩容。 第二个条件,链表过长但数组容量不足。当链表长度达到8时,如果数组容量小于64,优先扩容而不是转红黑树。如果数组容量达到64,才将链表转为红黑树。 为什么链表过长也要扩容?因为数组容量小,哈希冲突概率高。扩容后重新哈希,可以分散链表,避免过早转树节省内存。 扩容过程:容量翻倍,阈值翻倍,创建新数组,数据迁移。迁移时元素要么在原位置,要么在原位置加原容量,无需重新计算哈希。" 这是一个基于泊松分布的数据,就是如果它是一个嗯负载因子,如果过小的话可能频繁触发扩容,然后过大的话可能嗯容易造成过多的哈希冲突。 这是一个基于泊松分布的数据,就是如果它是一个嗯负载因子,如果过小的话可能频繁触发扩容,然后过大的话可能嗯容易造成过多的哈希冲突。

优化后的回答:"负载因子0.75是空间和时间的平衡点。 负载因子过小如0.5,空间利用率只有50%,浪费空间,扩容频繁性能下降。…

优化后的回答:"负载因子0.75是空间和时间的平衡点。 负载因子过小如0.5,空间利用率只有50%,浪费空间,扩容频繁性能下降。 负载因子过大如1.0,空间利用率达到100%,但哈希冲突增加,查询效率下降。 数学依据方面,根据泊松分布计算,负载因子0.75时链表长度达到8的概率小于千万分之一,意味着绝大多数桶的链表长度不超过4。 实验数据显示,负载因子0.75时空间利用率75%,平均链表长度0.5,查询时间复杂度O(1)。负载因子1.0时空间利用率100%,但查询可能退化。 历史原因,0.75是早期Java版本的经验值,经过大量实践验证性能最优。" 嗯先使用链表,然后再使用红黑树,是因为如果一开始使用红黑树的话,它的那个嗯插入和删除就要进行那个红黑树的那个插入删除它的操作复呃过程比较复杂,然后先使用链表的话,它就可以直接嗯进行插入删除。 嗯先使用链表,然后再使用红黑树,是因为如果一开始使用红黑树的话,它的那个嗯插入和删除就要进行那个红黑树的那个插入删除它的操作复呃过程比较复杂,然后先使用链表的话,它就可以直接嗯进行插入删除。 第一,空间开销。链表节点只需存储next指针,红黑树节点需要存储left、right、parent指针和颜色标记,空间开销大2到3倍。 第二,时间开销。数据量小时链表遍历很快,红黑树需要维护平衡,涉及旋转和变色操作,开销更大。当链表长度小于8时,链表的插入和删除更快。 第三,实际场景。根据泊松分布计算,链表长度达到8的概率小于千万分之一,绝大多数桶的链表长度不超过4。为极少数场景增加所有节点的开销不划算。 第四,设计哲学。先用简单结构,需要时再升级,避免过早优化。 为什么阈值是8?因为概率极低,实际中几乎不会发生。一旦发生,说明哈希函数设计有问题或遭遇攻击,此时转树是正确的优化方向。" 面试官:PECS知道是什么吗?就泛型,他处理泛型了。 优化后的回答:"PECS是泛型通配符的使用原则,全称是Producer Extends, Consumer Super。 如果泛型容器是生产者(只读),用 ?

extends T。 如果泛型容器是消费者(只写),用 ?

extends T。 如果泛型容器是消费者(只写),用 ? super T。 核心原则:如果需要从集合中读取数据,使用问号extends。如果需要往集合中写入数据,使用问号super。 Producer Extends用于读取。例如List问号extends Number可以读取,保证是Number或其子类,但不能写入,因为不知道具体类型。 Consumer Super用于写入。例如List问号super Integer可以写入Integer或其子类,但读取只能得到Object,不能获取具体类型。 实际应用,Collections的copy方法,第一个参数dest是消费者使用问号super,第二个参数src是生产者使用问号extends。 记忆口诀:频繁往外读取用extends,频繁往里插入用super,既要读又要写不用通配符。" 嗯发型擦除的话在编译期就会嗯擦除他的那个泛型信息,然后他就会让他通过那个类型检查。 嗯发型擦除的话在编译期就会嗯擦除他的那个泛型信息,然后他就会让他通过那个类型检查。 优化后的回答:"泛型擦除是Java泛型的实现机制。 定义:编译后泛型类型信息被擦除,替换为限定类型或Object。 擦除规则:无界限泛型擦除为Object,有界限泛型擦除为界限类型。例如T擦除为Object,T extends Number擦除为Number。 为什么需要擦除?为了兼容性,Java 1.4及之前的代码没有泛型,擦除机制让泛型代码可以在老版本JVM上运行,迁移成本低。 擦除的限制:不能用泛型创建数组、创建对象、instanceof判断、定义静态变量。原因都是运行时类型信息被擦除。 对比其他语言:Java是编译时泛型运行时擦除,是伪泛型。C++和C#是运行时泛型保留类型信息,是真泛型。" 嗯,它有核心线程数,最大线程数,线程存活时间,线程存活时间单位,线程工厂,任务队列以及拒绝策略。 嗯,它有核心线程数,最大线程数,线程存活时间,线程存活时间单位,线程工厂,任务队列以及拒绝策略。 第一个,核心线程数corePoolSize,常驻线程数量,不会被回收。

第二个,最大线程数maximumPoolSize,核心线程加临时线程的上限。 第三个,空闲线程存活时间keepAliveTime,临时线程空闲超过此时间会被回收…

第二个,最大线程数maximumPoolSize,核心线程加临时线程的上限。 第三个,空闲线程存活时间keepAliveTime,临时线程空闲超过此时间会被回收。 第五个,任务队列BlockingQueue,存放等待执行的任务。 第六个,线程工厂ThreadFactory,创建线程,可自定义线程名。 第七个,拒绝策略RejectedExecutionHandler,队列满且线程满时的处理策略。 四大拒绝策略:AbortPolicy默认抛异常,CallerRunsPolicy由调用线程执行,DiscardPolicy直接丢弃,DiscardOldestPolicy丢弃最老任务。" 面试官:那提交我们提交任务的时候,它大概的执行流程。 可以说一下就是先让核心线程去执行,如果核心线程已满,那么就把它加到那个任务队列中。然后任务队列满了,就会嗯让他去创建临时线程,让临时线程去执行。如果呃临时线线程也满了,那最终就会触发拒绝策略。 可以说一下就是先让核心线程去执行,如果核心线程已满,那么就把它加到那个任务队列中。然后任务队列满了,就会嗯让他去创建临时线程,让临时线程去执行。如果呃临时线线程也满了,那最终就会触发拒绝策略。 第一步,判断核心线程是否已满。如果当前线程数小于核心线程数,创建核心线程执行任务。 第二步,判断任务队列是否已满。如果核心线程已满但队列未满,任务加入队列等待。 第三步,判断最大线程数是否已满。如果队列已满但线程数小于最大线程数,创建临时线程执行任务。 第四步,执行拒绝策略。如果队列满且线程数达到最大值,执行拒绝策略。 具体示例:假设核心线程2个,最大线程4个,队列容量10。任务1和2创建核心线程执行,任务3到12加入队列等待,任务13和14队列满创建临时线程执行,任务15队列满且线程满触发拒绝策略。 线程回收:核心线程默认永不回收,临时线程空闲超过存活时间会被回收。" 面试官:我们java的序列化里面那个序列化的版本,版本id是什么?它的作用是什么? 序列化的版本。这个我不知道,我只了解序列化的概念,就是把它转成字节流。

序列化的版本。这个我不知道,我只了解序列化的概念,就是把它转成字节流。 在反序列化时,校验字节流中的类版本和当前 JVM…

序列化的版本。这个我不知道,我只了解序列化的概念,就是把它转成字节流。 在反序列化时,校验字节流中的类版本和当前 JVM 中的类版本是否一致。"serialVersionUID是序列化的版本标识。 作用:验证序列化对象的版本一致性,反序列化时对比UID,不一致则抛出InvalidClassException。 为什么需要:如果未显式指定UID,编译器会自动生成。修改类后编译器重新生成UID,导致反序列化失败。 正确做法:实现Serializable接口时,必须显式指定serialVersionUID。修改类后如果兼容旧版本,UID不变。如果不兼容,修改UID让反序列化失败。

News

浩鲸科技 秋招一面_牛客网

String 字面量直接赋值和直接 new 创建对象有什么区别? StringBuilder 跟 StringBuffer 有什么区别? Java 序列化里面的 serialVersionUID(序列化版本 ID)是什么?它的作用是什么? MySQL 有哪些隔离级别,默认的隔离级别是什么? MySQL 的 redo log、undo log、binlog 分别有什么作用? 用过 Redis…

@spots
Source: Nowcoder
See more like this