java八股文

180 阅读48分钟

1、java基础

1、Java 中的 final 关键字有哪些用法?

  • 修饰类: final 关键字修饰的类不能被继承。这通常用于创建不可变的类,例如 String 类。
  • 修饰方法: final 关键字修饰的方法不能被子类重写。这可以防止子类修改父类的行为。
  • 修饰变量:
    • 成员变量: final 关键字修饰的成员变量必须在声明时或构造方法中初始化,且初始化后不能再被修改。
    • 局部变量: final 关键字修饰的局部变量也只能被赋值一次。

2、GC 如何判断对象可以被回收?

Java 的垃圾回收器 (GC) 使用不同的算法来判断对象是否可以被回收。常见的算法包括:

  • 引用计数法: 每个对象都有一个引用计数器,记录有多少个地方引用它。当引用计数器为 0 时,对象被认为是垃圾。
  • 可达性分析法: 从 GC Roots (例如静态变量、活动线程等) 开始,如果一个对象无法通过任何路径到达,则被认为是垃圾。

目前,主流的 Java 虚拟机 (JVM) 使用可达性分析法来判断对象是否可回收。

3、Java 类加载器

Java 类加载器负责将字节码文件加载到内存中。类加载器的工作流程通常包括:

  1. 加载: 查找并加载字节码文件。
  2. 验证: 检查字节码文件的结构是否符合规范。
  3. 准备: 为静态变量分配内存并设置初始值。
  4. 解析: 将符号引用转换为直接引用。
  5. 初始化: 执行静态代码块和构造方法,完成类的初始化。

Java 中有三种主要的类加载器:

  • 启动类加载器 (Bootstrap ClassLoader): 加载 Java 核心类库。
  • 扩展类加载器 (Extension ClassLoader): 加载扩展目录下的类库。
  • 应用程序类加载器 (Application ClassLoader): 加载应用程序 classpath 下的类。

4、什么是 B/S 架构?什么是 C/S 架构?

  • B/S 架构 (Browser/Server): 客户端使用浏览器访问服务器。用户只需要在浏览器上安装插件,无需安装额外的应用程序。
  • C/S 架构 (Client/Server): 客户端需要安装专门的应用程序才能访问服务器。

5、Java 中的继承是单继承还是多继承?

Java 中只支持单继承,一个类只能继承一个父类。但是,Java 支持通过接口实现多重继承,一个类可以实现多个接口。

6、ArrayList 和 LinkedList 区别

特性ArrayListLinkedList
底层实现数组双向链表
访问速度快 (通过索引访问)慢 (需要遍历链表)
插入/删除慢 (需要移动元素)快 (只需要修改指针)
内存占用较小 (数组连续存储)较大 (链表需要存储节点信息)

7、如何实现对象克隆?

Java 中实现对象克隆有两种方式:

  • 实现 Cloneable 接口并重写 clone() 方法: 这是最常用的方式。
  • 使用序列化和反序列化: 将对象序列化到字节流,然后再反序列化得到新的对象。

8、什么是字节码?采用字节码的好处是什么?

  • 字节码: Java 编译器将 Java 代码编译成一种中间代码,称为字节码。字节码是一种平台无关的格式,可以在任何支持 Java 虚拟机的平台上运行。
  • 好处:
    • 平台无关性: 字节码可以在不同的操作系统和硬件平台上运行,实现了“一次编译,到处运行”。
    • 安全性: 字节码可以进行安全检查,防止恶意代码的执行。
    • 性能优化: JVM 可以对字节码进行优化,提高程序的运行效率。

9、标识符的命名规则。

  • 只能包含字母、数字、下划线 (_) 和美元符号 ($)。
  • 不能以数字开头。
  • 区分大小写。
  • 不能使用 Java 关键字。

10、String 是最基本的数据类型吗?

String 不是 Java 的基本数据类型,它是一个对象。Java 的基本数据类型包括 byteshortintlongfloatdoublebooleanchar

11、排序都有哪几种方法?请列举

常见的排序算法包括:

  • 冒泡排序
  • 选择排序
  • 插入排序
  • 快速排序
  • 归并排序
  • 堆排序
  • 希尔排序
  • 计数排序
  • 桶排序
  • 基数排序

12、双亲委托模型

双亲委托模型是 Java 类加载器的一种工作机制。它的核心思想是:

  • 当一个类加载器收到类加载请求时,它首先会委托给父类加载器去加载。
  • 只有当父类加载器无法完成加载任务时,子类加载器才会尝试加载。

好处:

  • 保证了 Java 核心类库的安全性,避免被篡改。
  • 避免了类的重复加载。

13、java 中有没有指针?

Java 中没有指针。Java 使用引用来操作对象,引用可以看作是指针的封装。

14、Java 中的异常体系

Java 中的异常体系分为两种:

  • 受检异常 (Checked Exception): 在编译时必须处理的异常,例如 IOException
  • 非受检异常 (Unchecked Exception): 在运行时可能发生的异常,例如 NullPointerException

Java 使用 try-catch-finally 语句块来捕获和处理异常。

15、String、StringBuffer、StringBuilder 有什么区别?

特性StringStringBufferStringBuilder
可变性不可变可变可变
线程安全性安全安全不安全
性能差 (每次修改都会创建新的字符串对象)较好最好

16、equals 与 == 的区别

  • ==: 比较的是对象的引用 (内存地址)。
  • equals: 比较的是对象的内容。

17、什么是 JDK?什么是 JRE?

  • JDK (Java Development Kit): Java 开发工具包,包含了编译器、调试器等开发工具,以及 JRE。
  • JRE (Java Runtime Environment): Java 运行时环境,包含了 JVM 和 Java 核心类库。

18、hashCode 与 equals

  • hashCode: 返回对象的哈希码,用于快速查找对象。
  • equals: 用于比较对象的内容是否相等。

关系:

  • 如果两个对象 equals 相等,则它们的 hashCode 必须相等。
  • 如果两个对象的 hashCode 相等,它们 equals 不一定相等。

19、面向对象和面向过程的区别

特性面向对象 (Object-Oriented)面向过程 (Procedure-Oriented)
思想以对象为中心,将问题分解为对象的交互以过程为中心,将问题分解为步骤
特点封装、继承、多态强调算法和流程
优点代码复用性高、可维护性强、易于扩展执行效率高

2、多线程并发

1、Thread、Runnable 的区别

  • 实现方式不同: Thread 类继承自 java.lang.Thread,Runnable 接口则需要实现 run() 方法。
  • 资源共享: Runnable 接口可以实现多个线程共享同一个资源,Thread 类则每个线程拥有自己的资源。
  • 扩展性: Runnable 接口更具扩展性,因为一个类可以实现多个接口,而 Java 只支持单继承。
  • 线程池: 线程池只能接受 Runnable 接口的线程。

2、JAVA 线程锁机制是怎样的?偏向锁、轻量级锁、重量级锁有什么区别?锁机制是如何升级的?

JAVA 线程锁机制主要通过 synchronized 关键字和 Lock 接口实现。锁的状态会根据竞争情况进行升级:

  • 偏向锁: 当一个线程访问同步块时,会将锁偏向该线程,减少锁竞争。
  • 轻量级锁: 当偏向锁失效时,会升级为轻量级锁,通过 CAS (Compare and Swap) 操作尝试获取锁。
  • 重量级锁: 当轻量级锁竞争激烈时,会升级为重量级锁,线程会进入阻塞队列等待获取锁。

区别:

锁类型优点缺点适用场景
偏向锁减少锁竞争,性能高线程切换开销大只有一个线程访问同步块
轻量级锁减少线程阻塞,性能较高CAS 操作消耗 CPU 资源多个线程交替访问同步块
重量级锁保证线程安全线程阻塞,性能较低多个线程同时访问同步块

锁升级过程: 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁,锁的升级是不可逆的。

3、Volatile 和 Synchronized 有什么区别?Volatile 能不能保证线程安全?DCL(Double Check Lock) 单例为什么要加 Volatile?

  • 可见性: Volatile 关键字保证共享变量的可见性,即一个线程修改了变量的值,其他线程可以立即看到。Synchronized 关键字不仅保证可见性,还保证原子性。
  • 原子性: Volatile 关键字不保证原子性,Synchronized 关键字保证原子性。
  • 有序性: Volatile 关键字禁止指令重排序,Synchronized 关键字保证有序性。

Volatile 不能保证线程安全: 因为 Volatile 不保证原子性,例如 i++ 操作不是原子性的,多个线程同时执行 i++ 操作可能会导致结果错误。

DCL 单例为什么要加 Volatile: 因为 DCL 中需要先判断实例是否为空,然后再创建实例。如果 instance 没有加 Volatile,可能会出现指令重排序,导致其他线程看到 instance 不为空,但是实例还没有初始化完成,从而导致错误。

4、线程池中线程复用原理

线程池通过维护一个线程队列,当有新的任务提交时,线程池会从队列中取出一个空闲线程来执行任务,而不是每次都创建新的线程。当线程执行完任务后,不会立即销毁,而是返回到线程队列中等待下一个任务。这样可以减少线程创建和销毁的开销,提高程序的效率。

5、并发的三大特性

  • 原子性: 一个操作是不可中断的,要么全部执行成功,要么全部执行失败。
  • 可见性: 一个线程修改了共享变量的值,其他线程可以立即看到。
  • 有序性: 程序执行的顺序按照代码的先后顺序执行。

6、ThreadLocal 内存泄露原因,如何避免

内存泄露原因: ThreadLocalMap 中存储的 Key 是弱引用,当线程结束后,Key 会被回收,但是 Value 仍然存在,如果没有手动清除,可能会导致内存泄露。

避免方法:

  • 及时调用 remove() 方法清除 ThreadLocalMap 中对应的 Value。
  • 使用完 ThreadLocal 变量后,及时将其设置为 null。

7、线程的生命周期?线程有几种状态

线程的生命周期包括以下几个状态:

  • 新建 (New): 线程刚被创建,但尚未启动。
  • 就绪 (Runnable): 线程已经启动,等待 CPU 执行。
  • 运行 (Running): 线程正在执行。
  • 阻塞 (Blocked): 线程被阻塞,例如等待 I/O 操作或获取锁。
  • 等待 (Waiting): 线程进入等待状态,例如调用 wait() 方法或 join() 方法。
  • 超时等待 (Timed Waiting): 线程进入超时等待状态,例如调用 wait(long timeout) 方法或 join(long timeout) 方法。
  • 终止 (Terminated): 线程执行完毕或被异常终止。

8、有 A,B,C 三个线程,如何保证三个线程同时执行?如何在并发情况下保证三个线程依次执行?如何保证三个线程有序交错进行?

  • 同时执行: 可以创建三个线程,然后分别调用 start() 方法启动线程。
  • 依次执行: 可以使用 join() 方法,例如在 A 线程中调用 B 线程的 join() 方法,B 线程执行完后,A 线程才能继续执行。
  • 有序交错执行: 可以使用 CountDownLatch 或 CyclicBarrier 来控制线程的执行顺序。

9、谈谈你对 AQS 的理解。AQS 如何实现可重入锁?

AQS (AbstractQueuedSynchronizer) 是一个抽象的队列同步器,提供了实现锁和其他同步组件的基础框架。AQS 使用一个 volatile int 类型的 state 变量来表示同步状态,通过 CAS 操作来修改 state 变量。

