String、StringBuffer、StringBuilder区别
string 类中使用 final关键字字符数组保存字符串,所以 String 对象是不可变的。而StringBuilder 与 StringBufer 也是使用字符数组保存字符
char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。线程安全性
String中的对象是不可变的,也就可以理解为常量,线程安全。StringBuilder 与 StringBuffer 定义了一些字符串的基本操作.
StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
对于三者使用的总结:
操作少量的数据 = String
单线程大量字符串操作 = StringBuilder
多线程大量字符串操作 = StringBuffer
spring bean的线程安全问题
spring中的bean默认都是单例的,ioc容器中一个类只会存在一个实例对象。这种设计是怎么保证线程安全的?
一般不会出现线程安全问题。在spring中,绝大部分bean都是无状态的,因此即使这些bean默认是单例的也不会出现线程安全问题的。比如controller、service、dao这些类,这些类里面通常不会含有成员变量,因此它们被设计成单例的。如果这些类中定义了实例变量,就线程不安全了,所以尽量避免定义实例变量。
对于spring中有状态的bean,比如RequestContextHolder、TransactionSynchronizationManager,LocaleContextHolder,为什么也能够设计成单例的呢? 它是怎么保证线程安全的?
对于有状态的bean,spring采用ThreadLocal进行处理,使它们成为线程安全可以共享的对象。
对于有状态的bean,也可以使用原型模式 (prototype),每次使用时都会重新生成一个对象,解决了线程不安全的问题。
索引失效的场景?
1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)
2.索引字段的值不能有null值,有null值会使该列索引失效
3.对于多列索引,不是使用的第一部分,则不会使用索引(最左原则)
4.like查询以%开头
5.如果列类型是字符串,那一定要在条件中将数据使用单引号引用起来,否则不使用索引
6.在索引的列上使用表达式或者函数会使索引失效
ArrayList 和 LinkedList的实现原理和区别?
1.数据结构实现:ArrayList是动态数组的数据结构实现,而LinkedList是双向链表的数据结构实现。
2.随机访问效率:ArrayList比LinkedList在随机访问的时候效率要高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。
3.增加和删除效率:在非首尾的增加和删除操作,LinkedList要比ArrayList效率要高,因为ArrayList增删操作要影响数组内的其他数据的下标。
4.内存空间占用:LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
5.线程安全:ArrayList和LinkedList都是不同步的,也就是不保证线程安全;
Spring Bean的作用域有哪些? 默认是哪种?
默认:单例 singleton
bean作用域:
singleton 单例
prototype多例
如果是web项目:
request
session
globlaSession
什么是MVCC
MVCC是多版本并发控制 Multi-Version Concurrent Contrl。
多版本意思是指数据库中一条数据有多个版本同时存在,在某个事务对其进行具体操作的时候,是需要查看这一条记录的隐藏列事务版本的id,比对事务id并根据事物的隔离级别从而去判断是哪个版本的数据。
准确的说,MVCC多版本并发控制指的是“维持一个数据的多个版本,使得读写操作没有冲突”这么一个概念。
是否了解缓存穿透、击穿、雪崩?
缓存穿透
概述:指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,可能导致DB挂掉。
解决方案:
1、查询返回的数据为空,仍把这个空结果进行缓存,但过期时间会比较短
2、布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对DB的查询
缓存击穿
概述:对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好这时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请可能会瞬间把DB压垮。
解决方案:
1、使用互斥锁:当缓存失效时,不立即去load db,先使用如Redis的setnx去设置一个互斥锁,当操作成功返回时再进行load db的操作并回设缓存,否则重试get缓存的方法
2、可以设置当前key逻辑过期,大概是思路如下:
①:在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
②:当查询的时候,从redis取出数据后判断时间是否过期
③:如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新
缓存雪崩
概述:设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。与缓存击穿的区别:雪崩是很多key,击穿是某一个key缓存。
解决方案:
将缓存失效时间分散开,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
线程的生命周期(线程的状态)?
新建状态:New
就绪状态:
运行状态:Runnable
阻塞状态:(Waiting)(Time_Waiting)(Blocked)
死亡状态:(Terminated)
说一说Collection结构图
Spring Bean依赖注入(DI)的方式有哪些?
setter注入:通过set方法注入依赖bean
构造器注入:通过构造器注入依赖bean
注解注入:
@AutoWired
@Resource
什么是小表驱动大表?
mysql在两表联查时,采用的是循环连接方式,即先查询出A表的数据(A表称为驱动表),以A表的数据作为结果集,根据关联条件循环查询B表数据
如果A表数据量过大,循环的次数就会越多,所以两表联查一定要让小表作为驱动表性能更好些
什么是进程?什么是线程?
进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程,但一个进程一般有多个线程。
进程在运行过程中,需要拥有独立的内存单元,否则如果申请不到,就会挂起。而多个线程能共享内存资源,这样就能降低运行的门槛,从而效率更高。
线程是cpu调度和分派的基本单位,在实际开发过程中,一般是考虑多线程并发。
如何实现数组和List之间的转换
//数组转 List ,使用 JDK 中 java.util.Arrays 工具类的 asList 方法 public static void testArray2List() { String[] strs = new String[]{"aaa", "bbb", "ccc"}; List<String> list = Arrays.asList(strs); for (String s : list) { System.out.println(s); } } //List 转数组 public static void testList2Array() { List<String> list = Arrays.asList("aaa", "bbb", "ccc"); String[] array = list.toArray(new String[list.size()]); for (String s : array) { System.out.println(s); } }
数组转List,使用JDK中java.util.Arrays工具类的asList方法
List转数组,使用List的toArray方法。无参toArray方法返回Object数组,传入初始化长度的数组对象,返回该对象数组
Autowired 和 Resource注解有什么区别?
1、@Autowired与@Resource都可以用来装配bean.都可以写在字段上,或写在setter方法上
2、@Autowired默认按类型装配 (这个注解是属于spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如: @Autowired(required=false),如果我们想使用名称装配可以搭配@Qualifier注解进行使用
3、@Resource (这个注解属于J2EE的),默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
什么是回表查询?
回表就是先通过数据库索引扫描出数据所在的行,再通过行主键id取出索引中未提供的数据,即基于非主键索引的查询需要多扫描一棵索引树。
#{}和${}的区别是什么
#{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值
Mybatis在处理${}时,就是把${}替换成变量的值
使用#{}可以有效的防止SQL注入,提高系统安全性
使用建议:
能用 #{} 的地方就用 #{},不用或少用 ${}
表名作参数时,必须用 ${}。如: select * from ${tableName}
order by 时,必须用 ${}。如: select * from t_user order by ${columnName)
使用的隔离级别有哪些? 不同的隔离级别会有什么问题?
未提交读(READ UNCOMMITTED):这个隔离级别下,其他事务可以看到本事务没有提交的部分修改。因此会造成脏读的问题(读取到了其他事务未提交的部分,而之后该事务进行了回滚)。这个级别的性能没有足够大的优势,但是又有很多的问题,因此很少使用.
已提交读(READ COMMITTED):其他事务只能读取到本事务已经提交的部分。这个隔离级别有不可重复读的问题,在同一个事务内的两次读取,拿到的结果竟然不一样,因为另外一个事务对数据进行了修改。
可重复读(REPEATABLE READ):可重复读隔离级别解决了上面不可重复读的问题(看名字也知道),但是不能完全解决幻读。MySql默认的事务隔离级别就是:REPEATABLE READ
可串行化(SERIALIZABLE):这是最高的隔离级别,可以解决上面提到的所有问题,因为他强制将所以的操作串行执行,这会导致并发性能极速下降,因此也不是很常用。
是否了解SpringBean的循环依赖问题?(三级缓存)
A对象 依赖 B对象 B对象 依赖 A对象
如果对象都是多例对象 会报循环依赖异常
如果是单例对象:
如果是通过构造器依赖的属性,会报循环依赖异常
如果是通过属性依赖产生的循环依赖,默认允许循环依赖
Spring是如何解决的?三级缓存
第一级缓存: 也叫单例池,存放已经经历了完整生命周期的Bean对象
第二级缓存: 存放早期暴露出来的Bean对象,实例化以后,就把对象放到这个Map中。(Bean可能只经过实例化,属性还未填充)。
第三级缓存:存放早期暴露的Bean的工厂。(用于解决 代理对象问题)
事务ACID的特性是什么?
A=Atomicity原子性:就是上面说的,要么全部成功,要么全部失败,不可能只执行一部分操作。
C=Consistency一致性:系统(数据库)总是从一个一致性的状态转移到另一个一致性的状态,不会存在中间状态。
I=Isolation隔离性:通常来说:一个事务在完全提交之前,对其他事务是不可见的.注意前面的通常来说加了红色,意味着有例外情况。
D=Durability持久性:一旦事务提交,那么就永远是这样子了,哪怕系统崩溃也不会影响到这个事务的结果。
什么是脏读?幻读?不可重复读?
脏读(Dirty read),当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
幻读(Phantom read):幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复读(Unrepeatableread):指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
说一下HashMap的实现原理?
HashMap的数据结构:底层使用hash表数据结构,即数组和链表的结合体。
HashMap基于Hash算法实现的
1.当我们往HashMap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标
2.存储时,如果出现hash值相同的key,此时有两种情况。
如果key相同,则覆盖原始值;
如果key不同(出现冲突),则将当前的key-value放入链表中
3.获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到
对应值。
HashMap JDK1.8之前
JDK1.8之前采用的是拉链法。拉链法:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
HashMap JDK1.8之后
相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。扩容resize( )时,红黑树拆分成的树的结点数小于等于临界值6个,则退化成链表。
SpringMVC的运行流程?
SpringMVC运行流程
(1) 用户发送请求至前端控制器DispatcherServlet;
(2)DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4) DispatcherServlet 调用 HandlerAdapter处理器适配器;
(5) HandlerAdapter 经过适配调用,具体处理器(Handler,也叫后端控制器)
(6) Handler执行完成返回ModelAndView;
(7) HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8) DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9) ViewResolver解析后返回具体View;
(10) DispatcherServlet对View进行渲染视图 (即将模型数据填充至视图中)
(11) DispatcherServlet响应用户。
什么是组合索引?什么是最左匹配原则?
建立三个字段的联合索引
联合索引 (a,b,c) 相当于建立了索: (a),(a,b),(a,b,c)
左侧的索引生效后,后面的索引才会生效,如果左侧的索引遇到了返回查询,那么左侧索引会生效 右侧索引不会生效
什么是线程安全问题?
多线程操作共享变量,导致访问数据出问题。
出现线程安全问题的条件:
有多个线程
有共享数据
其中一个线程修改了共享数据
SpringBoot中如何读取自定义的配置?
@Value
@ConfigurationProperties(prefix = "xxl.job")
Filter Interceptor、AOP的区别?
AOP使用的主要是动态代理
过滤器使用的主要是函数回调
拦截器使用是反射机制。
一个请求过来,先进行过滤器处理,看程序是否受理该请求。过滤器放过后 程序中的拦截器进行处理,处理完后进入,被AOP动态代理重新编译过的主要业务类进行处理。
Filter: 和框架无关,可以控制最初的http请求,但是更细一点的类和方法控制不了
Interceptor: 可以控制请求的控制器和方法,但控制不了请求方法里的参数(用于处理页面提交的请求响应并进行处理,例如做国际化,做主题更换,过滤等)。
Aspect: 可以自定义切入的点,有方法的参数,但是拿不到http请求,可以通过其他方式如RequestContextHolder获得。
都可以实现权限检查,日志记录。不同的是使用的范围不同,规范不同,深度不同
BeanFactory和FactoryBean区别
BeanFactory是Spring提供的存放Bean的工厂,FactoryBean是一个可生产Bean的工厂Bean。