Java面试题

 一、基础 - base相关


Java数据类型

基本数据类型:

byte:1字节,8位

short:2字节,16位

int:4字节,32位

long:8字节,64位

float:4字节,32位

double:8字节,64位

char:2字节,16位

boolean:1字节,8位(实际占用取决于JVM实现)

引用数据类型:

类(Class)

接口(Interface)

数组(Array)

枚举(Enum)

注解(Annotation)

Java语言特性

封装:将对象的属性和行为封装在一起,通过访问修饰符控制访问权限,提高代码的安全性和可维护性。

继承:子类继承父类的属性和方法,实现代码的复用和扩展。

多态:同一个方法在不同情况下表现出不同的行为,通过方法重写和方法重载实现。

权限修饰符

public:所有类都可以访问。

protected:同一包内或子类可以访问。

default(默认):同一包内可以访问。

private:仅在类内部可以访问。

适用范围:类、方法、变量、构造器。

final关键字

类:不能被继承。

方法:不能被重写。

变量:值不能被修改(常量)。

static关键字

类:静态变量和静态方法属于类,不属于实例。

方法:可以直接通过类名调用,不需要实例化对象。

代码块:在类加载时执行,用于初始化静态变量。

字符串拼接

+ 号拼接效率不高,因为每次拼接都会创建新的字符串对象。

应该使用 StringBuilder 或 StringBuffer,它们是可变的,不会每次都创建新对象。

Java异常体系

异常体系:Throwable -> Error 和 Exception -> RuntimeException 和其他 Exception。

常见运行时异常:NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException 等。

重写与重载

重写:子类重写父类的方法,方法名、参数列表、返回类型必须相同。

重载:同一个类中,方法名相同,参数列表不同。

接口与抽象类

接口:只能包含抽象方法(JDK1.8后可以有默认方法和静态方法),用于定义规范。

抽象类:可以包含抽象方法和具体方法,用于代码复用。

== 和 equals

==:比较对象的引用地址。

equals:比较对象的内容,默认实现与 == 相同,通常需要重写。

&与&&、|与||

& 和 |:逻辑与和逻辑或,不短路。

&& 和 ||:逻辑与和逻辑或,短路。

valueOf和toString

valueOf:将其他类型转换为字符串。

toString:返回对象的字符串表示。

String底层

String 是不可变的,底层使用 char[] 存储。

不可修改:每次修改都会创建新的 String 对象。

StringBuffer和StringBuilder

StringBuffer:线程安全,效率较低。

StringBuilder:非线程安全,效率较高。

创建类实例

使用 new 关键字。

使用反射。

使用克隆。

使用反序列化。

反射

反射:在运行时动态获取类的信息并操作。

应用场景:动态代理、注解处理、ORM框架等。

暴力反射:通过反射访问私有成员。

值传递与引用传递

Java是值传递,传递的是对象引用的副本。

动态代理

方式:JDK动态代理、CGLIB动态代理。

原理:JDK动态代理基于接口,CGLIB基于类继承。

区别:JDK代理要求目标对象实现接口,CGLIB不需要。

 二、基础 - 集合相关


Java集合体系

Collection:List、Set、Queue。

Map:HashMap、TreeMap、LinkedHashMap。

List和Set

List:有序、可重复。

Set:无序、不可重复。

去重原理:通过 hashCode 和 equals 方法判断。

hashCode和equals

hashCode:计算对象的哈希值。

equals:比较对象是否相等。

在 Set 中,先比较 hashCode,再比较 equals。

ArrayList底层

add:在数组末尾添加元素,扩容时增加50%。

remove:删除元素并移动后续元素。

扩容原理:默认容量10,扩容时增加50%。

LinkedList底层

add:在链表末尾添加节点。

remove:删除节点并调整前后节点的指针。

get:从头或尾遍历链表。

HashMap

JDK1.7 vs 1.8:1.7使用数组+链表,1.8使用数组+链表/红黑树。

put过程:计算hash值,定位数组索引,插入链表或红黑树。

二次hash:减少hash冲突。

负载因子:0.75,平衡时间和空间。

扩容:2倍,重新计算索引。

链表转红黑树:链表长度大于8且数组长度大于64。

红黑树转链表:链表长度小于6。

并发问题:可能导致死循环或数据丢失。

