diff --git a/backend b/backend new file mode 100644 index 00000000..69bf9209 --- /dev/null +++ b/backend @@ -0,0 +1,332 @@ +面试题1:Java中实现线程安全有哪些方式?请举例说明。 +考察点: 线程安全的基本概念、同步机制、并发工具类 + +答案要点: + +线程安全定义:多个线程访问共享资源时,不会产生不确定的结果。 + +实现方式: + +使用 synchronized 关键字:修饰方法或代码块,保证同一时刻只有一个线程执行。 +java +public synchronized void increment() { count++; } +使用 Lock 接口:如 ReentrantLock,提供更灵活的锁操作(可中断、可超时、公平锁)。 +java +Lock lock = new ReentrantLock(); +lock.lock(); +try { count++; } finally { lock.unlock(); } +使用原子类:如 AtomicInteger,基于CAS(无锁)实现线程安全。 +java +AtomicInteger count = new AtomicInteger(); +count.incrementAndGet(); +使用并发容器:如 ConcurrentHashMap、CopyOnWriteArrayList,内部已经处理好线程安全。 +使用 ThreadLocal:每个线程拥有自己的变量副本,避免共享。 +使用 volatile 关键字:保证可见性,但不保证原子性(适合状态标志位)。 +选择依据:根据并发度、竞争激烈程度、代码复杂度选择合适的方式。 + +面试题2:什么是死锁?如何避免死锁? +考察点: 死锁的四个必要条件、预防与避免策略 + +答案要点: + +死锁定义:两个或更多线程互相持有对方所需的资源,导致彼此永远等待。 + +产生死锁的四个必要条件(缺一不可): + +互斥:资源一次只能被一个线程占用。 +持有并等待:线程持有资源的同时等待其他资源。 +非抢占:线程已获得的资源不能被强行剥夺。 +循环等待:存在线程间的循环等待链。 +避免方法: + +破坏“持有并等待”:一次性申请所有资源。 +破坏“非抢占”:如果线程申请不到新资源,则释放已占有的资源(Lock 的 tryLock 支持超时释放)。 +破坏“循环等待”:按固定顺序申请资源(如锁顺序),避免环路。 +使用锁超时:lock.tryLock(timeout) 在规定时间内获取不到就放弃。 +使用并发工具:如 Semaphore、CountDownLatch 等高级工具,减少手动锁的使用。 +🔒 1. 乐观锁、悲观锁,如何实现 +面试题3:乐观锁和悲观锁的区别是什么?在Java中如何实现? +考察点: 两种锁的思想、适用场景、实现技术 + +答案要点: + +悲观锁: + +思想:认为并发冲突的概率很高,所以在操作数据前先加锁,阻塞其他线程。 + +实现:Java 中的 synchronized、ReentrantLock;数据库中的 select ... for update。 + +适用场景:写操作频繁、冲突严重的情况。 + +乐观锁: + +思想:认为并发冲突的概率较低,不加锁,在更新时检查数据是否被修改过,如果被修改则重试或失败。 + +实现: + +版本号机制:在数据表中加一个 version 字段,更新时检查 version 是否匹配。 + +sql +update table set count = ?, version = version + 1 where id = ? and version = oldVersion; +CAS(Compare And Swap):Java 原子类(如 AtomicInteger)底层使用 Unsafe 的 CAS 指令实现。 + +适用场景:读多写少、冲突概率低的场景,可以提高并发性能。 + +📦 2. JPA 的 N+1 问题 +面试题4:什么是 JPA 的 N+1 问题?如何解决? +考察点: ORM 性能问题、懒加载、查询优化 + +答案要点: + +N+1 问题定义:当查询一个实体及其关联集合时,如果先查主实体(1条SQL),然后遍历每个主实体去懒加载关联集合(N条SQL),总共执行了 N+1 条SQL,造成性能低下。 + +例如:查询所有用户(1条SQL),然后对每个用户查询其订单(N条SQL)。 + +产生原因:默认的懒加载策略(FetchType.LAZY)在访问关联属性时触发额外查询。 + +解决方法: + +使用 JOIN FETCH:在 JPQL 中显式指定关联抓取。 +sql +select u from User u join fetch u.orders +使用 @EntityGraph:通过注解定义抓取计划。 +java +@EntityGraph(attributePaths = "orders") +List findAll(); +使用批量抓取(Batch Fetch):设置 @BatchSize(size = 10),将 N 次查询合并为 N/size 次。 +使用 DTO 投影:只查询需要的字段,避免加载整个实体和关联。 +二级缓存:如果数据变动少,可以缓存关联对象,减少重复查询。 +注意:FetchType.EAGER 虽然能避免 N+1,但可能导致不必要的关联加载,需权衡使用。 + +♻️ 3. 幂等 +面试题5:什么是幂等性?在分布式系统中如何保证接口的幂等性? +考察点: 幂等概念、常见实现方案 + +答案要点: + +幂等性定义:无论调用一次还是多次,对系统的副作用(即状态变化)是相同的。例如 GET 请求天然幂等,DELETE 重复删除同一资源也是幂等的(结果都是删除成功或资源已不存在)。 + +为什么需要:防止重复提交(如订单创建、支付扣款)导致数据错误。 + +常见实现方案: + +唯一索引:数据库表中对关键字段(如订单号)建唯一索引,重复插入时抛异常。 +去重表:利用数据库的唯一约束,插入前先查去重表,若存在则直接返回结果。 +状态机:业务状态流转只能单向变化,例如订单状态“待支付”只能改为“支付成功”,不能重复支付。 +Token 机制:服务端生成 token 返回给前端,每次请求携带 token,服务端校验 token 是否存在(redis),存在则处理并删除 token,重复请求 token 已失效。 +分布式锁:使用 Redis 或 Zookeeper 实现锁,在业务执行前加锁,执行后释放,防止并发重复处理。 +全局唯一请求 ID:客户端生成 UUID 作为请求 ID,服务端根据请求 ID 进行幂等校验(结合 Redis 存储处理过的 ID)。 +🐘 4. PostgreSQL 索引与优化 +面试题6:PostgreSQL 中有哪些索引类型?如何选择合适的索引? +考察点: 索引类型、适用场景 + +答案要点: + +B-Tree 索引(默认): + +适用于等值查询和范围查询(=、<、>、BETWEEN、LIKE 'abc%')。 + +支持排序和唯一约束。 + +Hash 索引: + +仅适用于等值查询(=),但不常用(PostgreSQL 10 后改进,但仍不如 B-Tree 通用)。 + +GIN(Generalized Inverted Index)索引: + +适用于包含多个值的列,如数组、JSONB、全文检索。 + +例如:查询数组包含某元素 WHERE arr @> '[1,2]'。 + +GiST(Generalized Search Tree)索引: + +适用于几何类型、范围类型、全文检索(相比 GIN 更灵活但性能稍低)。 + +支持位置搜索、重叠判断等。 + +BRIN(Block Range INdex)索引: + +适用于超大型表且数据与物理存储顺序相关性高的列(如时间序列)。 + +只存储每块数据的摘要,占用空间极小,但扫描效率低于 B-Tree。 + +选择原则: + +根据查询条件(等值、范围、全文)、数据类型、数据分布选择。 + +对于高基数列(如主键),B-Tree 最合适。 + +对于多值类型,GIN 是首选。 + +对于只追加的日志表,BRIN 可以大幅节省空间。 + +面试题7:如何分析 PostgreSQL 中的慢查询?请列出关键步骤和常用命令。 +考察点: SQL 调优、执行计划解读 + +答案要点: + +启用慢查询日志:在 postgresql.conf 中设置: + +text +log_min_duration_statement = 1000 # 记录超过1秒的SQL +log_connections = on +log_disconnections = on +使用 EXPLAIN 分析执行计划: + +EXPLAIN (ANALYZE, BUFFERS) your_sql; 实际执行 SQL 并输出真实统计信息。 + +关键指标解读: + +Seq Scan:全表扫描,通常需要优化(除非表很小)。 + +Index Scan:使用索引扫描,效率较高。 + +Bitmap Heap Scan:结合多个索引扫描的结果。 + +cost:启动成本和总成本,估算值。 + +actual time:实际执行时间(ms)。 + +rows:预估行数与实际行数(若偏差大,说明统计信息过时,需执行 ANALYZE)。 + +Buffers:读取的缓存块数,可判断是否大量 I/O。 + +优化措施: + +创建合适的索引。 + +更新统计信息:ANALYZE table; + +调整 work_mem、shared_buffers 等参数。 + +改写 SQL(避免函数在索引列上,使用 JOIN 代替子查询等)。 + +🏗️ 5. Java 设计模式 +面试题8:你熟悉哪些设计模式?请举例说明在 Spring 框架中的应用。 +考察点: 设计模式掌握程度、与实际框架结合 + +答案要点: + +单例模式(Singleton): + +Spring 中 Bean 默认作用域为单例,容器中只存在一个实例。 + +工厂模式(Factory): + +BeanFactory 和 ApplicationContext 负责创建和管理 Bean 实例。 + +代理模式(Proxy): + +Spring AOP 基于动态代理(JDK 动态代理或 CGLIB)实现切面功能。 + +模板方法模式(Template Method): + +JdbcTemplate、RestTemplate 封装固定流程,子类或回调实现可变部分。 + +观察者模式(Observer): + +Spring 事件机制(ApplicationEvent 和 ApplicationListener)。 + +策略模式(Strategy): + +Resource 接口的不同实现(如 ClassPathResource、FileSystemResource)。 + +适配器模式(Adapter): + +Spring MVC 中的 HandlerAdapter 适配不同类型的处理器(Controller、HttpRequestHandler 等)。 + +装饰器模式(Decorator): + +BeanWrapper 对 Bean 实例进行包装,添加属性编辑功能。 + +🔍 6. 接口与抽象类的区别及使用场景 +面试题9:Java 中接口和抽象类有什么区别?各自在什么场景下使用? +考察点: 面向对象基础、设计原则 + +答案要点: + +语法区别: + +抽象类:用 abstract 修饰,可以有抽象方法和具体方法,可以有成员变量、构造方法。 + +接口:用 interface 修饰,方法默认 public abstract(Java 8 后可以有 default 和 static 方法),变量默认 public static final。 + +设计思想区别: + +抽象类:表示“是什么”(is-a)关系,定义一类事物的共同属性和行为,可以有状态(成员变量)。 + +接口:表示“能做什么”(like-a)关系,定义行为规范,强调能力,不能有状态。 + +使用场景: + +抽象类适用场景: + +多个类有公共代码,需要代码复用。 + +需要控制子类的访问权限或提供默认行为。 + +例如:HttpServlet 抽象类提供 service() 模板方法。 + +接口适用场景: + +需要定义多个不相关类的共同行为(如 Comparable、Runnable)。 + +需要实现多继承(一个类可以实现多个接口)。 + +解耦调用方与实现方,定义契约(如 API 接口)。 + +Java 8 后变化:接口可以有 default 方法,可以在不破坏实现类的情况下新增方法;可以有 static 方法,提供工具方法。但接口仍不能有实例变量。 + +📋 7. 数据库事务 +面试题10:什么是数据库事务的 ACID 特性?事务隔离级别有哪些?MySQL 默认隔离级别是什么? +考察点: 事务基本概念、隔离级别、并发问题 + +答案要点: + +ACID 特性: + +原子性(Atomicity):事务是一个不可分割的单元,要么全部成功,要么全部失败回滚。 + +一致性(Consistency):事务执行前后,数据库完整性约束没有被破坏(数据状态一致)。 + +隔离性(Isolation):多个事务并发执行时,相互隔离,互不干扰。 + +持久性(Durability):事务一旦提交,对数据的修改是永久性的。 + +事务并发问题: + +脏读:读到另一个事务未提交的数据。 +不可重复读:同一事务内多次读取同一数据,结果不一致(因其他事务修改并提交)。 +幻读:同一事务内多次查询,返回的记录数不同(因其他事务插入或删除)。 +事务隔离级别(由低到高): + +读未提交(Read Uncommitted):可能发生脏读、不可重复读、幻读。 +读已提交(Read Committed):避免脏读,但可能发生不可重复读和幻读(Oracle 默认)。 +可重复读(Repeatable Read):避免脏读和不可重复读,但可能发生幻读(MySQL InnoDB 默认,通过 MVCC 和间隙锁解决幻读)。 +可串行化(Serializable):最高级别,所有事务串行执行,无并发问题,但性能最低。 +MySQL InnoDB 默认隔离级别:可重复读(Repeatable Read)。 + +如何选择:根据业务对一致性的要求与并发性能的权衡,选择合适的隔离级别。 + +补充题:Spring 中如何使用事务?@Transactional 的常用属性有哪些? +考察点: Spring 事务管理 + +答案要点: + +声明式事务:使用 @Transactional 注解,可作用于类或方法。 + +常用属性: + +propagation:事务传播行为(REQUIRED、REQUIRES_NEW、NESTED 等)。 + +isolation:事务隔离级别(DEFAULT、READ_COMMITTED 等)。 + +timeout:事务超时时间(秒)。 + +readOnly:是否为只读事务(优化数据库连接,适合查询)。 + +rollbackFor:指定哪些异常触发回滚(默认运行时异常回滚,受检异常不回滚)。 + +noRollbackFor:指定哪些异常不回滚。 + +原理:基于 AOP 动态代理,在方法前后开启、提交或回滚事务 diff --git a/front b/front new file mode 100644 index 00000000..68f9d1fe --- /dev/null +++ b/front @@ -0,0 +1,722 @@ + JavaScript 篇 +1. 说说JavaScript中事件循环(Event Loop)的执行顺序? +考察点: 异步编程、宏任务与微任务、代码执行结果分析。 +答案: +JavaScript是单线程的,事件循环是其处理异步回调的机制。执行顺序如下: + +执行当前调用栈中的同步代码(也就是宏任务,如整个script)。 + +执行完同步代码后,检查微任务队列,如果有微任务(如 Promise.then、MutationObserver、queueMicrotask),则清空整个微任务队列。 + +从宏任务队列中取出一个任务执行(如 setTimeout、setInterval、I/O)。 + +执行完一个宏任务后,再次检查并清空微任务队列。 + +重复此过程,这就是事件循环。 + +面试必杀技: 可以顺便举例 console.log(1); setTimeout(() => console.log(2)); Promise.resolve().then(() => console.log(3)); console.log(4); 的输出顺序是 1, 4, 3, 2 。 + +2. 什么是闭包?在实际项目中有什么用? +考察点: 作用域链、内存泄漏、模块化思想。 +答案: +闭包是指一个函数能够访问其外部函数作用域中变量的能力(即使外部函数已经执行完毕)。 +实际应用场景: + +防抖/节流函数: 需要保存 timer 定时器 ID,防止被污染。 + +Vue3 / React Hooks: 为什么 Hooks 能记住上一次的状态?底层原理依赖了闭包。 + +创建私有变量: 模拟私有方法,实现数据隐藏和封装。 + +避坑: 闭包使用不当可能导致内存泄漏(因为变量一直被引用,无法被垃圾回收)。解决方法是在不需要使用时,将引用置为 null 。 + +📘 TypeScript 篇 +3. 说说interface(接口)与type(类型别名)的区别? +考察点: 类型系统、扩展方式、使用场景。 +答案: +两者在多数情况下可以互换,但核心区别在于: + +扩展方式: interface 使用 extends 继承,type 使用 & 实现交叉类型(联合类型)。 + +重复定义: interface 支持声明合并(重名时会自动合并属性),而 type 不允许重复定义。 + +适用范围: type 更灵活,可以定义原始类型(type Name = string)、联合类型(type ID = string | number)和元组。interface 更适合用于定义对象的形状,尤其是在面向对象编程或定义类的契约时。 + +面试官追问: 如果是写Vue3组件库或库文件,推荐用 interface 还是 type?通常对于对外暴露的API,优先用 interface,因为它更稳定且支持声明合并,方便用户扩展 。 + +4. 什么是类型守卫?如何自定义? +考察点: 类型收缩、is 关键字。 +答案: +类型守卫是一种在运行时检查以确保某个值属于特定类型的行为,它帮助TS在特定代码块中将变量推断为更具体的类型。 + +内置守卫: typeof、instanceof、in 操作符。 + +自定义守卫: 通过返回一个 参数名 is 类型 的谓词函数。 + +typescript +// 自定义守卫,判断是否是数组 +function isArray(value: any): value is any[] { + return Array.isArray(value); +} + +function foo(value: string | string[]) { + if (isArray(value)) { + value.map(···); // 在此作用域内,TS 能确定 value 是数组类型 + } +} +``` [citation:7] + +### 🧩 Element Plus 篇 + +#### 5. 如何自定义Element Plus的主题色? +**考察点:** 样式覆盖、SCSS变量、工程化配置。 +**答案:** +Element Plus 支持灵活的样式定制,主要有两种方式: +1. **仅替换颜色(简单项目):** 在 `vite.config.js` 或 `vue.config.js` 中,通过配置 `scss` 预处理器选项,覆盖 Element Plus 内部定义的 SCSS 变量,例如 `$--color-primary: #409EFF;` 改为自定义的颜色。 +2. **全量样式覆盖(复杂项目):** 使用官方提供的主题生成工具,或者创建一个自定义的 SCSS 文件,引入 Element Plus 的 SCSS 变量文件,并在引入前修改所有需要覆盖的变量。 + +#### 6. 说说你在使用El-Table时,遇到过哪些性能问题?怎么解决的? +**考察点:** 大数据渲染、虚拟滚动、树形表格。 +**答案:** +当表格有上千行数据时,直接渲染会导致页面卡顿甚至崩溃,因为浏览器 DOM 节点过多。 +**解决方案:** +1. **分页:** 最简单有效的方式,前端分页或后端分页。 +2. **虚拟滚动:** Element Plus 的 Table 组件支持虚拟滚动(需设置 `v-virtual-scroll` 指令或结合 `el-table-v2` 虚拟化表格组件)。它只渲染可视区域内的数据行,极大减少 DOM 节点数量。 +3. **按需加载:** 如果是树形表格,使用懒加载模式,展开时再去请求子节点数据。 + +### 🎨 Tailwind CSS 篇 + +#### 7. Tailwind CSS相比传统CSS最大的区别是什么? +**考察点:** 原子化CSS、实用优先、可维护性。 +**答案:** +最大的区别在于**开发思想**的不同: +* **传统CSS(语义化):** 我们倾向于先起一个类名(如 `.user-card`),然后在 CSS/SCSS 文件里定义这个类有哪些样式。 +* **Tailwind(原子化/实用优先):** 它提供大量的工具类(如 `flex`、`pt-4`、`text-center`),直接在 HTML/组件中组合这些类来实现样式,而不用离开模板去写自定义 CSS。 +* **优势:** 避免了命名焦虑,减少了 CSS 文件体积(通过 PurgeCSS 摇树优化),样式与组件耦合更紧密,修改样式不用在多个文件间跳转 [citation:8]。 + +#### 8. 在Tailwind中,如何复用一组复杂的样式类? +**考察点:** `@apply` 指令、组件抽象、代码复用。 +**答案:** +虽然 Tailwind 鼓励在模板中直接使用类,但当一组样式重复出现时,主要有两种复用方式: +1. **组件抽象(推荐):** 如果是前端框架(如 Vue/React),可以将这组样式封装成一个单独的组件(如 `BaseButton.vue`),然后在各处引用该组件。 +2. **使用 `@apply` 指令:** 在你的 CSS 文件中,可以通过 `@apply` 将多个工具类组合成一个自定义类。 + ```css + .btn-primary { + @apply py-2 px-4 bg-blue-500 text-white rounded-lg hover:bg-blue-700; + } +这种方式适合需要兼容传统思路或在特定情况下简化类名列表 。 + +💼 综合场景题 +9. 用Element Plus的Table组件展示数据,如何实现某一列根据条件显示不同颜色的Tag? +考察点: 作用域插槽、动态类绑定、状态切换。 +答案: +这属于典型的自定义列模板场景。 + +在 el-table-column 中使用 v-slot 作用域插槽,获取当前行数据 row。 + +根据 row 中的状态字段(如 status),动态绑定 el-tag 的 type 属性或自定义类名。 + +vue + + + +10. 在Vue3 + TypeScript项目中,如何为Element Plus的组件实例添加类型声明? +考察点: 类型导入、InstanceType、组件引用。 +答案: +在 Vue3 中使用