# 2025-02 面试题

## 宁波面试

### 什么是面向对象

> 面向对象（OOP）是一种程序设计范式，主要有：封装、继承、多态、抽象

### 什么是AOP

> AOP是一种 面向切面变成，将系统横切，比如日志内容、事务管理、安全控制，改进代码，通常配合Spring框架进行使用 。

### 什么是接口

> 方法声明、多重实现、常量、默认方法，接口提高了程序的灵活性和扩展性

### 说下ArrayList

> 动态数组，可以动态扩展，存储元素允许存储重复的元素，查询速度快，插入和删除性能较差，常见方法：`add`、`remove`、`clear`、`size`。

### List有哪些实现方法？ 

> 1. **ArrayList**:
> 2. **LinkedList**:
> 3. **Vector**:
> 4. **Stack**:
> 5. **CopyOnWriteArrayList**:
> 6. **Arrays.asList()**:

### ArrayList和LinkedList区别？

> 1. **内部实现**:
>    - ArrayList基于动态数组实现，通过数组存储元素，支持随机访问。
>    - LinkedList基于双向链表实现，每个节点包含对前一个和后一个节点的引用。
> 2. **随机访问**:
>    - ArrayList支持高效的随机访问，因为可以根据索引直接访问数组中的元素。
>    - LinkedList的随机访问效率较低，需要从头或尾部开始遍历链表，直到找到目标位置。
> 3. **空间占用**:
>    - ArrayList在添加元素时，可能需要重新分配内部数组的大小，会导致空间浪费。
>    - LinkedList每个节点都需要额外的空间存储前后节点的引用，可能会占用更多的内存。
> 4. **迭代性能**:
>    - ArrayList通过迭代器（Iterator）进行迭代操作效率较高。
>    - LinkedList在迭代操作时，由于需要遍历链表中的每个节点，性能相对较低。
>
> + **查找**: ArrayList更快（O(1) vs O(n)）。
> + **插入**: 如果是在末尾插入，ArrayList更快；如果在中间或开头插入，LinkedList更快（O(1) vs O(n)）。

### 如果有一个数组要去重怎么做

> 使用`Set`和`Stream`是最简洁和高效的方式，但如果对空间复杂度有严格要求，手动去重或排序加双指针的方法可能更合适。

### 对数组去重要做统计呢？

> 使用Java流做成Map集合，Stream流中GroupingBy利用分组，手动去重

### 说下String相关的API

> + **字面量方式**：`String str = "Hello";`
> + **使用**`new`**关键字**：`String str = new String("Hello");`
> + `length()`：返回字符串的长度（字符数）。
> + `equals(Object obj)`：比较两个字符串的内容是否相同。
> + `equalsIgnoreCase(String anotherString)`：忽略大小写的比较。
> + `compareTo(String anotherString)`：按字典顺序比较两个字符串，返回一个整数。
> + `compareToIgnoreCase(String str)`：忽略大小写的字典顺序比较。
> + `indexOf(String str)`：返回指定子字符串第一次出现的位置。
> + `lastIndexOf(String str)`：返回指定子字符串最后一次出现的位置。
> + `contains(CharSequence sequence)`：判断字符串是否包含指定的字符序列。
> + `charAt(int index)`：返回指定位置的字符。
> + `substring(int beginIndex)`：返回从指定索引到字符串末尾的子字符串。
> + `substring(int beginIndex, int endIndex)`：返回从指定开始索引到结束索引之间的子字符串。
> + `toUpperCase()`：将字符串转换为大写。
> + `toLowerCase()`：将字符串转换为小写。
> + `trim()`：去除字符串首尾的空格。
> + `replace(char oldChar, char newChar)`：替换字符串中的字符。
> + `replace(String target, String replacement)`：替换字符串中的子字符串。
> + `replaceAll(String regex, String replacement)`：根据正则表达式替换字符串中的子字符串。
> + `split(String regex)`：根据指定的正则表达式分割字符串，返回字符串数组。
> + `split(String regex, int limit)`：根据指定的正则表达式分割字符串，返回字符串数组，指定数组的最大长度。
> + `concat(String str)`：将指定字符串连接到当前字符串的末尾。
> + `String.join(CharSequence delimiter, CharSequence... elements)`：使用指定的分隔符连接多个字符串。
> + `toCharArray()`：将字符串转换为字符数组。
> + `valueOf(...)`：将其他类型（如整数、布尔值等）转换为字符串。
> + `isEmpty()`：判断字符串是否为空（长度为0）。
> + `startsWith(String prefix)`：判断字符串是否以指定的前缀开始。
> + `endsWith(String suffix)`：判断字符串是否以指定的后缀结束。
> + `format(String format, Object... args)`：根据指定格式创建格式化字符串。