AQS 实现可重入锁: AQS 使用一个线程 ID 来标识当前持有锁的线程,当同一个线程再次请求锁时,会增加 state 变量的值,释放锁时,会减少 state 变量的值。

10、并发、并行、串行的区别

  • 并发: 多个任务在同一时间段内执行,但实际上是交替执行的。
  • 并行: 多个任务在同一时刻同时执行。
  • 串行: 多个任务按顺序依次执行。

11、JAVA 如何开启线程?怎么保证线程安全?

  • 开启线程: 可以通过继承 Thread 类或实现 Runnable 接口来创建线程,然后调用 start() 方法启动线程。
  • 保证线程安全: 可以使用 synchronized 关键字、Lock 接口、Volatile 关键字、ThreadLocal 等方式来保证线程安全。

12、对线程安全的理解

线程安全是指多个线程同时访问共享资源时,不会出现数据不一致或错误的情况。

13、什么是 volatile?

Volatile 关键字用于保证共享变量的可见性、禁止指令重排序。

14、sleep()、wait()、join()、yield() 的区别

方法作用是否释放锁
sleep()使线程休眠指定时间不释放锁
wait()使线程进入等待状态,释放锁释放锁
join()等待线程执行完毕不释放锁
yield()提示调度器让出 CPU 资源不释放锁

15、对守护线程的理解

守护线程是一种特殊的线程,当所有非守护线程结束时,守护线程会自动结束。

16、简述线程池处理流程

  1. 当有新的任务提交到线程池时,线程池会先判断核心线程池是否已满,如果未满,则创建一个新的线程来执行任务。
  2. 如果核心线程池已满,则将任务添加到阻塞队列中。
  3. 如果阻塞队列已满,则判断最大线程池是否已满,如果未满,则创建一个新的线程来执行任务。
  4. 如果最大线程池已满,则执行拒绝策略。

17、线程池中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程?

  • 阻塞队列的作用: 存储等待执行的任务。
  • 先添加队列而不是先创建最大线程的原因: 避免创建过多的线程,导致系统资源耗尽。

18、为什么用线程池?解释下线程池参数?

  • 使用线程池的原因: 减少线程创建和销毁的开销,提高程序的效率。

  • 线程池参数:

    • corePoolSize:核心线程池大小。
    • maximumPoolSize:最大线程池大小。
    • keepAliveTime:线程空闲时间。
    • unit:时间单位。
    • workQueue:阻塞队列。
    • threadFactory:线程工厂。
    • handler:拒绝策略。

19、ThreadLocal 的原理和使用场景

原理: ThreadLocal 为每个线程维护一个独立的变量副本,线程之间不会相互影响。

使用场景: 存储线程相关的上下文信息,例如事务 ID、用户 ID 等。

20、如何对一个字符串快速进行排序?

可以通过 Arrays.sort()Collections.sort() 来快速对字符数组或字符串进行排序,时间复杂度通常为 O(n log n),该方法默认使用快速排序算法。

3、Spring 底层

1、描述一下 Spring Bean 的生命周期?

Spring Bean 的生命周期主要包含以下几个阶段:

  1. 实例化 Bean: Spring 容器根据 Bean 的定义(BeanDefinition)创建 Bean 实例。
  2. 属性填充: Spring 容器将 BeanDefinition 中定义的属性值注入到 Bean 实例中。
  3. 初始化前: 在 Bean 初始化之前,Spring 容器会调用 BeanPostProcessor 的 postProcessBeforeInitialization 方法。
  4. 初始化: Spring 容器调用 Bean 的初始化方法(例如:@PostConstruct 注解标注的方法、实现了 InitializingBean 接口的 afterPropertiesSet 方法、配置文件中指定的 init-method 方法)。
  5. 初始化后: 在 Bean 初始化之后,Spring 容器会调用 BeanPostProcessor 的 postProcessAfterInitialization 方法。
  6. 就绪: Bean 初始化完成后,就可以被 Spring 容器使用了。
  7. 销毁前: 在 Bean 销毁之前,Spring 容器会调用 Bean 的销毁方法(例如:@PreDestroy 注解标注的方法、实现了 DisposableBean 接口的 destroy 方法、配置文件中指定的 destroy-method 方法)。
  8. 销毁: Spring 容器销毁 Bean 实例。

2、Spring 事务的实现方式和原理以及隔离级别?

实现方式:

  • 声明式事务: 通过 AOP(Aspect-Oriented Programming)实现,将事务管理的代码从业务代码中分离出来。
  • 编程式事务: 在业务代码中手动编写事务管理的代码。

原理:

Spring 事务的本质是 AOP,它通过 AOP 拦截方法调用,在方法调用前后添加事务相关的操作(例如:开启事务、提交事务、回滚事务)。

隔离级别:

Spring 事务的隔离级别定义了多个事务并发执行时,一个事务对另一个事务的影响程度。Spring 支持以下几种隔离级别:

  • DEFAULT: 使用底层数据库的默认隔离级别。
  • READ_UNCOMMITTED: 允许读取未提交的数据,可能导致脏读、幻读、不可重复读。
  • READ_COMMITTED: 禁止读取未提交的数据,可以避免脏读,但可能导致幻读、不可重复读。
  • REPEATABLE_READ: 禁止读取其他事务已修改但未提交的数据,可以避免脏读、不可重复读,但可能导致幻读。
  • SERIALIZABLE: 事务串行执行,可以避免所有并发问题,但性能较低。

3、什么是 Spring?谈谈你对 IOC 和 AOP 的理解。

什么是 Spring:

Spring 是一个开源的 Java 框架,它提供了 IoC(Inversion of Control)和 AOP(Aspect-Oriented Programming)等核心特性,简化了 Java 应用程序的开发。

对 IOC 的理解:

IoC 是一种设计模式,它将对象的创建和依赖关系的管理交给 Spring 容器。通过 IoC,我们可以降低组件之间的耦合度,提高代码的可维护性和可测试性。

对 AOP 的理解:

AOP 是一种编程思想,它允许我们将横切关注点(例如:日志记录、性能监控、事务管理)从业务逻辑中分离出来,并通过 AOP 织入到业务逻辑中。AOP 可以提高代码的复用性和可维护性。

4、什么是 bean 的自动装配,有哪些方式?

什么是 bean 的自动装配:

Spring 容器可以自动地将 Bean 之间的依赖关系注入到 Bean 中,而无需手动配置。

自动装配方式:

  • no: 不进行自动装配,需要手动配置 Bean 之间的依赖关系。
  • byName: 根据 Bean 的名称进行自动装配。
  • byType: 根据 Bean 的类型进行自动装配。
  • constructor: 根据 Bean 的构造函数进行自动装配。
  • autodetect: Spring 容器自动检测使用哪种方式进行自动装配。

5、Spring 容器的启动流程是怎么样的?

Spring 容器的启动流程主要包含以下几个步骤:

  1. 创建 BeanFactory: Spring 容器首先创建一个 BeanFactory,用于存储 Bean 的定义和实例。
  2. 加载 BeanDefinition: Spring 容器从配置文件或注解中加载 Bean 的定义(BeanDefinition)。
  3. 注册 BeanDefinition: Spring 容器将 BeanDefinition 注册到 BeanFactory 中。
  4. 实例化 Bean: Spring 容器根据 BeanDefinition 创建 Bean 实例。
  5. 依赖注入: Spring 容器将 Bean 之间的依赖关系注入到 Bean 实例中。
  6. 初始化 Bean: Spring 容器调用 Bean 的初始化方法。
  7. 启动完成: Spring 容器启动完成后,就可以使用 Bean 了。

6、Spring 框架中都用到了哪些设计模式?

Spring 框架中使用了多种设计模式,例如:

  • 工厂模式: Spring 使用工厂模式来创建 Bean 实例。
  • 单例模式: Spring 容器中的 Bean 默认是单例的。
  • 代理模式: Spring 使用代理模式来实现 AOP。
  • 模板方法模式: Spring 使用模板方法模式来处理 Bean 的生命周期。
  • 观察者模式: Spring 使用观察者模式来处理事件。

7、谈谈你对 AOP 的理解

AOP(Aspect-Oriented Programming)是一种编程思想,它允许我们将横切关注点(例如:日志记录、性能监控、事务管理)从业务逻辑中分离出来,并通过 AOP 织入到业务逻辑中。AOP 可以提高代码的复用性和可维护性。

8、Spring 框架中 Bean 的创建过程是怎样的?

Spring 框架中 Bean 的创建过程主要包含以下几个步骤:

  1. 加载 BeanDefinition: Spring 容器从配置文件或注解中加载 Bean 的定义(BeanDefinition)。
  2. 实例化 Bean: Spring 容器根据 BeanDefinition 创建 Bean 实例。
  3. 属性填充: Spring 容器将 BeanDefinition 中定义的属性值注入到 Bean 实例中。
  4. 初始化 Bean: Spring 容器调用 Bean 的初始化方法。
  5. 注册 Bean: Spring 容器将 Bean 实例注册到 BeanFactory 中。

9、spring 是什么?

Spring 是一个开源的 Java 框架,它提供了 IoC(Inversion of Control)和 AOP(Aspect-Oriented Programming)等核心特性,简化了 Java 应用程序的开发。

10、BeanFactory 和 ApplicationContext 有什么区别?

  • BeanFactory: 是 Spring 容器的最基本接口,提供了访问 Bean 的方法。
  • ApplicationContext: 是 BeanFactory 的子接口,提供了更多的功能,例如:事件发布、国际化支持等。

11、Spring 框架中的 Bean 是线程安全的吗?如果线程不安全,要如何处理?

Spring 框架中的 Bean 默认是单例的,因此存在线程安全问题。如果 Bean 是线程不安全的,可以采用以下方法处理:

  • 使用 ThreadLocal: 为每个线程创建一个独立的 Bean 实例。
  • 使用 synchronized 关键字或 Lock 接口: 对共享资源进行同步。
  • 将 Bean 的作用域设置为 prototype: 每次请求都创建一个新的 Bean 实例。

12、Spring 如何处理事务?

Spring 通过 AOP(Aspect-Oriented Programming)来处理事务。它通过 AOP 拦截方法调用,在方法调用前后添加事务相关的操作(例如:开启事务、提交事务、回滚事务)。

13、spring 事务什么时候会失效?

Spring 事务失效的情况有很多,例如:

  • 未配置事务管理器: 如果没有配置事务管理器,Spring 就无法管理事务。
  • 事务传播机制错误: 如果事务传播机制设置不当,可能会导致事务失效。
  • 异常被捕获: 如果异常被捕获,事务可能无法回滚。
  • 手动提交事务: 如果手动提交了事务,可能会导致事务提前结束。

14、Spring 如何处理循环依赖问题?

Spring 通过三级缓存来解决循环依赖问题。

15、Spring 事务传播机制

Spring 事务传播机制定义了多个事务方法相互调用时,事务如何传播。Spring 支持以下几种传播机制:

  • REQUIRED (默认): 如果当前存在事务,则加入该事务;如果当前不存在事务,则创建一个新的事务。
  • SUPPORTS: 如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务方式执行。
  • MANDATORY: 如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。
  • REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则将当前事务挂起。
  • NOT_SUPPORTED: 以非事务方式执行,如果当前存在事务,则将当前事务挂起。
  • NEVER: 禁止在事务中执行,如果当前存在事务,则抛出异常。
  • NESTED: 如果当前存在事务,则创建一个嵌套事务;如果当前不存在事务,则创建一个新的事务。