线程安全集合

Hashtable:线程安全,效率低。

ConcurrentHashMap:线程安全,效率高。

原理:1.7使用分段锁,1.8使用CAS+synchronized。

 三、基础 - 其他
JDK1.8新特性

Lambda表达式:简化匿名内部类。

Stream流:简化集合操作。

函数式接口:只有一个抽象方法的接口。

接口默认方法:接口中可以有默认实现。

方法引用:简化Lambda表达式。

算法

冒泡排序:相邻元素比较交换。

选择排序:选择最小元素交换。

快排:分治法,选择基准元素,递归排序。

设计模式

常见设计模式:单例模式、工厂模式、观察者模式、策略模式等。

Socket与IO

Socket:网络通信的端点。

BIO:同步阻塞IO。

NIO:同步非阻塞IO。

AIO:异步非阻塞IO。

HTTP协议

请求内容:请求行、请求头、请求体。

响应内容:状态行、响应头、响应体。

常见响应码:200(成功)、404(未找到)、500(服务器错误)等。

HTTP vs HTTPS:HTTPS使用SSL/TLS加密。

TCP协议:面向连接的可靠传输协议。

 四、多线程相关


 1. 线程和进程有什么联系和区别?
联系:线程是进程的一部分,一个进程可以包含多个线程。

区别:

进程:是操作系统资源分配的基本单位,拥有独立的内存空间和系统资源。

线程:是CPU调度和分派的基本单位,共享进程的内存空间和系统资源。

 2. 创建线程的方式有哪些?线程的状态又有哪些?
创建线程的方式:

继承Thread类,重写run()方法。

实现Runnable接口,实现run()方法。

实现Callable接口,实现call()方法,通过FutureTask包装后创建线程。

使用线程池(如ExecutorService)。

线程的状态:

新建(New):线程被创建但未启动。

就绪(Runnable):线程已启动,等待CPU调度。

运行(Running):线程正在执行。

阻塞(Blocked):线程因等待某些资源(如锁、IO操作)而暂停执行。

等待(Waiting):线程等待其他线程的通知或中断。

超时等待(Timed Waiting):线程在指定时间内等待。

终止(Terminated):线程执行完毕或因异常终止。

 3. run()和start()方法有哪些区别?
run():是线程的执行体,直接调用run()方法不会启动新线程,而是在当前线程中执行。

start():启动新线程,并调用run()方法。

 4. 实现线程间通讯的方法有哪些?wait、notify、notifyAll分别的作用是什么?可以用在同步代码块之外吗?为什么?
实现线程间通讯的方法:

wait():使当前线程进入等待状态,直到其他线程调用notify()或notifyAll()。

notify():唤醒一个等待的线程。

notifyAll():唤醒所有等待的线程。

作用:

wait():释放锁并进入等待状态。

notify():唤醒一个等待的线程。

notifyAll():唤醒所有等待的线程。

是否可以用在同步代码块之外:

不可以。wait()、notify()、notifyAll()必须在同步代码块(synchronized)中调用,否则会抛出IllegalMonitorStateException。

 5. sleep和wait方法有什么区别?
sleep():使当前线程暂停执行指定时间,不释放锁。

wait():使当前线程进入等待状态,释放锁,直到其他线程调用notify()或notifyAll()。

 6. 什么是线程安全问题?什么情况下会产生?如何解决?
线程安全问题:多个线程同时访问共享资源时,可能导致数据不一致或错误。

产生原因:多个线程对共享资源进行非原子操作。

解决方法:

使用synchronized关键字。

使用Lock锁。

使用volatile关键字。

使用线程安全的集合类(如ConcurrentHashMap)。

 7. 什么是死锁?应该如何防止产生死锁?
死锁:两个或多个线程互相持有对方需要的资源,导致所有线程都无法继续执行。

防止方法:

避免嵌套锁。

按顺序获取锁。

使用超时机制。

使用tryLock()方法。

 8. Synchronized关键字的底层原理是什么? 可以用在哪些地方?分别的锁对象是什么?
底层原理:Synchronized通过操作系统的互斥量(Mutex)实现,JVM通过monitorenter和monitorexit指令实现。

使用位置:

方法:锁对象为当前实例对象。

静态方法:锁对象为类的Class对象。

