添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
首发于 Java学习
10万字208道Java经典面试题总结下篇(附答案,建议收藏)

10万字208道Java经典面试题总结下篇(附答案,建议收藏)

前言

最近有很多粉丝问我,有什么方法能够快速提升自己,通过阿里、腾讯、字节跳动、京东等互联网大厂的面试,我觉得短时间提升自己最快的手段就是背面试题,最近总结了Java常用的面试题,分享给大家,希望大家都能圆梦大厂,加油,我命由我不由天。

内容过多,文字限制,此为下篇。

上篇链接: 10万字208道Java经典面试题总结上篇(附答案,建议收藏)


目录

上篇:

1、JDK 和 JRE 有什么区别?
2、== 和 equals 的区别是什么?
3、final 在 java 中有什么作用?
4、java 中的 Math.round(-1.5) 等于多少?
5、String 属于基础的数据类型吗?
6、String str="i"与 String str=new String(“i”)一样吗?
7、如何将字符串反转?
8、String 类的常用方法都有那些?
9、new String("a") + new String("b") 会创建几个对象?
10、如何将字符串反转?
11、String 类的常用方法都有那些?
12、普通类和抽象类有哪些区别?
13、接口和抽象类有什么区别?
14、java 中 IO 流分为几种?
15、BIO、NIO、AIO 有什么区别?
16、Files的常用方法都有哪些?
17、什么是反射?
18、什么是 java 序列化?什么情况下需要序列化?
19、为什么要使用克隆?如何实现对象克隆?深拷贝和浅拷贝区别是什么?
20、throw 和 throws 的区别?
21、final、finally、finalize 有什么区别?
22、try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
23、常见的异常类有哪些?
24、hashcode是什么?有什么作用?
25、java 中操作字符串都有哪些类?它们之间有什么区别?
26、java 中都有哪些引用类型?
27、在 Java 中,为什么不允许从静态方法中访问非静态变量?
28、说说Java Bean的命名规范
29、Java Bean 属性命名规范问题分析
30、什么是 Java 的内存模型?
31、在 Java 中,什么时候用重载,什么时候用重写?
32、举例说明什么情况下会更倾向于使用抽象类而不是接口?
33、实例化对象有哪几种方式
34、byte类型127+1等于多少
35、Java 容器都有哪些?
36、Collection 和 Collections 有什么区别?
37、list与Set区别
38、HashMap 和 Hashtable 有什么区别?
39、说一下 HashMap 的实现原理?
40、set有哪些实现类?
41、说一下 HashSet 的实现原理?
42、ArrayList 和 LinkedList 的区别是什么?
43、如何实现数组和 List 之间的转换?
44、在 Queue 中 poll()和 remove()有什么区别?
45、哪些集合类是线程安全的
46、迭代器 Iterator 是什么?
47、Iterator 怎么使用?有什么特点?
48、Iterator 和 ListIterator 有什么区别?
49、怎么确保一个集合不能被修改?
50、队列和栈是什么?有什么区别?
51、Java8开始ConcurrentHashMap,为什么舍弃分段锁?
52、ConcurrentHashMap(JDK1.8)为什么要使用synchronized而不是如ReentranLock这样的可重入锁?
53、concurrentHashMap和HashTable有什么区别
54、HasmMap和HashSet的区别
55、请谈谈 ReadWriteLock 和 StampedLock
56、线程的run()和start()有什么区别?
57、为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
58、Synchronized 用过吗,其原理是什么?
59、JVM 对 Java 的原生锁做了哪些优化?
60、为什么 wait(), notify()和 notifyAll()必须在同步方法或者同步块中被调用?
61、Java 如何实现多线程之间的通讯和协作?
62、Thread 类中的 yield 方法有什么作用?
63、为什么说 Synchronized 是非公平锁?
64、请谈谈 volatile 有什么特点,为什么它能保证变量对所有线程的可见性?
65、为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理又是什么?什么是 CAS,它有什么特性?
66、乐观锁一定就是好的吗?
67、请尽可能详尽地对比下 Synchronized 和 ReentrantLock 的异同。
68、ReentrantLock 是如何实现可重入性的?
69、什么是锁消除和锁粗化?
70、跟 Synchronized 相比,可重入锁 ReentrantLock 其实现原理有什么不同?
71、那么请谈谈 AQS 框架是怎么回事儿?
72、AQS 对资源的共享方式?
73、如何让 Java 的线程彼此同步?
74、你了解过哪些同步器?请分别介绍下。
75、Java 中的线程池是如何实现的
76、创建线程池的几个核心构造参数
77、线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗?
78、volatile 关键字的作用
79、既然 volatile 能够保证线程间的变量可见性,是不是就意味着基于 volatile 变量的运算就是并发安全的?
80、ThreadLocal 是什么?有哪些使用场景?
81、请谈谈 ThreadLocal 是怎么解决并发安全的?
82、很多人都说要慎用 ThreadLocal,谈谈你的理解,使用 ThreadLocal 需要注意些什么?
83、为什么代码会重排序?
84、什么是自旋
85、多线程中 synchronized 锁升级的原理是什么?
86、synchronized 和 ReentrantLock 区别是什么?
87、Java Concurrency API 中的 Lock 接口(Lock interface)是什么?对比同步它有什么优势?
88、jsp 和 servlet 有什么区别?
89、jsp 有哪些内置对象?作用分别是什么?
90、forward 和 redirect 的区别?
91、说一下 jsp 的 4 种作用域?
92、session 和 cookie 有什么区别?
93、如果客户端禁止 cookie 能实现 session 还能用吗?
94、什么是上下文切换?
95、cookie、session、token
96、说一下 session 的工作原理?
97、http 响应码 301 和 302 代表的是什么?有什么区别?
98、简述 tcp 和 udp的区别?
99、tcp 为什么要三次握手,两次不行吗?为什么?
100、OSI 的七层模型都有哪些?
101、get 和 post 请求有哪些区别?
102、什么是 XSS 攻击,如何避免?
103、什么是 CSRF 攻击,如何避免?
104、如何实现跨域?说一下 JSONP 实现原理?
105、websocket应用的是哪个协议
106、说一下 tcp 粘包是怎么产生的?
107、请列举出在 JDK 中几个常用的设计模式?
108、什么是设计模式?你是否在你的代码里面使用过任何设计模式?
109、Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
110、在 Java 中,什么叫观察者设计模式(observer design pattern)?
111、使用工厂模式最主要的好处是什么?在哪里使用?
112、请解释自动装配模式的区别?

下篇:

113、举一个用 Java 实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类层次?
114、什么是 Spring 框架?Spring 框架有哪些主要模块?
115、使用 Spring 框架能带来哪些好处?
116、Spring IOC、AOP举例说明
117、什么是控制反转(IOC)?什么是依赖注入?
118、BeanFactory 和 ApplicationContext 有什么区别?
119、什么是 JavaConfig?
120、什么是 ORM 框架?
121、Spring 有几种配置方式?
122、请解释 Spring Bean 的生命周期?
123、Spring Bean 的作用域之间有什么区别?Spring容器中的bean可以分为5个范围:
124、如何在 Spring Boot 中禁用 Actuator 端点安全性?
125、什么是 Spring inner beans?
126、Spring 框架中的单例 Beans 是线程安全的么?
127、请解释 Spring Bean 的自动装配?
128、如何开启基于注解的自动装配?
129、什么是 Spring Batch?
130、spring mvc 和 struts 的区别是什么?
131、请举例解释@Required 注解?
132、Spring常用注解
133、项目中是如何实现权限验证的,权限验证需要几张表
134、谈谈controller,接口调用的路径问题
135、如何防止表单重复提交
136、Spring中都应用了哪些设计模式
137、请举例说明如何在 Spring 中注入一个 Java Collection?
138、mybatis 中 #{}和 ${}的区别是什么?
139、mybatis 是否支持延迟加载?延迟加载的原理是什么?
140、说一下 mybatis 的一级缓存和二级缓存?
141、mybatis 有哪些执行器(Executor)?
142、mybatis 和 hibernate 的区别有哪些?
143、myBatis查询多个id、myBatis常用属性
144、mybatis一级缓存、二级缓存
145、mybatis如何防止sql注入
146、hibernate 中如何在控制台查看打印的 sql 语句?
147、hibernate 有几种查询方式?
148、hibernate 实体类可以被定义为 final 吗?
149、在 hibernate 中使用 Integer 和 int 做映射有什么区别?
150、什么是 Spring Boot?Spring Boot 有哪些优点?
151、Spring Boot 中的监视器是什么?
152、什么是 YAML?
153、如何使用 Spring Boot 实现分页和排序?
154、如何使用 Spring Boot 实现异常处理?
155、单点登录
156、Spring Boot比Spring多哪些注解
157、打包和部署
158、Spring Boot如何访问不同的数据库
159、查询网站在线人数
160、easyExcel如何实现
161、什么是 Swagger?你用 Spring Boot 实现了它吗?
162、数据库的三范式是什么?
163、一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
164、如何获取当前数据库版本?
165、说一下 ACID 是什么?
166、char 和 varchar 的区别是什么?
167、float 和 double 的区别是什么?
168、Oracle分页sql
169、数据库如何保证主键唯一性
170、如何设计数据库
171、性别是否适合做索引
172、如何查询重复的数据
173、数据库一般会采取什么样的优化方法?
174、索引怎么定义,分哪几种
175、mysql 的内连接、左连接、右连接有什么区别?
176、RabbitMQ的使用场景有哪些?
177、RabbitMQ有哪些重要的角色?有哪些重要的组件?
178、RabbitMQ中 vhost 的作用是什么?
179、说一下 jvm 的主要组成部分?及其作用?
180、说一下 jvm 运行时数据区?
181、什么是类加载器,类加载器有哪些?
182、说一下类加载的执行过程?
183、JVM的类加载机制是什么?
184、什么是双亲委派模型?
185、怎么判断对象是否可以被回收?
186、说一下 jvm 有哪些垃圾回收算法?
187、说一下 jvm 有哪些垃圾回收器?
188、JVM栈堆概念,何时销毁对象
189、新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
190、详细介绍一下 CMS 垃圾回收器?
191、简述分代垃圾回收器是怎么工作的?
192、Redis是什么?
193、Redis都有哪些使用场景?
194、Redis有哪些功能?
195、Redis支持的数据类型有哪些?
196、Redis取值存值问题
197、Redis为什么是单线程的?
198、Redis真的是单线程的吗?
199、Redis持久化有几种方式?
200、Redis和 memecache 有什么区别?
201、Redis支持的 java 客户端都有哪些?
202、jedis 和 redisson 有哪些区别?
203、什么是缓存穿透?怎么解决?
204、怎么保证缓存和数据库数据的一致性?
205、Redis,什么是缓存穿透?怎么解决?
206、Redis怎么实现分布式锁?
207、Redis分布式锁有什么缺陷?
208、Redis如何做内存优化?