16、谈谈你对 IOC 的理解

IoC(Inversion of Control,控制反转)是一种设计模式,它将对象的创建和依赖关系的管理交给 Spring 容器。通过 IoC,我们可以降低组件之间的耦合度,提高代码的可维护性和可测试性。

17、SpringMVC 中的控制器是不是单例模式?如果是,如何保证线程安全?

SpringMVC 中的控制器默认是单例模式。由于是单例模式,因此存在线程安全问题。可以采用以下方法保证线程安全:

  • 使用 ThreadLocal: 为每个线程创建一个独立的控制器实例。
  • 使用 synchronized 关键字或 Lock 接口: 对共享资源进行同步。
  • 将控制器的作用域设置为 prototype: 每次请求都创建一个新的控制器实例。
  • 避免在控制器中使用可变实例变量: 尽量使用局部变量或不可变实例变量。

18、如何实现一个 IOC 容器

实现一个简单的 IoC 容器,主要需要以下几个步骤:

  1. 定义 BeanDefinition: 用于存储 Bean 的定义信息(例如:Bean 的名称、类型、属性等)。
  2. 创建 BeanFactory: 用于存储 BeanDefinition 和 Bean 实例。
  3. 加载 BeanDefinition: 从配置文件或注解中加载 BeanDefinition。
  4. 注册 BeanDefinition: 将 BeanDefinition 注册到 BeanFactory 中。
  5. 实例化 Bean: 根据 BeanDefinition 创建 Bean 实例。
  6. 依赖注入: 将 Bean 之间的依赖关系注入到 Bean 实例中。

19、解释下 Spring 支持的几种 bean 的作用域。

Spring 支持以下几种 Bean 的作用域:

  • singleton (默认): 单例模式,Spring 容器中只有一个 Bean 实例。
  • prototype: 原型模式,每次请求都会创建一个新的 Bean 实例。
  • request: 在同一个 HTTP 请求中,Bean 实例是共享的。
  • session: 在同一个 HTTP 会话中,Bean 实例是共享的。
  • global-session: 在所有 HTTP 会话中,Bean 实例是共享的。
  • application: 在整个 Web 应用程序中,Bean 实例是共享的。
  • websocket: 在同一个 WebSocket 连接中,Bean 实例是共享的。

4 、Spring MVC+Spring Boot

1. Spring Boot、Spring MVC 和 Spring 的区别?

  • Spring: Spring 框架是一个核心的 IoC (Inversion of Control) 容器,提供了 AOP (Aspect-Oriented Programming)、事务管理等众多功能。它是构建其他框架和模块的基础。
  • Spring MVC: Spring MVC 是 Spring 框架的一个模块,专注于开发 Web 应用。它实现了 MVC (Model-View-Controller) 设计模式,帮助我们构建灵活可维护的 Web 应用程序。
  • Spring Boot: Spring Boot 是一个基于 Spring 框架的脚手架,旨在简化 Spring 应用的配置和部署。它通过自动配置和约定优于配置的方式,大大减少了 Spring 项目的样板代码。

2. 什么是 Spring Boot?它的优缺点是什么?

Spring Boot 简化了 Spring 应用的搭建和开发过程,它具有以下特点:

  • 自动配置: 根据 classpath 中的依赖自动配置 Spring Bean,减少手动配置。
  • 嵌入式服务器: 内置 Tomcat、Jetty 等服务器,方便快速部署。
  • 起步依赖: 提供一系列预配置的 Starter,简化依赖管理。
  • 命令行工具: Spring Boot CLI 提供了快速开发 Spring 应用的命令行工具。

优点:

  • 简化开发: 自动配置和起步依赖大大减少了开发工作量。
  • 提高效率: 嵌入式服务器和命令行工具提高了开发效率。
  • 易于部署: 打包成可执行 JAR 包,方便部署。

缺点:

  • 封装性过强: 自动配置可能导致对底层原理理解不够深入。
  • 依赖问题: Starter 可能会引入不必要的依赖。
  • 版本升级问题: 版本升级较快,可能存在兼容性问题。

3. Spring Boot 的自动配置原理是什么?

Spring Boot 的自动配置基于以下几个核心机制:

  • @EnableAutoConfiguration: 开启自动配置,它实际上是一个复合注解,导入了 AutoConfigurationImportSelector
  • 条件注解: @Conditional 及其衍生注解(如 @ConditionalOnClass@ConditionalOnBean 等)根据条件决定是否进行自动配置。
  • Spring Factories 机制: 通过 SPI (Service Provider Interface) 机制加载 spring.factories 文件,从中获取需要自动配置的类。

4. Spring Boot 常用的 Starter 有哪些?

  • spring-boot-starter-web: 包含 Spring MVC、Tomcat 等 Web 开发所需依赖。
  • spring-boot-starter-data-jpa: 包含 JPA 相关依赖,用于数据库访问。
  • spring-boot-starter-test: 包含 JUnit、Mockito 等测试相关依赖。
  • spring-boot-starter-security: 包含 Spring Security 相关依赖,用于安全控制。
  • spring-boot-starter-thymeleaf: 包含 Thymeleaf 模板引擎相关依赖。

5. Spring Boot 支持哪些配置文件格式?

Spring Boot 支持以下两种配置文件格式:

  • Properties 文件: 键值对形式,例如 application.properties
  • YAML 文件: 使用缩进表示层级关系,更易读,例如 application.yml

6. Spring Boot 配置文件的加载顺序是什么?

Spring Boot 配置文件的加载顺序如下:

  1. 命令行参数
  2. 环境变量
  3. application.propertiesapplication.yml
  4. @PropertySource 注解

7. Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?

Spring Boot 支持多种日志框架,如 Log4j、Log4j2、SLF4j、Logback。推荐使用 SLF4j 作为日志门面,Logback 作为具体实现。Spring Boot 默认的日志框架是 Logback。

8. Spring Boot 如何实现热部署?

可以使用 Spring Boot DevTools 实现热部署。在 pom.xml 中添加 DevTools 依赖,修改代码后会自动重启应用,提高开发效率。

9. Spring Boot 如何打包?

可以使用 Maven 或 Gradle 将 Spring Boot 应用打包成 JAR 包或 WAR 包。JAR 包中包含了嵌入式服务器,可以直接运行。

10. Spring Boot 如何实现多数据源事务管理?

可以使用 Spring 的 @Transactional 注解来管理多数据源事务。需要在不同的数据源上配置不同的 PlatformTransactionManager

11. 什么是 Spring Profiles?

Spring Profiles 允许在不同的环境下(如开发、测试、生产)使用不同的配置。可以通过激活不同的 Profile 来切换配置。

12. 如何监视 Spring Boot 微服务?

可以使用 Spring Boot Actuator 提供的端点来监视微服务,例如:

  • /health: 查看应用健康状态。
  • /metrics: 查看应用性能指标。
  • /info: 查看应用基本信息。
  • /loggers: 查看和修改日志级别。

13. 如何实现 Spring Boot 应用程序的安全性?

可以使用 Spring Security 来实现 Spring Boot 应用程序的安全性。Spring Security 提供了认证、授权等功能,可以保护应用免受攻击。

14. Spring Boot 是否可以使用 XML 配置?

可以,但 Spring Boot 推荐使用 Java 配置,因为它更简洁、类型安全。

15. Spring MVC 的核心组件有哪些?

  • DispatcherServlet: 核心调度器,负责接收请求、分发请求、处理响应。
  • HandlerMapping: 负责根据请求信息找到对应的 Handler (Controller)。
  • HandlerAdapter: 负责调用 Handler 的方法处理请求。
  • ViewResolver: 负责根据 ModelAndView 返回 View 对象。
  • Handler: 具体的请求处理类 (Controller)。
  • View: 视图对象,负责渲染页面。

16. Spring MVC 的工作流程是怎样的?

  1. 客户端发送请求到 DispatcherServlet。
  2. DispatcherServlet 将请求交给 HandlerMapping 进行处理。
  3. HandlerMapping 根据请求信息找到对应的 Handler (Controller)。
  4. DispatcherServlet 将请求交给 HandlerAdapter 进行处理。
  5. HandlerAdapter 调用 Handler 的方法处理请求。
  6. Handler 处理完请求后,返回 ModelAndView 对象。
  7. DispatcherServlet 将 ModelAndView 对象交给 ViewResolver 进行处理。
  8. ViewResolver 根据 ModelAndView 返回 View 对象。
  9. DispatcherServlet 使用 View 对象渲染页面。
  10. DispatcherServlet 将响应返回给客户端。

17. Spring MVC 中如何进行重定向和转发?

  • 重定向: return "redirect:/url";
  • 转发: return "forward:/url";

18. Spring MVC 中如何从后台向前台传递数据?

可以使用 ModelAndView 对象将数据从后台传递到前台。

19. Spring MVC 中拦截器是如何工作的?

拦截器 (Interceptor) 用于在请求处理前后进行一些额外的操作,例如:

  • 权限验证
  • 日志记录
  • 性能监控

实现 HandlerInterceptor 接口,重写 preHandlepostHandleafterCompletion 方法。

20. 如何在 Spring MVC 拦截器中获取前台传入的参数?

可以通过 HttpServletRequest 对象获取前台传入的参数。

21. Spring MVC 中常用的控制器注解有哪些?

  • @Controller: 用于标识一个类为控制器。
  • @RestController: 相当于 @Controller + @ResponseBody,用于返回 JSON 数据。
  • @RequestMapping: 用于映射请求路径。
  • @GetMapping: 用于映射 GET 请求。
  • @PostMapping: 用于映射 POST 请求。

22. Spring MVC 中如何处理中文乱码问题?

  • POST 请求:

    • Spring Boot 方式:application.properties 中设置 spring.servlet.multipart.charset=UTF-8
    • 传统方式:web.xml 中配置 CharacterEncodingFilter
  • GET 请求:

    • Spring Boot 方式: 修改 Tomcat Connector 的 URIEncoding 属性为 UTF-8。
    • 传统方式:server.xml 中配置 URIEncoding="UTF-8"

23. MVC 设计模式的优点有哪些?

  • 分离关注点: 将应用分为模型 (Model)、视图 (View) 和控制器 (Controller),降低了代码耦合度。
  • 可维护性高: 修改其中一部分代码不会影响其他部分。
  • 可扩展性强: 方便添加新的功能。
  • 代码重用: 模型和视图可以被多个控制器共享。

24. Spring Boot 中的监视器是什么?

Spring Boot Actuator 提供了监视器,用于监控应用程序的运行状况、性能指标等。它通过一系列端点暴露了这些信息,方便我们了解应用程序的状态。

5、MyBatis

1. MyBatis 如何执行批量操作?

MyBatis 执行批量操作主要有两种方式:

  • ExecutorType.BATCH: 使用 JDBC 的 Statement.addBatch() 方法,将多个 SQL 语句添加到批处理中,然后一次性提交。这种方式可以提高性能,但需要注意 SQL 语句的相似性。
  • foreach 标签: 在 Mapper XML 文件中使用 foreach 标签遍历集合,动态生成 SQL 语句。这种方式更加灵活,可以处理不同的 SQL 语句,但性能可能不如 ExecutorType.BATCH

2. MyBatis 的缓存机制是怎样的?

