1、==与equals()区别
1)==
属于运算符,分两种场景:
- 基本数据类型(byte/short/int/long/float/double/char/boolean):直接比较数值是否相等;
- 引用数据类型(对象、数组、包装类):比较栈中保存的对象地址值,判断是不是同一个堆内存对象。
int a=10,b=10; a==b → true String s1=new String("abc"),s2=new String("abc"); s1==s2 → false(地址不同)2)equals()
Object中原生定义的实例方法,默认实现就是return this == obj,本质依旧比较地址; 只有重写之后才会比较对象内容:
String、Integer、Date等 JDK 内置类都重写了equals,比对内部数据;- 自定义实体类如果不重写,调用 equals 依旧等价于
==; - 规范:比较常量放前面
“abc”.equals(str),避免空指针。
核心总结
==:基本类型比值,引用类型比地址;equals:原生比地址,重写后比业务内容。
2、方法重载(Overload)VS 方法重写(Override)
(1)重载:同一个类内,方法名相同,参数列表不同
核心:编译期静态绑定满足条件:
- 同类中方法名完全一致;
- 参数个数、类型、顺序至少一处不一样;
- 返回值、修饰符、异常抛出不参与重载判定; 作用:同一行为适配不同入参,比如
println()支持各种类型入参。
public void test(){} public void test(int a){} // 合法重载(2)重写:子类重写父类继承的方法
核心:运行期动态绑定(多态核心)约束(里氏替换原则):
- 父子类关系,方法名、参数列表、返回值(协变)必须一致;
- 子类访问权限不能严于父类:父类 public,子类不能改成 private;
- 子类抛出受检异常不能比父类更大;
static/final/private方法不能重写; 作用:子类个性化实现父类抽象 / 通用逻辑。
简明对比
| 维度 | 重载 | 重写 |
|---|---|---|
| 位置 | 同一个类 | 父子类 |
| 方法名 | 必须一致 | 必须一致 |
| 参数 | 必须不同 | 必须完全一致 |
| 绑定时机 | 编译期 | 运行期 |
| 关键字 | 无 | 常搭配 @Override 注解校验 |
3、遍历集合时能否直接删除元素?
1)普通 for 循环(下标遍历)
ArrayList可以直接remove(index),但下标会自动收缩,容易漏删元素; 例:遍历删偶数下标元素,删除后后续元素前移,下一轮下标跳过数据。
2)增强 for 循环(foreach)
底层基于迭代器Iterator,遍历中直接调用集合自身remove()会触发并发修改异常ConcurrentModificationException。 原因:集合维护modCount修改计数器,迭代器初始化记录expectedModCount,新增 / 删除会修改 modCount,迭代时校验不一致直接抛异常。
3)正确安全删除方案
- Iterator 迭代器自身的 remove ():同步更新
expectedModCount,不会报错;
Iterator<String> it = list.iterator(); while(it.hasNext()){ String s = it.next(); if(条件) it.remove(); }- JDK8+
removeIf():底层迭代器实现,简洁安全; - 新建集合保存需要保留的数据,遍历完毕替换原集合。
结论
禁止 foreach 内直接集合 remove;迭代器自带 remove、removeIf 是标准安全写法。
4、静态方法不能直接访问非静态属性 / 方法的原因
加载时机不同静态资源(static 变量、静态方法)在类加载阶段就初始化,依托类本身; 非静态成员属于实例对象,必须
new出对象后堆内存分配完成才存在。 类加载时还没有任何实例,静态方法不知道要访问哪一个对象的成员变量。调用主体不同静态方法调用:
类名.静态方法(),没有隐含的this指针; 实例方法内部默认携带this引用,指向当前调用的对象,访问成员靠this.属性; 静态方法没有this,找不到对应的实例对象,自然无法直接拿实例属性。
解决方案
在静态方法中手动创建实例对象,通过对象引用访问非静态资源。
5、Java 实例对象加载与销毁完整生命周期
一、对象创建(加载→初始化)
- 类加载阶段:加载.class 文件、验证、准备(静态变量赋默认值)、解析、初始化(执行静态代码块、静态变量赋值);
- 分配堆内存:new 关键字在堆开辟内存,所有实例变量赋予默认初始值(int=0、引用 = null);
- 执行父类构造器:递归向上执行父类构造方法;
- 实例变量显式赋值、实例代码块执行;
- 执行当前类构造方法;
- 栈中引用变量指向堆内对象,对象创建完成。
二、对象销毁(GC 回收)
- 判定可回收:对象不存在任何有效强引用(引用置空、引用超出作用域、容器移除引用);
- 标记阶段:GC 通过可达性分析,判断该对象不在 GC Roots 引用链中,标记为待回收;
- 可选执行 finalize ():该方法只会执行一次,不建议依赖此方法释放资源;
- 清理回收:分代回收(年轻代 S0/S1、老年代),标记复制 / 标记整理清除堆内存,内存归还给 JVM 堆。
补充
程序员无法手动立刻销毁对象,只能切断引用,由 JVM 垃圾回收器自主调度回收。
6、局部变量、实例变量、静态变量多线程安全问题
1)三类变量存储位置
- 局部变量:栈帧中,每个线程私有栈,线程之间完全隔离;
- 实例变量:堆内存对象内,多个线程拿到同一个对象操作时共享;
- 静态变量:方法区,全局唯一,所有线程共享。
线程安全风险
- 局部变量:天生线程安全每个线程执行方法时创建独立栈变量,不存在共享竞争,无并发问题。
注意:如果局部变量持有共享对象引用(如 ArrayList),并发操作这个共享对象依然不安全。
实例变量:多线程共用同一个实例则不安全单例 Bean(Spring 默认单例)中的实例成员变量,多线程请求会并发读写,出现数据脏读、覆盖。
静态变量:全局共享,并发竞争最严重全局唯一,所有线程共用,读写不加控制一定会线程不安全。
保证线程安全常用方案
- 尽量使用局部变量,避免在单例类定义成员变量;
- 加锁:
synchronized同步锁、ReentrantLock显式锁; - 并发工具类:
Atomic原子类(数值)、ConcurrentHashMap、CopyOnWriteArrayList; - ThreadLocal:线程私有副本,每个线程独有一份数据;
- 不可变对象(String、Integer),天然安全;
- 读写分离、CAS 乐观锁、分布式锁(分布式场景)。
7、设计模式 + 策略模式详解
一、设计模式总览
分为三大类:
- 创建型:单例、工厂、建造者、原型(解决对象创建);
- 结构型:适配器、装饰器、代理、组合、外观(组装对象结构);
- 行为型:策略、观察者、责任链、模板方法、状态(处理对象交互逻辑)。
二、策略模式(Strategy)
核心思想
把一系列可互换的算法 / 业务规则封装成独立策略类,统一接口,运行时动态切换策略,消除大量if-else/switch。
组成角色
- 策略接口 Strategy:定义统一执行规范;
- 具体策略实现类:不同分支业务各自实现接口;
- 上下文 Context:持有策略引用,对外统一入口,负责调度执行策略。
适用场景
- 大量 if else 区分不同业务规则:支付方式(微信 / 支付宝 / 银行卡)、优惠策略(满减、折扣、优惠券)、文件解析策略;
- 算法需要频繁动态切换;
- 便于后续新增策略,开闭原则,新增策略只需新增实现类,不用修改原有代码。
简单示例
// 策略接口 public interface PayStrategy { void pay(BigDecimal money); } // 支付宝、微信各自实现PayStrategy // Context统一调用,运行时注入不同策略优点
去掉臃肿分支判断、代码解耦、易扩展、单元测试方便; 缺点:策略类数量会增多。
8、接口限流:单用户 1 分钟最多 100 次 + 多接口限流优化
方案 1:单机限流常用实现
计数器固定窗口Redis key:
limit:userId:接口名,value 访问次数,key 过期时间 60s;每次请求 incr,超过 100 直接拒绝。 缺陷:窗口临界点会出现瞬间双倍流量(临界突刺)。滑动窗口(Redis ZSet)记录一分钟内所有请求时间戳,每次删除一分钟前数据,统计剩余数量,精准平滑限流。
令牌桶 / 漏桶令牌桶:匀速生成令牌,请求拿到令牌才放行,支持突发流量;适合后端接口限流,Guava RateLimiter 单机令牌桶。
分布式推荐:Redis + Lua 脚本
保证计数判断 + 自增原子化,避免并发超量; key 设计:rate_limit:{userId}:{interfaceKey},过期 60s。
多接口统一限流优化方案
- 统一限流组件封装自定义注解
@RateLimit(limit=100,time=60),AOP 切面拦截所有接口,注解配置不同接口限流阈值,统一校验逻辑; - 分层限流网关层(Gateway/Nginx)做粗粒度全局限流,应用层做精细化用户维度限流;网关拦截无效流量,减轻服务压力;
- Redis key 统一命名规范区分用户、接口、应用,统一前缀,方便运维监控统计;
- 限流配置热更新将阈值存入 Nacos/Apollo 配置中心,不用重启服务动态修改限流次数;
- 降级兜底限流触发后返回友好提示,也可开启降级走缓存默认数据,拒绝无效重试。
9、项目中:分布式锁、声明式事务@Transactional、三级缓存、循环依赖实战拆解
(1)分布式锁
业务场景:集群部署下防止超卖、重复下单、定时任务重复执行。 主流方案:
- Redis 分布式锁:SETNX+EXPIRE,推荐 Redisson(解决死锁、锁续期、可重入、防羊群效应);
- Zookeeper 临时节点;
- Mysql 乐观锁版本号机制。 要点:可重入、防死锁、锁失效自动续期、避免长时间占用锁。
(2)声明式事务 @Transactional
- 底层 AOP 动态代理生效,只有 public 方法调用才生效;
- 失效经典场景:
- 同类方法内部调用(this.xxx () 不走代理);
- 非 public 方法、try-catch 吃掉异常不抛出;
- 多线程、传播行为配置错误;
- 事务传播机制、隔离级别、回滚规则(默认只回滚 RuntimeException);
- 实操:复杂业务手动控制事务边界,大事务拆分避免锁持有太久。
(3)三级缓存:本地缓存(Caffeine)+ Redis + DB
访问优先级:本地缓存 → Redis 分布式缓存 → MySQL 数据库
- Caffeine:JVM 堆内高性能本地缓存,访问最快,减轻 Redis 压力;
- Redis:分布式共享缓存,保证集群数据统一;
- MySQL 持久层; 解决缓存穿透、击穿、雪崩:布隆过滤器、互斥锁、热点数据永不过期、过期时间打散。 流程:查询先查本地缓存,无则查 Redis,还无查 DB,回写两级缓存。
(4)Spring 循环依赖
- 场景:A 依赖 B,B 又依赖 A;
- Spring 三级缓存解决:
- 一级缓存:完整初始化好的单例 Bean;
- 二级缓存:实例化完成但未注入属性的半成品 Bean;
- 三级缓存:Bean 工厂,用于生成 AOP 代理对象;
- 原理:先实例化 A 放入三级缓存,注入 A 时需要 B,实例化 B,B 注入 A 时从三级缓存拿到 A 的早期引用完成注入,最后 A 完整初始化移入一级缓存;
- 无法解决:构造器注入的循环依赖。
10、Vibe Coding 提升开发效率,解决的实际问题
Vibe Coding 定位
AI 驱动的智能编码助手,深度集成 IDE,依托上下文理解项目结构、代码规范、业务上下文,自动化编码工作。
实际落地价值
- CRUD 模板一键生成根据数据库表结构自动生成 Entity、Mapper、Service、Controller 基础代码,省去重复搬运代码时间;
- 复杂逻辑思路拆解 + 代码实现复杂工具类、正则、日期处理、集合流转换、Redis 工具方法,口述需求直接产出可运行代码;
- BUG 定位与修复粘贴异常堆栈、报错代码,AI 分析空指针、索引越界、事务失效、并发安全问题,给出修复方案;
- 单元测试编写自动生成 JUnit 单元测试、Mock 测试用例,提升代码覆盖率;
- 注释、代码重构、规范优化老旧杂乱代码格式化、抽取工具方法、优化 if 嵌套、遵循项目统一编码规范;
- 学习攻坚陌生技术(RabbitMQ、RAG、SpringAI)快速写 Demo 验证,快速踩坑验证方案可行性。
总结
把机械重复、低价值编码工作交给工具,自己聚焦业务架构设计、难点排查、方案选型,大幅缩短基础开发工时。
11、抗压能力回答(面试标准作答)
回答话术
- 我日常开发本身会面对版本迭代赶排期、线上紧急 bug 修复、需求临时变更等高压场景,已经适应高强度工作节奏;
- 面对紧急压力:先梳理问题优先级,线上故障优先止损,拆分任务分步解决,不会慌乱无序;遇到瓶颈先自主排查,卡壳及时同步组长沟通求助,不硬扛拖延进度;
- 心态层面:把压力当成技术沉淀的机会,上线复盘总结问题根源,优化后续开发流程避免重复踩坑;长期高压会主动调节作息,保证工作状态稳定,不会带着负面情绪影响团队协作。
12、上司分配简单基础任务如何看待
正向思路作答
- 摆正心态,没有无用的任务简单任务一般是熟悉项目结构、业务流程、代码规范、团队协作模式的切入点,新人阶段首要目标是熟悉整体项目,快速融入团队;
- 高标准完成小事哪怕简单需求,严谨自测、规范编码、做好注释、提交规范 MR,让领导看到做事认真靠谱的工作态度;
- 主动复盘深挖做完之余思考:这个功能为什么这么设计、有没有优化空间、相关联业务链路是什么,私下主动钻研相关模块;
- 适时主动争取进阶工作熟练吃透手头工作后,主动向领导同步学习进度,表明希望承接复杂业务、参与需求设计、难点优化等工作,循序渐进承担更多职责。
杜绝消极敷衍、觉得大材小用抵触工作。
13、偏向独立钻研技术还是团队沟通
均衡型最优回答(面试官最爱)
二者相辅相成,分场景取舍:
- 攻克陌生技术难点、底层原理调研、个人技术深耕阶段:偏向独立钻研安静梳理文档、动手写 Demo 验证,先形成自己完整理解,避免频繁打断同事;
- 业务需求对接、接口联调、方案评审、疑难卡点、线上问题:优先高效沟通闭门造车效率极低,需求理解偏差会写出错误代码;卡壳超过合理时间(半小时左右)及时和同事、组长沟通,避免卡死耽误整体进度;
- 日常习惯:独立钻研打好基础,工作中保持主动同步,定期同步进度、疑问,团队协作优先对齐信息,个人成长利用业余和碎片化时间深耕技术,二者兼顾。