面试 总结

面试总结

一、@SpringBootApplication注解的作用

@SpringBootApplication是SpringBoot应用的核心入口注解,本质是Spring框架为简化配置提供的"聚合式注解",其设计目标是替代传统Spring项目中XML配置+多注解组合的繁琐模式,核心由三大注解构成,且包含扩展特性:

1. 核心组成注解的深度解析

注解 底层本质 核心能力 专业细节
@SpringBootConfiguration 派生自@Configuration 标识当前类为SpringBoot的配置类,支持@Bean定义容器Bean 与普通@Configuration的区别:仅做语义强化,底层均为CGLIB动态代理;可嵌套配置类(@Import
@EnableAutoConfiguration 基于@Import实现 开启自动配置机制,根据依赖自动装配Bean 1. 底层通过SpringFactoriesLoader.loadFactoryNames()加载META-INF/spring.factoriesEnableAutoConfiguration对应的配置类; 2. 支持exclude/excludeName排除指定自动配置类(如@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)); 3. 自动配置类通过@Conditional系列注解实现"条件装配"(如ClassCondition、PropertyCondition)
@ComponentScan 扫描组件核心注解 扫描指定路径下的@Component衍生注解(@Controller/@Service/@Repository等) 1. 默认扫描注解所在类的包及其子包; 2. 支持includeFilters/excludeFilters过滤扫描类; 3. 多模块项目需通过scanBasePackages/scanBasePackageClasses指定扫描范围,避免Bean未加载

2. 关键扩展特性

  • 元注解支持 :该注解本身被@Inherited修饰,子类可继承父类的扫描规则(极少用);
  • 配置优先级 :自动配置类的优先级低于自定义@Bean,可通过@Primary调整Bean的优先级;
  • 禁用自动配置 :可通过spring.boot.enableautoconfiguration=false全局关闭自动配置。

二、SpringMVC解决跨域问题的方案

跨域的本质是浏览器的同源策略限制(协议、域名、端口、子域名任一不同即为跨域),SpringMVC针对跨域的解决方案围绕"突破浏览器OPTIONS预检请求限制"设计,核心分为三类,且需理解跨域配置的底层参数含义:

1. 跨域核心概念前置

  • 简单请求:满足「GET/HEAD/POST + 无自定义请求头 + Content-Type为application/x-www-form-urlencoded/multipart/form-data/text/plain」,无需预检;
  • 非简单请求:PUT/DELETE、自定义头、JSON格式等,浏览器先发送OPTIONS预检请求,验证服务器允许跨域后再发送真实请求;
  • 预检缓存 :通过maxAge配置,减少OPTIONS请求次数,提升性能。

2. 三类解决方案的全维度对比

方案类型 适用场景 核心配置参数及含义 优先级 生产规范
@CrossOrigin注解 单个接口/控制器的局部跨域 - origins/allowedOriginPatterns:允许的源(2.4+推荐后者,支持通配符如*.demo.com); - maxAge:预检缓存时间(秒); - allowedMethods:允许的HTTP方法; - allowCredentials:是否允许携带Cookie 最低 禁止使用*,需指定具体域名;方法级注解覆盖控制器级
WebMvcConfigurer配置类 全局跨域(无网关场景) 实现addCorsMappings(CorsRegistry registry),参数与@CrossOrigin一致 中等 1. 统一配置allowedOriginPatterns为业务域名列表; 2. allowCredentials=true时,allowedOriginPatterns不可为*
CorsFilter过滤器 全局跨域(网关/细粒度控制) 基于Servlet Filter实现,直接拦截请求并设置跨域响应头(Access-Control-Allow-Origin等) 最高 1. 配置在Filter链的最前端; 2. 结合网关(Gateway/Nginx)时,优先在网关层配置跨域,避免多层配置冲突

3. 生产级跨域配置原则

  • 禁止全量放行(*),需白名单化允许的域名;
  • 限制允许的HTTP方法(仅开放业务所需方法);
  • 预检缓存时间设置为3600秒以上,减少OPTIONS请求;
  • 跨域与认证结合时,确保allowCredentials=true,且前端请求携带withCredentials: true

三、SpringBoot实现多套环境配置的方式

SpringBoot的多环境配置核心遵循「配置隔离 + 环境激活 + 配置优先级」三大原则,旨在解决开发/测试/预发/生产等环境的配置隔离问题,且支持多层级配置覆盖:

1. 配置文件的分层与命名规范