MyBatis 提供了两级缓存:

  • 一级缓存 (SqlSession 级别) : 默认开启,作用范围是同一个 SqlSession。当执行查询时,MyBatis 会先从一级缓存中查找结果,如果存在则直接返回,否则执行 SQL 查询并将结果存入一级缓存。
  • 二级缓存 (SqlSessionFactory 级别) : 默认关闭,作用范围是同一个 SqlSessionFactory。当执行查询时,MyBatis 会先从二级缓存中查找结果,如果存在则直接返回,否则执行 SQL 查询并将结果存入二级缓存。

3. JDBC 编程有哪些不足之处,MyBatis 是如何解决的?

JDBC 编程的不足之处:

  • 代码冗长:需要手动编写大量的 JDBC 代码,如加载驱动、创建连接、prepareStatement、executeQuery 等。
  • SQL 语句硬编码:SQL 语句与 Java 代码耦合在一起,不易维护和修改。
  • 结果集处理繁琐:需要手动将结果集转换为 Java 对象。

MyBatis 的解决方案:

  • 封装 JDBC 操作:MyBatis 封装了 JDBC 操作,提供了简洁的 API,减少了代码量。
  • SQL 语句与 Java 代码分离:SQL 语句写在 XML 文件中,与 Java 代码分离,易于维护和修改。
  • 自动映射:MyBatis 可以自动将结果集映射为 Java 对象,简化了结果集处理。

4. MyBatis 编程步骤是什么样的?

  1. 创建 SqlSessionFactoryBuilder 对象。
  2. 加载 MyBatis 配置文件 (mybatis-config.xml)。
  3. 创建 SqlSessionFactory 对象。
  4. 创建 SqlSession 对象。
  5. 获取 Mapper 接口。
  6. 调用 Mapper 接口中的方法执行 SQL 语句。
  7. 关闭 SqlSession 对象。

5. MyBatis 中如何指定使用哪一种 Executor 执行器?

可以通过在 MyBatis 配置文件 (mybatis-config.xml) 中设置 defaultExecutorType 属性来指定 Executor 类型:

  • SIMPLE: 默认值,每次执行 SQL 语句都会创建一个新的 Statement 对象。
  • REUSE: 重用 Statement 对象。
  • BATCH: 使用 JDBC 批处理,提高性能。

6. MyBatis 的优缺点?

优点:

  • 简单易学:相比 Hibernate,MyBatis 更加简单易学。
  • 灵活:可以手动编写 SQL 语句,更加灵活。
  • 性能:可以进行 SQL 优化,提高性能。

缺点:

  • 需要编写 SQL 语句:相比 Hibernate,需要编写更多的 SQL 语句。
  • 不支持自动生成 SQL 语句:需要手动编写 SQL 语句。

7. #{} 和 ${} 的区别是什么?

  • #{}: 使用预编译 PreparedStatement,可以防止 SQL 注入。
  • ${}: 使用字符串拼接,存在 SQL 注入的风险。

8. 使用 MyBatis 的 Mapper 接口调用时有哪些要求?

  • Mapper 接口的方法名必须与 Mapper XML 文件中的 SQL 语句的 id 相同。
  • Mapper 接口的命名空间必须与 Mapper XML 文件中的 namespace 相同。
  • Mapper 接口的参数类型和返回值类型必须与 Mapper XML 文件中的 SQL 语句的参数类型和返回值类型相同。

9. MyBatis 是如何将 SQL 执行结果封装为目标对象并返回的?

MyBatis 使用反射机制将 SQL 执行结果封装为目标对象。它会根据结果集的列名和目标对象的属性名进行匹配,然后将结果集中的数据设置到目标对象的属性中。

10. MyBatis 是否可以映射 Enum 枚举类?

可以。需要在 Mapper XML 文件中配置 typeHandler,将 Enum 类型转换为数据库中的值。

11. MyBatis 和 Hibernate 的适用场景?

  • MyBatis: 适用于需要灵活控制 SQL 语句的场景,如复杂的查询、存储过程等。
  • Hibernate: 适用于需要快速开发、简化数据库操作的场景,如简单的 CRUD 操作。

12. 简述 MyBatis 的插件运行原理,如何编写一个插件?

MyBatis 的插件运行原理是基于拦截器 (Interceptor) 实现的。插件可以拦截 Executor、StatementHandler、ParameterHandler 和 ResultSetHandler 的方法,在这些方法执行前后进行一些额外的操作。

编写一个插件需要实现 Interceptor 接口,重写 intercept 方法,并在 MyBatis 配置文件中配置插件。

6、MySQL+数据库

1. 分表后非 sharding_key 的查询怎么处理,分表后的排序?

  • 非 sharding_key 查询: 可以使用全局索引或者多次查询后合并结果。
  • 分表后排序: 可以先在每个分表中排序,然后将结果合并排序。

2. 读写分离是怎么做的?

读写分离通过将读流量和写流量分发到不同的数据库服务器来实现。通常使用主从复制来实现读写分离。

3. 索引设计的原则?

  • 选择合适的列创建索引:经常用于查询、排序、过滤的列。
  • 使用较短的索引:索引越短,查询速度越快。
  • 避免创建过多索引:索引会增加写操作的负担。
  • 考虑索引的顺序:组合索引需要考虑列的顺序。

4. 分库分表的方式和分片策略有哪些?

  • 分库方式: 水平分库、垂直分库。
  • 分表方式: 水平分表。
  • 分片策略: Hash 取模、范围分片、列表分片。

5. MySQL 有哪几种数据存储引擎?有什么区别?

  • InnoDB: 支持事务、行级锁、外键,适用于 OLTP 应用。
  • MyISAM: 不支持事务、表级锁,适用于 OLAP 应用。

6. 事务的基本特性和隔离级别有哪些?

基本特性 (ACID):

  • 原子性 (Atomicity)
  • 一致性 (Consistency)
  • 隔离性 (Isolation)
  • 持久性 (Durability)

隔离级别:

  • 读未提交 (Read Uncommitted)
  • 读已提交 (Read Committed)
  • 可重复读 (Repeatable Read)
  • 串行化 (Serializable)

7. MySQL 主从同步原理?

  1. Master Server 记录 Binlog (Binary Log)。
  2. Slave Server 连接到 Master Server。
  3. Slave Server 请求 Master Server 发送 Binlog。
  4. Master Server 将 Binlog 发送给 Slave Server。
  5. Slave Server 执行 Binlog 中的 SQL 语句。

8. 聚簇索引和非聚簇索引是什么?

  • 聚簇索引: 索引和数据存储在一起,一个表只能有一个聚簇索引。
  • 非聚簇索引: 索引和数据分开存储,一个表可以有多个非聚簇索引。

9. 如何优化 SQL 慢查询?

  • 使用 EXPLAIN 分析 SQL 语句。
  • 优化索引。
  • 优化 SQL 语句。
  • 使用缓存。

10. MySQL 索引类型及对数据库性能的影响?

  • B-Tree 索引: 常用的索引类型,适用于范围查询。
  • Hash 索引: 适用于等值查询。
  • Full-text 索引: 适用于全文搜索。

索引可以提高查询速度,但会降低写操作的性能。

11. MySQL 的集群是如何搭建的?

可以使用 MySQL Cluster 或 Galera Cluster 搭建 MySQL 集群。

12. MySQL 索引的数据结构,各自优劣?

  • B-Tree: 适用于范围查询,查询效率稳定。
  • Hash: 适用于等值查询,查询效率高,但不支持范围查询。

13. 锁的类型有哪些?

  • 共享锁 (Shared Lock) : 读锁,多个事务可以同时持有。
  • 排他锁 (Exclusive Lock) : 写锁,只有一个事务可以持有。

14. MySQL 的锁有哪些?什么是间隙锁?

  • 行级锁: 锁住一行数据。
  • 表级锁: 锁住整个表。
  • 间隙锁 (Gap Lock) : 锁住一个范围,防止其他事务在该范围内插入数据。

15. MySQL 的索引结构是什么样的?

MySQL 的索引结构主要是 B-Tree。

16. 什么是最左前缀原则?什么是最左匹配原则?

  • 最左前缀原则: 指的是在使用联合索引时,查询条件必须包含联合索引的最左边的列,否则无法使用该索引。
  • 最左匹配原则: 指的是在使用联合索引时,查询条件中的列的顺序必须与联合索引中的列的顺序一致,否则无法完全使用该索引。

17. 索引的基本原理是什么?

索引的基本原理是通过创建一种数据结构(如 B-Tree),将数据按照一定的顺序排列,从而可以快速定位到需要查询的数据。

18. 如何对 MySQL 进行分库分表?

分库分表通常需要以下步骤:

  1. 选择合适的分库分表策略(如 Hash 取模、范围分片)。
  2. 创建分库分表。
  3. 配置路由规则,将查询请求路由到正确的分库分表。
  4. 进行测试和优化。

19. 多大数据量需要进行分库分表?

一般来说,当单表数据量达到百万级别或千万级别时,就需要考虑进行分库分表。具体情况需要根据业务需求和硬件资源来决定。

20. 事务的基本特性和隔离级别是什么?

基本特性 (ACID):

  • 原子性 (Atomicity)
  • 一致性 (Consistency)
  • 隔离性 (Isolation)
  • 持久性 (Durability)

隔离级别:

  • 读未提交 (Read Uncommitted)
  • 读已提交 (Read Committed)
  • 可重复读 (Repeatable Read)
  • 串行化 (Serializable)

21. 分库分表后,SQL 语句的执行流程是怎样的?

分库分表后,SQL 语句的执行流程通常如下:

  1. 客户端发送请求到中间件。
  2. 中间件根据路由规则将请求路由到相应的分库分表。
  3. 分库分表执行 SQL 语句。
  4. 中间件将结果合并返回给客户端。

22. 如何查看 MySQL 执行计划?

可以使用 EXPLAIN 命令查看 MySQL 执行计划。执行计划可以显示 SQL 语句的执行方式、使用的索引、扫描的行数等信息,帮助我们优化 SQL 语句。

23. ACID 靠什么保证的?

  • 原子性: 通过 undo log 保证,事务回滚时可以撤销之前的操作。
  • 一致性: 通过数据库的约束(如外键、唯一索引)和事务的隔离性保证。
  • 隔离性: 通过锁机制和 MVCC 机制保证。
  • 持久性: 通过 redo log 保证,即使数据库崩溃,也可以恢复之前提交的事务。

24. 简述 MyISAM 和 InnoDB 的区别?

特性MyISAMInnoDB
事务不支持支持
表级锁行级锁
外键不支持支持
索引非聚簇索引聚簇索引
适用场景OLAPOLTP

导出到 Google 表格

25. InnoDB 存储引擎的锁的算法有哪些?

InnoDB 存储引擎的锁的算法主要有三种:

  • Record Lock: 锁定一条记录。
  • Gap Lock: 锁定一个范围,但不包括记录本身。
  • Next-Key Lock: 锁定一个范围,包括记录本身。

26. 什么是脏读、幻读、不可重复读?如何解决?

  • 脏读: 一个事务读取了另一个事务尚未提交的数据。
  • 幻读: 一个事务多次读取数据,结果集中的记录数增加了。
  • 不可重复读: 一个事务多次读取同一条记录,结果不一致。

解决方法:

  • 调整事务隔离级别,将隔离级别设置为读已提交或可重复读。