### String对性能有要求建议不使用字符串拼接，那么用

> + 使用`StringBuffer`时要考虑线程安全性，但会牺牲一些性能。
> + 使用`StringBuilder`时要确保在单线程环境中使用，以获得更好的性能。
> + 在大多数情况下，如果不需要线程安全，推荐使用`StringBuilder`。

### Java常见的异常类

> + `IOException`：输入输出异常，通常在文件操作或网络通信时发生。
> + `SQLException`：与数据库操作相关的异常。
> + `ClassNotFoundException`：当应用程序试图加载一个类但找不到时抛出。
> + `FileNotFoundException`：当试图访问一个不存在的文件时抛出。
> + `ParseException`：在解析字符串时发生错误，通常与日期和数字格式化相关。
> + `InterruptedException`：当一个线程在等待、睡眠或其他阻塞状态被中断时抛出。
> + `NullPointerException`：当应用程序试图使用`null`对象时抛出，常见于调用方法或访问属性。
> + `ArrayIndexOutOfBoundsException`：当访问数组时索引超出范围时抛出。
> + `ClassCastException`：当试图将对象强制转换为不兼容的类时抛出。
> + `ArithmeticException`：在算术运算中发生错误，例如除以零。
> + `IllegalArgumentException`：当方法接收到不合法或不适当的参数时抛出。
> + `IllegalStateException`：当方法在不适当的状态下被调用时抛出。
> + `NumberFormatException`：当试图将字符串转换为数字类型时格式不正确。
> + `OutOfMemoryError`：当Java虚拟机无法分配足够的内存时抛出。
> + `StackOverflowError`：当线程的栈深度超过了JVM的限制时抛出，通常是由于递归调用导致的。
> + `NoClassDefFoundError`：当Java虚拟机或类加载器试图加载一个类，但找不到它的定义时抛出。

### 如果一个接口请求时间长你会怎么做？从数据库层面分析

> ###### **分析慢查询**
>
> + **使用慢查询日志**：启用数据库的慢查询日志，记录执行时间超过阈值的查询。分析这些查询，识别哪些查询耗时较长。
> + **执行计划分析**：使用`EXPLAIN`语句（对于MySQL等数据库）分析查询的执行计划，查看查询的执行路径、使用的索引、连接方式等，识别潜在的性能瓶颈。
>
> ###### **优化查询**
>
> + **索引优化**：
>   - 确保常用的查询条件字段上有适当的索引。索引可以显著提高查询性能。
>   - 避免在索引字段上使用函数或计算，这会导致索引失效。
> + **避免全表扫描**：尽量避免不必要的全表扫描，确保查询条件能够利用索引。
> + **简化查询**：避免复杂的联接和子查询，尽量使用简单的查询语句。考虑将复杂查询拆分为多个简单查询。
> + **选择合适的数据类型**：使用合适的数据类型可以减少存储和处理的开销。
>
> ###### **数据库设计优化**
>
> + **数据规范化与反规范化**：
>   - 确保数据库设计符合规范化原则，以减少数据冗余。
>   - 在某些情况下，适当的反规范化可以减少联接的复杂性，提高查询速度。
> + **分区表**：对于大表，可以考虑使用分区表，将数据分散到多个物理分区，提高查询性能。
>
> ###### 4. **增加缓存**
>
> + **使用缓存**：在应用层使用缓存（如Redis、Memcached）来存储频繁访问的数据，减少对数据库的直接访问。
> + **数据库查询缓存**：某些数据库（如MySQL）支持查询缓存，可以缓存查询结果。
>
> ###### 5. **连接池优化**
>
> + **使用连接池**：确保使用数据库连接池（如HikariCP、C3P0等），以减少连接的创建和关闭开销。
> + **调整连接池配置**：根据负载情况调整连接池的大小和其他参数，确保数据库连接的高效利用。
>
> ###### 6. **监控和调优**
>
> + **监控数据库性能**：使用数据库监控工具（如Prometheus、Grafana）监控数据库的性能指标，如查询响应时间、连接数、CPU和内存使用率等。
> + **定期维护**：定期进行数据库维护，如更新统计信息、重建索引、清理过时的数据等。
>
> ###### 7. **考虑异步处理**
>
> + **异步请求**：对于一些不需要立即返回结果的请求，可以考虑将其转为异步处理，用户可以在后台继续操作，而不是等待接口响应。
> + **消息队列**：使用消息队列（如RabbitMQ、Kafka）将长时间运行的任务异步化，提升接口响应速度。