代码块:锁对象为指定的对象。

 9. Synchronized在JDK1.6做了哪些优化?结合对象头聊聊Synchronized锁升级的详细过程。
优化:

偏向锁:减少无竞争情况下的锁开销。

轻量级锁:减少竞争情况下的锁开销。

重量级锁:在竞争激烈时使用操作系统互斥量。

锁升级过程:

无锁状态:对象头中无锁标记。

偏向锁:第一次获取锁时,对象头中记录线程ID,后续该线程再次获取锁时无需竞争。

轻量级锁:当有其他线程竞争时,偏向锁升级为轻量级锁,通过CAS操作获取锁。

重量级锁:当轻量级锁竞争激烈时,升级为重量级锁,使用操作系统互斥量。

 10. Synchronized是公平锁还是非公平锁?如果获取不到锁时会阻塞吗?
非公平锁:Synchronized是非公平锁,获取不到锁时会阻塞。

 11. 同步代码块中执行完wait/notify/notifyAll后会立马释放锁吗,为什么?
会:wait()、notify()、notifyAll()方法会释放锁,因为它们必须在同步代码块中调用,调用后会释放锁以便其他线程获取。

 12. Lock锁有哪些实现类?分别的特点是什么?
实现类:

ReentrantLock:可重入锁,支持公平和非公平模式。

ReentrantReadWriteLock:读写锁,允许多个读线程同时访问,但写线程独占。

特点:

ReentrantLock:灵活,支持条件变量、超时获取锁等。

ReentrantReadWriteLock:适用于读多写少的场景,提高并发性能。

 13. 什么是线程可重入?Synchronized具备吗?Lock锁呢?
线程可重入:线程可以重复获取已经持有的锁。

Synchronized:具备可重入性。

Lock锁:ReentrantLock具备可重入性。

 14. Synchronized关键字和JUC下Lock锁的异同?
相同点:都用于实现线程同步。

不同点:

Synchronized:自动释放锁,无法中断等待锁的线程。

Lock:手动释放锁,支持中断等待锁的线程,支持公平锁和非公平锁。

 15. 什么是AQS?说下你对AQS的理解
AQS(AbstractQueuedSynchronizer):是JUC包中实现锁和其他同步组件的基础框架,通过维护一个FIFO队列来管理线程的等待和唤醒。

 16. 乐观锁CAS原理了解过吗?什么是CAS的ABA问题?如何解决?
CAS(Compare-And-Swap):是一种乐观锁机制,通过比较内存值和预期值,如果相等则更新。

ABA问题:线程A读取值A,线程B将值改为B再改回A,线程A认为值未变,但实际上值已发生变化。

解决方法:使用版本号机制(如AtomicStampedReference)。

 17. 你了解JUC下的哪些常用工具类,分别有什么作用?(CountdownLatch、Cyclicbarrier、Simephore)
CountdownLatch:允许一个或多个线程等待其他线程完成操作。

CyclicBarrier:允许多个线程相互等待,直到所有线程都到达屏障点。

Semaphore:控制同时访问特定资源的线程数量。

 18. 说下volatile关键字,有什么作用?原理是什么?
作用:保证变量的可见性,禁止指令重排序。

原理:通过内存屏障(Memory Barrier)实现,确保变量在不同线程间的可见性。

 19. 说下ThreadLocal,有什么作用?有哪些主要方法,实现原理是什么?为什么会有内存泄漏问题?如何解决?
作用:为每个线程提供独立的变量副本。

主要方法:

get():获取当前线程的变量副本。

set(T value):设置当前线程的变量副本。

remove():移除当前线程的变量副本。

实现原理:每个线程维护一个ThreadLocalMap,存储线程本地变量。

内存泄漏问题:ThreadLocal引用的对象不会被回收,可能导致内存泄漏。

解决方法:使用完后调用remove()方法。

 20. 说下线程池的几大核心参数?分别有什么作用?线程池工作原理是什么样的?有几种默认的线程池?他们的7个核心参数为什么要那么设置?
核心参数:

corePoolSize:核心线程数。

maximumPoolSize:最大线程数。

keepAliveTime:线程空闲时间。

unit:时间单位。

workQueue:任务队列。

threadFactory:线程工厂。

handler:拒绝策略。

工作原理:

当任务提交时,如果线程数小于corePoolSize,创建新线程执行任务。

如果线程数达到corePoolSize,任务进入任务队列。

如果任务队列满,且线程数小于maximumPoolSize,创建新线程执行任务。

如果线程数达到maximumPoolSize,执行拒绝策略。

默认线程池:

FixedThreadPool:固定线程数,任务队列无界。

CachedThreadPool:线程数可变,任务队列无界。

SingleThreadExecutor:单线程,任务队列无界。

ScheduledThreadPool:定时任务线程池。

参数设置原因:根据任务类型和系统资源进行合理配置,避免资源浪费和任务堆积。

 21. 单例模式写法有哪几种?(懒汉和饿汉式)懒汉式中保证线程安全的写法是什么?为什么要用双重检查模式?
单例模式写法:

饿汉式:类加载时创建实例。

懒汉式:第一次使用时创建实例。

懒汉式线程安全写法:


public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
双重检查模式原因:

第一次检查避免不必要的同步。

第二次检查确保只有一个线程创建实例。

volatile关键字防止指令重排序,确保实例初始化完成后再赋值。


 1. 什么是索引?


索引是一种数据结构,用于加速数据库表中数据的检索速度。它类似于书籍的目录,可以帮助数据库快速定位到特定的数据行,而不需要扫描整个表。

 2. Mysql索引有哪些类型?什么场景使用哪种索引?
主键索引(Primary Key):唯一标识表中的每一行数据,不允许为空。

唯一索引(Unique Index):确保索引列中的值是唯一的,但允许为空。

普通索引(Normal Index):最基本的索引类型,没有唯一性限制。

全文索引(Full-Text Index):用于全文搜索,适用于文本字段。

组合索引(Composite Index):在多个列上创建的索引,适用于多条件查询。

使用场景:

主键索引:用于唯一标识每一行数据。

唯一索引:用于确保某些列的值唯一。

普通索引:用于加速查询,适用于频繁查询的列。

全文索引:用于文本搜索。

组合索引:用于多条件查询。

 3. Mysql实际使用的数据结构是什么(索引的数据结构)?为什么用这种结构?(如何提高磁盘IO效率)
Mysql(特别是InnoDB)使用B+树作为索引的数据结构。

原因:

B+树的平衡性:B+树是一种平衡树,能够保证树的高度较低,从而减少磁盘IO次数。

叶子节点的链表结构:B+树的叶子节点通过链表连接,适合范围查询。

磁盘IO效率:B+树的节点大小通常与磁盘页大小一致,能够最大限度地减少磁盘IO次数。

 4. B+tree和Btree的叶子节点和非叶子节点分别存储什么?他们还有哪些区别?
B树:

叶子节点和非叶子节点都存储数据。

适用于内存中的数据结构,因为每个节点都存储数据,导致树的高度较高。

B+树:

非叶子节点只存储索引信息,不存储数据。

叶子节点存储数据,并且通过链表连接,适合范围查询。

树的高度较低,适合磁盘存储。

区别:

B+树的非叶子节点只存储索引信息,而B树的非叶子节点存储数据。

B+树的叶子节点通过链表连接,适合范围查询。

B+树的高度较低,适合磁盘存储。

 5. Mysql两种存储引擎(InnoDB和MyISAM)有哪些区别?
InnoDB:

支持事务(ACID)。

支持行级锁和外键。

使用聚簇索引,数据存储在索引中。

适合高并发、事务性操作。

MyISAM:

不支持事务。

支持表级锁。

使用非聚簇索引,数据和索引分开存储。

适合读密集型应用。

 6. 如何进行Mysql优化?(SQL优化层面和服务器优化层面)
SQL优化层面:

使用索引:确保查询中使用的列上有合适的索引。

避免全表扫描:尽量使用索引覆盖查询。

优化查询语句:避免使用子查询、多表连接等复杂操作。

分页查询优化:使用LIMIT和OFFSET时,尽量减少数据量。

服务器优化层面:

硬件升级:增加内存、使用SSD等。

配置优化:调整my.cnf中的参数,如innodb_buffer_pool_size、query_cache_size等。

数据库设计:合理设计表结构,避免冗余数据。

分区表:对于大数据量的表,可以考虑分区。

 7. SQL调优你会从何入手?