27. 什么是 MVCC?

MVCC (Multi-Version Concurrency Control) 多版本并发控制,是一种用于提高数据库并发性能的技术。MVCC 通过为每行数据增加版本号,使得事务可以读取到不同版本的数据,从而避免了读写冲突。

7、Redis

1. Redis 是单线程的,如何提高多核 CPU 的利用率?

虽然 Redis 是单线程的,但可以通过以下方式提高多核 CPU 的利用率:

  • 部署多个 Redis 实例: 在同一台服务器上部署多个 Redis 实例,每个实例绑定一个或多个 CPU 核心。这样可以将请求分发到不同的实例上,充分利用多核 CPU 的性能。
  • 使用 Redis 集群: Redis 集群可以将数据分片存储在多个节点上,每个节点可以运行在不同的 CPU 核心上。这样可以提高并发处理能力,并充分利用多核 CPU 的性能。

2. Redis 集群方案什么情况下会导致整个集群不可用?

Redis 集群方案在以下情况下可能会导致整个集群不可用:

  • 所有 master 节点都宕机: Redis 集群依赖于 master 节点来提供服务。如果所有 master 节点都宕机,则整个集群将无法提供服务。
  • 超过一半的 master 节点宕机: Redis 集群使用投票机制来选举新的 master 节点。如果超过一半的 master 节点宕机,则无法选举出新的 master 节点,导致集群不可用。

3. Redis 是单进程单线程的吗?

严格来说,Redis 6.0 版本及以后引入了多线程模型来处理网络 I/O。但是,Redis 的核心操作(如数据读写、命令执行)仍然是由单线程完成的。因此,通常我们仍然认为 Redis 是单进程单线程的。

4. 什么是 Redis?

Redis (Remote Dictionary Server) 是一个开源的内存数据结构存储器,可以用作数据库、缓存和消息代理。它支持多种数据结构,如字符串(string)、哈希(hash)、列表(list)、集合(set)、有序集合(zset)等。

5. 简述 Redis 事务实现?

Redis 事务使用 MULTIEXECDISCARD 命令实现。

  • MULTI: 标记一个事务的开始。
  • EXEC: 执行事务中的所有命令。
  • DISCARD: 取消事务。

Redis 事务不支持回滚,如果事务中的某个命令执行失败,其他命令仍然会继续执行。

6. Redis 的持久化机制是什么?各自的优缺点?

Redis 提供了两种持久化机制:

  • RDB (Redis Database) : 定期将内存中的数据快照保存到磁盘上。

    • 优点: 简单易用,适用于备份和恢复。
    • 缺点: 可能丢失部分数据,因为 RDB 是定期保存的。
  • AOF (Append-Only File) : 将每个写操作命令追加到文件中。

    • 优点: 数据安全性高,可以保证数据不丢失。
    • 缺点: 文件体积大,恢复速度慢。

7. Redis 提供了哪几种持久化方式?

Redis 提供了以下两种持久化方式:

  • RDB: 通过 SAVEBGSAVE 命令生成 RDB 文件。
  • AOF: 通过 APPENDONLY 配置开启 AOF 持久化。

8. 怎么理解 Redis 事务?

Redis 事务可以将多个命令打包在一起执行,保证这些命令的原子性。但是,Redis 事务不支持回滚,如果事务中的某个命令执行失败,其他命令仍然会继续执行。

9. Redis 线程模型、单线程快的原因?

Redis 使用单线程模型来处理客户端请求。单线程快的原因:

  • 避免线程切换开销: 单线程避免了多线程之间的上下文切换开销。
  • 内存操作: Redis 的数据存储在内存中,读写速度非常快。
  • 高效的数据结构: Redis 使用了高效的数据结构,如跳跃表、压缩列表等。

10. Redis 的过期键的删除策略?

Redis 使用以下几种策略删除过期键:

  • 定时删除: 定期检查并删除过期键。
  • 惰性删除: 在访问某个键时,检查其是否过期,如果过期则删除。
  • 内存淘汰: 当内存不足时,根据一定的策略(如 LRU)删除部分键。

11. RDB 和 AOF 机制(同第 6 题)

12. Redis 集群会有写操作丢失吗?为什么?

Redis 集群在某些情况下可能会丢失写操作:

  • 异步复制: Redis 集群使用异步复制来实现主从同步。如果 master 节点在将写操作同步到 slave 节点之前宕机,则 slave 节点将丢失该写操作。
  • 网络分区: 如果网络分区导致 master 节点与部分 slave 节点断开连接,则 master 节点仍然可以接收写操作。但是,这些写操作可能无法同步到所有 slave 节点,导致数据丢失。

13. Redis 主从复制的核心原理?

Redis 主从复制的核心原理是:

  1. Slave 节点向 Master 节点发送 SYNC 命令。
  2. Master 节点将 RDB 文件发送给 Slave 节点。
  3. Slave 节点加载 RDB 文件。
  4. Master 节点将后续的写操作命令发送给 Slave 节点。
  5. Slave 节点执行这些写操作命令,保持与 Master 节点的数据一致。

14. Redis 集群方案?

Redis 集群方案通过将数据分片存储在多个节点上,提高了 Redis 的并发处理能力和可用性。Redis 集群使用 Hash Slot 来分配数据,每个节点负责一部分 Hash Slot。

15. Redis 的同步机制?

Redis 的同步机制包括主从复制和集群同步。主从复制用于实现 Master 节点和 Slave 节点之间的数据同步,集群同步用于实现集群中各个节点之间的数据同步。

16. Redis 事务相关的命令有哪些?

  • MULTI: 标记一个事务的开始。
  • EXEC: 执行事务中的所有命令。
  • DISCARD: 取消事务。
  • WATCH: 监视一个或多个键,如果在事务执行之前这些键被修改,则事务将失败。

17. 修改配置不重启 Redis 会实时生效吗?

部分配置可以实时生效,例如修改 loglevelslowlog-log-slower-than 等配置。但是,有些配置需要重启 Redis 才能生效,例如修改 portbind 等配置。

18. 如何实现集群中的 session 共享存储?

可以使用 Redis 集群来实现 session 共享存储。将 session 数据存储在 Redis 集群中,多个应用服务器可以共享这些 session 数据。

19. Redis 的内存用完了会发生什么?

如果 Redis 的内存用完了,Redis 将根据内存淘汰策略删除部分键,以释放内存。如果所有键都不能被删除,Redis 将返回错误信息。

20. 缓存雪崩、缓存穿透、缓存击穿?

  • 缓存雪崩: 大量缓存同时失效,导致大量请求直接访问数据库,造成数据库压力过大。
  • 缓存穿透: 查询不存在的键,导致每次请求都访问数据库。
  • 缓存击穿: 热点缓存失效,导致大量请求直接访问数据库。

21. 使用 Redis 有哪些好处?

使用 Redis 的好处:

  • 提高性能: Redis 将数据存储在内存中,读写速度非常快。
  • 减轻数据库压力: 可以将热点数据存储在 Redis 中,减少数据库的访问量。
  • 支持多种数据结构: Redis 支持多种数据结构,可以满足不同的业务需求。
  • 高可用性: Redis 集群提供了高可用性,可以保证服务的稳定运行。

8 、分布式+消息队列+微服务

1. 分布式锁解决方案

分布式锁用于在分布式环境下控制对共享资源的并发访问,保证数据的一致性。常见的解决方案有:

  • 基于数据库实现:

    • 优点:简单易懂,易于实现。
    • 缺点:性能较低,可能存在锁竞争问题。
  • 基于 Redis 实现:

    • 优点:性能高,实现简单。
    • 缺点:需要考虑 Redis 的高可用性。
  • 基于 ZooKeeper 实现:

    • 优点:可靠性高,可以防止死锁。
    • 缺点:性能相对较低,实现复杂。

2. 如何保证事务一致性?

事务一致性是指事务执行前后,数据从一个有效状态转换到另一个有效状态。可以从以下几个方面保证事务一致性:

  • ACID 特性: 原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

  • 分布式事务:

    • 2PC (Two-Phase Commit): 两阶段提交,保证多个事务参与者的数据一致性。
    • 3PC (Three-Phase Commit): 三阶段提交,改进了 2PC 的性能和容错性。
    • TCC (Try-Confirm-Cancel): 补偿事务,适用于最终一致性要求较高的场景。
    • 本地消息表: 通过在本地维护一个消息表,实现最终一致性。

3. ZooKeeper 和 Eureka 的区别?

  • ZooKeeper:

    • 强一致性(CP)
    • 提供分布式协调服务,如服务注册与发现、分布式锁、配置管理等。
  • Eureka:

    • CAP 理论中的 AP
    • 专注于服务注册与发现。

4. 负载均衡算法、类型?

算法:

  • 轮询(Round Robin)
  • 加权轮询(Weighted Round Robin)
  • 随机(Random)
  • 加权随机(Weighted Random)
  • 最小连接数(Least Connections)
  • 源地址哈希(Source IP Hash)

类型:

  • 集中式负载均衡: 使用独立的负载均衡设备。
  • 进程内负载均衡: 在应用内部实现负载均衡。
  • 分布式负载均衡: 由多台服务器共同承担负载均衡任务。

5. CAP 理论,BASE 理论?

  • CAP 理论:

    • 一致性(Consistency)
    • 可用性(Availability)
    • 分区容忍性(Partition Tolerance)
    • CAP 理论指出,一个分布式系统最多只能同时满足 CAP 中的两个特性。
  • BASE 理论:

    • 基本可用(Basically Available)
    • 软状态(Soft State)
    • 最终一致性(Eventually Consistent)
    • BASE 理论是对 CAP 理论的一种补充,强调在可用性优先的前提下,实现最终一致性。

6. Spring Cloud 和 Spring Cloud Alibaba 都有哪些组件?都解决了什么问题?

Spring Cloud:

  • Eureka: 服务注册与发现。
  • Ribbon: 客户端负载均衡。
  • Hystrix: 服务熔断与降级。
  • Zuul: 网关。
  • Config: 配置中心。

Spring Cloud Alibaba:

  • Nacos: 服务注册与发现、配置中心。
  • Sentinel: 流量控制、熔断降级。
  • Seata: 分布式事务。
  • RocketMQ: 消息队列。

解决的问题:

  • 服务注册与发现
  • 负载均衡
  • 服务容错
  • API 网关
  • 分布式配置
  • 分布式事务

7. ZooKeeper 的数据模型和节点类型?

数据模型:

  • 层次结构,类似于文件系统。

节点类型:

  • 持久节点(Persistent Node)
  • 临时节点(Ephemeral Node)
  • 顺序节点(Sequential Node)

8. 什么是中台?

中台是一种企业级的共享服务平台,它将通用的业务能力抽象出来,封装成可重用的服务,供前台业务使用。中台可以提高效率、降低成本、统一标准。

9. 如何拆分微服务?

拆分微服务需要考虑以下因素:

  • 业务领域
  • 团队组织
  • 技术能力
  • 数据独立性
  • 高内聚低耦合

10. 分布式事务解决方案(同第 2 题)

11. 如何设计出高内聚、低耦合的微服务?

  • 明确业务边界
  • 单一职责原则
  • 接口设计清晰
  • 松耦合
  • 高内聚

12. ZooKeeper Watch 机制?

