Java面试八股文--spring springBoot面试题

Spring Boot、Spring MVC 和 Spring 有什么区别

spring是⼀个IOC容器,⽤来管理Bean,使⽤依赖注⼊实现控制反转,可以很⽅便的整合各种框架,提供AOP机制弥补OOP的代码重复问题、更⽅便将不同类不同⽅法中的共同处理抽取成切⾯、⾃动注⼊给⽅法执⾏,⽐如⽇志、异常等

Spring Boot 是一种快速构建生产级 Spring 应用程序的框架,它致力于简化 Spring 框架的配置和使用。实现了自动配置,降低了项目搭建的复杂度。Spring Boot 能够帮助开发人员更快速地构建、运行和部署应用程序。

Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,,属于Spring框架的一个模块,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

spring是怎么创建对象的?

1、通过无参构造方法来创建对象

2、通过有参构造方法来创建对象

3、通过静态方法工厂创建对象

4、通过实例工厂创建对象

spring是怎么解决循环依赖?

循环依赖其实就是循环引用,也就是两个或者两个以上的bean对象互相持有对方,最终形成闭环。比如说A依赖B,B依赖C,C依赖A,形成循环依赖。

1. 首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来;

2. 然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来;

3. 这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A)。这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories),通过ObjectFactory 提前曝光,所以可以通过 ObjectFactory#getObject() 方法来拿到 A 对象。C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中;

4. 回到 B,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。

SpringBoot是如何实现自动装配的?

1. no:默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配。

2. byName:通过参数名 自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byname,之后容器试图匹配、装配和该 bean 的属性具有相同名字的 bean。

3. byType/taɪp/:通过参数类型自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byType,之后容器试图匹配、装配和该 bean 的属性具有相同类型的 bean。如果有多个 bean 符合条件,则抛出错误。

4. constructor/kənˈstrʌktər/:这个方式类似于 byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。

5. autodetect/dɪˈtekt/:首先尝试使用 constructor 来自动装配,如果无法工作,则使用 byType 方式。

Bean生命周期

1. 推断构造⽅法

2. 实例化

3. 填充属性,也就是依赖注⼊

4. 处理Aware回调

5. 初始化前,处理@PostConstruct注解

6. 初始化,处理InitializingBean/ɪˈnɪʃəlaɪzɪŋ/接⼝

7. 初始化后,进⾏AOP

8.销毁

Spring支持的bean的作用域?

Spring容器中的bean可以分为5个范围:

(1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。

(2)prototype:为每一个bean请求提供一个实例。

(3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。

(4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。

(5)global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

Spring事务传播机制

多个事务⽅法相互调⽤时,事务如何在这些⽅法间传播,⽅法A是⼀个事务的⽅法,⽅法A执⾏过程中调⽤了⽅法B,那么⽅法B有⽆事务以及⽅法B对事务的要求不同都会对⽅法A的事务具体执⾏造成影响,同时⽅法A的事务对⽅法B的事务执⾏也有影响,这种影响具体是什么就由两个⽅法所定义的事务传播类型所决定。

1. required(Spring默认的事务传播类型):如果当前没有事务,则⾃⼰新建⼀个事务,如果当前存在事务,则加⼊这个事务

2. supports/səˈport/:当前存在事务,则加⼊当前事务,如果当前没有事务,就以⾮事务⽅法执⾏

3. mandatory:当前存在事务,则加⼊当前事务,如果当前事务不存在,则抛出异常。

4. requires_new:创建⼀个新事务,如果存在当前事务,则挂起该事务。

5. not_supported:以⾮事务⽅式执⾏,如果当前存在事务,则挂起当前事务

6. never:不使⽤事务,如果当前事务存在,则抛出异常

7. nested/ˈnestɪd/:如果当前事务存在,则在嵌套事务中执⾏,否则REQUIRED的操作⼀样(开启⼀个事务)

注入问题

sprnig中AOP代理分为几大类和区别?

Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。

1.面向对象

指允许不同子类型的对象对同一消息作出不同的响应

什么是线程池,几种方式

线程池是解决突然大量爆发的一个线程去设计的,通过几个固定线程去做大量的操作而服务的,主要是去减少创建和销毁线程所需要的时间,提高效率。

newFixedThreadPool/fɪkst/:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。

newCachedThreadPool/kæʃt/:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

newScheduledThreadPool/ˈʃedjuːld/:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。

newSingleThreadExecutor/ɪɡˈzekjətər/:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。

3.进程 线程

jvm内存区域

程序计数器:是当前线程所执行的字节码的行号指示器,正在执行java方法的话,计数器记录的是虚拟机字节码指令的地址。如果还是Native 方法,则为空。

虚拟机栈:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

本地方法栈:为Native方法服务。

方法区:用于存放被虚拟机加载的类的元数据信息,如常量、静态变量和即时编译器编译后的代码。

堆:存放对象实例和数组,是垃圾回收的主要区域,分为新生代和老年代。

类加载

加载 通过类的完全限定名,查找此类字节码文件,利用字节码文件创建Class对象.

链接 验证、准备和解析(可选)

验证 确保Class文件符合当前虚拟机的要求,不会危害到虚拟机自身安全.

准备 进行内存分配,为static修饰的类变量分配内存,并设置初始值(0或null).不包含fifinal修饰的静态变量,因为fifinal变量在编译时分配.

解析 将常量池中的符号引用替换为直接引用的过程.直接引用为直接指向目标的指针或者相对偏移量等.检//查指定的类是否引用了其他的类/接口,是否能找到和加载其他的类/接口

初始化 主要完成静态块执行以及静态变量的赋值.先初始化父类,再初始化当前类.只有对类主动使用时才会初始化.

使用 new 实例化

卸载 GC

什么是STW?

是在垃圾回收算法执⾏过程当中,需要将JVM内存冻结的⼀种状态。在STW状态下,JAVA的所有线程都是停⽌执⾏的-GC线程除外,native⽅法可以执⾏。

JVM有哪些垃圾回收器?

新⽣代收集器:

Serial/ˈsɪriəl/

ParNew

Parallel Scavenge /ˈpærəlel/ /ˈskævɪndʒ/

⽼年代收集器:

CMS

Serial Old

Parallel Old

整堆收集器:

G1

G1垃圾回收器

G1之中不再区分年轻代和老年代,而是整堆回收,把内存划分成多个独立的子区域(Region)。

G1整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片。

G1收集器里面将整个内存区域都混合在一起了,但其本身依然在小范围内要进行年轻代和老年代的区分,保留了新生代和老年代,但他们不再是物理隔离的,而是通过一部分Region的集合且不需要Region是连续的,也就是说依然会采取不同的GC方式来处理不同的区域,G1只有逻辑上的分代概念,或者说每个分区都可能随G1的运行在不同代之间前后切换。

G1能充分利用多CPU,多核环境硬件优势,尽量缩短STW

说说对象分配规则

对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC。

大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝(新生代采用复制算法收集内存)。

长期存活的对象进入老年代。虚拟机为每个对象定义了一个年龄计数器,如果对象经过了1次Minor GC那么对象会进入Survivor区,之后每经过一次Minor GC那么对象的年龄加1,知道达到阀值对象进入老年区。

动态判断对象的年龄。如果Survivor区中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代。

空间分配担保。每次进行Minor GC时,JVM会计算Survivor区移至老年区的对象的平均大小,如果这个值大于老年区的剩余值大小则进行一次Full GC,如果小于检查HandlePromotionFailure设置,如果true则只进行Monitor GC,如果false则进行Full GC。

ThreadLocal的底层原理

1. ThreadLocal是Java中所提供的线程本地存储机制,可以利⽤该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意⽅法中获取缓存的数据

2. ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对象)中都存在⼀个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值

3. 如果在线程池中使⽤ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使⽤完之后,应该要把设置的key,value,也就是Entry对象进⾏回收,但线程池中的线程不会回收,⽽线程对象是通过强引⽤指向ThreadLocalMap,ThreadLocalMap也是通过强引⽤指向Entry对象,线程不被回收,Entry对象也就不会被回收,从⽽出现内存泄漏,解决办法是,在使⽤了ThreadLocal对象之后,⼿动调⽤ThreadLocal的remove⽅法,⼿动清楚Entry对象

volatile关键字的作用

保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

禁止进行指令重排序。

在访问 volatile 变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此 volatile 变量是一种比 sychronized 关键字更轻量级的同步机制。

死锁

多个线程同时被阻塞

造成死锁的⼏个原因:

1. ⼀个资源每次只能被⼀个线程使⽤

2. ⼀个线程在阻塞等待某个资源时,不释放已占有资源

3. ⼀个线程已经获得的资源,在未使⽤完之前,不能被强⾏剥夺

4. 若⼲线程形成头尾相接的循环等待资源关系

这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满⾜其中某⼀个条件即可。⽽其中前3个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系。

设计模式

单例模式

单例模式是一种常用的软件设计模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在,整个系统只能使用一个对象实例。

懒汉模式就是用的时候再去创建对象,饿汉模式就是提前就已经加载好的静态static对象,双重检查模式就是两次检查避免多线程造成创建了多个对象。

代理模式

代理模式是给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

工厂模式

简单工厂模式又叫静态工厂方法模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

抽象工厂模式

抽象工厂模式是在简单工厂的基础上将未来可能需要修改的代码抽象出来,通过继承的方式让子类去做决定。

观察者模式

观察者模式是定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。

原型模式 适配器模式

单例模式

TCP的三次握⼿和四次挥⼿

TCP协议是7层⽹络协议中的传输层协议,负责数据的可靠传输。

在建⽴TCP连接时,需要通过三次握⼿来建⽴,过程是:

1. 客户端向服务端发送⼀个SYN

2. 服务端接收到SYN后,给客户端发送⼀个SYN_ACK

3. 客户端接收到SYN_ACK后,再给服务端发送⼀个ACK

在断开TCP连接时,需要通过四次挥⼿来断开,过程是:

1. 客户端向服务端发送FIN

2. 服务端接收FIN后,向客户端发送ACK,表示我接收到了断开连接的请求,客户端你可以不发数据

了,不过服务端这边可能还有数据正在处理

3. 服务端处理完所有数据后,向客户端发送FIN,表示服务端现在可以断开连接

4. 客户端收到服务端的FIN,向服务端发送ACK,表示客户端也会断开连接了

TCP 与 UDP 的区别,以及各自的优缺点

1、TCP面向连接(如打电话要先拨号建立连接):UDP是无连接的,即发送数据之前不需要建立连接。

2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。tcp通过校验和,重传控制,序号标识,滑动窗口、确认应答实现可靠传输。如丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。

3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。

4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP对系统资源要求较多,UDP对系统资源要求较少。

你可能感兴趣的:(Java面试,spring,spring,boot,jvm)