## 无锡面试

### mybatis中#和$区别

| 特性         | `#`                                       | `$`                                                 |
| ------------ | ----------------------------------------- | --------------------------------------------------- |
| SQL 注入防范 | 会对参数值进行预编译和转义，防止 SQL 注入 | 不会进行任何转义，直接拼接参数值，容易发生 SQL 注入 |
| 使用场景     | 用于所有需要传递参数的地方（推荐使用）    | 用于动态拼接 SQL（如动态表名、列名等）              |
| 性能         | 使用预编译 SQL，性能较好                  | 直接拼接，可能导致性能问题，且不安全                |

### SpringCloud和SpringBoot区别

> 在微服务架构中，**Spring Boot** 用来创建各个微服务，而 **Spring Cloud** 用来处理这些微服务之间的协作和通信。两者是可以互补的，Spring Boot提供基础构建，而Spring Cloud负责微服务架构中的复杂问题。
>
> **MySQL中递归**

```java
WITH RECURSIVE org_chart AS (
 -- 基础查询：选取所有顶层管理者（没有上级的员工）
 SELECT id, name, manager_id, 1 AS level
 FROM employees
 WHERE manager_id IS NULL
 UNION ALL
 -- 递归查询：选取下一级的员工，并将层级+1
 SELECT e.id, e.name, e.manager_id, oc.level + 1
 FROM employees e
 JOIN org_chart oc ON e.manager_id = oc.id
)
-- 最终查询：输出所有员工及其层级
SELECT id, name, manager_id, level
FROM org_chart
ORDER BY level, id;
```

## 苏州面试

### 粘包和半包

- **粘包**：接收方一次性收到多个数据包（TCP 流式传输无边界导致）。
- **半包**：一个数据包被拆分成多次接收。
- **原因**：TCP 传输基于字节流，无消息边界概念；发送方写入速度与接收方读取速度不匹配。

---

### Netty 解决粘包/半包

- **解码器**：
  - **定长解码器**：`FixedLengthFrameDecoder`。
  - **分隔符解码器**：`DelimiterBasedFrameDecoder`。
  - **长度字段解码器**：`LengthFieldBasedFrameDecoder`（最灵活）。
- **自定义协议**：在数据包头部添加长度字段，接收方根据长度解析。

---

### MyBatis 防止 SQL 注入

- **使用 `#{}` 占位符**：通过 `PreparedStatement` 预编译，参数化查询。
- **避免 `${}` 拼接**：`${}` 直接替换为字符串，有注入风险。
- **过滤输入**：对用户输入进行校验或转义（如 MyBatis 的 `like` 查询需手动处理）。

---

### MyBatis 底层处理 SQL 注入

- **预编译机制**：`#{}` 会被替换为 `?`，由数据库驱动进行参数填充，输入内容不会作为 SQL 语法解析。
- **源码逻辑**：`SqlSourceBuilder` 解析 SQL，替换 `#{}` 为占位符，生成 `BoundSql`。

---

### Spring 依赖注入方式

- **构造器注入**：通过构造函数传入依赖（推荐，保证不可变性）。
- **Setter 注入**：通过 Setter 方法注入。
- **字段注入**：直接通过 `@Autowired` 注解字段（不推荐，难测试）。
- **接口注入**：实现特定接口（已较少使用）。

---

### @Autowired 黄色波浪线问题

- **原因**：存在多个同类型 Bean，Spring 无法确定注入哪一个。
- **解决**：
  - **@Qualifier**：指定 Bean 名称。
  - **@Primary**：标记首选 Bean。
  - **明确类型**：使用具体实现类而非接口。
  - **Lombok**：使用 `@RequiredArgsConstructor` 构造器注入。