京东后端开发实习生面试主要涵盖数据库、Java并发编程、Spring框架原理、JVM、算法等多个方面,考察技术深度与广度,部分问题涉及原理剖析与代码实现。 以下是具体问题解析:
数据库相关- 怎么建索引:需根据查询条件选择合适字段,如常用于WHERE、JOIN、ORDER BY的字段;遵循最左前缀原则,联合索引按查询频率从高到低排序;避免过度索引,因索引会占用存储空间并降低写入性能。
- 索引失效:常见情况有对索引列使用函数或计算,如WHERE YEAR(create_time) = 2024;使用NOT、!=、NOT IN等否定操作符;使用OR连接条件时,若其中一个字段无索引;索引列参与隐式类型转换,如字符串类型字段用数字查询。
- 聚簇索引与非聚簇索引:聚簇索引的叶子节点存储完整数据记录,表数据按索引顺序存储,一个表只能有一个聚簇索引,通常主键为聚簇索引;非聚簇索引叶子节点存储主键值,需回表查询完整数据,可建多个。
- 什么时候不用回表:当查询的字段都包含在非聚簇索引中时,即覆盖索引,无需回表。
- B+树和B树区别:B+树非叶子节点只存键值,数据全在叶子节点,且叶子节点通过指针连接,查询效率稳定,范围查询效率高;B树每个节点既存键值也存数据,查询效率不稳定,范围查询需中序遍历。
- ACID:原子性(Atomicity)指事务是不可分割的工作单位,要么全执行,要么全不执行;一致性(Consistency)指事务执行前后数据库从一个一致状态变为另一个一致状态;隔离性(Isolation)指多个事务并发执行时互不干扰;持久性(Durability)指事务一旦提交,对数据库的改变就是永久的。
- 幻读是什么,怎么解决幻读:幻读指一个事务在读取某个范围内的记录时,另一个事务在该范围内插入新记录,导致前一个事务再次读取时出现幻影行。可通过设置事务隔离级别为可串行化(SERIALIZABLE)或使用临键锁解决。
- 用什么锁解决幻读:临键锁(Next-Key Lock),是记录锁和间隙锁的组合,锁定记录及记录前的间隙,防止其他事务插入数据。
- 使用临键锁遇到过什么问题:可能导致锁范围过大,降低并发性能;在高并发场景下,易引发死锁。
- 全表查询怎么避免:合理使用索引,确保查询条件能利用索引;优化SQL语句,避免使用SELECT *,只查询需要的字段;对大表进行分表分库,减少单表数据量。
Java并发编程相关- Java线程安全的方式:使用同步代码块(synchronized关键字);使用锁(如ReentrantLock);使用并发集合(如ConcurrentHashMap、CopyOnWriteArrayList);使用原子类(如AtomicInteger、AtomicLong)。
- 乐观锁悲观锁:悲观锁认为并发冲突频繁,每次操作都加锁,如synchronized、ReentrantLock;乐观锁认为并发冲突少,先操作,操作失败再重试,通过版本号或CAS实现。
- 公平锁怎么实现:通过队列实现,新请求线程加入队列尾部,按顺序获取锁,如ReentrantLock的公平锁模式。
- CAS怎么实现:CAS(Compare-And-Swap)是一种无锁算法,包含三个操作数:内存值V、预期值A、新值B。只有当V等于A时,才将V更新为B,否则不更新,通过硬件指令保证原子性。
- 怎么解决ABA问题:添加版本号,每次更新时版本号加1;使用AtomicStampedReference类,它内部维护了一个对象引用和一个整数类型的版本号。
- AQS的原理:AQS(AbstractQueuedSynchronizer)是一个抽象的队列同步器,通过一个volatile int类型的state变量表示同步状态,通过CLH队列(双向链表)实现线程排队,通过CAS操作更新state变量,实现公平锁和非公平锁。
- 线程池原理:线程池维护一个线程集合和一个任务队列,当有新任务提交时,若线程池中有空闲线程,则直接执行任务;否则将任务放入任务队列,当线程池中的线程数小于核心线程数时,创建新线程执行任务;若任务队列已满且线程数等于最大线程数,则执行拒绝策略。
- 线程池的拒绝策略:AbortPolicy(直接抛出异常,默认策略);CallerRunsPolicy(由提交任务的线程执行该任务);DiscardPolicy(直接丢弃任务);DiscardOldestPolicy(丢弃队列中最老的任务,然后重新提交任务)。
Spring框架原理相关- 介绍IOC:控制反转(Inversion of Control),将对象的创建、依赖注入等控制权从应用程序代码转移到Spring容器中,通过配置文件或注解定义对象之间的关系,容器负责实例化、组装和管理对象。
- 为什么要IOC,开发者自己管理对象创建不行吗:IOC降低了代码的耦合度,提高了代码的可维护性和可测试性;便于实现依赖注入,方便地替换对象的实现;便于管理对象的生命周期。
- 循环依赖、三级缓存:循环依赖指两个或多个对象相互依赖,形成闭环。Spring通过三级缓存解决循环依赖问题,一级缓存存放完整Bean,二级缓存存放早期Bean引用,三级缓存存放Bean工厂对象,通过提前暴露对象引用解决循环依赖。
- AOP原理:面向切面编程(Aspect-Oriented Programming),将横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,通过动态代理(JDK动态代理或CGLIB动态代理)实现,在方法调用前后插入切面逻辑。
JVM相关- final和finally的区别:final用于修饰类、方法、变量,表示不可变;finally用于异常处理,与try-catch配合使用,无论是否发生异常,finally块中的代码都会执行。
- Final方法可以用作AOP吗:不可以,因为final方法不能被重写,而AOP是通过动态代理实现,需要代理对象调用目标方法,若方法是final的,则无法代理。
- 堆里面存了类的什么信息(除了对象实例):还存储类的元数据,如类名、字段、方法、接口等信息,以及运行时常量池。
算法相关- 双线程打印奇偶数:可通过共享变量和wait()、notify()方法或使用synchronized锁和标志位实现线程间通信,一个线程打印奇数,一个线程打印偶数。
- 堆排序:先将数组构建成一个大顶堆,然后将堆顶元素(最大值)与堆末尾元素交换,再对剩余元素重新构建大顶堆,重复上述过程,直到堆中只剩一个元素。
- 快排:选择一个基准元素,将数组分为两部分,一部分小于基准,一部分大于基准,然后对这两部分递归地进行快速排序。