分析慢查询日志:使用EXPLAIN分析查询计划,找出性能瓶颈。

检查索引:确保查询中使用的列上有合适的索引。

优化查询语句:简化查询,避免不必要的操作。

调整服务器配置:根据实际情况调整my.cnf中的参数。

 8. Mysql的数据IO查找流程是什么样的?
查询解析:解析SQL语句,生成查询计划。

查询优化:优化查询计划,选择最优的执行路径。

索引查找:根据索引定位到数据行。

数据读取:从磁盘或缓存中读取数据。

结果返回:将结果返回给客户端。

 9. Mysql中如何合理使用索引?有哪些会使索引失效的情况?
合理使用索引:

在频繁查询的列上创建索引。

使用组合索引时,注意索引列的顺序。

避免在索引列上进行函数操作。

索引失效的情况:

在索引列上进行函数操作。

使用!=、<>、NOT IN等操作符。

使用LIKE时,通配符在开头(如LIKE '%abc')。

数据类型不匹配。

 10. 什么是聚簇索引、非聚簇索引?什么是覆盖索引?什么是回表查询?
聚簇索引:数据行存储在索引中,索引和数据是一体的。InnoDB的主键索引就是聚簇索引。

非聚簇索引:索引和数据分开存储,索引中存储的是指向数据的指针。MyISAM使用非聚簇索引。

覆盖索引:查询中需要的所有数据都在索引中,不需要回表查询数据行。

回表查询:通过非聚簇索引找到数据行的主键,再通过主键查询数据行。

 11. Mysql如何进行慢查询排查(哪个关键字)?分别会列出来哪些信息项?重点关注哪一些信息项?
使用EXPLAIN关键字进行慢查询排查。

信息项:

id:查询的标识符。

select_type:查询的类型。

table:查询的表。

type:连接类型。

possible_keys:可能使用的索引。

key:实际使用的索引。

key_len:索引的长度。

ref:与索引比较的列。

rows:扫描的行数。

Extra:额外的信息。

重点关注:

type:连接类型,如ALL表示全表扫描。

rows:扫描的行数,越少越好。

Extra:额外的信息,如Using filesort、Using temporary等。

 12. 事务的特性是什么?Mysql事务隔离级别有哪几种?分别会产生什么问题?Mysql默认隔离级别是什么?Oracle呢?
事务特性(ACID):

原子性(Atomicity):事务是一个不可分割的工作单位,要么全部执行,要么全部不执行。

一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏。

隔离性(Isolation):事务之间相互隔离,互不影响。

持久性(Durability):事务一旦提交,其结果是永久性的。

Mysql事务隔离级别:

读未提交(Read Uncommitted):允许脏读、不可重复读和幻读。

读已提交(Read Committed):防止脏读,但允许不可重复读和幻读。

可重复读(Repeatable Read):防止脏读和不可重复读,但允许幻读。

串行化(Serializable):防止脏读、不可重复读和幻读,但性能较低。

Mysql默认隔离级别:可重复读(Repeatable Read)。

Oracle默认隔离级别:读已提交(Read Committed)。

 13. 分别说下你对Mysql的行锁、表锁,悲观锁、乐观锁、间隙锁的理解
行锁:锁定表中的某一行数据,适用于高并发场景。

表锁:锁定整个表,适用于低并发场景。

悲观锁:假设数据会被并发修改,因此在读取数据时加锁,防止其他事务修改。

乐观锁:假设数据不会被并发修改,因此在提交时检查数据是否被修改,如果没有则提交,否则回滚。

间隙锁:锁定索引记录之间的间隙,防止其他事务插入数据,适用于范围查询。

 14. Mysql的varchar和char的区别?
char:固定长度字符串,不足长度时用空格填充,适合存储长度固定的数据。

varchar:可变长度字符串,存储实际长度的数据,适合存储长度不固定的数据。

 15. Mysql内连接(inner join)、外连接(left join)分别的写法?
内连接(Inner Join):


SELECT * FROM table1 INNER JOIN table2 ON table1.id = table2.id;
左外连接(Left Join):


SELECT * FROM table1 LEFT JOIN table2 ON table1.id = table2.id;
 16. 什么是索引下推?有什么作用?