113、举一个用 Java 实现的装饰模式(decorator design pattern)?它是作用于对象层次还是类层次?

在Java IO中运用了装饰器模式,inputStream作为抽象类,其下有几个实现类,表示从不同的数据源输入:

  1. byteArrayInputStream
  2. fileInputStream
  3. StringBufferInputStream
  4. PipedInputStream,从管道产生输入;
  5. SequenceInputStream,可将其他流收集合并到一个流内;

FilterInputStream作为装饰器在JDK中是一个普通类,其下面有多个具体装饰器比如BufferedInputStream、DataInputStream等。

FilterInputStream内部封装了基础构件:

protected volatile InputStream in;

而BufferedInputStream在调用其read()读取数据时会委托基础构件来进行更底层的操作,而它自己所起的装饰作用就是缓冲,在源码中可以很清楚的看到这一切。




114、什么是 Spring 框架?Spring 框架有哪些主要模块?

Spring是一个控制反转和面向切面的容器框架。

Spring有七大功能模块:

1、Core

Core模块是Spring的核心类库,Core实现了IOC功能。

2、AOP

Apring AOP模块是Spring的AOP库,提供了AOP(拦截器)机制,并提供常见的拦截器,供用户自定义和配置。

3、orm

提供对常用ORM框架的管理和支持,hibernate、mybatis等。

4、Dao

Spring提供对JDBC的支持,对JDBC进行封装。

5、Web

对Struts2的支持。

6、Context

Context模块提供框架式的Bean的访问方式,其它程序可以通过Context访问Spring的Bean资源,相当于资源注入。

7、MVC

MVC模块为spring提供了一套轻量级的MVC实现,即Spring MVC。


115、使用 Spring 框架能带来哪些好处?

1、轻量级框架、容器

Spring是一个容器,管理对象的生命周期和配置。基于一个可配置原型prototype,你的bean可以使单利的,也可以每次需要时都生成一个新的实例。

2、控制反转IOC

Spring通过控制反转实现松耦合。

3、支持AOP

Spring提供对AOP的支持,它允许将一些通用任务,如安全、事务、日志等进行集中式处理,从而提高了程序的复用性。

4、轻量级框架

5、方便测试

Spring提供Junit4的支持,可以通过注解方便测试spring程序。

6、对Java中很多API进行了封装

7、方便集成各种优秀框架

如Struts、hibernate、mybstis。

8、支持声明式事务处理

只需通过配置就可以完成对事务的管理,而无须手动编程。


116、Spring IOC、AOP举例说明

1、IOC理论的背景

我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑。

如果我们打开机械式手表的后盖,就会看到与上面类似的情形,各个齿轮分别带动时针、分针和秒针顺时针旋转,从而在表盘上产生正确的时间。图1中描述的就是这样的一个齿轮组,它拥有多个独立的齿轮,这些齿轮相互啮合在一起,协同工作,共同完成某项任务。我们可以看到,在这样的齿轮组中,如果有一个齿轮出了问题,就可能会影响到整个齿轮组的正常运转。

齿轮组中齿轮之间的啮合关系,与软件系统中对象之间的耦合关系非常相似。对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础。现在,伴随着工业级应用的规模越来越庞大,对象之间的依赖关系也越来越复杂,经常会出现对象之间的多重依赖性关系,因此,架构师和设计师对于系统的分析和设计,将面临更大的挑战。对象之间耦合度过高的系统,必然会出现牵一发而动全身的情形。

耦合关系不仅会出现在对象与对象之间,也会出现在软件系统的各模块之间,以及软件系统和硬件系统之间。如何降低系统之间、模块之间和对象之间的耦合度,是软件工程永远追求的目标之一。为了解决对象之间的耦合度过高的问题,软件专家Michael Mattson提出了IOC理论,用来实现对象之间的“解耦”,目前这个理论已经被成功地应用到实践当中,很多的J2EE项目均采用了IOC框架产品Spring。

2、什么是控制反转

IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”,还有些书籍翻译成为“控制反向”或者“控制倒置”。

1996年,Michael Mattson在一篇有关探讨面向对象框架的文章中,首先提出了IOC 这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。IOC理论提出的观点大体是这样的:借助于“第三方”实现具有依赖关系的对象之间的解耦,如下图:

大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。

我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统(拿掉IoC容器后的系统):

我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,A、B、C、D这4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话,当你在实现A的时候,根本无须再去考虑B、C和D了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!

我们再来看看,控制反转(IOC)到底为什么要起这么个名字?我们来对比一下:

软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

软件系统在引入IOC容器之后,这种情形就完全改变了,如图3所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

通过前后的对比,我们不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

3、IOC的别名:依赖注入(DI)

2004年,Martin Fowler探讨了同一个问题,既然IOC是控制反转,那么到底是“哪些方面的控制被反转了呢?”,经过详细地分析和论证后,他得出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。于是,他给“控制反转”取了一个更合适的名字叫做“依赖注入(Dependency Injection)”。他的这个答案,实际上给出了实现IOC的方法:注入。所谓依赖注入,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

所以,依赖注入(DI)和控制反转(IOC)是从不同的角度的描述的同一件事情,就是指通过引入IOC容器,利用依赖关系注入的方式,实现对象之间的解耦。

我们举一个生活中的例子,来帮助理解依赖注入的过程。大家对USB接口和USB设备应该都很熟悉吧,USB为我们使用电脑提供了很大的方便,现在有很多的外部设备都支持USB接口。

现在,我们利用电脑主机和USB接口来实现一个任务:从外部USB设备读取一个文件。

电脑主机读取文件的时候,它一点也不会关心USB接口上连接的是什么外部设备,而且它确实也无须知道。它的任务就是读取USB接口,挂接的外部设备只要符合USB接口标准即可。所以,如果我给电脑主机连接上一个U盘,那么主机就从U盘上读取文件;如果我给电脑主机连接上一个外置硬盘,那么电脑主机就从外置硬盘上读取文件。挂接外部设备的权力由我作主,即控制权归我,至于USB接口挂接的是什么设备,电脑主机是决定不了,它只能被动的接受。电脑主机需要外部设备的时候,根本不用它告诉我,我就会主动帮它挂上它想要的外部设备,你看我的服务是多么的到位。这就是我们生活中常见的一个依赖注入的例子。在这个过程中,我就起到了IOC容器的作用。

通过这个例子,依赖注入的思路已经非常清楚:当电脑主机读取文件的时候,我就把它所要依赖的外部设备,帮他挂接上。整个外部设备注入的过程和一个被依赖的对象在系统运行时被注入另外一个对象内部的过程完全一样。

我们把依赖注入应用到软件系统中,再来描述一下这个过程:

对象A依赖于对象B,当对象 A需要用到对象B的时候,IOC容器就会立即创建一个对象B送给对象A。IOC容器就是一个对象制造工厂,你需要什么,它会给你送去,你直接使用就行了,而再也不用去关心你所用的东西是如何制成的,也不用关心最后是怎么被销毁的,这一切全部由IOC容器包办。

在传统的实现中,由程序内部代码来控制组件之间的关系。我们经常使用new关键字来实现两个组件之间关系的组合,这种实现方式会造成组件之间耦合。IOC很好地解决了该问题,它将实现组件间关系从程序内部提到外部容器,也就是说由容器在运行期将组件间的某种依赖关系动态注入组件中。

4、IOC为我们带来了什么好处

我们还是从USB的例子说起,使用USB外部设备比使用内置硬盘,到底带来什么好处?

第一、USB设备作为电脑主机的外部设备,在插入主机之前,与电脑主机没有任何的关系,只有被我们连接在一起之后,两者才发生联系,具有相关性。所以,无论两者中的任何一方出现什么的问题,都不会影响另一方的运行。这种特性体现在软件工程中,就是可维护性比较好,非常便于进行单元测试,便于调试程序和诊断故障。代码中的每一个Class都可以单独测试,彼此之间互不影响,只要保证自身的功能无误即可,这就是组件之间低耦合或者无耦合带来的好处。

第二、USB设备和电脑主机的之间无关性,还带来了另外一个好处,生产USB设备的厂商和生产电脑主机的厂商完全可以是互不相干的人,各干各事,他们之间唯一需要遵守的就是USB接口标准。这种特性体现在软件开发过程中,好处可是太大了。每个开发团队的成员都只需要关心实现自身的业务逻辑,完全不用去关心其它的人工作进展,因为你的任务跟别人没有任何关系,你的任务可以单独测试,你的任务也不用依赖于别人的组件,再也不用扯不清责任了。所以,在一个大中型项目中,团队成员分工明确、责任明晰,很容易将一个大的任务划分为细小的任务,开发效率和产品质量必将得到大幅度的提高。

第三、同一个USB外部设备可以插接到任何支持USB的设备,可以插接到电脑主机,也可以插接到DV机,USB外部设备可以被反复利用。在软件工程中,这种特性就是可复用性好,我们可以把具有普遍性的常用组件独立出来,反复利用到项目中的其它部分,或者是其它项目,当然这也是面向对象的基本特征。显然,IOC不仅更好地贯彻了这个原则,提高了模块的可复用性。符合接口标准的实现,都可以插接到支持此标准的模块中。

第四、同USB外部设备一样,模块具有热插拔特性。IOC生成对象的方式转为外置方式,也就是把对象生成放在配置文件里进行定义,这样,当我们更换一个实现子类将会变得很简单,只要修改配置文件就可以了,完全具有热插拨的特性。

以上几点好处,难道还不足以打动我们,让我们在项目开发过程中使用IOC框架吗?

5、IOC容器的技术剖析

IOC中最基本的技术就是“反射(Reflection)”编程, 目前.Net C#、Java和PHP5等语言均支持,其中PHP5的技术书籍中,有时候也被翻译成“映射”。有关反射的概念和用法,大家应该都很清楚,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象。这种编程方式可以让对象在生成时才决定到底是哪一种对象。反射的应用是很广泛的,很多的成熟的框架,比如象Java中的Hibernate、Spring框架,.Net中 NHibernate、Spring.Net框架都是把“反射”做为最基本的技术手段。