配置文件类型 命名规则 作用 加载优先级
主配置文件 application.yml/properties 全局通用配置,用于激活环境、定义公共配置 基础层
环境专属配置文件 application-{profile}.yml/properties 各环境专属配置(如application-prod.yml),覆盖主配置的同名参数 激活层
外部配置文件(服务器/容器) 服务器指定路径(如/opt/config/) 生产环境的敏感配置(如数据库密码),无需打包到jar包 最高层

2. 环境激活的全维度方式(优先级从低到高)

  1. 配置文件激活 :主配置文件中spring.profiles.active=prod(基础方式,开发环境用);
  2. JVM参数激活java -Dspring.profiles.active=prod -jar app.jar(测试环境常用);
  3. 命令行参数激活java -jar app.jar --spring.profiles.active=prod(生产环境主流);
  4. 环境变量激活 :设置系统环境变量SPRING_PROFILES_ACTIVE=prod(容器化部署常用);
  5. Maven打包激活 :在pom.xml中配置<profiles>,打包时mvn clean package -Pprod,结合resources过滤替换配置(CI/CD流水线常用);
  6. 编程式激活SpringApplication.setAdditionalProfiles("prod")(极少用,灵活性低)。

3. 高级特性:多环境配置增强

  • 配置继承 :通过spring.profiles.include=common,prod激活多个环境配置(common为公共配置);
  • 占位符引用 :配置中可通过${spring.profiles.active}引用当前激活的环境,如logging.file.name=app-${spring.profiles.active}.log
  • 配置加密:生产环境敏感配置(密码)需通过Jasypt等工具加密,避免明文存储;
  • 配置中心集成:结合Nacos/Apollo等配置中心,实现动态环境配置切换(生产级最佳实践)。

4. 配置加载的完整优先级(高→低)

命令行参数 > 系统环境变量 > JVM参数 > 外部配置文件(服务器路径) > 应用内环境配置文件 > 应用内主配置文件 > 默认配置。

四、final、finally、finalize的深度区别

三者是Java中名称相似但归属、语义、底层机制完全不同的概念,需从JVM层面理解其核心差异:

概念 语言归属 核心语义与底层机制 适用场景 专业注意事项
final 关键字(语法) 1. 修饰类:类不可被继承(JVM层面禁止生成子类的字节码); 2. 修饰方法:方法不可被重写(字节码中标记ACC_FINAL); 3. 修饰变量: - 基本类型:值不可变(编译期常量); - 引用类型:引用地址不可变(对象内容可变); - 局部变量:必须显式赋值; - 成员变量:必须在声明/构造器中赋值 定义常量、防止类/方法被篡改、线程安全(不可变对象) 1. final变量在JVM中存储在常量池,提升访问效率; 2. final方法不支持动态分派(重写),调用效率更高; 3. final类的方法默认final(无需显式声明)
finally 关键字(异常) 配合try-catch使用,finally块在「try块执行完毕/异常抛出后、方法返回前」执行(JVM通过异常表实现);仅当JVM强制退出(System.exit(0))时不执行 资源释放(流、连接、锁) 1. finally块的执行优先级高于try/catch中的return; 2. finally中抛出异常会覆盖原异常(需避免); 3. try-with-resources可替代finally释放资源(AutoCloseable接口)
finalize 方法(Object) 1. 属于Object的protected方法,JVM在GC回收对象前调用(仅一次); 2. 底层通过Finalizer线程处理,执行时机不可控; 3. JDK9标记为@Deprecated,JDK17移除 历史上用于清理非堆内存资源(如JNI资源) 1. 执行时机不可控(可能OOM前才执行); 2. 可能导致对象复活(finalize中重新引用对象); 3. 替代方案:Cleaner类(JDK9+)、PhantomReference(虚引用)

五、List、Set、Map的全维度区别

三者是Java集合框架(Collection/Map体系)的核心接口,其差异源于数据结构设计、语义约束、性能模型的不同,需从底层实现视角理解:

1. 核心体系与底层实现

接口 父接口 核心数据结构(主流实现) 核心语义约束 时间复杂度(平均)
List Collection ArrayList(动态数组)、LinkedList(双向链表) 有序(插入序)、可重复、索引访问 随机访问:ArrayList O(1)、LinkedList O(n);增删:ArrayList尾部O(1)、中间O(n);LinkedList头尾O(1)、中间O(n)
Set Collection HashSet(HashMap底层)、TreeSet(红黑树)、LinkedHashSet(LinkedHashMap) 无序(默认)、不可重复、无索引 添加/查询/删除:HashSet O(1)、TreeSet O(logn);LinkedHashSet O(1)(保留插入序)
Map HashMap(数组+链表+红黑树)、TreeMap(红黑树)、LinkedHashMap(HashMap+双向链表) 键值对、键唯一、值可重复、无索引 添加/查询/删除:HashMap O(1)、TreeMap O(logn);LinkedHashMap O(1)(保留插入序)