索引下推(Index Condition Pushdown, ICP)是Mysql 5.6引入的优化技术,它允许在存储引擎层直接过滤数据,而不是在服务器层过滤。这样可以减少服务器层的数据传输量,提高查询性能。

 17. SQL解析顺序(FROM JOIN ON WHERE GROUP BY HAVING SELECT DISTINCT ORDER BY LIMIT)
FROM:选择表。

JOIN:连接表。

ON:连接条件。

WHERE:过滤条件。

GROUP BY:分组。

HAVING:分组后的过滤条件。

SELECT:选择列。

DISTINCT:去重。

ORDER BY:排序。

LIMIT:限制结果集。

 18. exists和in有哪些区别?
exists:用于检查子查询是否返回结果,返回true或false。

in:用于检查某个值是否在子查询的结果集中。

区别:

exists适用于子查询返回结果集较大的情况,因为它只关心是否返回结果。

in适用于子查询返回结果集较小的情况,因为它需要检查每个值是否在结果集中。

 19. count(1)、count(列名)、count(*) 的区别?
count(1):统计行数,忽略列的值。

count(列名):统计列中非NULL值的行数。

count(*):统计所有行数,包括NULL值。

 20. where 和 on的区别
where:用于过滤表中的数据,在连接操作之后执行。

on:用于指定连接条件,在连接操作之前执行。

 21. union和union all的区别
union:合并两个结果集,并去重。

union all:合并两个结果集,不去重。

 22. 什么是当前读?什么是快照读?
当前读:读取最新的数据,使用锁机制保证数据的一致性。

快照读:读取事务开始时的数据快照,不加锁,适用于读已提交和可重复读隔离级别。

 23. 听过InnoDB的Mvcc技术吗?说下是什么?
MVCC(Multi-Version Concurrency Control)是InnoDB实现的一种并发控制技术,它通过保存数据的多个版本来实现事务的隔离性。每个事务读取的数据是事务开始时的快照,而不是最新的数据,从而避免了加锁带来的性能问题。

 24. Mybatis底层的原理?一级缓存和二级缓存是什么?
Mybatis底层原理:

Mybatis通过XML或注解配置SQL语句,将SQL语句与Java方法映射。

执行SQL语句时,Mybatis通过JDBC与数据库交互,并将结果映射为Java对象。

一级缓存:

默认开启,作用域为SqlSession,同一个SqlSession中相同的查询会使用缓存。

二级缓存:

需要手动开启,作用域为Mapper,不同的SqlSession之间共享缓存。

 25. Mybatis #{}和${}的区别?
#{}:预编译处理,防止SQL注入,适用于参数传递。

${}:字符串替换,直接将参数拼接到SQL语句中,存在SQL注入风险,适用于动态表名、列名等。

 26. Mybatis mapper层可以进行方法重载吗?为什么?
Mybatis的Mapper层不支持方法重载,因为Mybatis通过方法签名(方法名+参数类型)来唯一标识一个SQL语句。如果方法重载,会导致方法签名冲突,无法唯一标识SQL语句。

 27. Mysql存储过程、存储函数、触发器、视图(View)分别用来干嘛的?创建语法是什么?
存储过程:封装一组SQL语句,可以在数据库中重复执行,提高代码复用性。


DELIMITER $$
CREATE PROCEDURE procedure_name(IN param1 INT, OUT param2 INT)
BEGIN
    -- SQL statements
END $$
DELIMITER ;
存储函数:封装一组SQL语句,返回一个值,可以在SQL语句中调用。


DELIMITER $$
CREATE FUNCTION function_name(param1 INT) RETURNS INT
BEGIN
    -- SQL statements
    RETURN result;
END $$
DELIMITER ;
触发器:在特定事件(如插入、更新、删除)发生时自动执行的SQL语句。


CREATE TRIGGER trigger_name
BEFORE INSERT ON table_name
FOR EACH ROW
BEGIN
    -- SQL statements
END;
视图(View):虚拟表,基于查询结果集,可以简化复杂的查询。


CREATE VIEW view_name AS
SELECT column1, column2
FROM table_name
WHERE condition;
这些工具可以帮助开发者更好地管理和优化数据库操作。

好的,以下是对Spring相关问题的详细说明:

 1. 说下你对Spring的理解,和SpringBoot有什么关系?
