1.请简要谈⼀下Java中的equals与==的区别?
(1) ==是运算符,而equals()是方法
(2) 基本数据类型中,==比较的是他们的数据是否相等,而在引用数据类型中==比较的是地址值;equals()方法更多的比较的是值的具体内容,前提是让要比较的双方重写equals()方法,否则,调用的是Object类中的equals(),而Object类中的equals()就是调用的==运算符;在包装类,以及特殊的 类中例如String,Date,File... ,底层默认重写了equals(),所以可以直接比较;
2. JDK8中的HashMap做了什么改变?
1> 由原来的 数组 + 链表 ,换为 数组 + 链表 + 红黑树
2> jdk7中实例化HashMap时底层会创建一个长度为16的entry[]数组,而jdk8中实例化时不会创建数组,而在put第一个键值对的时候才会创建长度为16 node[]的数组
3>在添加数据时,通过hashCode值的特定算法算出在数组中存放的位置,如果此位置没有元素,则添加成功,如果有元素,则以链表的方式存储在当前位置,这里jdk7和8有区别,7是头插,8是尾插;头插效率高,而尾插需要遍历,效率低一点;
4> jdk8中当链表的长度 > 8,并且数组长度大于64的时候,该列链表转换为红黑树存储
3. 请按顺序选出SpringMVC中 DispatcherServlet 正确的⼯作流程
A:当⽤户向服务器发送请求时,会被 DispatcherServlet 拦截。
B:DispatcherServlet 根据 ModelAndView 对象来调⽤适合的视图解析器 ViewResolver。
C:DispatherServlet 解析⽤户访问的 URL,并调⽤处理器映射器 HandlerMapping。
D:处理器映射器 HandlerMapping 映射到对应的后端处理器 Handler(注 意这⾥只是找到了对应的 Controller 类,并没有执其中的⽅法),Handler 对象以及 Handler 对象相关的拦截器对象会被封装到 HandlerExecutionChain 对象中返回给DispatcherServlet。
E:ViewResolver 解析 Model 和 View 返回具体的 view 给 DispatcherServlet。
F:DispatcherServlet 对 view 进⾏渲染,返回具体的视图给客户端显示, 如 JSP,JSON、XML、PDF等。
G:DispatcherServlet 根据后端处理器 Handler 对象来调⽤适合的处理器 适配器。
H:HandlerAdapter 调⽤ Handler 对象执⾏ Handler 中的⽅法,在 Handler 的⽅法中,可以做⼀些额外的⼯作,如消息转换(如 JSON、XML 和 Java 对象的互转)、数据转换(如 String 和 Integer、Double的互 转)、数据格式化(如⽇期)、数据校验(后端校验),最终返回 ModelAndView 对象给 DispatcherServlet,该对象包含视图名和数据模型。
A > C > D > G > H > B > E > F
4.MySQL的ACID分别代表什么?
原子性(Atomicity,或称不可分割性)
一致性(Consistency)
隔离性(Isolation)
持久性(Durability)
原子性:原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
一致性:事务必须使数据库从一个一致性状态变换为另一个一致性状态
隔离性:事务的隔离性是指一个事务的执行不能被其他事务干扰,即事务内部的操作和使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间具有隔离性
持久性:一个事务一旦被提交,他对数据库中的数据的改变是永久性的,接下来的其他操作或数据库故障不应该对他有任何影响
5. Redis的缓冲异常有哪些以及应对⽅案,能简要描述⼀下吗?
缓存穿透
缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。
非法请求的限制,主要是指参数校验、鉴权校验等,从而一开始就把大量的非法请求拦截在外,这在实际业务开发中是必要的手段。
缓存空值或者默认值,如果从缓存取不到的数据,在数据库中也没有取到,那我们仍然把这个空结果进行缓存,同时设置一个较短的过期时间。通过这个设置的默认值存放到缓存,这样第二次到缓存中获取就有值了,而不会继续访问数据库,可以防止有大量恶意请求是反复用同一个key进行攻击。
使用布隆过滤器快速判断数据是否存在。那什么是布隆过滤器呢,简单来说,就是可以引入了多个相互独立的哈希函数,保证在给定的空间和误判率下,完成元素判重。因为我们知道,存在hash碰撞这样一种情况,那如果只使用一个hash函数,则碰撞冲突的概率明显会变大,那为了减少这种冲突,我们可以多引入几个hash函数,而布隆过滤器算法的核心思想就是利用多个不同的hash函数来解决这样一种冲突。它的优点是空间效率高,查询时间短,远超其他算法,而它的缺点就是会存在一定的误识别率,它不能完全保证请求过来的key,通过布隆过滤器的校验,就一定有这个数据,毕竟理论上还是会存在冲突情况,无论概率多小。但是,只要没有通过布隆过滤器的校验,那么这个key就一定不存在,只要利用这一点其实就已经可以过滤掉大部分不存在的key的请求了,在正常场景下已然足够了。
缓存击穿
在缓存失效的瞬间,有大量线程来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
简单粗暴的对热点数据不设置过期时间,这样不会过期,自然也就不会出现上述情况了,如果后续想清理,可以通过后台进行清理。
添加互斥锁,即当过期之后,除了请求过来的第一个查询的请求可以获取到锁请求到数据库,并再次更新到缓存中,其他的会被阻塞住,直到锁被释放,同时新的缓存也被更新上去了,后续请求又会请求到缓存上,这样就不会出现缓存击穿了。
缓存雪崩
由于缓存层承载着大量请求,有效地保护了存储层,但是如果缓存层由于某些原因不可用(宕机)或者大量缓存由于超时时间相同在同一时间段失效(大批key失效/热点数据失效),大量请求直接到达存储层,存储层压力过大导致系统雪崩。
实际设置过期时间时,应当尽量避免大量key同时过期的场景,如果真的有,那就通过随机、微调、均匀设置等方式设置过期时间,从而避免同一时间过期。
添加互斥锁,使得构建缓存的操作不会在同一时间进行。
双key策略,主key是原始缓存,备key为拷贝缓存,主key失效时,可以访问备key,主key缓存失效时间设置为短期,备key设置为长期。
后台更新缓存策略,采用定时任务或者消息队列的方式进行redis缓存更新或移除等。
6. hashCode与equals
hashCode介绍: 作用是获取哈希码,也称为散列码,它实际上是返回一个int整数,这个哈希吗的作用是确定该对象在哈希表中的索引位置,hashCode()定义在jdk的Object类中,java中的任何类都包含有hashCode函数.
为什么要有hashCode(): 以HashSet如何检查重复为例来说明为什么要有hashCode()
对象加入HashSet时,HashSet会先计算对象的hashCode值来判断对象加入的位置,看该位置是否有值,如果没有添加成功,如果有,则调用equals()来检查两个对象是否真的相同,如果两者相同,HashSet就不会让其加入操作成功.如果不同的话,就会重新散列到其他位置,这样就大大减少了equals的次数,相应就大大提高了执行速度;
如果两个对象相等,其hashCode也一定是相等的
两个对象相等,对两个对象分别调用equals方法都返回true
两个对象有相同的hashCode值,他们也不一定相等
因此equals方法被重写过,则hashCode方法也必须被重写
hashCode()的默认行为是对堆上的对象产生独特值,如果没有重写hashCode(),则该类的两个对象无论如何都不相等,即使这两个对象指向相同的数据.
7. final关键字
修饰类表示类不可被继承,
修饰方法表示方法不可被重写,但可以重载,
修饰变量表示一旦被赋值就不能修改
8. String StringBuffer StringBuilder
String是final修饰的,不可变,每次操作都会产生新的String对象
StringBuffer和StringBuilder都是在原对象上操作
StringBuffer线程安全,StringBuilder线程不安全
StringBuffer的方法都是用synchronized修饰的
性能: StringBuilder > StringBuffer > String
经常需要改变字符串内容时使用后面两个,
优先使用StringBuilder,多线程使用共享变量时使用StringBuffer
9. 方法重载和方法重写的区别
重载:发生在同一个类中,方法名相同,参数(参数类型,参数个数,参数顺序)不同
重写:发生在字父类中,方法名,形参列表必须相同,返回值范围小于等于父类,抛出的异常小于等于父类,访问修饰符类型大于等于父类,如果父类的访问修饰符类型为private,则该方法不可被重写
10. SpringBean 的生命周期
1、解析xml配置或注解配置的类,得到BeanDefinition;
2、通过BeanDefinition反射创建Bean对象;
3、对Bean对象进行属性填充;
4、回调实现了Aware接口的方法,如BeanNameAware;
5、调用BeanPostProcessor的初始化前方法;
6、调用init初始化方法;
7、调用BeanPostProcessor的初始化后方法,此处会进行AOP;
8、将创建的Bean对象放入一个Map中;
9、业务使用Bean对象;
10、Spring容器关闭时调用DisposableBean的destory()方法;
11. sleep()和wait()的异同
1> 相同点:一旦执行方法,都可以使当前线程进入阻塞状态。
2> 不同点:
①:两个方法声明的位置不同:sleep()是Thread类里的方法,wait()是Object类里的方法
②:调用的要求不同:sleep()可以在任何需要的场景下调用,而wait()必须使用在同步方法或同步代码块中
③:关于是否释放同步锁:如果两个方法都声明在同步代码块或同步方法上,sleep()不会释放锁,wait()会释放锁,这里的sleep()到时间了会自动重新等待cpu的资源,而wait()则必须执行notify()或notifyAll()唤醒线程,等待cpu的资源;
12. Redis支持的七种类型数据
key-value
String(字符串)
List(列表)
Set(集合)
Sorted Set(有序集合)
Hash(哈希)
Module(模块)
Streams(流信息)
13. @Transactional 的作用范围
1:方法,推荐将注解使用与方法上,不过需要注意的是:该注解只能用到public方法上,否则不生效。
2:类,如果这个注解使用在类上的话,表明该注解中所有的public方法都生效。
3:接口,不推荐使用
14. mysql的索引
官方定义:一种帮助mysql提高查询效率的数据结构
优点:大大加快数据查询速度
缺点:
维护索引需要耗费数据库资源
索引需要占用磁盘空间
当对表的数据进行增删改的时候,因为要维护索引,速度会受到影响
会导致底层数据结构变化,需要进行重排序;所以一般只在常用的搜索字段上建立索引
复合索引指的是一个索引包含多个列,遵循最左前缀原则;
最左前缀原则:顾名思义是最左优先,以最左边的为起点任何连续的索引都能匹配上
>mysql 引擎在查询为了更好利用索引,在查询过程中会动态调整查询字段顺序以便利用索引,因此只要包含最左前缀的查询组合都可以利用组合索引
15. spring的作用域
分别为 singleton、prototype、request、session、global-session
singleton:单例类型,默认作用域,单例bean
prototype:原型类型,一个bean定义多个对象实例
request(请求作用域)为每一个request请求定义一个bean的实例,当请求结束后,这个bean就会失效,从而被spring的垃圾回收站回收
session(会话作用域)与request类似,同一个session中共享同一个bean的实例,不同的session绘画中使用的不同的bean的实例
global-session(全局作用域)所有的绘画共享同一个bean实例
以上三个都是web环境下使用的,一般搭配sringMVC使用,因为前端控制器包含了相关的状态
16. group by、having、order by、limit 顺序
SELECT s.*, CAST(AVG(sc.s_score) AS DECIMAL(10,2)) as avg
FROM Student s
JOIN Score sc ON sc.s_id = s.s_id
GROUP BY sc.s_id -- GROUP BY 是要在 JOIN 连接查询之后
HAVING avg > 60 -- HAVING 条件放在 GROUP BY 之后
ORDER BY avg desc -- ORDER BY 排序条件放在 HAVING 之后
LIMIT 0,5 -- LIMIT 放在 HAVING 之后
17. explain查询SQL
>1. 是否有使用索引:
查看explain查询结果里的key字段,如果为索引字段或(primaryKey)的话,表示使用了索引,如果为null,则表示没有使用索引
>2. 扫描的多少行:
查看explain查询结果里面的row列,里面明确显示查询多少行
>3. filter:
表示最终记录数量/扫描记录数 ===> 表示一个百分比!
select_type表示查询类型,explain查询结果里的字段SIMPLE表示简单查询,PRIMARY表示有子查询SQL的查询
18. 索引什么时候没用?
有or关键字
复合索引没有遵循最左前缀原则
模糊查询%在查询字段前面
where中的索引列有运算
where中的索引列使用函数
mysql认为全表扫描更快时
19. spring框架中的bean是线程安全的吗
默认单例,没有对线程方面进封装,所以是线程不安全的
但是共享也不一定会有线程安全问题
如果bean定义的共享数据,且数据还可以被修改,这样的话就会存在线程安全问题,
controller,service,dao层本身是不安全的,但是如果只是在这些方法中相互调用的话,就不会有线程安全问题
dao层会操作数据库,但是每一个和数据库连接的connection都会被数据库的事务机制管理,如果spring开启了事务的话,也是不会产生线程安全问题的
如果我们在bean中必须定义有状态的实例或变量,可以通过以下方法:
>最简单的,将默认单例bean换成prototype(原型)多例的
>使用ThreadLocal将变量变为线程私有
>synchronized,lock
20. 项目中如何使用事务
Spring事务管理分为声明式和编程式两种,我们一般都是使用声明式的注解方式来开启事务,基于AOP,将具体业务逻辑与事务处理解耦,
SpringBoot开启事务,首先在主启动类上加上@EnableTransactionManagement该注解有两个属性
>proxyTargetClass,默认为false基于接口(jdk动态代理),为true则基于子类(cglib动态代理)
>model默认为PROXY,另一值为ASPECTJ该属性表示使用的是哪种事务切面,
21. 什么场景下适合启用事务?
1: 一个方法中有多个update,delete,insert操作,可以通过添加事务保证原子性,操作要么同时成功,要么同时失败!
2: 多表查询统计场景下,可以通过事务控制将时间拉起到同一时间节点,保证数据的一致性!
22. 脏读,不可重复读,幻读
脏读: A事务对数据进行的修改,但还没有提交,此时B事务进来并使用了这个未提交的数据,这个时候A事务碰到异常回滚数据,B事务使用的这个数据就是脏数据
不可重复读: 在A事务内,对同一条数据进行查询,在此期间,B事务也访问了该条数据,并对这条数据进行修改,这时A事务可能会发生两次查询结果不一致的情况,因此称为是不可重复读
幻读: A事务对表中的数据进行了修改,这种修改涉及到表中的全部数据行,此时B事务也对表中的数据进行修改,而这种修改只是插入一条新数据,这样的话,A事务修改完在查询,发现还有没有修改过来的数据,就像是发生幻觉一样,这个叫做幻读
23. Spring事务的传播机制
required(Spring事务的默认传播类型): 支持当前事务,如果当前没有事务,就新建一个事务
supports:支持当前事务,如果没有当前事务,就无事务执行
mandatory: 支持当前事务,如果当前没有事务,抛异常
requires_new: 如果当前没有事务就新建一个事务,如果有,则将当前事务挂起,重新建一个事务
not_supported: 非事务执行,如果当前有事务,则挂起
never: 不使用事务,如果有事务,则抛异常
nested: 如果当前事务存在,则在嵌套事务中执行,否则和required操作一样(开启一个新事务)
24. spring事务什么时候会失效?
spring事务的原理是AOP,如果事务失效了,表示AOP不起作用了
发生了自调用,类里面方法使用了this调用本类的方法,此时这个this对象不是代理类,而是xxxService对象本身,解决: 让这个this变为xxxService的代理类即可
EmployeeService proxy =(EmployeeService) AopContext.currentProxy();
proxy.saveEmployee();
方法不是public的,@Transactional只能用在public方法上,要是非要用在非public方法上,可以开启ASPECTJ事务切面
数据库不支持事务
没有被spring管理
异常被吃掉,事务不会回滚(或者抛出的异常没有被定义,默认为RuntimeException)
25. SQL耗时,慢查询,以及慢查询都怎么优化
在业务系统中,除了使用主键进行的查询,其他的都在测试库上测试其耗时,慢查询的统计主要由运维在做,会定期将业务中的慢查询反馈给我们
慢查询的优化首先要搞明白慢的原因是什么?查询条件没有命中索引?是加载了不需要的数据列,还是数据量太大?
优化也是针对这几方面老考虑的
首先分析语句,看看是否加载了额外的数据,可能加载了许多结果中并不需要的列,对语句进行分析以及重写.
分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引
如果对SQL语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表.
26. Redis单线程快的原因
1) 纯内存操作
2) 核心是基于非阻塞的io多路复用机制
3) 单线程反而避免了多线程的频繁上下文切换来的性能问题
27. mysql的执行流程
简单概括:
1、我们在客户端发起一个sql语句
2、连接器判断用户登录以及用户权限
3、缓存命中,走缓存,直接返回查询结果
3、缓存没命中到达分析器,对sql语句进行分析,包括预处理和解析过程
4、优化器,对sql语句进行优化
5、执行器,调用存储引擎,执行具体的sql操作
6、将操作记录在undo log中,并存储回滚段指针和事务id
7、通过索引查找数据
8、写入redo log
9、写入bin log 提交事务
28. Redis设置key,value,以及过期时间
//连接redis
Jedis jedis = new Jedis("192.168.31.206",6379);
//设置值的时候设置过期时间
jedis.setex(key,second,value);
//根据键删除值
jedis.del("abc");
//key: 就是key; value: 就是key的value; second: 过期的秒数;
29. 前端跟后端怎么传参数?
1、HTML标签form表单提交
在后台可通过对应的name属性获取相应的值。
from表单中action属性标识提交数据的地址。
method属性指明表单提交的方式
后端Controller控制层
@RequestParam("name")定义前端的name属性
2、在springMVC中。controller中方法的参数是自动注入的,在使用注解的方式下,通常有
@RequestParam:取queryString当中的参数
@PathVariable:取在RequestMapping中定义的占位符中的参数(/test/{id})
@RequestBody:取request这个消息体,以json格式传参数(可以组装json对象)
在不使用注解的情况下,默认有一些对象可以自动注入,例如:
HttpServletRequest、HttpServletResponse、MultipartFile、MultipartRequest
@RequestParam:接受对象类型(Integer、String、Boolean等基本数据类型),不能接收自定义类型。
获取自定义类型时,不使用注解即可获取
不带注解:接收基本数据类型,若接收为自定义类型,会组装参数中与自定义类型属性名和类型相符的参数。
在组装对象时可以使用已被获取过的参数
从请求体中获取,用到的注解@RequestBody,当请求体是一段json数据时,@RequestBody会解析该json字符串并将其驻入指定的自定义类型中。
注意:通过@RequestBody的方式可以接收以json数据传输的对象,但前提时请求的Content-Type必须为application/json,并且引入jackson依赖。
接收前端传递的数组
接收数组可以使用注解方式的@RequestParam、@RequestBody或者无注解的方式,也可以同时使用他们三个
public String acceptArray(
Integer[] ids,
@RequestParam("ids1[]") Integer[] ids1,
@RequestBody Integer[] ids2
){
}
30. 数据库内连接和外连接有什么区别
内连接: 指连接结果仅包括符合连接条件的行,参与连接的两个表都应该符合连接条件.
等值连接
外连接: 连接条件不仅包含符合连接条件的行,同时也包含自身不符合条件的行. 包括左外连接、右外连接和全外连接;
左外连接就是在等值连接基础上加上主表中的未匹配数据
全外连接是在等值连接的基础上将左表和右表未匹配上的数据都加上
31. get和post的区别
post更安全,可以发送更大,更多的数据,但速度比get慢,
post用于修改和写入数据,get一般用于分享搜索筛选之类的操作.
32. union和union all的区别
union:
对两个结果集进行并集操作,不包括重复行,相当于distinct,同时进行默认规则排序
会对获取的结果进行排序操作
union all:
对两个结果集进行并集操作,包括重复行,即所有的结果全部显示,不管是不是重复
不会对获取的结果进行排序操作
总结: union all只是合并查询结果,并不会进行去重和排序操作,在没有去重前提下,使用union all的执行效率要比union高.
33. 为什么要使用springBoot
1.遵循约定优于配置的原则 ;
2.提供starter POM,高效包管理;
3.简化配置,无需XML ;
4.内嵌servlet容器 ;
5.与主流框架集成简单。
34. 字节流和字符流的区别
①: 每次读写的字节数不同
>字符流是块读写,字节流是字节读写
>字符流带有缓存,字节流没有
java流在处理上分为字符流和字节流.字符流处理的单元为两个字节的Unicode字符,分别操作字符,字符数组,字符串,而字节流处理单元为一个字节,操作字节和字节数组.
字符流和字节流,一个属性范围小,一个属性范围大,字符流只能是字符这一种类型,但是字节流可以是字符,可以是二进制文件,可以是音频,可以是各种各样的类型,只要符合字节形式存储的都可以接字节流,而字符流只能接字符;