【1】猫眼娱乐后端开发面试题整理
[1]. 全量索引和增量索引的区别
全量索引:检索系统在启动时一次性读取当前数据库中的所有数据,建立索引。
增量索引:系统运行过程中,监控数据库的变化,即增量,实时加载更新,构建索引。
[2]. 解释一下控制反转
控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。
[3]. Spring中的常见注解
Spring Bean相关注解
@SpringBootApplication:一个组合注解,包括了@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解。用于标识SpringBoot应用程序的入口类。
@Component:用于标识一个类是Spring容器中的组件。泛指各种组件,当类不属于@Controller、@Services等的时候,可以使用@Component来标注这个类。
@Autowired:@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果想按照名称(byName)来装配,可以结合@Qualifier注解一起使用。
AOP相关注解
@Before:前置通知,在方法执行之前执行。
@After:后置通知,在方法执行之后执行。
@Around:环绕通知,围绕着方法执行。
JPA相关注解
@Id:用于标记实体类的主键字段。
@Entity:用于标识一个类是一个JPA实体,对应于数据库中的一个表。
@Transien:用于标记一个字段不被持久化,即不映射到数据库表中。
@Basic:表示一个属性到数据库表的字段的映射,默认情况下所有属性都使用此注解。
Lombok相关注解
@Slf4j:用于在Java类中自动生成日志记录器。
@NoArgsConstructor:注解在类上,为类提供一个无参的构造方法。
@AllArgsConstructor:注解在类上,为类提供一个全参的构造方法。
[4]. 解释一下CAS
CAS(Compare and Swap) 是一种用于实现多线程同步的原子操作,不需要使用传统的锁机制来保证线程安全,可以减少锁的使用,解决了加锁释放锁导致的上下文切换的问题,提高了并发性能。
CAS 操作包含三个操作数:内存地址V,旧的预期值A,计算后要修改后的新值B。在执行操作之前,先比较当前内存V中的值是否等于期望值A,如果相等,则执行修改值为B;如果不相等,则不执行修改操作,继续进行比较,直到内存V中的值与期望值A相等为止。
[5]. 介绍一下锁升级
锁升级的意义在于提高多线程环境下的性能和吞吐量,减少同步操作的开销,并尽量避免线程切换的开销。
锁升级是Java中synchronized关键字实现同步机制时,锁状态随着竞争情况逐渐升级的一个过程。synchronized锁有四种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。
当第一个线程访问同步块并获取锁时,锁从无锁状态升级为偏向锁。
当第二个线程尝试获取同一个锁时,如果发现该锁已经是偏向锁,并且偏向的线程仍然存活,则JVM会将锁升级为轻量级锁。如果偏向的线程已经不存在,则第二个线程直接获得偏向锁,无需进行锁升级。
当多个线程竞争同一个轻量级锁时,JVM会将锁升级为重量级锁,并让所有等待锁的线程进入阻塞状态。
[6]. Bean的生命周期
Spring中Bean的生命周期就是Bean在Spring中从创建到销毁的整个过程。
可以划分为五大步:
- 第一步:实例化Bean
- 第二步:Bean属性赋值
- 第三步:初始化Bean
- 第四步:使用Bean
- 第五步:销毁Bean
[7]. 介绍一下Java中常用的集合
Java中的集合类主要由单列集合Collection和双列集合Map这两个接口派生而出,其中Collection接口又派生出三个子接口,分别是Set、List、Queue。
Set接口表示无序的,元素不可重复的集合,常用的实现类有HashSet和TreeSet;
List接口表示有序的,元素可以重复的集合,常用的实现类有ArrayList和LinkedList;
Queue接口表示先进先出(FIFO)的队列,常用的实现类有LinkedList、PriorityQueue。
Map接口表示具有映射关系(key-value)的集合,常用的实现类有HashMap和TreeMap。
[8]. 线程安全的List集合有哪些
Vector:底层实现和ArrayList一样,所有操作元素的方法都加了synchronized关键字来保证多线程环境中操作时的线程安全,但同时也导致操作Vector的效率非常低。
Collections.synchronizedList:通过Collections.synchronizedList方法包装一个普通的List,使得所有对List的操作都会被同步机制保护,从而保证线程安全。
CopyOnWriteArrayList:CopyOnWriteArrayList通过在写操作时复制底层数组来实现线程安全,适用于读多写少的并发场景。
[9]. 介绍一下Java中基本的数据类型
基本数据类型分为整数类型、浮点类型、字符类型和布尔类型。其中整数类型有byte(1个字节),short(2个字节),int(4个字节),long(8个字节)四个,浮点类型有float(4个字节)、double(8个字节)两个,再加上字符类型char(2个字节)和布尔类型boolean(1个字节)。
[10]. 手撕:判断链表是否有环
法一:快慢指针
class Main {
public boolean hasCycle(ListNode head) {
// 定义快慢指针,初始都指向头节点
ListNode fast = head;
ListNode slow = head;
// 当快指针不为空且快指针的下一个节点不为空时,继续循环
while (fast != null && fast.next != null) {
// 快指针每次移动两步
fast = fast.next.next;
// 慢指针每次移动一步
slow = slow.next;
// 如果快慢指针相遇,说明存在环,返回true
if (fast == slow) {
return true;
}
}
// 快慢指针没有相遇,说明不存在环,返回false
return false;
}
}
法二:哈希表
class Main {
public boolean hasCycle(ListNode head) {
// 创建一个HashSet集合,用于存储遍历过的节点
Set<listnode> set = new HashSet<listnode>();
// 当链表头节点不为空时,继续循环
while (head != null) {
// 如果集合中已经存在当前节点,说明链表存在环,返回true
if (!set.add(head)) {
// 返回true,表示链表存在环
return true;
}
// 将当前节点移动到下一个节点
head = head.next;
}
// 遍历完链表后,未发现环,返回false
return false;
}
}