Spring 是一个开源的轻量级Java开发框架,旨在简化企业级应用的开发。它提供了依赖注入(DI)和面向切面编程(AOP)等核心功能,帮助开发者构建松耦合、可测试的应用程序。

Spring Boot 是Spring框架的一个子项目,旨在简化Spring应用的初始搭建以及开发过程。它通过自动配置和约定大于配置的原则,减少了大量的XML配置,使得开发者可以更快速地启动和运行Spring应用。

关系:Spring Boot是基于Spring框架的,它简化了Spring应用的配置和部署,使得开发者可以更专注于业务逻辑的实现。

 2. Spring有哪些核心注解?分别有什么作用?
@Component:通用组件注解,用于标记一个类为Spring管理的Bean。

@Controller:用于Web控制器,通常与Spring MVC一起使用。

@Service:用于业务逻辑层,表示一个服务类。

@Repository:用于数据访问层,表示一个数据访问对象(DAO)。

@Autowired:用于自动装配Bean,Spring会自动注入依赖。

@Qualifier:当有多个相同类型的Bean时,用于指定具体的Bean。

@Value:用于注入配置文件中的属性值。

@Configuration:用于标记一个类为配置类,通常与@Bean一起使用。

@Bean:用于在配置类中声明一个Bean。

 3. 说下对Spring IOC的理解,怎么理解控制反转?
Spring IOC(Inversion of Control,控制反转) 是一种设计模式,它将对象的创建和依赖关系的管理从应用程序代码中移除,转交给Spring容器来管理。

控制反转:传统的应用程序中,对象的创建和依赖关系的管理是由开发者自己控制的。而在Spring中,这些控制权被反转给了Spring容器,开发者只需要配置好Bean的依赖关系,Spring容器会自动管理这些Bean的生命周期和依赖注入。

 4. 说下对Spring AOP的理解、有哪些通知?使用场景有哪些?底层原理是什么?
Spring AOP(Aspect-Oriented Programming,面向切面编程) 是一种编程范式,它允许开发者将横切关注点(如日志、事务管理等)与业务逻辑分离。

通知类型:

@Before:在目标方法执行前执行。

@After:在目标方法执行后执行(无论是否抛出异常)。

@AfterReturning:在目标方法成功执行后执行。

@AfterThrowing:在目标方法抛出异常后执行。

@Around:在目标方法执行前后都可以执行,可以控制目标方法的执行。

使用场景:

日志记录

事务管理

权限控制

性能监控

底层原理:Spring AOP的实现基于动态代理技术。Spring会为目标对象创建一个代理对象,当调用目标方法时,实际上是调用代理对象的方法,代理对象会在目标方法执行前后执行相应的通知逻辑。

 5. 说下Spring MVC的流程(从访问一个URL到得到页面结果的具体流程:DispatcherServlet的职责流程)
客户端请求:用户通过浏览器或其他客户端发送请求。

DispatcherServlet:请求首先到达DispatcherServlet,它是Spring MVC的前端控制器。

HandlerMapping:DispatcherServlet通过HandlerMapping找到处理请求的Controller。

Controller:DispatcherServlet将请求转发给对应的Controller。

ModelAndView:Controller处理请求后,返回一个ModelAndView对象。

ViewResolver:DispatcherServlet通过ViewResolver解析ModelAndView中的视图名称,找到对应的视图。

View:DispatcherServlet将ModelAndView中的数据传递给视图,视图渲染后返回给客户端。

 6. 对Spring声明式事务的理解?Spring的事务隔离级别有哪些?Spring事务传播行为有哪些?
声明式事务:通过注解或XML配置的方式,将事务管理逻辑与业务逻辑分离,Spring会自动管理事务的开启、提交和回滚。

事务隔离级别:

DEFAULT:使用数据库默认的隔离级别。

READ_UNCOMMITTED:允许读取未提交的数据(脏读)。

READ_COMMITTED:只能读取已提交的数据(防止脏读)。

REPEATABLE_READ:在同一个事务中多次读取相同的数据,结果一致(防止不可重复读)。

SERIALIZABLE:最高隔离级别,防止幻读。

事务传播行为:

REQUIRED:如果当前没有事务,就新建一个事务;如果有,就加入当前事务。

SUPPORTS:如果当前有事务,就加入当前事务;如果没有,就以非事务方式执行。