2. 关键特性深度解析

  • List
    • 有序性:通过索引维护元素顺序,支持add(int index, E)get(int index)等索引操作;
    • 可重复性:元素的equals()返回true仍可添加;
    • 并发问题:ArrayList/LinkedList非线程安全,并发场景需用CopyOnWriteArrayList
  • Set
    • 不可重复性:依赖元素的hashCode()equals()(HashSet)或Comparable/Comparator(TreeSet);
    • 无序性:HashSet的元素顺序由hash值决定,与插入序无关;LinkedHashSet通过双向链表保留插入序;TreeSet按自然序/自定义序排序。
  • Map
    • 键唯一性:同Set的不可重复规则;
    • 空值支持:HashMap允许1个null键、多个null值;TreeMap不允许null键(需比较);
    • 扩容机制:HashMap默认初始容量16,负载因子0.75,扩容为2倍;JDK8中链表长度≥8且数组长度≥64时转为红黑树。

3. 适用场景与选型原则

  • List:需按序存储、频繁索引访问、允许重复元素(如订单列表、日志记录);
  • Set:需元素唯一性、无需索引(如用户ID集合、标签去重);
  • Map:需键值映射、快速查找(如缓存、配置项、数据字典)。

六、ArrayList和LinkedList的深度对比

二者均实现List接口,但底层数据结构的差异导致性能、内存、功能呈现极致分化,需结合JVM内存模型理解:

1. 底层实现与内存模型

特性 ArrayList LinkedList
数据结构 动态扩容的Object数组 双向链表(每个节点含prev/next/item)
内存布局 连续内存块(JVM堆中连续分配) 非连续内存(节点分散在堆中)
扩容机制 1. 空参构造:初始容量0,首次add扩容为10,后续扩容为原容量1.5倍; 2. 指定容量:扩容为原容量1.5倍; 3. 扩容时复制数组(Arrays.copyOf),性能损耗 无扩容机制,按需创建节点,无内存冗余
内存开销 数组扩容产生冗余空间(未使用的数组位置) 每个节点额外存储prev/next指针(约24字节/节点,64位JVM)

2. 性能特性与测试指标

操作类型 ArrayList LinkedList 性能结论
随机访问(get) O(1),直接通过索引定位数组元素 O(n),需从表头/表尾遍历到目标节点 ArrayList秒杀LinkedList
尾部增删(add/remove) O(1)(无扩容时),扩容时O(n) O(1),直接修改尾节点指针 无扩容时性能接近,扩容时ArrayList慢
中间增删 O(n),需移动后续元素 O(n),需遍历找到节点+修改指针 数据量大时LinkedList略优(移动成本<数组复制)
遍历效率 迭代器/普通for循环均高效(连续内存缓存友好) 迭代器遍历高效,普通for循环(get(index))极低(多次遍历) ArrayList遍历效率更高

3. 功能扩展与选型

  • ArrayList:实现RandomAccess接口(标记支持快速随机访问),适合大数据量读取、少量尾部增删;
  • LinkedList:实现Deque接口,支持队列/双端队列/栈的所有操作(如offerFirst/pollLast),适合频繁头尾增删(如消息队列、任务栈);
  • 并发场景:二者均非线程安全,ArrayList可用CopyOnWriteArrayList,LinkedList无官方并发实现(需手动加锁)。

七、重载(Overload)和重写(Override)的深度区别

二者是Java多态的两大实现方式,分别对应「编译时多态(静态多态)」和「运行时多态(动态多态)」,需从JVM字节码层面理解其实现机制:

1. 核心机制与底层实现

特性 重载(Overload) 重写(Override)
多态类型 编译时多态(静态绑定) 运行时多态(动态绑定)
实现机制 编译器根据方法签名(方法名+参数列表)在编译期确定调用的方法(字节码中直接指定方法名) JVM在运行时根据对象的实际类型(而非引用类型),通过方法表(vtable)查找并调用方法
适用范围 同一个类(含父类方法在子类的重载) 父子类(需满足继承关系)
方法签名 方法名相同,参数列表(个数/类型/顺序)不同 方法签名(方法名+参数列表)完全相同
返回值 无强制约束(可不同) 需满足「协变返回类型」:子类返回值为父类返回值的子类(JDK5+支持)
访问修饰符 无强制约束 子类修饰符不能比父类更严格(如父类public→子类不能private)
异常声明 无强制约束 子类抛出的异常需是父类异常的子类/子集,不可抛出更宽泛的检查异常
方法绑定 编译期绑定(静态分派) 运行期绑定(动态分派)
关键字 无(无需注解) 推荐使用@Override注解(编译期校验)