反射技术其实很早就出现了,但一直被忽略,没有被进一步的利用。当时的反射编程方式相对于正常的对象生成方式要慢至少得10倍。现在的反射技术经过改良优化,已经非常成熟,反射方式生成对象和通常对象生成方式,速度已经相差不大了,大约为1-2倍的差距。

我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言的的反射编程,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

6、IOC容器的一些产品

Sun ONE技术体系下的IOC容器有:轻量级的有Spring、Guice、Pico Container、Avalon、HiveMind;重量级的有EJB;不轻不重的有JBoss,Jdon等等。Spring框架作为Java开发中SSH(Struts、Spring、Hibernate)三剑客之一,大中小项目中都有使用,非常成熟,应用广泛,EJB在关键性的工业级项目中也被使用,比如某些电信业务。

.Net技术体系下的IOC容器有: 404 Not Found 、Castle等等。Spring.Net是从Java的Spring移植过来的IOC容器,Castle的IOC容器就是Windsor部分。它们均是轻量级的框架,比较成熟,其中Spring.Net已经被逐渐应用于各种项目中。

7、使用IOC框架应该注意什么

使用IOC框架产品能够给我们的开发过程带来很大的好处,但是也要充分认识引入IOC框架的缺点,做到心中有数,杜绝滥用框架。

(1)软件系统中由于引入了第三方IOC容器,生成对象的步骤变得有些复杂,本来是两者之间的事情,又凭空多出一道手续,所以,我们在刚开始使用IOC框架的时候,会感觉系统变得不太直观。所以,引入了一个全新的框架,就会增加团队成员学习和认识的培训成本,并且在以后的运行维护中,还得让新加入者具备同样的知识体系。

(2)由于IOC容器生成对象是通过反射方式,在运行效率上有一定的损耗。如果你要追求运行效率的话,就必须对此进行权衡。