ZooKeeper Watch 机制允许客户端监听 ZooKeeper 节点的变化。当节点发生变化时,ZooKeeper 会通知客户端。

13. DDD 领域驱动设计?

DDD 是一种软件设计方法,它强调以领域模型为中心,将业务逻辑封装在领域模型中。DDD 可以帮助我们设计出高内聚、低耦合的微服务。

14. Spring Cloud 核心组件及其作用?(同第 6 题)

15. Dubbo 的整体架构设计及分层?

Dubbo 是一款高性能的 RPC 框架。其架构设计包括:

  • Provider: 服务提供者。
  • Consumer: 服务消费者。
  • Registry: 服务注册中心。
  • Monitor: 服务监控。

分层:

  • Service Layer: 服务接口层。
  • RPC Layer: 远程调用层。
  • Transport Layer: 网络传输层。

16. 中台和微服务有什么关系?

中台和微服务都是为了解决大型应用的复杂性问题。微服务是一种架构风格,而中台是一种企业级的共享服务平台。中台可以基于微服务架构构建。

17. 分布式架构下,Session 共享有什么方案?

  • 集中式 Session 存储: 使用 Redis 等集中式缓存存储 Session 数据。
  • Session 复制: 在多个应用服务器之间复制 Session 数据。
  • Cookie 存储: 将 Session 数据存储在 Cookie 中。

18. Spring Cloud 和 Dubbo 的区别?

特性Spring CloudDubbo
定位微服务框架RPC 框架
通信协议HTTPDubbo 协议
服务发现Eureka/NacosZooKeeper
负载均衡RibbonDubbo
熔断降级Hystrix/SentinelDubbo

导出到 Google 表格

19. 如何实现接口的幂等性?

  • 唯一 ID: 为每个请求分配一个唯一 ID,如果重复请求的 ID 相同,则直接返回结果。
  • Token 机制: 每次请求时生成一个 Token,并在处理请求时验证 Token。
  • 状态机: 使用状态机来记录请求的处理状态。

20. 微服务的链路追踪、持续集成、AB 发布要怎么做?

  • 链路追踪: 使用 SkyWalking、Jaeger 等工具进行链路追踪。
  • 持续集成: 使用 Jenkins、GitLab CI 等工具进行持续集成。
  • AB 发布: 通过流量控制,将一部分流量引流到新版本,观察其表现。

21. RPC、RMI 的理解?

  • RPC (Remote Procedure Call): 远程过程调用,允许程序像调用本地函数一样调用远程服务。
  • RMI (Remote Method Invocation): 远程方法调用,是 Java 特有的 RPC 实现。

22. 分布式 ID 生成方案?

  • UUID: 通用唯一标识符。
  • 数据库自增 ID:
  • Redis 自增 ID:
  • 雪花算法 (Snowflake):
  • Leaf:

23. Hystrix 的理解?

Hystrix 是一个用于服务容错的库,它可以防止雪崩效应。Hystrix 通过熔断、降级等机制来保护服务。

24. 微服务的理解,优缺点?

理解:

微服务是一种架构风格,它将一个大型应用拆分成多个小型服务,每个服务都可以独立部署和扩展。

优点:

  • 灵活
  • 可扩展
  • 容错性高

缺点:

  • 复杂性高
  • 分布式事务
  • 运维成本高

25. 分布式事务如何处理?

处理方式:

  • 2PC (Two-Phase Commit): 两阶段提交,保证多个事务参与者的数据一致性。
  • 3PC (Three-Phase Commit): 三阶段提交,改进了 2PC 的性能和容错性。
  • TCC (Try-Confirm-Cancel): 补偿事务,适用于最终一致性要求较高的场景。
  • 本地消息表: 通过在本地维护一个消息表,实现最终一致性。
  • Seata: 一款开源的分布式事务解决方案,提供了多种事务模式。

26. 你的项目中是怎么保证微服务敏捷开发的?

  • 自动化测试: 编写单元测试、集成测试、端到端测试,保证代码质量。
  • 持续集成/持续交付 (CI/CD): 使用 Jenkins、GitLab CI 等工具实现自动化构建、测试、部署。
  • 小步快跑: 将大功能拆分成小功能,快速迭代发布。
  • 拥抱变化: 快速响应需求变化,及时调整开发计划。
  • 团队协作: 团队成员之间密切协作,共同完成开发任务。

27. 简述 ZooKeeper 的命名服务、配置管理、集群管理?

  • 命名服务: ZooKeeper 可以作为分布式环境下的命名服务,提供统一的命名空间。
  • 配置管理: ZooKeeper 可以存储应用的配置信息,并提供实时更新和通知机制。
  • 集群管理: ZooKeeper 可以管理集群中的节点,如 Master 选举、故障转移等。

28. 简述 ZAB 协议?

ZAB (ZooKeeper Atomic Broadcast) 协议是 ZooKeeper 的核心协议,用于保证分布式一致性。ZAB 协议包括以下两个阶段:

  • Leader election: 选举 Leader 节点。
  • Atomic broadcast: 原子广播,将事务 Proposal 广播到所有 Follower 节点,并保证 Proposal 的有序性。

9、消息中间件

1. Kafka 如何处理消息顺序、重复发送、重复消费、消息丢失?

  • 消息顺序: Kafka 通过 Partition 内的有序性来保证消息的顺序。每个 Partition 中的消息按照发送的顺序存储,Consumer 按照顺序消费 Partition 中的消息。
  • 重复发送: Kafka 可能会因为网络抖动等原因导致消息被重复发送。Producer 可以通过幂等性配置来避免消息重复发送。
  • 重复消费: Consumer 可能会因为 offset 提交不及时等原因导致消息被重复消费。Consumer 可以通过幂等性处理来避免消息重复消费。
  • 消息丢失: Kafka 可能会因为 Broker 宕机等原因导致消息丢失。Producer 可以通过 ACK 机制来保证消息不丢失。

2. 如何保证消息消费的幂等性?

消息消费的幂等性指的是 Consumer 多次消费同一条消息,结果都是一致的。可以通过以下方式保证消息消费的幂等性:

  • 唯一 ID: 为每条消息分配一个唯一 ID,Consumer 消费消息时,先检查该 ID 是否已经被消费过。
  • 数据库乐观锁: 在数据库中增加一个版本号字段,每次更新数据时,先检查版本号是否与预期一致。
  • 状态机: 使用状态机来记录消息的处理状态。

3. Kafka 中 ZooKeeper 的作用?

ZooKeeper 在 Kafka 中主要起以下作用:

  • Broker 管理: ZooKeeper 负责管理 Kafka 集群中的 Broker 节点,包括 Broker 的注册、发现、状态监控等。
  • Partition 管理: ZooKeeper 负责管理 Kafka Topic 的 Partition 信息,包括 Partition 的创建、分配、Leader 选举等。
  • Consumer Group 管理: ZooKeeper 负责管理 Consumer Group 的信息,包括 Consumer Group 的注册、offset 维护等。

4. 如何保证消息不丢失?

可以通过以下方式保证消息不丢失:

  • ACK 机制: Producer 发送消息时,需要等待 Broker 的确认。
  • 多副本机制: Kafka Topic 可以设置多个副本,当某个 Broker 宕机时,其他副本可以继续提供服务。
  • 持久化存储: Kafka 将消息持久化存储在磁盘上,即使 Broker 宕机,消息也不会丢失。

5. RabbitMQ 事务消息?

RabbitMQ 事务消息通过 AMQP 事务机制实现。Producer 发送消息时,需要开启一个事务,然后发送消息,最后提交事务。如果事务提交失败,则消息不会被发送。

6. 简述 Kafka 的 Rebalance 机制?

Kafka Rebalance 指的是当 Consumer Group 中的 Consumer 数量发生变化时,Kafka 会重新分配 Partition 给 Consumer。Rebalance 可能会导致 Consumer 消费到重复消息或者漏消费消息。

7. 如何保证消息的顺序?

可以通过以下方式保证消息的顺序:

  • 单 Partition: 将所有消息发送到同一个 Partition 中,Consumer 按照顺序消费该 Partition 中的消息。
  • 消息分片: 将消息按照一定的规则分片,然后发送到不同的 Partition 中,Consumer 按照规则消费这些 Partition 中的消息。

8. MQ 有什么用?有哪些具体的使用场景?

MQ (Message Queue) 消息队列用于在不同的系统之间传递消息。

作用:

  • 解耦
  • 异步
  • 削峰填谷

使用场景:

  • 异步处理: 例如,用户注册后,发送邮件、短信等操作可以异步处理。
  • 流量削峰: 例如,秒杀活动时,可以将请求先放入消息队列,然后由 Consumer 逐步处理。
  • 系统解耦: 例如,订单系统和支付系统可以通过消息队列解耦。

9. 如何保证消息的高效读写?

  • Kafka:

    • 顺序写磁盘
    • 零拷贝
    • 批量发送
  • RabbitMQ:

    • 持久化
    • 预取

10. Kafka 的性能好在什么地方?

Kafka 的性能好在以下几个方面:

  • 顺序写磁盘: Kafka 将消息顺序写入磁盘,避免了随机写磁盘的开销。
  • 零拷贝: Kafka 使用零拷贝技术,减少了数据在内核空间和用户空间之间的拷贝次数。
  • 批量发送: Kafka 支持批量发送消息,减少了网络传输的开销。

11. Kafka 在什么情况下会出现消息丢失及解决方案?

消息丢失的情况:

  • Producer 没有收到 ACK
  • Broker 宕机
  • Consumer offset 提交不及时

解决方案:

  • 配置 Producer ACK 机制
  • 使用多副本机制
  • 定期提交 Consumer offset

12. 如果让你设计一个 MQ,你会如何设计?

设计一个 MQ 需要考虑以下方面:

  • 消息模型:
  • 存储:
  • 传输:
  • 可靠性:
  • 扩展性:
  • 监控:

13. RabbitMQ 镜像队列机制?

RabbitMQ 镜像队列机制用于提高队列的可用性。镜像队列会将队列的数据复制到多个节点上,当某个节点宕机时,其他节点可以继续提供服务。

14. 如何进行产品选型?

产品选型需要考虑以下因素:

  • 业务需求:
  • 性能:
  • 可靠性:
  • 扩展性:
  • 成本:
  • 社区活跃度:

15. Kafka 是 pull?push?优劣势分析?

Kafka 使用 Pull 模式。

优点:

  • Consumer 可以根据自己的需求拉取消息。
  • Consumer 可以控制消费速率。

缺点:

  • Consumer 需要主动拉取消息。

16. 简述 Kafka 架构设计?

Kafka 架构设计包括:

  • Producer:
  • Broker:
  • Consumer:
  • ZooKeeper:

17. RabbitMQ 如何确保消息发送?消息接收?

  • 消息发送:
  • 消息接收:

18. 简述 RabbitMQ 的架构设计?

RabbitMQ 的架构设计包括:

  • Exchange:
  • Queue:
  • Binding:
  • Message:

19. 使用 MQ 如何保证分布式事务的最终一致性?

可以使用本地消息表或事务消息来实现分布式事务的最终一致性。

20. 解释什么是 RabbitMQ 死信队列、延时队列?

  • 死信队列: 用于存储无法被 Consumer 正常消费的消息。
  • 延时队列: 用于存储需要延迟消费的消息。

10、力扣算法

1、x的平方根

2、删除排序数组中的重复项

3、斐波那契数列

