Java面试八股(Java基础,Spring,SpringBoot篇)
java基础
- JDK,JRE,JVM
- Java语言的特点
- Java常见的运行时异常
- Java为什么要封装
- 自增自减
- +=的隐式转换
- 移位运算符
- 1. 左移运算符(`<<`)
- 2. 带符号右移运算符(`>>`)
- 3. 无符号右移运算符(`>>>`)
- 可变参数
- break,continue,return 的区别及作用?
- this 关键字有什么作用?
- 深拷贝
- 浅拷贝
- finally 代码块是否一定执行?
- BigDecimal
- try-with-resources语句
- 语法:
- 与传统 `try…finally` 对比
- `Java`序列化中如果有些字段不想进行序列化,怎么办?
- 序列化
- SPI机制
- String的三大技术
- String 底层数据结构是什么?
- String 不可变性如何实现?
- 字符串常量池
- String s1 = new String("abc");这句话创建了几个字符串对象?
- intern()方法
- +号拼接字符串
- 什么是泛型?
- `Exception` 和 `Error`的区别
- 给你个jar包,里面有A类,B类,你要写C类来扩展它们。
- 基本数据类型和引用数据类型对比
- Integer和int的区别
- 如何理解面向对象?
- 多态存在的三个条件
- java面向对象,为什么还保留的基本数据类型?
- 值传递
- 泛型和通配符
- 在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
- 对象实体,对象引用的区别
- 对象实体
- 对象引用
- 1. 构造器`Constructor`是否可被`override`?为什么?
- 2.` String`, `StringBuilder`和 `StringBuffer` 的区别是什么?`String`为什么是不可变的
- 3. 对象的相等与指向他们的引用相等,两者有什么不同?
- 4. 重载和重写的区别?
- 5. 在一个静态方法内调用一个非静态成员为什么是非法的?
- 6. 简述线程,进程的基本概念。以及他们之间关系?
- 7. 一个类的构造方法的作用是什么 若一个类没有声明构造方法,该程序能正确执行吗 ?为什么?
- 9. 成员变量与局部变量的区别有哪些?
- final、finalize 和 finally 的不同之处?
- `==`和`equals`的区别
- Java创建对象的五种方式?
- 接口和抽象类的区别
- 抽象类和普通类的区别?
- throw和throws
- Static可以修饰哪些?
- 1.1 成员变量(静态变量)
- 1.2 成员方法(静态方法)
- 1.3 静态初始化块
- 集合体系结构
- 有一个list,里面的元素是String类型,需要找出里面值为"abc"的并remove掉,该怎么做?
- ArrayList 的底层数据结构是什么?如何扩容?时间复杂度?
- ArrarList和LinkedList区别
- LinkedList 为什么不能实现 RandomAccess 接口?
- HashMap 的底层实现原理是什么?JDK 1.8 之前和之后的区别?
- HashCode方法
- 为什么两个对象的 `hashCode` 值相等,它们也不一定是相等的?
- 总结:
- 为什么重写 equals() 时必须重写 hashCode() 方法?
- 保持哈希契约的一致性
- 保证集合操作的正确性
- map的key可以重复吗,能不能为空?
- 为什么 HashSet 存和取的顺序不一样?
- HashSet 使用什么机制进行数据去重
- 集合遍历的几种方式
- synchronized 和 java.util.concurrent.locks.Lock 的异同?
- 线程的创建方式
- 线程有哪些基本状态?
- 反射是什么?
- IOC和AOP是通过什么机制来实现的?
- Spring的核心思想,说说你的理解?
- Spring事务
- Spring事务的实现原理
- `BeanFactory` 和 `ApplicationContext`有什么区别?
- AOP 面向切面编程
- Spring IOC 是什么?使用场景有哪些?Spring 事务,事务的属性,数据库隔离级别
- IOC中的bean
- 什么是Spring的bean?
- Bean 的生命周期
- Bean 的配置方式:
- Bean的作用域
- 依赖注入的三种方式
- 单例bean是线程安全的吗
- 单例bean和非单例的生命周期是否一样?
- Spring 如何解决循环依赖?
- Spring MVC 的核心组件有哪些?
- MVC分层
- 单点登录
- cookie和session的区别
- get和post请求的区别
- HTTP请求状态码
- 过滤器和拦截器的区别
- HTTP方法
- Spring 设计模式
- Spring Boot、Spring MVC 和 Spring 有什么区别?
- SpringMVC 怎么样设定重定向和转发的?
- 当一个方法向`AJAX`返回特殊对象,比如`Object`,`List`等,需要做什么处理?
- SpringMVC 用什么对象从后台向前台传递数据的?
- @Component 和 @Bean 的区别?
- @Autowired和@Resource注解的区别
- @Controller和@RestController注解的区别
- 将一个类声明为 Bean 的注解有哪些?
- Spring Boot自动装配原理**
- 为什么在`web`开发中,声明控制器 `bean` 只能用 `@Controller`?
- 依赖冲突问题
- @Transactional注解
- SpringBoot原理, SpringBoot为什么大大简化了 `Spring` 开发?
- SpringBoot中哪里用到了反射知识?
- 配置文件相关
- Spring Boot 四大核心注解
JDK,JRE,JVM
+-------------------+
| JDK | ← Java 开发工具包,包含开发和运行 Java 程序所需的工具和环境
| +-------------+ |
| | JRE | | ← Java 运行环境,包含JVM和一些类库
| | +-------+ | |
| | | JVM | | | ← Java 虚拟机,执行 Java 程序的引擎
| | +-------+ | |
| +-------------+ |
+-------------------+
JVM:是Java程序运行的引擎,负责将字节码转换为机器码并执行,实现Java的跨平台特性
![[Pasted image 20250506140234.png]]
![[Pasted image 20250506140625.png]]
Java在执行流程上兼具“编译”和“解释”两种特性:它首先通过 javac 编译器将源代码编译成与平台无关的字节码(.class 文件),然后由JVM以解释或即时编译(JIT)的方式将字节码转为本地机器码并执行。因此,Java通常被称为“编译-解释型”或“混合型”语言.
栈区: 存储局部变量、方法调用和返回值。每当一个方法被调用时,JVM会为该方法创建一个栈帧(stack frame),并将其压入当前线程的栈中。当方法执行完成后,栈帧会被弹出,释放该方法所占的栈内存。
堆区: 存放new创建的对象和数组, 类的实例变量; JVM不定期检查堆区,如果对象没有引用指向它,内存将被回收.
方法区: 用于存储类信息、常量池、静态变量、方法代码等数据。可以认为方法区是类级别的内存区域,而栈区和堆区存储的是实例级别的数据。
成员变量(字段)指的是在类中、方法外声明的字段,它们包括两大类:实例变量和 静态变量
Java语言的特点
-
跨平台:
java程序编译后生成和平台无关的字节码文件,由针对不同操作系统的JVM执行,实现一次编译处处运行的跨平台能力 -
面向对象
-
健壮性:有垃圾回收和异常处理机制
Java常见的运行时异常
-
空指针异常(
NullPointerException), -
数组/字符串下标越界(
ArrayIndexOutOfBoundsException & StringIndexOutOfBoundsException) -
算术异常(
ArithmeticException) -
类型转换异常(
ClassCastException)
Java为什么要封装
- 隐藏对象的内部实现细节,仅暴露必要接口,实现“高内聚、低耦合”。
- 作用:保护数据安全:通过
private字段限制直接访问,防止非法修改。
灵活维护:内部逻辑修改不影响外部调用。
自增自减
++ 和 -- 运算符可以放在变量之前,也可以放在变量之后:
- 前缀形式(例如
++a或--a):先自增/自减变量的值,然后再使用该变量,例如,b = ++a先将a增加 1,然后把增加后的值赋给b。 - 后缀形式(例如
a++或a--):先使用变量的当前值,然后再自增/自减变量的值。例如,b = a++先将a的当前值赋给b,然后再将a增加 1。
![[Pasted image 20250506142657.png]]
- Java 里使用
long类型的数据一定要在数值后面加上 L,否则将作为整型解析。 - Java 里使用
float类型的数据一定要在数值后面加上 f 或 F,否则将无法通过编译。
System.out.println(42 == 42.0);// true
当使用 == 操作符比较 42 和 42.0 时,Java 会进行类型转换。会将 int 类型的 42 自动提升为 double 类型,然后与 42.0 进行比较。
由于 42 转换为double类型后是 42.0,与 42.0 的值相等,所以比较结果为 true。
+=的隐式转换
- 复合赋值(
+=)会在赋值时对结果做一次“自动缩小转换
a += b; // ⇔ a = (TypeOfA) (a + b);
而普通赋值 a = a + b 则不含此隐式转换,若右侧类型更宽必须显式强转,否则编译报错
移位运算符
1. 左移运算符(<<)
作用:将 a 的二进制位向左移动 n 位,右边补 0。
效果:相当于 a * 2ⁿ(在不溢出的情况下)。
示例:
int a = 3; // 二进制:00000011
int b = a << 2; // 左移两位:00001100,即12
System.out.println(b); // 输出12
2. 带符号右移运算符(>>)
作用:将 a 的二进制位向右移动 n 位,左边用原来的符号位(最高位)补齐。
效果:相当于 a / 2ⁿ,并保留符号(正数补0,负数补1)。
示例:
int a = -8; // 二进制(补码):11111000
int b = a >> 2; // 右移两位:11111110,即-2
System.out.println(b); // 输出-2
3. 无符号右移运算符(>>>)
作用:将 a 的二进制位向右移动 n 位,左边统一补 0(不管正负)。
适用:适用于 int 和 long 类型,特别是处理位运算时。
示例:
int a = -8; // 补码:11111000
int b = a >>> 2; // 无符号右移:00111110(高位补0)
System.out.println(b); // 输出1073741822(一个大正数)
可变参数
- 可变参数编译后实际会被转换成一个数组
public static void method1(String... args) {//......
}
- 可变参数只能作为方法的最后一个参数
public static void method2(String arg1, String... args) {//......
}
- 如果出现方法重载,会优先匹配固定参数的方法:
![[Pasted image 20250506190537.png]]
这里输出aaa方法2.
break,continue,return 的区别及作用?
break跳出整个循环,不再执行循环continue结束当前循环,继续执行下次循环return程序返回,不再执行下面的代码(结束当前的方法 直接返回)
this 关键字有什么作用?
-
this是指向对象本身的一个指针 -
用来区分成员变量和局部变量:
public Student(String name) {// 使用 this 来区分成员变量 name 和局部变量 namethis.name = name;}
- 可用来传递当前对象,引用当前对象的方法等
深拷贝
深拷贝是把对象及其引用的所有对象都复制一份,新对象和原对象在内存中没有任何关联。修改新对象不会影响原对象,包括对象中引用的其他对象。
浅拷贝
浅拷贝是指对象的字段值的复制,当对象的字段是基本数据类型时,会直接复制数值;当对象的字段是引用类型时,浅拷贝只是复制了引用地址,新对象和原对象仍然引用同一个内存地址
finally 代码块是否一定执行?
- 无论是否发生异常,
finally代码块都会执行。 - 例外情况:
JVM崩溃- 线程被终止
- 无限循环或死锁导致无法执行到
finally。
BigDecimal
BigDecimal对小数进行运算时,不会出现精度损失
使用BigDecimal(String val)构造方法或者 BigDecimal.valueOf(double val) 静态方法来创建对象:
BigDecimal a = new BigDecimal("1.0");
BigDecimal类型的数据进行比较时应使用compareTo()方法,而不是equals(),因为 equals() 方法不仅仅会比较值的大小(value)还会比较精度(scale),而 compareTo() 方法比较的时候会忽略精度。
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.equals(b));//false,不应该使用equals方法
compareTo() 方法可以比较两个 BigDecimal 的值,如果相等就返回0,如果第1个数比第2个数大则返回1,反之返回-1。
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.compareTo(b));//0
try-with-resources语句
不用再像try...catch...finally那样手动关闭 (close) 资源,使用前要实现 AutoCloseable 接口.
语法:
try (ResourceType res = new ResourceType()) {// 使用 res 执行操作
} catch (ExceptionType e) {// 异常处理
}
- 在
try()中声明资源,调用结束后自动关闭
与传统 try…finally 对比
| 特性 | try-with-resources | 传统 try…finally |
|---|---|---|
| 资源关闭 | 自动调用 close()按逆序关闭支持异常抑制 | 手动调用 close()易漏写或漏捕获 |
| 代码简洁性 | 声明式管理,省略多余 finally 代码 | 大量样板 finally 和嵌套 try…catch |
| 异常处理 | 抑制式管理关闭异常,不丢失原抛出异常 | 关闭异常可覆盖原异常,需手动链式处理 |
Java序列化中如果有些字段不想进行序列化,怎么办?
在Java原生的序列化机制中(即实现 Serializable 接口的类),可以使用 transient 关键字来标记不需要序列化的字段。被 transient 修饰的字段在序列化时会被忽略,反序列化后其值会被重置为默认值(如 0、null 等)。
import java.io.Serializable;public class User implements Serializable {private String username;private transient String password; // 不会被序列化// 构造方法、getter 和 setter 省略
}
序列化
- 序列化:将数据结构或对象转换成可以存储或传输的形式,通常是二进制字节流,也可以是
JSON,XML等文本格式 - 反序列化:将在序列化过程中所生成的数据转换为原始数据结构或者对象的过程
SPI机制
-
解释:
JDK内置的一种服务提供发现机制,用来启用框架扩展和替换组件,,比如java.sql.Driver接口,不同厂商可以针对同一接口做出不同的实现,比如MySQL和PostgreSQL都有不同的实现提供给用户 -
SPI机制可以为某个接口寻找服务实现。其主要思想是将装配的控制权移到程序之外,它的核心思想是解耦。
![[Pasted image 20250508111708.png]]
![[Pasted image 20250508083736.png]]
String的三大技术
-
压缩:
String内部用byte[]存储,节省空间 -
常量池:避免重复创建字符串对象
-
不可变:保证线程安全、哈希值稳定
String 底层数据结构是什么?
- JDK 8 及之前是
final char[], JDK 9 以后是final byte[]
String 不可变性如何实现?
-
存储数据的数组被
final修饰(引用不可变) -
String没有提供修改数组内容的方法 -
String类本身被final修饰
![[Pasted image 20250507091559.png]]
字符串常量池
![[Pasted image 20250507093543.png]]
String s1 = new String(“abc”);这句话创建了几个字符串对象?
答案:会创建1或 2个字符串对象。
如果字符串常量池中已经有一个,则不再创建新的,直接引用;如果没有,则创建一个。
堆中肯定有一个,因为只要使用了new关键字,肯定会在堆中创建一个
intern()方法
String.intern() 方法可以让 String 对象在运行时加入到JVM的字符串常量池, 并返回常量池中该字符串的唯一实例引用,从而保证具有相同字符序列的字符串仅被存储一次,可减少内存占用和提高比较效率。
原理:
-
当执行
s.intern()时,JVM 首先在字符串常量池中查找是否已有与s.equals(...)相等的字符串: -
若存在,则立即返回该已有实例的引用;
-
否则,将
s加入常量池,并返回其自身引用。
示例:
String s1 = new String("Hello");
String s2 = "Hello";
String s3 = s1.intern();System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true
s1 是堆上创建的对象, s2 与 s3 都引用常量池中的 "Hello" 实例,因此 s2 == s3 为 true。
+号拼接字符串
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing"; // 编译期常量 -> 常量池驻留
String str4 = str1 + str2; // 运行时新建对象
String str5 = "string"; // 直接字面量 -> 常量池驻留System.out.println(str3 == str4); // false
System.out.println(str3 == str5); // true
System.out.println(str4 == str5); // false
什么是泛型?
-
一种参数化类型机制, 允许在定义类,接口,方法的时候使用参数, 可以提高代码的复用性、类型安全性(比如集合使用泛型来约束元素类型)
-
类型擦除 : 代码在编译阶段会移除泛型信息,把泛型信息替换成原始类型(通常是Object)
Exception 和 Error的区别
Exception 和 Error 都是 Throwable 类的子类
Throwable
├── Error // 严重错误(程序无法处理)
└── Exception // 程序可处理的异常├── RuntimeException // 非受检异常(Unchecked)└── 其他Exception // 受检异常(Checked)
| 特征 | Error | Exception |
|---|---|---|
| 严重性 | JVM 无法处理的问题 | 程序可处理的异常 |
| 是否可恢复 | 不可恢复(程序应终止) | 可恢复(通过捕获处理) |
| 是否强制捕获 | 非受检(无需 try-catch 或 throws) | 受检异常需处理,非受检异常(如 RuntimeException)不用处理 |
| 典型示例 | OutOfMemoryError、StackOverflowError | IOException、NullPointerException |
给你个jar包,里面有A类,B类,你要写C类来扩展它们。
- 已有类
A和B,需要编写类C来扩展它们的功能。 - Java 不支持多继承,直接继承
A和B不可行。 - 最终方案是:
- 创建
A的子类AChild和B的子类BChild。 - 在
C类中组合AChild和BChild的实例。
- 创建
设计原则:
-
开闭原则:通过子类扩展(而非修改父类)实现功能增强。
-
组合优于继承:用组合实现多逻辑复用,避免多重继承的复杂性。
-
单一职责原则:
AChild和BChild各自只负责对父类功能的扩展,C负责组合逻辑。
// 原有类
public class A {public void log(String message) {System.out.println("Log: " + message);}
}public class B {public boolean validate(String input) {return input != null;}
}// 扩展子类
public class AChild extends A {@Overridepublic void log(String message) {super.log("[Enhanced] " + message); // 增强日志格式}
}public class BChild extends B {@Overridepublic boolean validate(String input) {return super.validate(input) && input.length() > 0; // 增加非空校验}
}// 组合类
public class C {private AChild aChild = new AChild();private BChild bChild = new BChild();public void process(String input) {if (bChild.validate(input)) {aChild.log("Valid input: " + input);} else {aChild.log("Invalid input: " + input);}}
}
基本数据类型和引用数据类型对比
| 维度 | 基本数据类型 | 对象类型 |
|---|---|---|
| 存储内容 | 直接存储值 | 存储引用 |
| 内存位置 | 值存储在栈内存 | 引用在栈,对象在堆内存 |
是否可为 null | 不能 | 可以 |
| 性能 | 操作速度快 | 操作较慢 |
| 内存占用 | 固定大小(如 int 占 4 字节) | 对象内存包含对象头、字段等,通常更大 |
| 方法调用 | 按值传递(复制值) | 按引用传递(复制引用地址) |
| 泛型支持 | 不能直接用于泛型(需包装类) | 可直接用于泛型 |
Integer和int的区别
-
integer初始值是null,int初始值是0; -
integer存放在堆内存,int存放在栈内存 -
integer是一个对象类型,封装了很多方法,使用的时候更加灵活
如何理解面向对象?
-
面向过程注重的是解决问题的
步骤, 比如洗衣服,打开洗衣机,放入衣服,启动洗衣机,漂洗,烘干 -
面向对象关注“参与者”(对象),把“人”“洗衣机”“衣服”都看作对象,赋予它们属性与行为,通过对象协作完成业务。
-
还有个计算圆面积的例子, 如果使用面向过程的方法, 是直接定义半径啥的进行计算, 但是如果使用面向对象的方式, 首先会定义一个圆类, 然后提供一些方法来计算圆面积,
多态存在的三个条件
-
存在继承关系
-
子类重写父类的方法
-
向上转型: 通过父类引用指向子类对象
Animal a = new Dog(); // 向上转型
a.speak(); // 调用 Dog 的 speak()
java面向对象,为什么还保留的基本数据类型?
-
基本数据类型的性能效率比对象更高,而且占用的内存更小
-
基本数据类型不需要进行垃圾回收
值传递
Java中将实参传递给方法的方式是值传递:
- 如果参数是基本类型的话,传递的就是基本类型的字面量值的拷贝,会创建副本。
- 如果参数是引用类型,传递的就是实参所引用的对象在堆中地址值的拷贝,同样也会创建副本。
泛型和通配符
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
-
子类构造方法默认会调用父类的无参构造方法,来初始化继承的成员。
-
如果父类没有无参构造方法,子类必须显式调用父类的有参构造方法。
对象实体,对象引用的区别
对象实体
- 是类的实例,包含了类定义的属性和方法。
- 存储在
堆内存中。 - 每次使用
new创建的对象,都是一个新的实体。
对象引用
- 是指向对象实体的变量,存储在
栈内存中。 - 保存了对象实体在堆内存中的地址。
- 多个引用变量可以指向同一个对象实体。
![[Pasted image 20250511203349.png]]
全对.
1. 构造器Constructor是否可被override?为什么?
不能,因为 构造器不是继承自父类的方法, 而且构造器方法名要和类名相同, 如果重写的话,子类的构造方法会和父类相同,所以不满足这个条件, 不属于继承体系的一部分
2. String, StringBuilder和 StringBuffer 的区别是什么?String为什么是不可变的
-
String不可变,StringBuilder,StringBuffer可变 -
String天然线程安全, 但是String拼接生成新对象,性能最差 -
StringBuffer线程安全,性能低, -
StringBuilder线程不安全,性能较高 -
如果是单线程,使用
StringBuilder; 多线程环境下使用StringBuffer
3. 对象的相等与指向他们的引用相等,两者有什么不同?
- 引用相等是指两个引用变量是否指向同一个对象实例,使用
==进行比较,比较的是两个引用变量的内存地址是否相同. - 对象相等是指两个
对象的内容是否相同,使用equals()方法比较。
4. 重载和重写的区别?
-
重载发生发生在同一类中(方法与方法之间),方法名相同而参数类型或者参数个数,顺序不同
-
重写发生在类之间,指的是子类继承父类方法并提供新的实现,方法签名(方法名+参数列表)必须完全相同
5. 在一个静态方法内调用一个非静态成员为什么是非法的?
-
静态方法是属于类的,不依赖于任何对象实例;
-
非静态成员属于对象实例,静态方法在非静态成员存在之前就已经存在了
6. 简述线程,进程的基本概念。以及他们之间关系?
- 程序是指一组有序的指令集合,表示了某个任务的逻辑和操作步骤,并不具备运行能力。
- 进程是程序的一次执行实例,是资源分配的基本单位,进程之间相互独立
- 线程是进程中的一个执行单元,是
CPU调度的基本单位 - 一个程序可以对应多个进程(如多次运行),一个进程可以包含多个线程。线程依赖于进程存在,不能独立于进程存在。
7. 一个类的构造方法的作用是什么 若一个类没有声明构造方法,该程序能正确执行吗 ?为什么?
-
构造方法的作用是在创建对象时初始化对象的状态;
-
能正常运行,如果一个类没有定义任何构造方法,编译器会自动生成一个无参构造方法;
-
但是如果类中已经声明了带参数的构造方法,编译器将不会再自动生成默认的无参构造方法。如果还需要使用无参构造方法,必须显式声明;
9. 成员变量与局部变量的区别有哪些?
-
成员变量定义在
类中方法外, 整个类都可以访问,成员变量储存在堆/方法区中,有默认初始化值 -
局部变量定义在
方法/代码块内, 只有在该方法/代码块内才能访问,局部变量储存在栈中,无默认初始化值
final、finalize 和 finally 的不同之处?
-
final是一个修饰符,可以修饰变量、方法和类。如果final修饰变量,意味着该变量在初始化后不能被改变。修饰类则该类不能被继承; 修饰一个方法时,表明这个方法不能被重写 -
finalize()方法用于在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。 -
finally是一个关键字,与try和catch一起用于异常的处理。finally块一定会被执行,无论在try块中是否有发生异常.但在调用System.exit()、JVM 崩溃、无限循环或进程被强制杀死等极端情况下,finally不会执行
==和equals的区别
==比较基本数据类型时比较的是内容, 比较引用数据类型比较的是地址值(引用)equals比较的是引用,但是有一些类比如String重写之后就是比较的是内容
Java创建对象的五种方式?
(1)new关键字
(2)反射 Class.newInstance 和Constructor.newInstance
try {// 方法1:Class.newInstance()(已过时,推荐Constructor)Class<?> clazz = Class.forName("com.example.Employee");Employee emp1 = (Employee) clazz.newInstance(); // Deprecated in Java 9+// 方法2:Constructor.newInstance()Constructor<Employee> constructor = Employee.class.getDeclaredConstructor();Employee emp2 = constructor.newInstance();
} catch (Exception e) {e.printStackTrace();
}
(3)Clone方法
public class Employee implements Cloneable {private String name;@Overridepublic Employee clone() throws CloneNotSupportedException {return (Employee) super.clone(); // 浅拷贝}
}// 使用克隆创建对象
Employee original = new Employee("Alice");
Employee copy = original.clone();
(4)反序列化
接口和抽象类的区别
-
类是单继承的(包括抽象类), 但一个类可以实现多个接口,接口是多继承;
-
成员变量:接口中的成员变量只能是
public static final类型的,不能被修改且必须有初始值。抽象类的成员变量可以有任何修饰符(private,protected,public),可以在子类中被重新定义或赋值。 -
抽象类可以有构造方法, 接口没有构造方法
抽象类和普通类的区别?
-
抽象类用
abstract关键字定义,不能被实例化,只能作为其他类的父类。普通类没有abstract关键字,可以实例化。 -
抽象类可以包含
抽象方法和非抽象方法。抽象方法没有方法体,必须由子类实现。普通类只能包含非抽象方法。
abstract class Animal {// 抽象方法public abstract void makeSound();// 非抽象方法public void eat() {System.out.println("This animal is eating.");}
}
throw和throws
throw用于抛出异常throws用于方法签名中声明该方法可能抛出的异常
Static可以修饰哪些?
1.1 成员变量(静态变量)
使用 static 修饰的字段称为静态变量(或类变量),在类加载时分配内存,所有对象实例共享同一份数据。
public class Example {public static int count = 0; // 静态变量
}
1.2 成员方法(静态方法)
静态方法,属于类本身,可通过 ClassName.method() 调用,不依赖于具体实例。
public class Example {public static void printHello() { // 静态方法System.out.println("Hello");}
}
1.3 静态初始化块
静态初始化块在类第一次加载时执行一次,用于对复杂静态变量进行初始化。
public class Example {public static Map<String, String> map;static {map = new HashMap<>();map.put("key", "value");}
}
集合体系结构
![[Pasted image 20250515134740.png]
有一个list,里面的元素是String类型,需要找出里面值为"abc"的并remove掉,该怎么做?
-
注意不能在增强
for循环中直接调用list.remove()方法,否则会触发ConcurrentModificationException错误示例:
List<String> list = new ArrayList<>(Arrays.asList("abc", "def", "abc"));
for (String s : list) { if ("abc".equals(s)) {list.remove(s); // 直接调用 list.remove() ❌}
}
- 使用迭代器:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String s = iterator.next();if ("abc".equals(s)) { // 避免空指针异常iterator.remove(); // 安全移除}
}
ArrayList 的底层数据结构是什么?如何扩容?时间复杂度?
-
底层数据结构是动态数组,支持快速随机访问
-
扩容机制: 使用无参构造方法进行初始化时,默认容量是
0, 第一次添加元素时扩容到10, 以后如果容量不足,会触发扩容,新容量为旧容量的1.5倍. -
如果一次添加多个元素,1.5倍扩容后还放不下,则容量大小为实际元素个数为准
-
时间复杂度: 尾部插入
O(1),中间插入O(n), 随机访问O(1), 删除元素O(n)
ArrarList和LinkedList区别
-
ArrayList底层基于动态数组, 适合读多写少的场景,而且线程不安全 -
LinkedList底层基于链表,适合写多、读操作不依赖随机访问的场景; -
对于 删除 和 新增元素 操作谁的性能更好, 要看情况, 若只对单条数据插入或删除,
ArrayList的速度更好。如果是批量随机的插入删除数据,LinkedList的速度更好, 因为ArrayList每插入一条数据,要移动插入点及之后的数据。
LinkedList 为什么不能实现 RandomAccess 接口?
RandomAccess 是一个标记接口,用来表明实现该接口的类支持随机访问(即可以通过索引访问元素)。 LinkedList 底层数据结构是链表,内存地址不连续,只能通过指针来定位,不支持随机快速访问,所以不能实现 RandomAccess 接口。
HashMap 的底层实现原理是什么?JDK 1.8 之前和之后的区别?
- 底层实现:- JDK 1.8 前是数组 + 链表(拉链法解决哈希冲突)。JDK 1.8 后是数组 + 链表/红黑树(当链表长度 ≥ 8 时,链表转为红黑树;当树节点数 ≤ 6 时,退化为链表)。
HashCode方法
为什么两个对象的 hashCode 值相等,它们也不一定是相等的?
答: 因为 hashCode() 使用的哈希算法可能会让不同对象产生相同的哈希值
总结:
- 如果两个对象的
hashCode值不相等,则这两个对象不相等 - 如果两个对象的
hashCode值相等,那这两个对象不一定相等(哈希碰撞)。 - 如果两个对象的
hashCode值相等并且equals()方法也返回true,这两个对象才相等。
为什么重写 equals() 时必须重写 hashCode() 方法?
有规范指定需要同时重写hashcode与equals是方法,许多容器如HashMap、HashSet都依赖于这个规范。
因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。
保持哈希契约的一致性
Java的规范规定:如果两个对象通过 equals() 方法比较相等,那么它们的 hashCode() 方法必须返回相同的整数。这是为了确保在哈希结构中能够正确地存储和检索对象。如果只重写了 equals() 而没有重写 hashCode(),可能会导致逻辑上相等的对象被分配到不同的哈希桶中,从而无法正确地查找或去重。
保证集合操作的正确性
以 HashMap 为例,其 put() 和 get() 方法首先会根据键对象的 hashCode() 计算哈希值,然后在对应的桶中通过 equals() 方法查找匹配的键。如果 equals() 和 hashCode() 的实现不一致,可能会导致无法找到已存储的键,造成数据丢失或重复存储
map的key可以重复吗,能不能为空?
Map不允许重复的键(key)- 是否允许
null键取决于具体的Map实现;例如HashMap和LinkedHashMap允许一个null键,而Hashtable、ConcurrentHashMap、TreeMap等不允许。
为什么 HashSet 存和取的顺序不一样?
HashSet 基于哈希表 实现的,不保证元素的插入顺序。
HashSet 使用什么机制进行数据去重
使用的是哈希值(hashCode)+ equals 方法来判断元素是否重复。
具体如下:
-
当添加元素时,先计算元素的
hashCode()值; -
然后查找该哈希值所在的桶(bucket);
-
在桶中会遍历已有元素,通过
equals()比较是否有“相等”的元素:-
如果有,认为是重复元素,不添加;
-
如果没有,则加入。
-
集合遍历的几种方式
-
增强 for 循环
foreach -
forEach循环 -
普通
for循环 -
迭代器
-
Stream API遍历
synchronized 和 java.util.concurrent.locks.Lock 的异同?
相同:
- 都用于解决线程安全问题
- 都能保证可见性和原子性
- 都使用互斥机制
不同 :
-
synchronized是一个关键字,Lock是一个接口 -
Lock的锁控制比synchronized更加灵活,可以更精确的控制锁的范围和粒度 -
synchronized的使用更加简单
线程的创建方式
-
继承
Thread类 -
实现
Runnable接口 -
使用
Callable接口和Future接口 -
使用
线程池创建线程
线程有哪些基本状态?
-
新建(
NEW):线程刚创建但hai未启动 -
可运行(RUNNABLE):调用线程的
start()方法后,线程进入可运行状态,等待被线程调度器分配CPU时间片执行。 -
阻塞(BLOCKED):线程在等待获取一个被其他线程持有的监视器锁时进入阻塞状态。
-
等待(WAITING):线程无限期地等待另一个线程执行特定操作(如
notify()或notifyAll())以唤醒它。 -
计时等待(TIMED_WAITING):线程在指定的时间内等待另一个线程的操作,超过时间后自动唤醒。常见的方法包括
sleep()、join(long)、wait(long)等。 -
终止(TERMINATED):线程执行完毕或因异常退出,进入终止状态。
反射是什么?
-
反射是指在程序运行时,对于任意一个类,都能获取到这个类的所有属性和方法,对于任意一个对象,都能调用它的任意属性和方法
-
反射允许程序在运行时动态地获取类的信息并操作类的属性、方法、构造器等
-
传统方式是 “通过类创建对象”,而反射是 “通过对象反向获取类的信息”
Spring,SpringBoot相关
- JDK,JRE,JVM
- Java语言的特点
- Java常见的运行时异常
- Java为什么要封装
- 自增自减
- +=的隐式转换
- 移位运算符
- 1. 左移运算符(`<<`)
- 2. 带符号右移运算符(`>>`)
- 3. 无符号右移运算符(`>>>`)
- 可变参数
- break,continue,return 的区别及作用?
- this 关键字有什么作用?
- 深拷贝
- 浅拷贝
- finally 代码块是否一定执行?
- BigDecimal
- try-with-resources语句
- 语法:
- 与传统 `try…finally` 对比
- `Java`序列化中如果有些字段不想进行序列化,怎么办?
- 序列化
- SPI机制
- String的三大技术
- String 底层数据结构是什么?
- String 不可变性如何实现?
- 字符串常量池
- String s1 = new String("abc");这句话创建了几个字符串对象?
- intern()方法
- +号拼接字符串
- 什么是泛型?
- `Exception` 和 `Error`的区别
- 给你个jar包,里面有A类,B类,你要写C类来扩展它们。
- 基本数据类型和引用数据类型对比
- Integer和int的区别
- 如何理解面向对象?
- 多态存在的三个条件
- java面向对象,为什么还保留的基本数据类型?
- 值传递
- 泛型和通配符
- 在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
- 对象实体,对象引用的区别
- 对象实体
- 对象引用
- 1. 构造器`Constructor`是否可被`override`?为什么?
- 2.` String`, `StringBuilder`和 `StringBuffer` 的区别是什么?`String`为什么是不可变的
- 3. 对象的相等与指向他们的引用相等,两者有什么不同?
- 4. 重载和重写的区别?
- 5. 在一个静态方法内调用一个非静态成员为什么是非法的?
- 6. 简述线程,进程的基本概念。以及他们之间关系?
- 7. 一个类的构造方法的作用是什么 若一个类没有声明构造方法,该程序能正确执行吗 ?为什么?
- 9. 成员变量与局部变量的区别有哪些?
- final、finalize 和 finally 的不同之处?
- `==`和`equals`的区别
- Java创建对象的五种方式?
- 接口和抽象类的区别
- 抽象类和普通类的区别?
- throw和throws
- Static可以修饰哪些?
- 1.1 成员变量(静态变量)
- 1.2 成员方法(静态方法)
- 1.3 静态初始化块
- 集合体系结构
- 有一个list,里面的元素是String类型,需要找出里面值为"abc"的并remove掉,该怎么做?
- ArrayList 的底层数据结构是什么?如何扩容?时间复杂度?
- ArrarList和LinkedList区别
- LinkedList 为什么不能实现 RandomAccess 接口?
- HashMap 的底层实现原理是什么?JDK 1.8 之前和之后的区别?
- HashCode方法
- 为什么两个对象的 `hashCode` 值相等,它们也不一定是相等的?
- 总结:
- 为什么重写 equals() 时必须重写 hashCode() 方法?
- 保持哈希契约的一致性
- 保证集合操作的正确性
- map的key可以重复吗,能不能为空?
- 为什么 HashSet 存和取的顺序不一样?
- HashSet 使用什么机制进行数据去重
- 集合遍历的几种方式
- synchronized 和 java.util.concurrent.locks.Lock 的异同?
- 线程的创建方式
- 线程有哪些基本状态?
- 反射是什么?
- IOC和AOP是通过什么机制来实现的?
- Spring的核心思想,说说你的理解?
- Spring事务
- Spring事务的实现原理
- `BeanFactory` 和 `ApplicationContext`有什么区别?
- AOP 面向切面编程
- Spring IOC 是什么?使用场景有哪些?Spring 事务,事务的属性,数据库隔离级别
- IOC中的bean
- 什么是Spring的bean?
- Bean 的生命周期
- Bean 的配置方式:
- Bean的作用域
- 依赖注入的三种方式
- 单例bean是线程安全的吗
- 单例bean和非单例的生命周期是否一样?
- Spring 如何解决循环依赖?
- Spring MVC 的核心组件有哪些?
- MVC分层
- 单点登录
- cookie和session的区别
- get和post请求的区别
- HTTP请求状态码
- 过滤器和拦截器的区别
- HTTP方法
- Spring 设计模式
- Spring Boot、Spring MVC 和 Spring 有什么区别?
- SpringMVC 怎么样设定重定向和转发的?
- 当一个方法向`AJAX`返回特殊对象,比如`Object`,`List`等,需要做什么处理?
- SpringMVC 用什么对象从后台向前台传递数据的?
- @Component 和 @Bean 的区别?
- @Autowired和@Resource注解的区别
- @Controller和@RestController注解的区别
- 将一个类声明为 Bean 的注解有哪些?
- Spring Boot自动装配原理**
- 为什么在`web`开发中,声明控制器 `bean` 只能用 `@Controller`?
- 依赖冲突问题
- @Transactional注解
- SpringBoot原理, SpringBoot为什么大大简化了 `Spring` 开发?
- SpringBoot中哪里用到了反射知识?
- 配置文件相关
- Spring Boot 四大核心注解
IOC和AOP是通过什么机制来实现的?
-
IOC通过反射,依赖注入,工厂模式,容器机制来实现 -
AOP基于动态代理来实现,有JDK动态代理和CGLB动态代理
Spring的核心思想,说说你的理解?
![[Pasted image 20250529104755.png]]
Spring事务
-
Spring事务保证一组数据库操作要么全部成功,要么全部失败(原子性) -
分为声明式事务和编程式事务,声明式事务通过注解,如
@Transactional实现。编程式事务通过
TransactionTemplate 或 PlatformTransactionManager 手动控制。
-
事务属性:
propagation(传播行为):如REQUIRED、REQUIRES_NEW等
isolation(隔离级别):见下文
timeout:超时时间
readOnly:只读事务(可优化性能)
rollbackFor:哪些异常触发回滚 -
数据库隔离级别:
| 隔离级别 | 说明 | 可避免问题 |
|---|---|---|
READ UNCOMMITTED | 读未提交 | 无法避免任何问题 |
READ COMMITTED | 读已提交(Oracle 默认) | 避免脏读 |
REPEATABLE READ | 可重复读(MySQL 默认) | 避免脏读、不可重复读 |
SERIALIZABLE | 串行化(最高级别) | 避免所有并发问题 |
Spring事务的实现原理
- 核心机制:通过
AOP动态代理管理事务。 - 流程:
- 使用
@Transactional注解标记方法。 - 生成代理对象,在方法执行前开启事务(
beginTransaction())。 - 方法执行成功则提交事务(
commit()),失败则回滚(rollback())。
- 使用
BeanFactory 和 ApplicationContext有什么区别?
-
BeanFactory:Spring的基础容器,提供基本的依赖注入功能,采用延迟加载。 -
ApplicationContext:BeanFactory的子接口,是更高级的容器,支持国际化、事件发布、AOP等,采用预加载策略。
![[Pasted image 20250513142459.png]]
- 延迟加载: 真正需要
bean时才创建bean实例
AOP 面向切面编程
-
AOP是面向切面编程, 将和业务无关的共性代码逻辑封装起来,减少重复的代码,降低模块之间的耦合度, 使用场景有日志记录和权限控制,性能监控等 -
AOP的实现原理是动态代理(JDK动态代理,CGLB代理) -
切入点:需要织入逻辑(增强功能)的方法。
-
通知:织入的具体逻辑(增强功能的代码逻辑),如前置、后置、环绕通知。
-
切面:绑定通知与切入点的关系。
-
用途:统一日志处理、性能监控、事务管理
-
好处: 减少代码冗余,提升可维护性。
Spring IOC 是什么?使用场景有哪些?Spring 事务,事务的属性,数据库隔离级别
-
IOC(控制反转) : 对象创建、依赖管理的控制权由程序本身转移到IOC容器 -
IOC的好处 : 解耦合,提高程序可维护性和可测试性 -
容器的作用是创建
bean实例并管理bean的生命周期,并实现依赖注入 -
DI(依赖注入)是实现IOC的一种方式, 就是将依赖对象注入到目标对象中(给对象传递需要使用的其他对象),实现解耦合 -
DI的实现方式有setter注入,构造器注入,字段注入(使用@Autowired注解) -
DI的使用场景 : 控制对象生命周期,自动注入依赖对象(如@Autowired), 配置管理(如使用application.yml或@Value等)
//Setter 注入
public class UserService {private UserRepository userRepository;public void setUserRepository(UserRepository userRepository) {this.userRepository = userRepository;}
}
//构造器注入
public class UserService {private final UserRepository userRepository;public UserService(UserRepository userRepository) {this.userRepository = userRepository;}
}
//字段注入
public class UserService {@Autowiredprivate UserRepository userRepository;
}
IOC中的bean
-
IOC容器中,默认bean对象只有一个实例对象(单例模式)。 -
第三方的
bean不能通过使用@Autowired,@Componet等方式注入,这时要采用配置类中使用@Bean注解的方式:
@Configuration
public class ExternalServiceConfig {@Beanpublic ExternalService externalService() {// ExternalService 来自第三方库,无法加 @Componentreturn new ExternalService("apiKey", timeout);}
}
什么是Spring的bean?
Spring的Bean,是由Spring容器“产生"并管理”的Java对象,bean的创建、配置以及生命周期都由容器负责
Bean 的生命周期
-
Bean实例化:通过反射或工厂方法创建Bean实例。 -
属性赋值:为
Bean设置相关属性和依赖 -
初始化:完成
Bean的初始化。 -
使用:
Bean处于活动状态,可以在应用程序中使用。 -
销毁:容器关闭时调用销毁方法释放资源
Bean 的配置方式:
-
XML配置:通过<beans>和<bean>标签定义Bean。 -
使用注解(如
@Component、@Service、@Controller、@Repository) 自动扫描注册Bean。 -
使用
@Configuration配置类结合@Bean注解注册Bean。
Bean的作用域
-
Singleton(单例): 同一IoC容器中仅创建一个实例 -
Prototype(原型): 每次请求(getBean()或注入)均产生新实例 -
Request: 每个 HTTP 请求对应一个实例 -
Session: 每个 HTTP 会话(Session)对应一个实例 -
Application: 整个ServletContext(Web 应用) 中仅一个实例 -
WebSocket: 每个WebSocket会话对应一个实例
依赖注入的三种方式
-
构造器注入:通过构造方法注入
-
Setter注入:通过Setter方法注入 -
字段注入:直接在字段上使用
@Autowired注解
单例bean是线程安全的吗
取决于Bean的设计。默认情况下,无状态的单例Bean是线程安全的,而有状态的单例Bean可能存在线程安全问题。
无状态 : 不能被修改,比如service层的接口
有状态 : 比如成员变量
单例bean和非单例的生命周期是否一样?
不一样, spring只管理单例bean的生命周期,对于非单例的bean,spring创建好之后,就不会管理后续的生命周期了
Spring 如何解决循环依赖?
Spring通过三级缓存来解决循环依赖问题.
Spring创建Bean的流程:
-
先去
一级缓存中获取,存在就返回。 -
如果不存在或者对象正在创建中,去
二级缓存中获取。 -
如果还没有获取到,就去
三级缓存中获取,通过执行ObjectFactory的getObject()就可以获取该对象,获取成功之后,从三级缓存移除,并将该对象加入到二级缓存。
Spring MVC 的核心组件有哪些?
-
DispatcherServlet:核心的中央处理器,负责接收请求、分发,并给予客户端响应。 -
HandlerMapping:处理器映射器,根据 URL 去匹配查找能处理的Handler,并会将请求涉及到的拦截器和Handler一起封装。 -
HandlerAdapter:处理器适配器,根据HandlerMapping找到的Handler,适配执行对应的Handler; -
Handler:请求处理器,处理实际请求的处理器。 -
ViewResolver:视图解析器,根据Handler返回的逻辑视图 / 视图,解析并渲染真正的视图,并传递给DispatcherServlet响应客户端
MVC分层
-
M是模型,V是视图,C是控制器
-
视图V为用户提供交互界面
-
模型M, 代表存储数据的载体或者
java pojo, 分为两类:数据承载bean和业务处理bean -
数据承载
bean如数据传输对象(DTO) -
业务处理
bean如服务层(Service) -
控制器C处理请求
单点登录
-
身份提供者:集中式认证中心,用户首次登录后颁发
JWT Token。 -
服务提供者:各应用接收并校验
Token,有效则允许访问。 -
Token传递:可通过HTTP Header、Cookie或重定向参数传递
cookie和session的区别
-
存储位置:
Cookie在客户端(浏览器),Session在服务器端。 -
Cookie占用内存更小,容易暴露,Session存储容量更大,更安全。
get和post请求的区别
-
get请求参数写在url后面,暴露在地址栏,而且url有长度限制 -
post请求的参数写在请求体中,没有长度限制 -
传输敏感数据时推荐使用
post
HTTP请求状态码
-
200 OK:请求成功。 -
301 Moved Permanently:资源被永久移动到新URI,应使用新的URI。 -
302 Found:资源临时位于不同URI,应继续使用原URI发起请求。 -
400 Bad Request:请求格式错误。 -
401 Unauthorized:请求需要身份验证。 -
403 Forbidden:认证成功但权限不足。 -
404 Not Found:请求的资源不存在。 -
405 Method Not Allowed:请求方法不允许。 -
500 Internal Server Error:服务器内部错误 -
502 Bad Gateway:网关/代理错误,上游服务器无响应
过滤器和拦截器的区别
-
Filter(过滤器)是Servlet规范,Interceptor(拦截器)属于Spring MVC层 -
Filter可以拦截所有请求,包括静态资源。 -
Interceptor只能拦截DispatcherServlet处理的请求(即控制器Controller请求)。
HTTP方法
| 请求类型 | 描述 |
|---|---|
| GET | 获取资源 |
| POST | 创建新资源 |
| PUT | 更新现有资源 |
| DELETE | 删除资源 |
| PATCH | 部分更新现有资源 |
Spring 设计模式
-
代理模式:比如
Spring AOP,通过JDK动态代理和CGLIB代理实现 -
单例模式:
Spring默认将每个Bean定义作为单例管理,一个容器中同一Bean只产生一个实例,便于集中管理和资源复用。 -
模板方法模式:比如
JmsTemplate、JdbcTemplate、JpaTemplate,将固定流程抽象成模板,减少重复代码 。 -
工厂模式:通过
BeanFactory/FactoryBean或自定义工厂方法创建对象,客户端不用关心实例化细节,统一通过接口获取Bean实例,实现了解耦与扩展。
Spring Boot、Spring MVC 和 Spring 有什么区别?
-
Spring:是核心框架,提供依赖注入(IoC)和面向切面编程(AOP)的基础功能,是其他模块的基石。 -
Spring MVC:是基于Spring的Web框架,专注于MVC模式(模型-视图-控制器),用于处理HTTP请求和响应,支持URL路由、模板引擎等Web开发功能。 -
Spring Boot:是Spring的快速开发工具,通过自动配置(如内嵌Tomcat)和Starter依赖简化项目搭建
SpringMVC 怎么样设定重定向和转发的?
-
转发:返回视图名前加
forward:,如return "forward:/path"。 -
重定向:返回视图名前加
redirect:,如return "redirect:/path"。
当一个方法向AJAX返回特殊对象,比如Object,List等,需要做什么处理?
- 使用
@ResponseBody注解将返回值序列化为JSON/XML,并配合@RestController或配置消息转换器(如 Jackson)。示例:
SpringMVC 用什么对象从后台向前台传递数据的?
- 可以使用
Model、ModelMap或ModelAndView对象来传递数据
@Component 和 @Bean 的区别?
-
@Component用于类,@Bean用于方法。 -
@Component标注的类不用编写配置,@Bean定义的Bean需要在配置类中定义方法来返回对象 -
@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如注入第三方的类成为bean时,只能通过@Bean来实现。
@Autowired和@Resource注解的区别
| @Autowired | @Resource | |
|---|---|---|
| 自动装配机制 | 基于类型进行自动装配。如果多个匹配,需结合 @Qualifier按名称选择。 | 优先按名称装配(指定 name 属性),若找不到对应名称的Bean,则按类型装配。 |
| 注入位置 | 可用于字段、构造函数、方法参数等多种场景。 | 常用于字段和方法参数的注入。 |
| 灵活性 | 配合@Qualifier可灵活指定注入的 Bean,适合复杂场景。 | 主要通过名称查找,灵活性稍逊于 @Autowired 和 @Qualifier 的组合。 |
@Controller和@RestController注解的区别
-
@Controller返回的是视图名称,需要结合模型和视图解析器进行页面渲染。 -
@RestController将方法返回值直接填入HTTP响应体中,返回的通常是JSON。
将一个类声明为 Bean 的注解有哪些?
-
@Component:通用注解,可标注任意类为Spring组件。如果一个不知道Bean属于哪个层,可以使用@Component注解。 -
@Repository: 持久层 (Dao层),主要用于数据库相关操作。 -
@Service: 服务层,主要涉及一些复杂的逻辑,需要用到Dao层。 -
@Controller: 对应Spring MVC控制层,主要用于接受用户请求并调用Service层返回数据给前端页面
Spring Boot自动装配原理**
使用 @EnableAutoConfiguration 读取 META-INF/spring.factories 中的配置类,在满足一定条件下将对应 Bean 注入容器,从而实现按需加载的“约定优于配置”
为什么在web开发中,声明控制器 bean 只能用 @Controller?
-
Spring MVC在启动时会扫描所有被@Controller或@RestController标注的类,注册为Handler。 -
如果使用
@Component,虽然这个类被注入了Spring容器,但它不会被当作控制器,不能接收HTTP请求。
依赖冲突问题
@Autowired 注入依赖时,是按类型注入的。如果同一个类型的Bean有多个,Spring不知道注入哪一个,会报错, 解决方式如下:
@Primary: 当存在多个同类型的Bean时,标注了 @Primary 的Bean会被默认注入。
@Component
@Primary
public class Apple implements Fruit {//...
}
@Component
public class Banana implements Fruit {//...
}
@Autowired
private Fruit fruit; // 注入 Apple,因为它被标注为 @Primary
@Qualifier: 明确指定要注入哪一个Bean(按名称注入),搭配 @Autowired 使用。
@Component("apple")
public class Apple implements Fruit {//...
}@Component("banana")
public class Banana implements Fruit {//...
}@Autowired
@Qualifier("banana") //bean名字默认为类名首字母小写
private Fruit fruit; // 注入 banana Bean
@Resource:按名称注入,类似于 @Autowired + @Qualifier。但默认按名称注入,找不到才按类型注入。
@Component("banana")
public class Banana implements Fruit {//...
}@Resource(name = "banana")
private Fruit fruit; // 注入 banana Bean
注意:@Resource 不能和 @Qualifier 一起使用,也不支持 @Primary。
@Transactional注解
使用 @Transactional 注解时,只有出现RuntimeException才回滚异常。rollbackFor属性用于控制出现何种异常类型时, 回滚事务。
@Transactional(rollbackFor = Exception.class)
public void someMethod() throws Exception {// 即使是受检异常,也会触发回滚throw new Exception("Checked Exception");
}
SpringBoot原理, SpringBoot为什么大大简化了 Spring 开发?
-
在于起步依赖和自动配置两方面, 只需引入
SpringBoot的起动依赖, 就间接引入了很多其他依赖,web开发所需要的所有的依赖都有了 (得益于Maven的依赖传递) -
自动配置就是当容器启动后,一些配置类、
bean对象就自动存入到了容器中,不需要手动声明,从而简化了开发,省去了繁琐的配置。 -
如何实现自动配置? 原理就是在配置类中定义一个
@Bean标识的方法,Spring会自动调用配置类中使用@Bean标识的方法,并把方法的返回值注入到IOC容器中
SpringBoot中哪里用到了反射知识?
依赖注入(DI): Spring Boot使用反射来实现依赖注入.
组件扫描:SpringBoot 通过扫描包路径来发现和注册组件(例如,@Controller、@Service、@Repository等)。
AOP面向切面: Spring Boot 使用 AOP 实现一些横切关注点,如事务管理、日志记录等。
动态代理: Spring Boot 中的缓存、事务管理等,使用了动态代理。动态代理是通过反射在运行时创建代理对象的一种机制。
配置属性处理: @ConfigurationProperties 注解将外部配置(如 application.yml)映射到pojo中,Spring Boot 通过反射遍历目标类的字段并调用相应的 setter方法,将值注入到对象中
配置文件相关
-
Spring Boot 配置文件优先级: 命令行参数 > 系统属性参数 >
properties参数 >yml参数 >yaml参数 -
加载顺序:YAML(
.yml/.yaml)先加载,随后加载.properties,以便让.properties覆盖同名配置
Spring Boot 四大核心注解
-
@SpringBootApplication
一个组合注解, 组合了@SpringBootConfiguration、@EnableAutoConfiguration与@ComponentScan,用于标识主配置类并触发自动配置与组件扫描。 -
@EnableAutoConfiguration
根据类路径中的依赖和已声明的@Bean,自动加载并配置常见组件,其实现依赖于META-INF/spring.factories中的自动配置类列表。 -
@ComponentScan
指定包路径下的组件(@Component、@Service、@Repository、@Controller等)自动注册到IoC容器,支持基于注解的组件扫描与发现 -
@ConfigurationProperties
将外部化配置(application.properties/.yml)映射到POJO中,支持类型安全的属性绑定和校验,常与@EnableConfigurationProperties一同使用。
相关文章:
Java面试八股(Java基础,Spring,SpringBoot篇)
java基础 JDK,JRE,JVMJava语言的特点Java常见的运行时异常Java为什么要封装自增自减的隐式转换移位运算符1. 左移运算符(<<)2. 带符号右移运算符(>>)3. 无符号右移运算符(>>>) 可变…...
Python编程基础(二)| 列表简介
引言:很久没有写 Python 了,有一点生疏。这是学习《Python 编程:从入门到实践(第3版)》的课后练习记录,主要目的是快速回顾基础知识。 练习1: 姓名 将一些朋友的姓名存储在一个列表中…...
支持向量机(SVM):解锁数据分类与回归的强大工具
在机器学习的世界中,支持向量机(Support Vector Machine,简称 SVM)一直以其强大的分类和回归能力而备受关注。本文将深入探讨 SVM 的核心功能,以及它如何在各种实际问题中发挥作用。 一、SVM 是什么? 支持…...
代谢组数据分析(二十五):代谢组与蛋白质组数据分析的异同
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍蛋白质组定义与基因的关系蛋白质组学(Proteomics)检测技术蛋白质的鉴定与定量分析蛋白质“鉴定”怎么做蛋白质“定量”怎么做蛋白质鉴定与定量对比应用领域代谢组定义代谢组学(M…...
002 flutter基础 初始文件讲解(1)
在学习flutter的时候,要有“万物皆widget”的思想,这样有利于你的学习,话不多说,开始今天的学习 1.创建文件 进入trae后,按住ctrlshiftP,输入Flutter:New Project,回车,…...
AI 让无人机跟踪更精准——从视觉感知到智能预测
AI 让无人机跟踪更精准——从视觉感知到智能预测 无人机跟踪技术正在经历一场前所未有的变革。曾经,我们只能依靠 GPS 或简单的视觉识别来跟踪无人机,但如今,人工智能(AI)结合深度学习和高级视觉算法,正让无人机的跟踪变得更加智能化、精准化。 尤其是在自动驾驶、安防监…...
Launcher3体系化之路
👋 欢迎来到Launcher 3 背景 车企对于桌面的排版布局好像没有手机那般复杂,但也有一定的需求。部分场景下,要考虑的上下文比手机要多一些,比如有如下的一些场景: 手车互联。HiCar,CarPlay,An…...
用wireshark抓了个TCP通讯的包
昨儿个整理了下怎么用wireshark抓包,链接在这里:捋捋wireshark 今天打算抓个TCP通讯的包试试,整体来说比较有收获,给大家汇报一下。 首先就是如何搞到可以用来演示TCP通讯的客户端、服务端,问了下deepseek,…...
VR/AR 显示瓶颈将破!铁电液晶技术迎来关键突破
在 VR/AR 设备逐渐走进大众生活的今天,显示效果却始终是制约其发展的一大痛点。纱窗效应、画面拖影、眩晕感…… 传统液晶技术的瓶颈让用户体验大打折扣。不过,随着铁电液晶技术的重大突破,这一局面有望得到彻底改变。 一、传统液晶技术瓶颈…...
【前端】Vue中实现pdf逐页转图片,图片再逐张提取文字
给定场景:后端无法实现pdf转文字,由前端实现“pdf先转图片再转文字”。 方法: 假设我们在< template>中有一个元素存放我们处理过的canvas集合 <div id"canvasIDpdfs" />我们给定一个按钮,编写click函数&…...
焦虑而烦躁的上午
半年了,每逢周末或者节假日都被催着去医院。 今天早上依旧,还在睡梦之中,就被喊醒“赶紧得,抢上儿童医院的票了!” 无奈,从床上爬起来,草草用过早餐之后,奔赴儿童医院!…...
Python使用
Python学习,从安装,到简单应用 前言 Python作为胶水语言在web开发,数据分析,网络爬虫等方向有着广泛的应用 一、Python入门 相关基础语法直接使用相关测试代码 Python编译器版本使用3以后,安装参考其他教程…...
分类预测 | Matlab实现CNN-LSTM-Attention高光谱数据分类
分类预测 | Matlab实现CNN-LSTM-Attention高光谱数据分类 目录 分类预测 | Matlab实现CNN-LSTM-Attention高光谱数据分类分类效果功能概述程序设计参考资料 分类效果 功能概述 代码功能 该MATLAB代码实现了一个结合CNN、LSTM和注意力机制的高光谱数据分类模型,核心…...
【解决方案-RAGFlow】RAGFlow显示Task is queued、 Microsoft Visual C++ 14.0 or greater is required.
目录 一、长时间显示:Task is queued 二、GraphRAG消耗大量Token 三、error: Microsoft Visual C 14.0 or greater is required. Get it with “Microsoft C Build Tools“ 四、ModuleNotFoundError: No module named infinity.common; infinity is not a package 五…...
爬虫到智能数据分析:Bright Data × Kimi 智能洞察亚马逊电商产品销售潜力
前言 电商数据分析在现代商业中具有重要的战略价值,通过对消费者行为、销售趋势、商品价格、库存等数据的深入分析,企业能够获得对市场动态的精准洞察,优化运营决策,预测市场趋势、优化广告投放、提升供应链效率,并通…...
高级前端工程师必备的 JS 设计模式入门教程,常用设计模式案例分享
目录 高级前端工程师必备的 JS 设计模式入门教程,常用设计模式案例分享 一、什么是设计模式?为什么前端也要学? 1、设计模式是什么 2、设计模式的产出 二、设计模式在 JS 里的分类 三、常用设计模式实战讲解 1、单例模式(S…...
unix/linux source 命令,其发展历程详细时间线、由来、历史背景
追本溯源,探究技术的历史背景和发展脉络,能够帮助我们更深刻地理解其设计哲学和存在的意义。source 命令(或者说它的前身和等效形式)的历史,与 Unix Shell 本身的发展紧密相连。 让我们一起踏上这段追溯之旅,探索 source 命令的由来和发展历程。 早期 Unix Shell 与命令…...
2023年电赛C题——电感电容测量装置
一、赛题 二、题目分析——损耗角正切值 对于一个正常的正弦波信号,如果通过的是一个电阻或一条导线,那么它的电流信号和电压信号是一致的(有电压才有电流),没有相位差。 但是如果正弦波经过了一个电感或电容…...
pycharm打印时不换行,方便对比观察
原来: 优化: import torch torch.set_printoptions(linewidth200) 优化结果:...
因泰立科技:镭眸T51激光雷达,打造智能门控新生态
在高端门控行业,安全与效率是永恒的追求。如今,随着科技的飞速发展,激光雷达与TOF相机技术的融合,为门控系统带来了前所未有的智能感知能力,开启了精准守护的新时代。因泰立科技的镭眸T51激光雷达,作为这一…...
Microsoft Fabric - 尝试一下Data Factory一些新的特性(2025年5月)
1.简单介绍 Microsoft Fabric是微软提供的一个数据管理和分析的统一平台,感觉最近的新特性也挺多的。 Data Factory是Microsoft Fabric的一个功能模块,也是一个cloud service。Data Factory可以和多种数据源进行连接,同时提供了data movemen…...
NodeJS全栈开发面试题讲解——P10微服务架构(Node.js + 多服务协作)
✅ 10.1 单体架构和微服务的主要区别是什么? 维度单体架构微服务架构模块组织所有功能打包在一个代码仓库中拆分为多个独立服务部署方式部署一次包含全部逻辑各服务独立部署、独立扩缩容开发协作多人协作易冲突团队按服务划分,职责清晰可维护性功能多时…...
【前端】javascript和Vue面试八股
面试暂时没有遇到过考这么深的,一般还是问一些生命周期和性能相关。 Q:什么情况下“ a 1 && a 2 && a 3 ”同时成立 A:对象的valueOf与toString方法:当一个对象与一个原始值(如数字)进…...
WEB3——区块链留言板(留言上链),查看web3日志-入门项目推荐
区块链留言板(留言上链) 目标:构建一个用户可以“写入留言、读取历史留言”的 DApp。 内容: Solidity 编写留言合约,存储留言内容和发送者地址。 提供 API: GET /messages:获取留言列表 POST…...
开源库免费API服务平台 ALLBEAPI
开源库API化平台 ALLBEAPI 🌊 GitHub仓库地址:https://github.com/TingjiaInFuture/allbeapi 为优秀开源库提供免费 API 服务,让开发者无需安装和部署即可直接调用。 🌐 API 接入地址 基础 URL: https://res.allbeapi.top 所…...
【配置vscode默认终端为git bash】
配置vscode默认终端为git bash 点击左下角小齿轮,点击设置,搜索terminal.integrated.profiles.windows,点击在setting.json中编辑 第一部分是当前的所有的终端,第二部分是配置默认的终端"terminal.integrated.defaultProfi…...
Cloudflare
Cloudflare 是一个网络基础设施和网站安全服务提供商,它的主要作用是让网站 更快、更安全、更可靠。简单来说,它是一个“护盾 加速器”。 🧩 Cloudflare 的主要功能: 1. 🚀 加速网站访问(CDN)…...
Cypress + TypeScript + Vue3
🚀 从零构建 Cypress + TypeScript + Vue3 组件测试环境【详细实战教程】 组件测试是前端开发中不可忽视的一环,它能够帮助我们在开发阶段就发现 UI 与交互逻辑问题。本文将带你手把手搭建基于 Cypress + TypeScript + Vue3 的组件测试环境,包含完整目录结构、配置文件、组…...
Oracle DG库控制文件IO错误导致宕机的应急处理
Oracle DG库控制文件IO错误导致宕机的应急处理 事故现场偷天换日棋差一招事故现场 一套Oracle 19c DG环境的备库宕机。 根据告警时间检查实例宕机时间点附近的alert日志有如下重要信息: 2025-05-25T23:34:10.705385+08:00 KCF: read, write or open error, block=0x3377ee …...
技术深度解析:《鸿蒙5.0+:全场景能效的产业革命》
引言:万物智联时代的功耗新范式 产业痛点: 全球IoT设备年耗电量突破200TWh,传统系统架构难以支撑千亿级终端低功耗需求。鸿蒙5.0战略定位: 通过全场景能效架构(端侧极致优化跨端智能…...