(3)、具体到IOC框架产品(比如:Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。

(4)IOC框架产品本身的成熟度需要进行评估,如果引入一个不成熟的IOC框架产品,那么会影响到整个项目,所以这也是一个隐性的风险。

我们大体可以得出这样的结论:一些工作量不大的项目或者产品,不太适合使用IOC框架产品。另外,如果团队成员的知识能力欠缺,对于IOC框架产品缺乏深入的理解,也不要贸然引入。最后,特别强调运行效率的项目或者产品,也不太适合引入IOC框架产品,象WEB2.0网站就是这种情况。


117、什么是控制反转(IOC)?什么是依赖注入?

借助Spring实现具有依赖关系的对象之间的解耦。

对象A运行需要对象B,由主动创建变为IOC容器注入,这便是控制反转。

获得依赖对象的过程被反转了,获取依赖对象的过程由自身创建变为由IOC容器注入,这便是依赖注入。


118、BeanFactory 和 ApplicationContext 有什么区别?

1、BeanFactory是Spring的最底层接口,包含bean的定义,管理bean的加载,实例化,控制bean的生命周期,特点是每次获取对象时才会创建对象。

ApplicationContext是BeanFactory的子接口,拥有BeanFactory的全部功能,并且扩展了很多高级特性,每次容器启动时就会创建所有的对象。

  1. ApplicationContext的额外功能:
  2. 继承MessageSource,支持国际化;
  3. 统一的资源文件访问方式;
  4. 提供在监听器中注册bean;
  5. 同时加载过个配置文件;
  6. 载入多个(有继承关系)上下文,使得每个上下文都专注于一个特定的层次,比如应用的web层;

2、BeanFactory通常以编程的方式被创建,ApplicationContext可以以声明的方式创建,如使用ContextLoader。

3、BeanFactory 和 ApplicationContext都支持BeanPostProcessor,BeanFactoryPostProcessor,但BeanFactory需要手动注册,ApplicationContext则是自动注册。


119、什么是 JavaConfig?

JavaConfig是Spring3.0新增的概念,就是以注解的形式取代Spring中繁琐的xml文件。

JavaConfig结合了xml的解耦和java编译时检查的优点。

  1. @Configuration,表示这个类是配置类;
  2. @ComponentScan,相当于xml的<context:componentScan basepackage=>;
  3. @Bean,相当于xml的<bean id="student" class="com.guor.entity">;
  4. @EnableWebMvc,相当于xml的<mvc:annotation-driven>;
  5. @ImportResource,相当于xml的<import resource="application-context-cache.xml">;
  6. @PropertySource,用于读取properties配置文件;
  7. @Profile,一般用于多环境配置,激活时可用@ActiveProfile("dev")注解;

120、什么是 ORM 框架?

ORM(Object-relational mapping),对象关系映射。

是为了解决面向对象与关系型数据库存在的不匹配问题。

ORM框架的优点:

  1. 开发效率更高
  2. 数据访问更抽象、轻便
  3. 支持面向对象封装

121、Spring 有几种配置方式?

1、xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="Index of /schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="Index of /schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="jackma" class="com.tyq.dto.User">
        <property name="name" value="jackma" />
        <property name="age" value="55" />
        <property name="dog" ref="jm" />
     </bean>
    <bean id="jm" class="com.tyq.dto.Dog">
        <property name="name" value="jack" />
        <property name="breed" value="金毛" />
        <property name="age" value="2" />
    </bean>
</beans>

2、基于注解的方式

项目越来越大,基于xml配置太麻烦,Spring 2.x时代提供了声明bean的注解。

(1)Bean的定义

@Component、@Controller、@Service、@Repository。

(2)Bean的注入

@Autowire

3、基于Java的方式

Spring 3.x以后,可以通过Java代码装配Bean。

@Configuration
public class DemoConfig {
    @Bean
    public User zs(){
        return new User();
    @Bean
    public Dog dog(){
        return  new Dog();
    @Bean  //两个狗
    public Dog haqi(){
        return new Dog();
}
@Component("zs")
public class User {
    private String name;
    private int age;
    private Dog dog;
  //get,set方法略
}

原来就是配置类啊,通过@Bean、@Component、getBean方式进行Bean的注册和发现。


122、请解释 Spring Bean 的生命周期?

  1. 通过构造器或工厂方法创建bean实例;
  2. 为bean的属性赋值;
  3. 调用bean的初始化方法;
  4. 使用bean;
  5. 当容器关闭时,调用bean的销毁方法;

123、Spring Bean 的作用域之间有什么区别?

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

  1. singleton:这种bean范围是默认的,这种范围确保不管接受多少请求,每个容器中只哟一个bean的实例,单例模式;
  2. prototype:为每一个bean提供一个实例;
  3. request:在请求bean范围内为每一个来自客户端的网络请求创建一个实例,在请求完毕后,bean会失效并被垃圾回收器回收;
  4. session:为每个session创建一个实例,session过期后,bean会随之消失;
  5. global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet公用全局的存储变量的话,那么全局变量需要存储在global-session中。

124、如何在 Spring Boot 中禁用 Actuator 端点安全性?

默认情况下,所有敏感的HTTP端点都是安全的,只有具有Actuator角色的用户才能访问它们。安全性是使用标准的HTTPServletRequest.isUserInRole方法实施的。我们可以使用management.security.enable = false来禁用安全性。只有在执行机构端点在防火墙后访问时,才建议禁用安全性。


125、什么是 Spring inner beans?

在Spring框架中,无论何时bean被使用时,当仅被调用一个属性。可以将这个bean声明为内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现。比如,在我们的应用程序中,一个Customer类引用了一个Person类,我们要做的是创建一个Person实例,然后再Customer内部使用。

package com;
public class Customer {
    private Person person;
class Person{
    private int id;
    private String name;
    private int age;
}


<bean id="CustomerBean" class="com.Customer">
	<property name="person">
		<bean class="com.person">
			<property name="id" value=1 />
			<property name="name" value="素小暖" />
			<property name="age" value=18 />
		</bean>
	</property>
</bean>

126、Spring 框架中的单例 Beans 是线程安全的么?

Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态,所以在某种程度上说Spring的单例bean时线程安全的。如果你的bean有多种状态的话,比如view model,就需要自行保证线程安全啦。

最浅显的解决办法就是将多态bean的作用域由singleton变更为prototype。


127、请解释 Spring Bean 的自动装配?

Spring支持IOC,自动装配不用类实例化,直接从bean容器中取。

1、配置在xml中

<bean id="employeeDAO" class="com.guor.EmployeeDAOImpl" autowire="byName" />

2、@Autowired自动装配


128、如何开启基于注解的自动装配?

要使用 @Autowired,需要注册 AutowiredAnnotationBeanPostProcessor,可以有以下两种方式来实现:

引入配置文件中的<bean>下引入 <context:annotation-config>

<beans>
    <context:annotation-config />
</beans>

在bean配置文件中直接引入AutowiredAnnotationBeanPostProcessor

<beans>
    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
</beans>

129、什么是 Spring Batch?

1、什么是spring batch?

spring batch是一个轻量级的、完善的批处理框架,它主要的目的在于帮助企业建立健壮、高效的批处理应用。

spring batch是Spring的一个子项目,它使用java语言并基于spring框架作为基础开发,使得已经使用Spring框架的开发者或者是企业可以更加容易访问和利用企业服务。

spring batch提供了大量可重用的组件,包括了日志、追踪、事务、任务作业统计、任务重启、跳过、重复、资源管理。

对大数据量和高性能的批处理任务,spring batch同样提供了高级功能和特性来支持。

例如:分区功能、远程功能。

总的来说,spring batch可以支持简单的、复杂的和大数据量的批处理作业。

2、spring batch业务场景

周期性的提交批处理

把一个任务并行处理

消息驱动应用分级处理

大规模并行批处理

手工或调度使任务失败之后重新启动

有依赖步骤的顺序执行(使用工作流驱动扩展)

处理时跳过部分记录

成批事务:为小批量的或有的存储过程/脚本的场景使用


130、spring mvc 和 struts 的区别是什么?

1、拦截机制的不同

Struts2是类级别的拦截,每次请求就会创建一个Action,和Spring整合时Struts2的ActionBean注入作用域是原型模式prototype,然后通过setter,getter吧request数据注入到属性。Struts2中,一个Action对应一个request,response上下文,在接收参数时,可以通过属性接收,这说明属性参数是让多个方法共享的。Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了,只能设计为多例。

SpringMVC是方法级别的拦截,一个方法对应一个Request上下文,所以方法直接基本上是独立的,独享request,response数据。而每个方法同时又何一个url对应,参数的传递是直接注入到方法中的,是方法所独有的。处理结果通过ModeMap返回给框架。在Spring整合时,SpringMVC的Controller Bean默认单例模式Singleton,所以默认对所有的请求,只会创建一个Controller,有应为没有共享的属性,所以是线程安全的,如果要改变默认的作用域,需要添加@Scope注解修改。

Struts2有自己的拦截Interceptor机制,SpringMVC这是用的是独立的Aop方式,这样导致Struts2的配置文件量还是比SpringMVC大。

2、底层框架的不同

Struts2采用Filter(StrutsPrepareAndExecuteFilter)实现,SpringMVC(DispatcherServlet)则采用Servlet实现。Filter在容器启动之后即初始化;服务停止以后坠毁,晚于Servlet。Servlet在是在调用时初始化,先于Filter调用,服务停止后销毁。

3、性能方面

Struts2是类级别的拦截,每次请求对应实例一个新的Action,需要加载所有的属性值注入,SpringMVC实现了零配置,由于SpringMVC基于方法的拦截,有加载一次单例模式bean注入。所以,SpringMVC开发效率和性能高于Struts2。

4、配置方面

spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高。


131、请举例解释@Required 注解?

@Required注解应用于bean属性的setter方法,它表明影响的bean属性在配置时必须放在XML配置文件中。

十九、请举例说明@Qualifier 注解?

如果在xml中定义了一种类型的多个bean,同时在java注解中又想把其中一个bean对象作为属性,那么此时可以使用@Qualifier加@Autowired来达到这一目的,若不加@Qualifier这个注解,在运行时会出现“ No qualifying bean of type [com.tutorialspoint.Student] is defined: expected single matching bean but found 2: student1,student2”这个异常。


132、Spring常用注解

Spring常用注解(绝对经典)


133、项目中是如何实现权限验证的,权限验证需要几张表

通过了解,现在最普遍的权限管理模型就是RBAC(Role-Based Access Control)。

1、权限控制分类

菜单功能

url控制(控制访问不同的控制器)

2、RBAC的优缺点

(1)优点

简化了用户和权限的关系

易扩展、易维护

(2)缺点

RBAC模型没有提供操作顺序的控制机制,这一缺陷使得RBAC模型很难适应哪些对操作次序有严格要求的系统。

3、RBAC支持的安全原则

(1)最小权限原则

RBAC可以将角色配置成其完成任务所需的最小权限集合。

(2)责任分离原则

可以通过调用相互独立互斥的角色来共同完成敏感的任务,例如要求一个记账员和财务管理员共同参与统一过账操作。

(3)数据抽象原则

可以通过权限的抽象来体现,例如财务操作用借款、存款等抽象权限,而不是使用典型的读写权限。

4、远古时代的权限控制

当时还没有RBAC,也没有这个概念,就是一堆程序员在那鼓捣,觉得登录这块该做点什么。

1、新建一个用户,对这个用户进行赋予权限。
2、但是一旦用户多了,权限复杂了,这工作量也是蛮大的。

5、RBAC

RBAC 1.0

直接上图,一目了然,当程序不是很复杂的时候,RBAC就是这样设计的,我们公司的权限验证模块就是这样设计的。

简简单单,五张表,解决。

RBAC 2.0

基于RBAC 1.0模型的基础上,进行了角色的访问控制

RBAC2中的一个基本限制是互斥角色的限制,互斥角色是指各自权限可以互相制约的两个角色。对于这类角色一个用户在某一次活动中只能被分配其中的一个角色,不能同时获得两个角色的使用权。

该模型有以下几种约束

互斥角色 :同一用户只能分配到一组互斥角色集合中至多一个角色,支持责任分离的原则。互斥角色是指各自权限互相制约的两个角色。对于这类角色一个用户在某一次活动中只能被分配其中的一个角色,不能同时获得两个角色的使用权。常举的例子:在审计活动中,一个角色不能同时被指派给会计角色和审计员角色。

基数约束 :一个角色被分配的用户数量受限;一个用户可拥有的角色数目受限;同样一个角色对应的访问权限数目也应受限,以控制高级权限在系统中的分配。例如公司的领导人有限的;

先决条件角色 :可以分配角色给用户仅当该用户已经是另一角色的成员;对应的可以分配访问权限给角色,仅当该角色已经拥有另一种访问权限。指要想获得较高的权限,要首先拥有低一级的权限。就像我们生活中,国家主席是从副主席中选举的一样。

运行时互斥 :例如,允许一个用户具有两个角色的成员资格,但在运行中不可同时激活这两个角色。

6、rbac的实现理论分析

进入登录页面;

拿到通过post传过来的用户名和密码;

使用orm进行过滤查找;

如果能找到值,则说明登录成功:登录成功后调用rbac初始化函数,初始化函数的主要功能是获取用户的权限和菜单保存到session中,并跳转客户列表页面;如果失败,页面进行友好提示;

7、url权限控制关键代码


134、谈谈controller,接口调用的路径问题

1、Spring MVC如何匹配请求路径

@RequestMapping是用来映射请求的,比如get请求、post请求、或者REST风格与非REST风格的。该注解可以用在类上或方法上,如果用在类上,表示是该类中所有方法的父路径。

@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest {
    @RequestMapping("/testRequestMapping")
    public String testRequestMapping(){
        System.out.println("testRequestMapping");
        return SUCCESS;
}

在类上还添加了一个@Controller注解,该注解在SpringMVC中负责处理由DispatcherServlet分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个model,然后再把该model返回给对应的view进行展示。

我们可以通过“springmvc/testRequestMapping”这个路径来定位到testRequestMapping这个方法,然后执行方法内的方法体。

RequestMapping可以实现模糊匹配路径,比如:

  1. ?表示一个字符;
  2. *表示任意字符;
  3. **匹配多层路径;

/springmvc/**/testRequestMapping 就可以匹配/springmvc/stu/getStudentInfo/testRequestMapping 这样的路径了。

2、SpringMVC如何获取请求的参数

(1)@PathVariable

该注解用来映射请求URL中绑定的占位符。通过@PathVariable可以将URL中占位符的参数绑定到controller处理方法的入参中。

@RequestMapping("/testPathVariable/{id}")
public String testPathVariable(@PathVariable(value="id") Integer id){
    System.out.println("testPathVariable:" + id);
    return SUCCESS;
}

在index.jsp中我们添加一条连接,用来触发一个请求:

<a href="springmvc/testPathVariable/1">testPathVariable</a>

(2) @RequestParam

该注解也是用来获取请求参数的,那么该注解和@PathVariable有什么不同呢?

@RequestMapping(value="/testRequestParam")
public String testRequestParam(@RequestParam(value="username") String username, @RequestParam(value="age", required=false, defaultValue="0") int age){
    System.out.println("testRequestParam" + " username:" + username + " age:" +age);
    return SUCCESS;
}

在index.jsp添加超链接标签

<a href="springmvc/testRequestParam?username=jackie&age=12">testRequestParam</a>

3、REST风格的请求

在SpringMVC中业务最多的应该是CRUD了

@RequestMapping(value="/testRest/{id}", method=RequestMethod.PUT)
public String testRestPut(@PathVariable(value="id") Integer id){
    System.out.println("test put:" + id);
    return SUCCESS;
@RequestMapping(value="/testRest/{id}", method=RequestMethod.DELETE)
public String testRestDelete(@PathVariable(value="id") Integer id){
    System.out.println("test delete:" + id);
    return SUCCESS;
@RequestMapping(value="/testRest", method=RequestMethod.POST)
public String testRest(){
    System.out.println("test post");
    return SUCCESS;
@RequestMapping(value="/testRest/{id}", method=RequestMethod.GET)
public String testRest(@PathVariable(value="id") Integer id){
    System.out.println("test get:" + id);
    return SUCCESS;
}

135、如何防止表单重复提交

1、通过JavaScript屏蔽提交按钮(不推荐)

2、给数据库增加唯一键约束(简单粗暴)

3、利用Session防止表单重复提交(推荐)

4、使用AOP自定义切入实现


136、Spring中都应用了哪些设计模式

1、简单工厂模式

简单工厂模式的本质就是一个工厂类根据传入的参数,动态的决定实例化哪个类。

Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得bean对象。

2、工厂方法模式

应用程序将对象的创建及初始化职责交给工厂对象,工厂Bean。

定义工厂方法,然后通过config.xml配置文件,将其纳入Spring容器来管理,需要通过factory-method指定静态方法名称。

3、单例模式

Spring用的是双重判断加锁的单例模式,通过getSingleton方法从singletonObjects中获取bean。

     /**
     * Return the (raw) singleton object registered under the given name.
     * <p>Checks already instantiated singletons and also allows for an early
     * reference to a currently created singleton (resolving a circular reference).
     * @param beanName the name of the bean to look for
     * @param allowEarlyReference whether early references should be created or not
     * @return the registered singleton object, or {@code null} if none found
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        singletonObject = singletonFactory.getObject();
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);
        return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }

4、代理模式

Spring的AOP中,使用的Advice(通知)来增强被代理类的功能。Spring实现AOP功能的原理就是代理模式(① JDK动态代理,② CGLIB字节码生成技术代理。)对类进行方法级别的切面增强。

5、装饰器模式

装饰器模式:动态的给一个对象添加一些额外的功能。

Spring的ApplicationContext中配置所有的DataSource。这些DataSource可能是不同的数据库,然后SessionFactory根据用户的每次请求,将DataSource设置成不同的数据源,以达到切换数据源的目的。

在Spring中有两种表现:

一种是类名中含有Wrapper,另一种是类名中含有Decorator。

6、观察者模式

定义对象间的一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

Spring中观察者模式一般用在listener的实现。

7、策略模式

策略模式是行为性模式,调用不同的方法,适应行为的变化 ,强调父类的调用子类的特性 。

getHandler是HandlerMapping接口中的唯一方法,用于根据请求找到匹配的处理器。

8、模板方法模式

Spring JdbcTemplate的query方法总体结构是一个模板方法+回调函数,query方法中调用的execute()是一个模板方法,而预期的回调doInStatement(Statement state)方法也是一个模板方法。


137、请举例说明如何在 Spring 中注入一个 Java Collection?

Spring注入有四种方式,

  1. set注入;
  2. 构造器注入;
  3. 基于注解的注入;
  4. xml配置文件注入;

想要注入java collection,就是注入集合类:

  1. list
  2. set
  3. map
  4. props:该标签支持注入键和值都是字符串类型的键值对。

list和set都使用value标签;map使用entry标签;props使用prop标签;


138、mybatis 中 #{}和 ${}的区别是什么?

  1. #{}带引号,${}不带引号;
  2. #{}可以防止SQL注入;
  3. ${}常用于数据库表名、order by子句;
  4. 一般能用#{}就不要使用${};

139、mybatis 是否支持延迟加载?延迟加载的原理是什么?

1、mybatis 是否支持延迟加载?

延迟加载其实就是讲数据加载时机推迟,比如推迟嵌套查询的时机。

延迟加载可以实现先查询主表,按需实时做关联查询,返回关联表结果集,一定程度上提高了效率。

mybatis仅支持关联对象association和关联集合对象collection的延迟加载,association是一对一,collection是一对多查询,在mybatis配置文件中可以配置lazyloadingEnable=true/false。

2、延迟加载的原理是什么?

使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法。

比如调用a.getB().getName(),拦截器方法invoke()发现a.getB()为null,会单独发送事先准备好的查询关联B对象的sql语句,把B查询出来然后调用a.setB(b),也是a的对象的属性b就有值了,然后调用getName(),这就是延迟加载的原理。


140、说一下 mybatis 的一级缓存和二级缓存?

一级缓存是session级别的缓存,默认开启,当查询一次数据库时,对查询结果进行缓存,如果之后的查询在一级缓存中存在,则无需再访问数据库;

二级缓存是sessionFactory级别的缓存,需要配置才会开启。当进行sql语句查询时,先查看一级缓存,如果不存在,访问二级缓存,降低数据库访问压力。


141、mybatis 有哪些执行器(Executor)?

1、mybatis有三种基本的Executor执行器:

(1)、SimpleExecutor

每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

(2)、PauseExecutor

执行update或select,以sql做为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而且放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。

(3)、BatchExecutor

执行update,将所有sql通过addBatch()都添加到批处理中,等待统一执行executeBatch(),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

2、作用范围:

Executor的这些特点,都严格限制在SqlSession生命周期范围内。

3、Mybatis中如何指定使用哪一种Executor执行器?

在mybatis的配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。


142、mybatis 和 hibernate 的区别有哪些?

1、两者最大的区别

针对简单逻辑,都有对应的代码生成工具,可以生成简单基本的dao层方法;

针对高级查询,mybatis要手动编写sql语句和resultMap,而hibernate有良好的映射机制;

2、开发难度对比

hibernate > mybatis

3、日志统计

hibernate有自己的日志统计功能,而mybatis需要借助log4j来记录日志。

4、数据库扩展比较

hibernate > mybatis

5、缓存机制比较

因为hibernate对查询对象有良好的管理机制,用户无需关心sql,所以使用二级缓存如果出现脏数据,系统会报错。

而mybatis,如果不能获取最新数据,应该避免缓存的使用,脏数据的出现会给系统的正常运行带来很大的隐患。

6、如何选择

  1. mybatis需要编写sql和映射规则,工作量大于hibernate;
  2. mybatis支持的工具也有限,不能像hibernate那样有许多插件可以帮助生成映射代码和关联关系;
  3. 对于性能要求不太苛刻的系统,比如管理系统、ERP等推荐hibernate;
  4. 对于性能要求高、响应快、灵活的系统,比如电商系统,推荐使用mybatis;

143、myBatis查询多个id、myBatis常用属性

myBatis查询多个id(我居然回答用对象来传递...)

Page<UserPoJo>  getUserListByIds(@Param("ids") List<Integer> ids);


<!--根据id列表批量查询user-->
<select id="getUserListByIds" resultType="com.guor.UserPoJo">
    select * from student
    where id in
    <foreach collection="ids" item="userid" open="(" close=")" separator=",">
        #{userid}
    </foreach>
</select>

145、mybatis如何防止sql注入

注意:但凡是sql注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或URL传递的参数,并且这个变量或参数是组成sql语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,这是安全领域里的【外部数据不可信任】的原则,纵观web安全领域的各种攻击方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到,就是变量的检测、过滤、验证下手,确保变量是开发者所预想的。

1、检查变量数据类型和格式

数据类型检查,sql执行前,要进行数据类型检查,如果是邮箱,参数就必须是邮箱的格式,如果是日期,就必须是日期格式;

只要是有固定格式的变量,在SQL语句执行前,应该严格按照固定格式去检查,确保变量是我们预想的格式,这样很大程度上可以避免SQL注入攻击。

如果上述例子中id是int型的,效果会怎样呢?无法注入,因为输入注入参数会失败。比如上述中的name字段,我们应该在用户注册的时候,就确定一个用户名规则,比如5-20个字符,只能由大小写字母、数字以及汉字组成,不包含特殊字符。此时我们应该有一个函数来完成统一的用户名检查。不过,仍然有很多场景并不能用到这个方法,比如写博客,评论系统,弹幕系统,必须允许用户可以提交任意形式的字符才行,否则用户体验感太差了。

2、过滤特殊符号

3、绑定变量,使用预编译语句


146、为什么要使用 hibernate?

  1. hibernate对jdbc进行了封装,简化了JDBC的重复性代码;
  2. hibernate对dao有一个封装类hibernateTemplate,可以继承它,实现简单的CRUD接口。
  3. hibernate使用注解和配置文件,可以对实体类和映射文件进行映射;
  4. hibernate有事务管理机制,保证了数据的安全性;
  5. hibernate有一级缓存和二级缓存;

146、hibernate 中如何在控制台查看打印的 sql 语句?

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

147、hibernate 有几种查询方式?

1、导航对象图查询:根据已加载的对象,导航到其他对象。

例如,对于已经加载的Customer对象,调用它的getOrders().iterator()方法就可以导航到所有关联的Order对象,假如在关联级别使用了延迟加载检索策略,那么首次执行此方法时,hibernate会从数据库中加载关联的Order对象,否则就从缓存中获得Order对象。

2、OID方式:按照对象的OID来检索对象

Session的get()和load()方法提供了这种功能,如果在应用程序中先知道了OID,就可以使用这种方式检索对象。

get()和load()的用法完全一样,都需要两个参数,一个是持久化对象类名class,一个是行号OID,返回固定的某一行的数据,但是需要注意的是,当输入的OID不存在时,get()会返回一个空对象,load()则直接报错。

3、HQL检索方式:(hibernate query language)

使用面向对象的HQL查询语言,session的find()方法用于执行HQL查询语句。此外,hibernate还提供了query接口,它是hibernate提供的专门的HQL查询接口,能够执行各种复杂的HQL查询语句。

它具备以下功能:

  1. 在查询语句中设定各种查询条件;
  2. 支持投影查询,即仅检索出对象的部分属性;
  3. 支持分页查询;
  4. 支持连接查询;
  5. 支持分组查询;
  6. 提供内置函数;
  7. 能够调用用户自定义的SQL函数;
  8. 支持子查询;
  9. 支持动态绑定参数;

例如:

Query query = session.createQuery(“from UserPo”);

获得一个query对象,注意参数字符串中不是一个SQL语句,from后面的是持久化对象名称;

List list = query.list();

就可以获取数据库中对应表的数据集合。

4、QBC检索方式:Query By Criteria的API来检索对象

这种API封装了基于字符串形式的查询语句,提供了更加面向对象的接口。

例:Criteria criteria = session.createCriteria(UserPo.class);

创建一个Criteria对象,参数是所关联的持久化对象,criteria.add( Restrictions.ge ("id",2));将查询条件加入对象中,后面的操作就和Query对象一样了。

5、本地SQL

使用本地数据库的SQL查询语句,hibernate会负责把检索到的JDBC ResultSet结果映射为持久化对象图。


148、hibernate 实体类可以被定义为 final 吗?

可以将hibernate的实体类定义为final,但这种做法不好。

因为hibernate会使用代理模式在延迟关联的情况下提高性能,如果你把实体类定义成final类之后,因为Java不允许对final类进行扩展,所以hibernate就无法再使用代理了,如此一来就限制了使用可以提升性能的手段。

不过,如果你的持久化类实现了一个接口,而且在该接口中声明了所有定义于实体类中的所有public的方法的话,就能避免出现前面所说的不利后果。


149、在 hibernate 中使用 Integer 和 int 做映射有什么区别?

hibernate是面向对象的ORM,所以一般定义成封装类型,要看数据库中的定义,如果数据库中有对应字段存在null值,就要定义Integer。也可以定义基本类型,在配置文件中写清楚即可。


150、什么是 Spring Boot?Spring Boot 有哪些优点?

1、Spring Boot简介

基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突、引用的不稳定性得到了解决。

2、Spring Boot 有哪些优点?

  1. 快速构建项目,可以选一些必要的组件;
  2. 对主流框架的无配置集成;
  3. 内嵌Tomcat容器,项目可独立运行;
  4. 删除了繁琐的xml配置文件;
  5. 极大地提高了开发和部署效率;
  6. 提供starter,简化maven配置;

3、SpringBoot有哪些缺点?

  1. 版本迭代速度快,一些模块改动很大;
  2. 由于无须配置,报错时很难定位;

151、Spring Boot 中的监视器是什么?

监听器也叫listener,是servlet的监听器,可以用于监听web应用程序中某些对象的创建、销毁、增加、修改、删除等动作的发生,然后做出相应的响应处理。当范围对象的状态发生变化时,服务器自动调用监听器对象中的方法,常用于系统加载时进行信息初始化,统计在线人数和在线用户,统计网站的访问量。

配置监听器的方法:

通过@Component把监听器加入Spring容器中管理;

在application.properties中添加context.listener.classes配置;

在方法上加@EventListener注解;


152、什么是 YAML?

YAML是JSON的一个超集,可以非常方便地将外部配置以层次结构形式存储起来。YAML可以作为properties配置文件的替代。

YAML使用的注意事项:

  1. 在properties文件中是以"."进行分割的,在yml中是用"."进行分割的;
  2. yml的数据格式和json的格式很像,都是K-V格式,并且通过":"进行赋值;
  3. 每个冒号后面一定要加一个空格;

153、如何使用 Spring Boot 实现分页和排序?

使用Spring Data Jpa可以实现将可分页的传递给存储库方法。


156、Spring Boot比Spring多哪些注解

Spring Boot常用注解(绝对经典)


157、打包和部署

Spring和Spring Boot都支持maven和Gradle通用打包管理技术。

Spring Boot相对Spring的一些优点:

  1. 提供嵌入式容器支持;
  2. 使用命令java -jar独立运行jar;
  3. 部署时可以灵活指定配置文件;

最近项目是分布式的项目,都是通过分项目打包部署,然后部署在docker中运行。


158、Spring Boot如何访问不同的数据库

可以使用druidDataSource创建DataSource,然后通过jdbcTemplate执行sql。


159、查询网站在线人数

通过监听session对象的方式来实现在线人数的统计和在线人信息展示,并且让超时的自动销毁。

对session对象实现监听,首先必须继承HttpSessionListener类,该程序的基本原理就是当浏览器访问页面的时候必定会产生一个session对象,当关闭该页面的时候必然会删除session对象。所以每当产生一个新的session对象就让在线人数+1,当删除一个session对象就让在线人数-1。
还要继承一个HttpSessionAttributeListener,来实现对其属性的监听。分别实现attributeAdded方法,attributeReplace方法以及attributeRemove方法。
sessionCreated//新建一个会话的时候触发,也可以说是客户端第一次喝服务器交互时触发。
sessionDestroyed//销毁会话的时候,一般来说只有某个按钮触发进行销毁,或者配置定时销毁。
HttpSessionAttributeListener有三个方法需要实现
attributeAdded//在session中添加对象时触发此操作 笼统的说就是调用setAttribute这个方法时候会触发的
attributeRemoved//修改、删除session中添加对象时触发此操作 笼统的说就是调用 removeAttribute这个方法时候会触发的
attributeReplaced//在Session属性被重新设置时。

160、easyExcel如何实现

异步读取

新建一个 ExcelModelListener 监听类出来,并且 继承 AnalysisEventListener 类

package com.zh.oukele.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.zh.oukele.model.ExcelMode;
import java.util.ArrayList;
import java.util.List;
 *  监听器
public class ExcelModelListener extends AnalysisEventListener<ExcelMode> {
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
    private static final int BATCH_COUNT = 5;
    List<ExcelMode> list = new ArrayList<ExcelMode>();
    private static int count = 1;
    @Override
    public void invoke(ExcelMode data, AnalysisContext context) {
        System.out.println("解析到一条数据:{ "+ data.toString() +" }");
        list.add(data);
        count ++;
        if (list.size() >= BATCH_COUNT) {
            saveData( count );
            list.clear();
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData( count );
        System.out.println("所有数据解析完成!");
        System.out.println(" count :" + count);
     * 加上存储数据库
    private void saveData(int count) {
        System.out.println("{ "+ count +" }条数据,开始存储数据库!" + list.size());
        System.out.println("存储数据库成功!");
}

161、什么是 Swagger?你用 Spring Boot 实现了它吗?

Swagger是用于生成RestFul Web服务的可视化表示工具,它使文档和服务器可视化更新;

当定义好Swagger后,可以调用服务端接口,来查看接口的返回值,验证返回数据的正确性;


162、数据库的三范式是什么?

1、列不可再分;

2、每一行数据只做一件事,只与一列相关,主键;

3、每个属性都与主键有直接关系,而不是间接关系;

三大范式只是设计数据库的基本理念,可以建立冗余较小、结构合理的数据库。如果有特殊情结,当然要特殊对待,数据库设计最重要的是看需求和性能,需求>性能>表结构。

所以不能一味的追求三范式建立数据库。


163、一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?

一般情况下,我们创建的表类型是InnoDB。

不重启MySQL,如果新增一条记录,id是8;

重启,ID是6;因为InnoDB表只把自增主键的最大ID记录在内存中,如果重启,已删除的最大ID会丢失。

如果表类型是MyISAM,重启之后,最大ID也不会丢失,ID是8;

InnoDB必须有主键(建议使用自增主键,不用UUID,自增主键索引查询效率高)、支持外键、支持事务、支持行级锁。

系统崩溃后,MyISAM很难恢复;

综合考虑,优先选择InnoDB,MySQL默认也是InnoDB。


164、如何获取当前数据库版本?

//MySQL,,mysql -v
select version();
//Oracle 
select * from v$version;

165、说一下 ACID 是什么?

ACID是数据库事务执行的四大基本要素,包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。

1、原子性

整个事务中的所有操作,要么全部完成,要不全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被roolback回滚到事务开始前的状态,就像这个事务从未执行过一样。

2、一致性

事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。

3、隔离性

隔离状态执行事务,使他们好像是系统在给定时间内执行的唯一操作。

如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性确保每一个事务在系统中认为只有自己在使用系统。这种属性称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。

4、持久性

一个成功的事务将永久的改变系统的状态。


166、char 和 varchar 的区别是什么?

  1. char的长度是固定的,varchar的长度的可变的;
  2. char的效率比varchar的效率高;
  3. char占用空间比varchar大,char在查询时需要使用trim;

167、float 和 double 的区别是什么?

1、float 和 double 的区别是什么?

(1)内存中占有的字节数不同

单精度浮点数在内存中占有4个字节;

双精度浮点数在内存中占有8个字节;

(2)有效数字位数不同

单精度浮点数有效数字8位;

双精度浮点数有效数字16位;

(3)数值取值范围不同

单精度浮点数的表示范围:-3.40E+38~3.40E+38

双精度浮点数的表示范围:-1.79E+308~-1.79E+308

(4)在程序中处理速度不同

一般来说,CPU处理单精度浮点数的速度比双精度浮点数的速度快

如果不声明,默认小数是double类型,如果想用float,要进行强转;

2、例如

float f = 1.3;会编译报错,正确的写法是float f = (float)1.3;或者float a = 1.3f;(f或F都可以不区分大小写)

3、注意

float是八位有效数字,第七位会四舍五入;

4、面试题

(1)java中3*0.1==0.3将会返回什么?true还是false?

答:返回false,因为浮点数不能完全精确的表示出来,一般会损失精度;

(2)java中float f = 3.4;是否正确?

答:不正确。因为3.4是双精度浮点数,将双精度赋给单精度属于向下转型,会造成精度损失,因此需要强制类型转换float=(float)3.4;或者写成float f = 3.4f;


168、Oracle分页sql

#不带排序的
SELECT * FROM (
SELECT ROWNUM AS rowno, t.* FROM worker t where ROWNUM <=20) table_alias 
WHERE table_alias.rowno > 10;


#带排序的
SELECT * FROM (
SELECT tt.*, ROWNUM AS rowno FROM (  
SELECT t.* FROM worker t ORDER BY wkid aSC) tt WHERE ROWNUM <= 20) table_alias 
WHERE table_alias.rowno >= 10;

169、数据库如何保证主键唯一性

1、主键约束

主键列上没有任何两行具有相同值(即重复值),不允许空(NULL);

2、唯一性约束

保证一个字段或者一组字段里的数据都与表中其它行的对应数据不同。和主键约束不同,唯一性约束允许为null,但是只能有一行;

3、唯一性索引

不允许具有索引值相同的行,从而禁止重复的索引和键值;

4、三者的区别

  • 约束是用来检查数据的正确性;
  • 索引是用来优化查询的;
  • 创建唯一性约束会创建一个约束和一个唯一性索引;
  • 创建唯一性索引只会创建一个唯一性索引;
  • 主键约束和唯一性约束都会创建一个唯一性索引。

170、如何设计数据库

1、数据库设计最起码要占用这个项目开发的40%以上的时间

2、数据库设计不仅仅停留在页面demo的表面

页面内容所需字段,在数据库设计中只是一部分,还有系统运转、模块交互、中转数据、表之间的联系等等所需要的字段,因此数据库设计绝对不是简单的基本数据存储,还有逻辑数据存储。

3、数据库设计完成后,项目80%的设计开发都要存在你的脑海中

每个字段的设计都要有他存在的意义,要清楚的知道程序中如何去运用这些字段,多张表的联系在程序中是如何体现的。

4、数据库设计时就要考虑效率和优化问题

数据量大的表示粗粒度的,会冗余一些必要字段,达到用最少的表,最弱的表关系去存储海量的数据。大数据的表要建立索引,方便查询。对于含有计算、数据交互、统计这类需求时,还有考虑是否有必要采用存储过程。

5、添加必要的冗余字段

像创建时间、修改时间、操作用户IP、备注这些字段,在每张表中最好都有,一些冗余的字段便于日后维护、分析、拓展而添加。

6、设计合理的表关联

若两张表之间的关系复杂,建议采用第三张映射表来关联维护两张表之间的关系,以降低表之间的直接耦合度。

7、设计表时不加主外键等约束关联,系统编码阶段完成后再添加约束性关联

8、选择合适的主键生成策略

数据库的设计难度其实比单纯的技术实现难很多,他充分体现了一个人的全局设计能力和掌控能力,最后说一句,数据库设计,很重要,很复杂。


171、性别是否适合做索引

区分度不高的字段不适合做索引,因为索引页是需要有开销的,需要存储的,不过这类字段可以做联合索引的一部分。


172、如何查询重复的数据

1、查询重复的单个字段(group by)

select 重复字段A, count(*) from 表 group by 重复字段A having count(*) > 1

2、查询重复的多个字段(group by)

select 重复字段A, 重复字段B, count(*) from 表 group by 重复字段A, 重复字段B having count(*) > 1

173、数据库一般会采取什么样的优化方法?

1、选取适合的字段属性

  • 为了获取更好的性能,可以将表中的字段宽度设得尽可能小。
  • 尽量把字段设置成not null
  • 执行查询的时候,数据库不用去比较null值。
  • 对某些省份或者性别字段,将他们定义为enum类型,enum类型被当做数值型数据来处理,而数值型数据被处理起来的速度要比文本类型块很多。

2、使用join连接代替子查询

3、使用联合union来代替手动创建的临时表

注意:union用法中,两个select语句的字段类型要匹配,而且字段个数要相同。

4、事务

要么都成功,要么都失败。

可以保证数据库中数据的一致性和完整性。事务以begin开始,commit关键字结束。

如果出错,rollback命令可以将数据库恢复到begin开始之前的状态。

事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方式为用户提供一种安全的访问方式,这样就可以保证用户的操作不被其他的用户干扰。

5、锁定表

尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在大应用中。

由于在事务执行的过程中,数据库会被锁定,因此其它用户只能暂时等待直到事务结束。

有的时候可以用锁定表的方法来获得更好的性能,

共享锁:其它用户只能看,不能修改

lock table person in share mode;

对于通过lock table 命令主动添加的锁来说,如果要释放它们,只需发出rollback命令即可。

6、使用外键

锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性,这个时候可以使用外键。

7、使用索引

索引是提高数据库查询速度的常用方法,尤其是查询语句中包含max()、min()、order by这些命令的时候,性能提高更为显著。

一般来说索引应该建在常用于join、where、order by的字段上。尽量不要对数据库中含有大量重复的值得字段建立索引。

8、优化的查询语句

在索引的字段上尽量不要使用函数进行操作。

尽量不要使用like关键字和通配符,这样做法很简单,但却是以牺牲性能为代价的。

避免在查询中进行自动类型转换,因为类型转换也会使索引失效。


174、索引怎么定义,分哪几种

  1. b-tree索引,如果不建立索引的情况下,oracle就自动给每一列都加一个B 树索引;
  2. normal:普通索引
  3. unique:唯一索引
  4. bitmap:位图索引,位图索引特定于只有几个枚举值的情况,比如性别字段;
  5. 基于函数的索引

175、mysql 的内连接、左连接、右连接有什么区别?

  1. 内连接,显示两个表中有联系的所有数据;
  2. 左链接,以左表为参照,显示所有数据,右表中没有则以null显示
  3. 右链接,以右表为参照显示数据,,左表中没有则以null显示

176、RabbitMQ的使用场景有哪些?

1、解决异步问题

例如用户注册,发送邮件和短信反馈注册成功,可以使用RabbitMQ消息队列,用户无需等待反馈。

2、服务间解耦

订单系统和库存系统,中间加入RabbitMQ消息队列,当库存系统出现问题时,订单系统依旧能正常使用,降低服务间耦合度。

3、秒杀系统

利用RabbitMQ的最大值,实现秒杀系统。


177、RabbitMQ有哪些重要的角色?有哪些重要的组件?

1、RabbitMQ有哪些重要的角色?

客户端、RabbitMQ、服务端。

2、有哪些重要的组件?

(1)connectionFactory(连接管理器)

应用程序与RabbitMQ之间建立连接的管理器。

(2)Channel(信道)

消息推送使用的信道。

(3)RoutingKey(路由键)

用于把生产者的数据分配到交换机上。

(4)Exchange(交换机)

用于接受和分配消息。

(5)BindKey(绑定键)

用于把交换机的消息绑定到队列上

(6)Queue(队列)

用于存储生产者消息。


178、RabbitMQ中 vhost 的作用是什么?

vhost可以理解为mini版的RabbitMQ,其内部均含有独立的交换机、绑定、队列,最重要的是拥有独立的权限系统,可以做到vhost范围内的用户控制。从RabbitMQ全局考虑,不同的应用可以跑在不同的vhost上,作为不同权限隔离的手段。


179、说一下 jvm 的主要组成部分?及其作用?

JVM包括类加载子系统、堆、方法区、栈、本地方法栈、程序计数器、直接内存、垃圾回收器、执行引擎。

1、类加载子系统

类加载子系统负责加载class信息,加载的类信息存放于方法区中。

2、直接内存

直接内存是在Java堆外的、直接向系统申请的内存空间。访问直接内存的速度会由于Java堆。出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。

3、垃圾回收器

垃圾回收器可以对堆、方法区、直接内存进行回收。

4、执行引擎

执行引擎负责执行虚拟机的字节码,虚拟机会使用即时编译技术将方法编译成机器码后再执行。


180、说一下 jvm 运行时数据区?

运行时数据区包括堆、方法区、栈、本地方法栈、程序计数器。

1、堆

堆解决的是对象实例存储的问题,垃圾回收器管理的主要区域。

2、方法区

方法区可以认为是堆的一部分,用于存储已被虚拟机加载的信息,常量、静态变量、即时编译器编译后的代码。

3、栈

栈解决的是程序运行的问题,栈里面存的是栈帧,栈帧里面存的是局部变量表、操作数栈、动态链接、方法出口等信息。

(1)栈帧

每个方法从调用到执行的过程就是一个栈帧在虚拟机栈中入栈到出栈的过程。

(2)局部变量表

用于保存函数的参数和局部变量。

(3)操作数栈

操作数栈又称操作栈,大多数指令都是从这里弹出数据,执行运算,然后把结果压回操作数栈。

4、本地方法栈

与栈功能相同,本地方法栈执行的是本地方法,一个Java调用非Java代码的接口。

5、程序计数器(PC寄存器)

程序计数器中存放的是当前线程所执行的字节码的行数。JVM工作时就是通过改变这个计数器的值来选取下一个需要执行的字节码指令。


181、什么是类加载器,类加载器有哪些?

1、什么是类加载器?

类加载器负责加载所有的类,其为所有被载入内存的类生成一个java.lang.Class实例对象。

2、类加载器有哪些?

JVM有三种类加载器:

(1)启动类加载器

该类没有父加载器,用来加载Java的核心类,启动类加载器的实现依赖于底层操作系统,属于虚拟机实现的一部分,它并不继承自java.lang.classLoader。

(2)扩展类加载器

它的父类为启动类加载器,扩展类加载器是纯java类,是ClassLoader类的子类,负责加载JRE的扩展目录。

(3)应用程序类加载器

它的父类为扩展类加载器,它从环境变量classpath或者系统属性java.lang.path所指定的目录中加载类,它是自定义的类加载器的父加载器。


182、说一下类加载的执行过程?

当程序主动使用某个类时,如果该类还未被加载到内存中,JVM会通过加载、连接、初始化3个步骤对该类进行类加载。

1、加载

加载指的是将类的class文件读入到内存中,并为之创建一个java.lang.Class对象。

类的加载由类加载器完成,类加载器由JVM提供,开发者也可以通过继承ClassLoader基类来创建自己的类加载器。

通过使用不同的类加载器可以从不同来源加载类的二进制数据,通常有如下几种来源:

  1. 从本地文件系统加载
  2. 从jar包加载
  3. 通过网络加载
  4. 把一个Java源文件动态编译,并执行加载

2、连接

当类被加载之后,系统为之生成一个对应的Class对象,接着进入连接阶段,连接阶段负责将类的二进制数据合并到JRE中。

类连接又可分为三个阶段:

(1)验证

文件格式验证

元数据验证

字节码验证

符号引用验证

(2)准备

为类的静态变量分配内存,并设置默认初始值。

(3)解析

将类的二进制数据中的符号引用替换成直接引用。

3、初始化

为类的静态变量赋予初始值。


183、JVM的类加载机制是什么?

JVM类加载机制主要有三种:

1、全盘负责

类加载器加载某个class时,该class所依赖的和引用其它的class也由该类加载器载入。

2、双亲委派

先让父加载器加载该class,父加载器无法加载时才考虑自己加载。

3、缓存机制

缓存机制保证所有加载过的class都会被缓存,当程序中需要某个class时,先从缓存区中搜索,如果不存在,才会读取该类对应的二进制数据,并将其转换成class对象,存入缓存区中。

这就是为什么修改了class后,必须重启JVM,程序所做的修改才会生效的原因。


184、什么是双亲委派模型?

如果一个类收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器执行,如果父加载器还存在其父加载器,则进一步向上委托,依次递归,请求将最终到达顶层的启动类加载器,如果父类加载器可以完成父加载任务,就成功返回,如果父加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派模型。

双亲委派模式的优势:

避免重复加载;

考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委派模式传递到启动加载器,而启动加载器在核心Java API中发现同名的类,发现该类已经被加载,就不会重新加载网络传递的Integer类,而直接返回已加载过的Integer.class,这样可以防止核心API库被随意篡改。


185、怎么判断对象是否可以被回收?

1、引用计数算法

(1)判断对象的引用数量

通过判断对象的引用数量来决定对象是否可以被回收;

每个对象实例都有一个引用计数器,被引用+1,完成引用-1;

任何引用计数为0的对象实例可以被当做垃圾回收;

(2)优缺点

优点:执行效率高,程序受影响较小;

缺点:无法检测出循环引用的情况,导致内存泄漏;

2、可达性分析算法

通过判断对象的引用链是否可达来决定对象是否可以被回收。

如果程序无法再引用该对象,那么这个对象肯定可以被回收,这个状态称为不可达。

那么不可达状态如何判断呢?

答案是GC roots,也就是根对象,如果一个对象无法到达根对象的路径,或者说从根对象无法引用到该对象,该对象就是不可达的。

以下三种对象在JVM中被称为GC roots,来判断一个对象是否可以被回收。

(1)虚拟机栈的栈帧

每个方法在执行的时候,JVM都会创建一个相应的栈帧(操作数栈、局部变量表、运行时常量池的引用),当方法执行完,该栈帧就从栈中弹出,这样一来,方法中临时创建的独享就不存在了,或者说没有任何GC roots指向这些临时对象,这些对象在下一次GC的时候便会被回收。

(2)方法区中的静态属性

静态属性数据类属性,不属于任何实例,因此该属性自然会作为GC roots。这要这个class在,该引用指向的对象就一直存在,class也由被回收的时候。

class何时会被回收?

  1. 堆中不存在该类的任何实例
  2. 加载该类的classLoader已经被回收
  3. 该类的java.lang.class对象没有在任何地方被引用,也就是说无法通过反射访问该类的信息

(3)本地方法栈引用的对象


186、说一下 jvm 有哪些垃圾回收算法?

1、对象是否已死算法

  • 引用计数器算法
  • 可达性分析算法

2、GC算法

(1)标记清除算法

如果对象被标记后进行清除,会带来一个新的问题--内存碎片化。如果下次有比较大的对象实例需要在堆上分配较大的内存空间时,可能会出现无法找到足够的连续内存而不得不再次触发垃圾回收。

(2)复制算法(Java堆中新生代的垃圾回收算法)

  1. 先标记待回收内存和不用回收内存;
  2. 将不用回收的内存复制到新的内存区域;
  3. 就的内存区域就可以被全部回收了,而新的内存区域也是连续的;

缺点是损失部分系统内存,因为腾出部分内存进行复制。

(3)标记压缩算法(Java堆中老年代的垃圾回收算法)

对于新生代,大部分对象都不会存活,所以复制算法较高效,但对于老年代,大部分对象可能要继续存活,如果此时使用复制算法,效率会降低。

标记压缩算法首先还是标记,将不用回收的内存对象压缩到内存一端,此时即可清除边界处的内存,这样就能避免复制算法带来的效率问题,同时也能避免内存碎片化的问题。

老年代的垃圾回收算法称为“Major GC”。


187、说一下 jvm 有哪些垃圾回收器?

说一下 jvm 有哪些垃圾回收器?


188、JVM栈堆概念,何时销毁对象

  1. 类在程序运行的时候就会被加载,方法是在执行的时候才会被加载,如果没有任何引用了,Java自动垃圾回收,也可以用System.gc()开启回收器,但是回收器不一定会马上回收。
  2. 静态变量在类装载的时候进行创建,在整个程序结束时按序销毁;
  3. 实例变量在类实例化对象时创建,在对象销毁的时候销毁;
  4. 局部变量在局部范围内使用时创建,跳出局部范围时销毁;

189、新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

新生代回收器:Serial、ParNew、Parallel Scavenge

老年代回收器:Serial Old、Parallel Old、CMS

新生代回收器一般采用的是复制算法,复制算法效率较高,但是浪费内存;

老生代回收器一般采用标记清楚算法,比如最常用的CMS;


190、详细介绍一下 CMS 垃圾回收器?

CMS 垃圾回收器是Concurrent Mark Sweep,是一种同步的标记-清除,CMS分为四个阶段:

初始标记,标记一下GC Root能直接关联到的对象,会触发“Stop The World”;

并发标记,通过GC Roots Tracing判断对象是否在使用中;

重新标记,标记期间产生对象的再次判断,执行时间较短,会触发“Stop The World”;

并发清除,清除对象,可以和用户线程并发进行;


191、简述分代垃圾回收器是怎么工作的?

分代回收器分为新生代和老年代,新生代大概占1/3,老年代大概占2/3;

新生代包括Eden、From Survivor、To Survivor;

Eden区和两个survivor区的 的空间比例 为8:1:1 ;

垃圾回收器的执行流程:

  1. 把 Eden + From Survivor 存活的对象放入 To Survivor 区;
  2. 清空 Eden + From Survivor 分区,From Survivor 和 To Survivor 分区交换;
  3. 每次交换后存活的对象年龄+1,到达15,升级为老年代,大对象会直接进入老年代;
  4. 老年代中当空间到达一定占比,会触发全局回收,老年代一般采取标记-清除算法;

192、Redis是什么?

Redis是一个key-value存储系统,它支持存储的value类型相对更多,包括string、list、set、zset(sorted set --有序集合)和hash。这些数据结构都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,Redis支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中,Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。


193、Redis都有哪些使用场景?

  1. Redis是基于内存的nosql数据库,可以通过新建线程的形式进行持久化,不影响Redis单线程的读写操作
  2. 通过list取最新的N条数据
  3. 模拟类似于token这种需要设置过期时间的场景
  4. 发布订阅消息系统
  5. 定时器、计数器

194、Redis有哪些功能?

1、基于本机内存的缓存

当调用api访问数据库时,假如此过程需要2秒,如果每次请求都要访问数据库,那将对服务器造成巨大的压力,如果将此sql的查询结果存到Redis中,再次请求时,直接从Redis中取得,而不是访问数据库,效率将得到巨大的提升,Redis可以定时去更新数据(比如1分钟)。

2、如果电脑重启,写入内存的数据是不是就失效了呢,这时Redis还提供了持久化的功能。

3、哨兵(Sentinel)和复制

Sentinel可以管理多个Redis服务器,它提供了监控、提醒以及自动的故障转移功能;

复制则是让Redis服务器可以配备备份的服务器;

Redis也是通过这两个功能保证Redis的高可用;

4、集群(Cluster)

单台服务器资源总是有上限的,CPU和IO资源可以通过主从复制,进行读写分离,把一部分CPU和IO的压力转移到从服务器上,但是内存资源怎么办,主从模式只是数据的备份,并不能扩充内存;

现在我们可以横向扩展,让每台服务器只负责一部分任务,然后将这些服务器构成一个整体,对外界来说,这一组服务器就像是集群一样。


195、Redis支持的数据类型有哪些?

  1. 字符串
  2. hash
  3. list
  4. set
  5. zset

196、Redis取值存值问题

1、先把Redis的连接池拿出来

JedisPool pool = new JedisPool(new JedisPoolConfig(),"127.0.0.1");
Jedis jedis = pool.getResource();

2、存取值

jedis.set("key","value");
jedis.get("key");
jedis.del("key");
//给一个key叠加value
jedis.append("key","value2");//此时key的值就是value + value2;
//同时给多个key进行赋值:
jedis.mset("key1","value1","key2","value2");

3、对map进行操作

Map<String,String> user = new HashMap();
user.put("key1","value1");
user.put("key2","value2");
user.put("key3","value3");
jedis.hmset("user",user);
//取出user中key1 
List<String> nameMap = jedis.hmget("user","key1");
//删除其中一个键值
jedis.hdel("user","key2");
//是否存在一个键
jedis.exists("user");
//取出所有的Map中的值:
Iterator<String> iter = jedis.hkeys("user").iterator();
while(iter.next()){
    jedis.hmget("user",iter.next());
}

197、Redis为什么是单线程的?

  1. 代码更清晰,处理逻辑更简单;
  2. 不用考虑各种锁的问题,不存在加锁和释放锁的操作,没有因为可能出现死锁而导致的性能问题;
  3. 不存在多线程切换而消耗CPU;
  4. 无法发挥多核CPU的优势,但可以采用多开几个Redis实例来完善;

198、Redis真的是单线程的吗?

Redis6.0之前是单线程的,Redis6.0之后开始支持多线程;

redis内部使用了基于epoll的多路服用,也可以多部署几个redis服务器解决单线程的问题;

redis主要的性能瓶颈是内存和网络;

内存好说,加内存条就行了,而网络才是大麻烦,所以redis6内存好说,加内存条就行了;

而网络才是大麻烦,所以redis6.0引入了多线程的概念,

redis6.0在网络IO处理方面引入了多线程,如网络数据的读写和协议解析等,需要注意的是,执行命令的核心模块还是单线程的。


199、Redis持久化有几种方式?

redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。

RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;

AOF,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。

其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。

如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样。


200、Redis和 memecache 有什么区别?

1、Redis相比memecache,拥有更多的数据结构和支持更丰富的数据操作。

(1)Redis支持key-value,常用的数据类型主要有String、Hash、List、Set、Sorted Set。

(2)memecache只支持key-value。

2、内存使用率对比,Redis采用hash结构来做key-value存储,由于其组合式的压缩,其内存利用率会高于memecache。

3、性能对比:Redis只使用单核,memecache使用多核。

4、Redis支持磁盘持久化,memecache不支持。

Redis可以将一些很久没用到的value通过swap方法交换到磁盘。

5、Redis支持分布式集群,memecache不支持。


201、Redis支持的 java 客户端都有哪些?

Redisson、Jedis、lettuce 等等,官方推荐使用 Redisson。


202、jedis 和 redisson 有哪些区别?

Jedis 和 Redisson 都是Java中对Redis操作的封装。Jedis 只是简单的封装了 Redis 的API库,可以看作是Redis客户端,它的方法和Redis 的命令很类似。Redisson 不仅封装了 redis ,还封装了对更多数据结构的支持,以及锁等功能,相比于Jedis 更加大。但Jedis相比于Redisson 更原生一些,更灵活。


203、什么是缓存穿透?怎么解决?

1、缓存穿透

一般的缓存系统,都是按照key去缓存查询,如果不存在对用的value,就应该去后端系统查找(比如DB数据库)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

2、怎么解决?

对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert之后清理缓存。

对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该Bitmap过滤。

3、缓存雪崩

当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,会给后端系统带来很大的压力,导致系统崩溃。

4、如何解决?

  1. 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其它线程等待;
  2. 做二级缓存;
  3. 不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀;

204、怎么保证缓存和数据库数据的一致性?

1、淘汰缓存

数据如果为较为复杂的数据时,进行缓存的更新操作就会变得异常复杂,因此一般推荐选择淘汰缓存,而不是更新缓存。

2、选择先淘汰缓存,再更新数据库

假如先更新数据库,再淘汰缓存,如果淘汰缓存失败,那么后面的请求都会得到脏数据,直至缓存过期。

假如先淘汰缓存再更新数据库,如果更新数据库失败,只会产生一次缓存穿透,相比较而言,后者对业务则没有本质上的影响。

3、延时双删策略

如下场景:同时有一个请求A进行更新操作,另一个请求B进行查询操作。

  1. 请求A进行写操作,删除缓存
  2. 请求B查询发现缓存不存在
  3. 请求B去数据库查询得到旧值
  4. 请求B将旧值写入缓存
  5. 请求A将新值写入数据库

次数便出现了数据不一致问题。采用延时双删策略得以解决。

public void write(String key,Object data){
    redisUtils.del(key);
    db.update(data);
    Thread.Sleep(100);
    redisUtils.del(key);
}

这么做,可以将1秒内所造成的缓存脏数据,再次删除。这个时间设定可根据俄业务场景进行一个调节。

4、数据库读写分离的场景

两个请求,一个请求A进行更新操作,另一个请求B进行查询操作。

  1. 请求A进行写操作,删除缓存
  2. 请求A将数据写入数据库了,
  3. 请求B查询缓存发现,缓存没有值
  4. 请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
  5. 请求B将旧值写入缓存
  6. 数据库完成主从同步,从库变为新值

依旧采用延时双删策略解决此问题。


205、Redis,什么是缓存穿透?怎么解决?

1、缓存穿透

一般的缓存系统,都是按照key去缓存查询,如果不存在对用的value,就应该去后端系统查找(比如DB数据库)。一些恶意的请求会故意查询不存在的key,请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。

2、怎么解决?

对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert之后清理缓存。

对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该Bitmap过滤。

3、缓存雪崩

当缓存服务器重启或者大量缓存集中在某一时间段失效,这样在失效的时候,会给后端系统带来很大的压力,导致系统崩溃。

4、如何解决?

  1. 在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其它线程等待;
  2. 做二级缓存;
  3. 不同的key,设置不同的过期时间,让缓存失效的时间尽量均匀;

206、Redis怎么实现分布式锁?

使用Redis实现分布式锁

redis命令:set users 10 nx ex 12 原子性命令

//使用uuid,解决锁释放的问题
@GetMapping
public void testLock() throws InterruptedException {
    String uuid = UUID.randomUUID().toString();
    Boolean b_lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
    if(b_lock){
        Object value = redisTemplate.opsForValue().get("num");
        if(StringUtils.isEmpty(value)){
            return;
        int num = Integer.parseInt(value + "");
        redisTemplate.opsForValue().set("num",++num);
        Object lockUUID = redisTemplate.opsForValue().get("lock");
        if(uuid.equals(lockUUID.toString())){
            redisTemplate.delete("lock");