4、环形链表

5、二叉树遍历

6、合并两个有序数组

7、Dota2参议院

8、寻找数组的中心索引

9、二叉树的最小深度

10、反转链表

11、预测赢家

12、井字游戏

13、优势洗牌

14、子数组最大平均数

15、三个数的最大乘积

16、冒泡排序

17、插入排序

18、希尔排序

19、归并排序

11、网络通信

1. 线程池解决多线程 BIO 编程会出现的问题

多线程 BIO (Blocking IO) 编程中,每个客户端连接都需要一个线程来处理。当客户端连接数增加时,线程数量也会急剧增加,导致以下问题:

  • 线程创建和销毁开销大: 大量线程的创建和销毁会消耗大量的系统资源。
  • 线程上下文切换开销大: 线程切换会导致 CPU Cache 失效,增加 CPU 负担。
  • 资源占用过多: 每个线程都需要占用一定的内存空间,大量线程会占用过多的内存资源。

线程池可以有效地解决这些问题。线程池维护一个线程队列,将客户端请求提交到线程池中,由线程池中的线程来处理。这样可以限制线程数量,减少线程创建和销毁的开销,提高系统资源利用率。

2. TCP 和 UDP 有什么区别?

特性TCP (Transmission Control Protocol)UDP (User Datagram Protocol)
连接面向连接无连接
可靠性可靠传输不可靠传输
有序性有序传输无序传输
速度
开销
适用场景传输大量数据,要求可靠性传输少量数据,速度优先

导出到 Google 表格

3. 描述下 HTTP 和 HTTPS 的区别?

特性HTTP (Hypertext Transfer Protocol)HTTPS (Hypertext Transfer Protocol Secure)
安全性不安全安全
加密无加密通过 SSL/TLS 加密
端口80443
证书需要 SSL 证书

导出到 Google 表格

HTTPS 通过 SSL/TLS 协议对 HTTP 通信进行加密,保证了数据的机密性、完整性和身份认证。

4. 怎么使用 Netty 实现网络通信?

使用 Netty 实现网络通信的步骤:

  1. 创建 ServerBootstrap 和 Bootstrap 对象。
  2. 配置 ServerBootstrap,绑定监听端口和 Handler。
  3. 配置 Bootstrap,连接到服务器。
  4. 创建 ChannelPipeline,添加 Handler。
  5. 启动服务或连接到服务器。

5. BIO 编程会出现什么问题?

  • 阻塞: BIO 编程中,一个线程只能处理一个连接,如果连接长时间没有数据传输,线程会被阻塞。
  • 并发量低: 由于线程阻塞,BIO 编程无法支持高并发连接。
  • 资源消耗大: 每个连接都需要一个线程来处理,大量连接会消耗大量的系统资源。

6. JAVA 有哪几种 IO 模型?有什么区别?

Java 有以下几种 IO 模型:

  • BIO (Blocking IO): 阻塞 IO,一个线程处理一个连接。
  • NIO (Non-blocking IO): 非阻塞 IO,一个线程可以处理多个连接。
  • 多路复用 IO: 通过 select、poll 或 epoll 监听多个连接的 IO 事件,一个线程可以处理多个连接。
  • AIO (Asynchronous IO): 异步 IO,IO 操作完成后会通知线程。

7. IO 的常用类和方法,以及如何使用?

Java IO 中常用的类和方法:

  • InputStream:字节输入流

    • read():读取一个字节或一组字节
  • OutputStream:字节输出流

    • write():写入一个字节或一组字节
  • Reader:字符输入流

    • read():读取一个字符或一组字符
  • Writer:字符输出流

    • write():写入一个字符或一组字符
  • File:文件类

    • createNewFile():创建新文件
    • exists():判断文件是否存在
  • FileInputStreamFileOutputStreamFileReaderFileWriter

使用方法:

  1. 创建 IO 对象。
  2. 调用 IO 对象的方法进行读写操作。
  3. 关闭 IO 对象。

8. 如何使用 NIO 实现网络通信?

使用 NIO 实现网络通信的步骤:

  1. 创建 Selector 对象。
  2. 创建 ServerSocketChannel 或 SocketChannel 对象。
  3. 将 Channel 注册到 Selector 上,监听 OP_ACCEPT 或 OP_READ 事件。
  4. 循环监听 Selector 上的事件。
  5. 根据事件类型进行处理。

9. select,poll 和 epoll 有什么区别?

特性selectpollepoll
支持连接数有限无限制无限制
IO 事件水平触发水平触发边缘触发
性能较低中等

导出到 Google 表格

10. 五种 IO 模型分别是哪些?

  • 阻塞 IO (Blocking IO)
  • 非阻塞 IO (Non-blocking IO)
  • 多路复用 IO (Multiplexing IO)
  • 信号驱动 IO (Signal Driven IO)
  • 异步 IO (Asynchronous IO)

11. 网络操作 IO 讲解

网络操作 IO 主要包括以下几个方面:

  • Socket 编程: 使用 Socket API 进行网络通信。
  • IO 模型: 选择合适的 IO 模型来提高网络通信的效率。
  • 协议: 使用合适的网络协议(如 TCP、UDP)进行通信。
  • 序列化: 将数据序列化成字节流进行传输。

12. Java 中流类的超类主要有哪些?

Java 中流类的超类主要有:

  • InputStream
  • OutputStream
  • Reader
  • Writer

13. JAVA NIO 的几个核心组件是什么?分别有什么作用?

JAVA NIO 的核心组件:

  • Channel:表示一个连接。
  • Buffer:用于存储数据。
  • Selector:用于监听多个 Channel 的 IO 事件。

14. 为什么图片、视频、音乐、文件等都是要字节流来读取?

因为计算机中所有的数据都是以二进制形式存储的,字节是最小的存储单位。字节流可以处理任何类型的数据,包括文本、图片、视频、音乐、文件等。

15. TCP 为什么是三次握手,而不是两次?

三次握手是为了保证连接的可靠性。

  1. 客户端发送 SYN 包到服务器。
  2. 服务器收到 SYN 包后,发送 SYN+ACK 包到客户端。
  3. 客户端收到 SYN+ACK 包后,发送 ACK 包到服务器。

通过三次握手,双方都可以确认对方的接收能力和自己的发送能力。如果是两次握手,则无法保证连接的可靠性。

12、JVM调优

1. JVM 的内存模型

JVM 内存模型(Java Memory Model,JMM)描述了 Java 程序中变量的存储方式以及线程如何与这些变量交互。JVM 内存模型主要分为以下几个区域:

  • 堆(Heap): 对象实例和数组存储的区域,是垃圾回收的主要场所。
  • 方法区(Method Area): 存储类信息、常量、静态变量等,JDK 8 及之后被元空间(Metaspace)取代。
  • 虚拟机栈(VM Stack): 每个线程都有一个虚拟机栈,用于存储方法调用栈帧。
  • 本地方法栈(Native Method Stack): 与虚拟机栈类似,用于存储本地方法调用栈帧。
  • 程序计数器(Program Counter): 记录当前线程执行的字节码指令地址。

2. 类加载器

类加载器(ClassLoader)负责将 Class 文件加载到 JVM 中。Java 中有三种默认的类加载器:

  • 启动类加载器(Bootstrap ClassLoader): 加载 JDK 核心类库。
  • 扩展类加载器(Extension ClassLoader): 加载扩展目录中的类库。
  • 应用程序类加载器(Application ClassLoader): 加载应用程序 classpath 中的类库。

3. STW

STW(Stop-The-World)指的是 JVM 在执行垃圾回收时,需要暂停所有正在运行的线程。STW 会导致应用程序停顿,影响用户体验。STW 主要发生在以下阶段:

  • Minor GC: 新生代垃圾回收。
  • Major GC/Full GC: 老年代垃圾回收或整个堆的垃圾回收。

4. 对象的生命周期

一个对象从加载到 JVM,再到被 GC 清除,经历了以下过程:

  1. 加载(Loading): 类加载器将 Class 文件加载到 JVM 中。

  2. 链接(Linking):

    • 验证(Verification): 检查 Class 文件的结构是否符合规范。
    • 准备(Preparation): 为静态变量分配内存并设置初始值。
    • 解析(Resolution): 将符号引用转换为直接引用。
  3. 初始化(Initialization): 执行类构造器(clinit)和对象初始化。

  4. 使用(Using): 程序使用对象。

  5. 标记(Marking): GC 标记不再被引用的对象。

  6. 清除(Sweeping/Collecting): GC 清除被标记的对象。

  7. 卸载(Unloading): 类被卸载。

5. GC Root

GC Root 是指一组必须活跃的对象,垃圾回收器会从 GC Root 开始搜索,所有与 GC Root 存在引用链的对象都被认为是活跃对象,不会被回收。GC Root 包括:

  • 虚拟机栈中的引用对象
  • 方法区中的静态变量
  • 本地方法栈中的引用对象
  • 常量池中的字符串常量

6. 判断对象是否是垃圾

判断对象是否是垃圾有两种算法:

  • 引用计数法: 每个对象都有一个引用计数器,当对象被引用时计数器加 1,引用失效时计数器减 1。当计数器为 0 时,对象被认为是垃圾。
  • 可达性分析法: 从 GC Root 开始搜索,所有与 GC Root 存在引用链的对象都被认为是活跃对象,否则被认为是垃圾。

7. Java 程序卡顿优化

如果 Java 程序运行一段时间后变得卡顿,可以从以下几个方面进行优化:

  • 分析: 使用工具(如 JProfiler、VisualVM)分析程序的 CPU 使用率、内存使用情况、线程状态等,找出性能瓶颈。
  • 优化代码: 优化算法、减少对象创建、使用缓存等。
  • 调整 JVM 参数: 调整堆大小、垃圾回收器等。
  • 使用性能更高的组件: 例如,使用 Netty 代替 BIO 进行网络通信。

8. JAVA 类加载全过程

JAVA 类加载全过程包括:

  1. 加载(Loading): 类加载器将 Class 文件加载到 JVM 中。

  2. 链接(Linking):

    • 验证(Verification): 检查 Class 文件的结构是否符合规范。
    • 准备(Preparation): 为静态变量分配内存并设置初始值。
    • 解析(Resolution): 将符号引用转换为直接引用。
  3. 初始化(Initialization): 执行类构造器(clinit)和对象初始化。

9. 解决错标记和漏标记问题

错标记和漏标记是垃圾回收过程中可能出现的问题。

  • 错标记: 将本不应该回收的对象标记为垃圾。
  • 漏标记: 将应该回收的对象没有标记为垃圾。

可以使用以下方法解决错标记和漏标记问题:

  • 增量更新(Incremental Update): 在垃圾回收的同时,允许应用程序线程继续运行,并及时更新引用关系。
  • 原始快照(Snapshot-At-The-Beginning,SATB): 在垃圾回收开始时,记录下所有的对象引用关系,垃圾回收器根据这些信息进行标记。

10. 查看 JAVA 进程的 JVM 参数

可以使用以下命令查看 JAVA 进程的 JVM 参数:

  • jps:查看正在运行的 Java 进程。
  • jinfo -flags <pid>:查看指定进程的 JVM 参数。

11. JVM 参数