2. 关键注意事项

  • 重载的判定仅看参数列表:参数名、返回值、异常、修饰符均不影响重载判定;
  • 重写的里氏替换原则:子类重写方法需保证"替换父类对象后程序行为不变";
  • 桥接方法:泛型父类的方法在子类重写时,编译器会生成桥接方法(如Object m(Object)String m(String)),保证方法签名匹配;
  • final/static/private方法不可重写:final方法标记为ACC_FINAL,static方法属于类而非对象,private方法对子类不可见,均无法动态绑定。

八、Java深浅拷贝的深度区别

拷贝的核心是创建对象的独立副本,避免"引用传递"导致的对象修改联动问题,深浅拷贝的本质差异在于是否递归拷贝引用类型成员变量,需结合JVM内存模型理解:

1. 核心定义与内存模型

拷贝类型 核心语义 内存模型(简化)
浅拷贝 仅拷贝对象本身(基本类型成员拷贝值,引用类型成员拷贝引用地址),新对象与原对象共享引用类型成员 原对象:{基本类型a=1,引用类型b→地址X}; 拷贝对象:{基本类型a=1,引用类型b→地址X}
深拷贝 递归拷贝对象本身及所有引用类型成员(包括多层嵌套引用),新对象与原对象完全独立 原对象:{基本类型a=1,引用类型b→地址X}; 拷贝对象:{基本类型a=1,引用类型b→地址Y(新对象)}

2. 实现方式与底层限制

拷贝类型 主流实现方式 优势 局限性
浅拷贝 1. 实现Cloneable接口,重写clone()(调用super.clone()); 2. 手动new对象并赋值基本类型成员 实现简单、性能高 引用类型成员共享,修改会联动;Cloneable是标记接口,无方法,设计不优雅
深拷贝 1. 递归实现Cloneable:所有引用类型成员均实现Cloneable,手动拷贝; 2. 序列化实现:实现Serializable,通过对象流读写; 3. 第三方工具:Apache Commons Lang的SerializationUtils、Gson/Jackson序列化 完全隔离对象,无修改联动问题 1. 递归Clone实现繁琐(多层嵌套需逐层实现); 2. 序列化无法拷贝transient修饰的成员; 3. 性能低于浅拷贝

3. 进阶特性与使用规范

  • 不可变对象无需拷贝:如String、Integer等不可变类,引用传递即可,无需拷贝;
  • transient关键字:序列化深拷贝时,transient修饰的成员不会被拷贝(可用于跳过无需拷贝的大对象);
  • Cloneable的设计缺陷 :未实现Cloneable调用clone()会抛出CloneNotSupportedException,违反接口设计原则;
  • 生产级方案:优先使用序列化(如Jackson)实现深拷贝,代码简洁且适配多层嵌套;浅拷贝仅用于无引用类型成员的简单对象;
  • 并发场景:拷贝后的对象若用于多线程,需保证原对象和拷贝对象均线程安全。
相关推荐
爬台阶的蚂蚁2 小时前
Spring AI Alibaba基础概念
java·spring·ai
计算机学姐2 小时前
基于SpringBoot的演唱会抢票系统
java·spring boot·后端·spring·tomcat·intellij-idea·推荐算法
huohuopro2 小时前
Mybatis的七种传参方式
java·开发语言·mybatis
Lee_SmallNorth2 小时前
变态需求之【角色不同访问数据库的用户不同】
java·开发语言·数据库
扶苏-su2 小时前
Java网络编程:InetAddress 详解
java·开发语言·网络
李慕婉学姐2 小时前
Springboot连锁火锅管理及预测系统sh5s1gn1(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
Anastasiozzzz2 小时前
leetcode力扣hot100困难题--4.俩个正序数列的中位数
java·算法·leetcode·面试·职场和发展
木风小助理2 小时前
JavaStreamAPI的性能审视,优雅语法背后的隐形成本与优化实践
java·前端·数据库
Chan163 小时前
《Java并发编程的艺术》| ConcurrentHashMap 在 JDK 1.7 与 1.8 的底层实现
java·spring boot·java-ee·intellij-idea·juc