MANDATORY:如果当前有事务,就加入当前事务;如果没有,就抛出异常。

REQUIRES_NEW:新建一个事务,如果当前有事务,就将当前事务挂起。

NOT_SUPPORTED:以非事务方式执行,如果当前有事务,就将当前事务挂起。

NEVER:以非事务方式执行,如果当前有事务,就抛出异常。

NESTED:如果当前有事务,就在当前事务中嵌套一个新的事务。

 7. 什么情况下会让Spring事务失效?
非public方法:Spring事务注解只能作用于public方法。

异常未抛出:如果方法中捕获了异常但没有抛出,事务不会回滚。

自定义异常:默认情况下,Spring事务只对RuntimeException及其子类异常回滚,如果抛出自定义异常,需要配置rollbackFor属性。

事务传播行为:某些传播行为(如NOT_SUPPORTED)会导致事务失效。

多线程:在多线程环境下,事务管理器无法跨线程管理事务。

 8. Spring Boot的自动装配原理是什么?
自动装配:Spring Boot通过@EnableAutoConfiguration注解和spring.factories文件,自动扫描并加载项目中的配置类。它会根据项目的依赖和配置文件中的属性,自动配置Spring应用上下文。

原理:

Spring Boot会扫描META-INF/spring.factories文件,找到所有自动配置类。

根据@Conditional注解的条件,判断是否需要加载某个配置类。

自动配置类会根据项目中的依赖和配置文件中的属性,自动配置Spring应用上下文。

 9. Spring Boot项目的启动加载流程大概说下
启动类:Spring Boot应用的启动类通常包含@SpringBootApplication注解。

自动配置:Spring Boot会自动扫描并加载META-INF/spring.factories文件中的自动配置类。

Bean加载:Spring Boot会根据自动配置类和项目中的Bean定义,加载并初始化Bean。

Servlet容器启动:如果项目中包含Web依赖,Spring Boot会启动嵌入式的Servlet容器(如Tomcat)。

应用启动:Spring Boot应用启动完成后,可以通过CommandLineRunner或ApplicationRunner接口执行一些初始化逻辑。

 10. Spring Boot项目读取配置文件的方式有几种?
@Value注解:通过@Value注解注入配置文件中的属性值。

Environment对象:通过Environment对象获取配置文件中的属性值。

@ConfigurationProperties注解:通过@ConfigurationProperties注解将配置文件中的属性映射到一个Java对象。

PropertySource注解:通过@PropertySource注解指定自定义的配置文件。

 11. 如何自定义Spring Boot Starter?
创建Maven项目:创建一个Maven项目,定义好依赖。

编写自动配置类:编写一个自动配置类,使用@Configuration和@Conditional注解。

创建spring.factories文件:在META-INF目录下创建spring.factories文件,指定自动配置类。

打包发布:将项目打包成jar文件,发布到Maven仓库。

使用自定义Starter:在其他Spring Boot项目中引入自定义Starter的依赖,即可使用其功能。

 12. BeanFactory和FactoryBean的区别?
BeanFactory:是Spring IOC容器的核心接口,负责Bean的创建、管理和依赖注入。

FactoryBean:是一个特殊的Bean,它本身是一个工厂,可以用来创建其他Bean。通过FactoryBean,开发者可以自定义Bean的创建逻辑。

 13. Spring Bean的生命周期是什么?
实例化:Spring容器根据Bean定义创建Bean实例。

属性赋值:Spring容器为Bean的属性注入依赖。

初始化:调用Bean的初始化方法(如@PostConstruct注解的方法)。

使用:Bean被应用程序使用。

销毁:当Spring容器关闭时,调用Bean的销毁方法(如@PreDestroy注解的方法)。

 14. Spring如何解决IOC中的循环依赖问题?
循环依赖:当两个或多个Bean相互依赖时,可能会导致循环依赖问题。

解决方法:

三级缓存:Spring通过三级缓存(singletonObjects、earlySingletonObjects、singletonFactories)来解决循环依赖问题。

提前暴露:在Bean的实例化阶段,Spring会将Bean的引用提前暴露到二级缓存中,使得其他Bean可以引用这个未完全初始化的Bean。

代理对象:对于AOP代理对象,Spring会在Bean实例化后立即创建代理对象,并将其放入缓存中,避免循环依赖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值