JVM 参数有很多,可以分为以下几类:

  • 堆大小参数: -Xms(初始堆大小)、-Xmx(最大堆大小)
  • 垃圾回收器参数: -XX:+UseSerialGC-XX:+UseParallelGC-XX:+UseConcMarkSweepGC-XX:+UseG1GC
  • 其他参数: -XX:MaxPermSize(最大永久代大小,JDK 8 及之前)、-XX:MetaspaceSize(初始元空间大小,JDK 8 及之后)

12. 内存模型以及分区

(同第 1 题)

13. 双亲委派机制

双亲委派机制指的是类加载器在加载类时,先委托父类加载器加载,如果父类加载器无法加载,再由子类加载器加载。

作用:

  • 保证类加载的安全性,避免核心类库被篡改。
  • 避免类重复加载。

14. 三色标记

三色标记是垃圾回收器用于标记对象的算法。

  • 白色: 对象未被访问。
  • 灰色: 对象被访问,但其引用对象未被访问。
  • 黑色: 对象被访问,且其引用对象也被访问。

15. JVM 参数

(同第 11 题)

16. JVM 垃圾回收器

JVM 有多种垃圾回收器,如:

  • Serial GC: 串行垃圾回收器,适用于小型应用。
  • Parallel GC: 并行垃圾回收器,适用于多核 CPU 的应用。
  • CMS GC: 并发标记清除垃圾回收器,适用于对停顿时间有要求的应用。
  • G1 GC: G1 垃圾回收器,适用于大型应用。
  • ZGC: ZGC 垃圾回收器,适用于超大型应用。

17. 栈内存溢出

栈内存溢出发生在以下情况:

  • 线程请求的栈深度超过了 JVM 允许的最大深度。
  • 方法调用链过长,导致栈帧过多。

18. Java 内存分配与回收策略

  • 内存分配:

    • 新对象优先分配到 Eden 区。
    • 长期存活的对象进入老年代。
  • 内存回收:

    • Minor GC:回收 Eden 区和 Survivor 区中的垃圾对象。
    • Major GC:回收老年代中的垃圾对象。
    • Full GC:回收整个堆中的垃圾对象。

19. 为什么要设计这么多垃圾回收器?

不同的垃圾回收器有不同的特点,适用于不同的场景。设计多种垃圾回收器可以满足不同应用的需求。

20. JVM 垃圾回收算法

JVM 垃圾回收算法主要有:

  • 标记-清除算法: 标记垃圾对象,然后清除。
  • 复制算法: 将内存分为两块,每次只使用一块,垃圾回收时将存活对象复制到另一块。
  • 标记-整理算法: 标记垃圾对象,然后将存活对象整理到一起,清除边界以外的内存。

21. JVM 调优

  • 监控 JVM 运行时状态。
  • 使用性能分析工具。
  • 分析 GC 日志。

JVM 调优是一个复杂的过程,需要根据具体的应用场景和性能瓶颈进行调整。

补充说明

  • JVM 内存模型 在不同的 JDK 版本中可能会有所不同。例如,JDK 8 及之后使用元空间(Metaspace)取代了永久代。
  • 垃圾回收器 的选择需要根据应用的特点进行选择。例如,如果应用对停顿时间要求较高,则可以选择 CMS GC 或 G1 GC。
  • JVM 调优 需要综合考虑多个因素,例如堆大小、垃圾回收器、GC 参数等。

13、搜索引擎

1. 倒排索引

倒排索引是 Elasticsearch 中最核心的数据结构。它是一种将文档中的词语(term)映射到文档列表的数据结构。每个词语都对应一个倒排列表,列表中包含了所有包含该词语的文档。

示例:

词语倒排列表
Elasticsearch[文档1,文档3]
搜索[文档1,文档2,文档3]
引擎[文档2,文档3]

导出到 Google 表格

好处:

  • 快速搜索: 可以根据词语快速找到包含该词语的文档。
  • 支持复杂查询: 可以进行布尔查询、模糊查询、范围查询等。

2. 分词器

分词器(Analyzer)用于将文本分割成词语。Elasticsearch 提供了多种内置分词器,如:

  • Standard Analyzer: 默认分词器,按照空格和标点符号进行分词。
  • Simple Analyzer: 按照空格进行分词。
  • Whitespace Analyzer: 按照空格进行分词,保留标点符号。
  • Stop Analyzer: 过滤掉停用词。

还可以使用自定义分词器,以满足特定的需求。

3. 大数据量聚合

Elasticsearch 对于大数据量的聚合,采用了多种优化策略:

  • 近似聚合: 对于 cardinality 聚合等需要计算唯一值的聚合,可以使用近似算法,如 HyperLogLog++,以减少内存占用。
  • 分片聚合: 聚合操作在每个分片上并行执行,然后将结果合并。
  • 预聚合: 可以预先计算一些聚合结果,存储起来,以提高查询性能。

4. Master 选举

Elasticsearch 使用 Zen Discovery 模块进行 Master 选举。当集群中的 Master 节点宕机时,其他节点会通过投票机制选举出新的 Master 节点。

5. 个性化搜索方案

个性化搜索方案通常包括以下几个方面:

  • 用户画像: 构建用户画像,了解用户的兴趣和偏好。
  • 相关性排序: 根据用户画像和搜索词,调整搜索结果的排序。
  • 推荐: 根据用户画像,向用户推荐相关的商品或内容。

6. 索引文档过程

Elasticsearch 索引文档的过程如下:

  1. 客户端发送索引请求到 Elasticsearch 节点。
  2. 节点根据路由规则将请求转发到对应的分片。
  3. 分片将文档转换为倒排索引。
  4. 倒排索引存储在 Lucene 中。

7. 中文分词

中文分词是中文搜索的关键。可以使用以下方法进行中文分词:

  • Elasticsearch 内置中文分词器:ik_max_wordik_smart
  • 第三方中文分词器: 如 Jieba、HanLP。

8. Elasticsearch 简介

(同问题 16)

9. ES 查询数据原理

ES 查询数据的工作原理如下:

  1. 客户端发送查询请求到 Elasticsearch 节点。
  2. 节点根据查询类型将请求转发到对应的分片。
  3. 分片根据倒排索引查找匹配的文档。
  4. 分片将结果返回给节点。
  5. 节点将结果合并返回给客户端。

10. Lucene 内部结构

Lucene 是一个开源的搜索库,Elasticsearch 基于 Lucene 构建。Lucene 内部结构主要包括:

  • Index: 索引文件。
  • Segment: 段文件,是 Lucene 中最小的搜索单元。
  • Document: 文档,是 Lucene 中存储数据的基本单位。
  • Field: 字段,是 Document 中的属性。
  • Term: 词语,是 Field 中的最小单位。

11. 倒排索引

(同问题 1)

12. ES 部署优化

ES 部署优化可以从以下几个方面入手:

  • 硬件配置: 选择合适的 CPU、内存、磁盘。
  • JVM 参数: 调整 JVM 堆大小、垃圾回收器。
  • 索引设置: 优化索引分片数、副本数。
  • 集群设置: 合理配置集群参数。

13. ES 集群架构

ES 集群架构通常包括:

  • Master 节点: 负责集群管理、索引创建、分片分配等。
  • Data 节点: 存储数据、提供搜索服务。
  • Client 节点: 负责转发请求。

14. 查找倒排索引

Elasticsearch 根据词语的哈希值查找对应的倒排索引。

15. ES 写入数据原理

ES 写入数据的工作原理如下:

  1. 客户端发送写入请求到 Elasticsearch 节点。
  2. 节点根据路由规则将请求转发到对应的分片。
  3. 分片将数据写入到内存缓冲区。
  4. 当内存缓冲区达到一定阈值时,将数据刷新到磁盘上的 segment 文件中。
  5. 多个 segment 文件定期合并成更大的 segment 文件。

16. ES 简介

Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎。它基于 Lucene 构建,提供强大的搜索、分析和可视化功能。

17. 拼写纠错

拼写纠错通常基于以下技术实现:

  • 编辑距离: 计算两个词语之间的编辑距离,距离越小,相似度越高。
  • N-gram: 将词语分割成 N 个字符的序列,然后计算序列之间的相似度。
  • 字典: 使用字典存储常见的拼写错误,并提供纠正建议。

14、安全验证

1. 如何设计一个开放授权平台?

设计一个开放授权平台需要考虑以下几个方面:

  • OAuth 2.0 协议: 遵循 OAuth 2.0 协议,提供标准的授权流程和接口。
  • 多租户支持: 支持多个第三方应用接入,每个应用拥有独立的 Client ID 和 Client Secret。
  • 权限管理: 提供细粒度的权限控制,可以控制应用可以访问哪些资源。
  • 安全: 采用安全的认证和授权机制,保护用户数据。
  • 监控和日志: 提供完善的监控和日志功能,方便排查问题。

2. 如何设计一个权限认证框架?

设计一个权限认证框架需要考虑以下几个方面:

  • 认证: 验证用户的身份,例如用户名密码、第三方登录等。
  • 授权: 授予用户访问资源的权限。
  • 访问控制: 根据用户的权限,控制用户可以访问哪些资源。
  • 会话管理: 管理用户的登录状态。

3. 什么是 CSRF 攻击?如何防止?

CSRF (Cross-Site Request Forgery) 跨站请求伪造攻击指的是攻击者通过伪造用户请求,在用户不知情的情况下执行非法操作。

防止 CSRF 攻击的方法:

  • 验证 Referer: 检查请求的 Referer 头部,判断请求是否来自合法的页面。
  • 使用 CSRF Token: 在表单中添加一个随机生成的 Token,服务器验证该 Token 是否有效。
  • 使用 SameSite Cookie: 设置 Cookie 的 SameSite 属性,限制 Cookie 在跨站请求中的发送。

4. 什么是 JWT 令牌?和普通令牌有什么区别?

JWT (JSON Web Token) 是一种基于 JSON 的令牌,用于在客户端和服务器之间传递信息。

区别:

  • 自包含: JWT 令牌包含了用户信息,无需查询数据库。
  • 可扩展: JWT 令牌可以自定义 Payload,添加额外的信息。
  • 安全性高: JWT 令牌可以使用签名进行验证,防止篡改。

5. 如果没有 Cookie,Session 还能进行身份验证吗?

可以。可以使用 JWT 令牌或 URL 参数来进行身份验证。

6. Cookie 和 Session 有什么区别?

特性CookieSession
存储位置客户端 (浏览器)服务器端
安全性较低,容易被篡改较高
生命周期可以设置过期时间一般在会话结束时失效
存储容量较小较大

导出到 Google 表格

7. 什么是认证和授权?

  • 认证 (Authentication): 验证用户的身份,例如用户名密码、第三方登录等。
  • 授权 (Authorization): 授予用户访问资源的权限。

8. 什么是 OAuth 2.0 协议?有哪几种认证方式?

OAuth 2.0 是一种授权框架,用于授权第三方应用访问用户资源,而无需将用户的用户名和密码 передавать.

认证方式:

  • 授权码模式 (Authorization Code Grant):
  • 简化模式 (Implicit Grant):
  • 密码模式 (Resource Owner Password Credentials Grant):
  • 客户端模式 (Client Credentials Grant):

9. 什么是 SSO?与 OAuth 2.0 有什么关系?

SSO (Single Sign-On) 单点登录指的是用户只需要登录一次,就可以访问多个应用系统。

关系:

OAuth 2.0 可以用于实现 SSO。用户在一个应用系统中登录后,可以获取到其他应用系统的访问权限,无需再次登录。