1.底层代码
比较简单的就是HashMap的原理了
首先它的数据结构是由:一维数组,单向链表以及1.8之后的红黑树组成。它的默认长度是16.
具体的步骤为:
- 通过键的HashCode值计算出数组的下标
- 判断出改下标位置是否为空,如果是空的,就直接添加
- 如果不是空的,就调用Key的equals方法进行比较Key的值
- 如果相等就覆盖Value的值;不相等就将键值对放到数据后面形成单向链表
- 在1.8之后,长度到达8后,就会转换成红黑树
2.SpringBoot的一些了解
1.配置文件的引入
在一个叫做spring-boot-autoconfigure包下保存大量的自动配置类;在一个叫做META-INFO/spring.factotiries文件下保存类;自动配置类生效就要一些特定的条件,比如说依赖或者是注解,如@ComponentScan、@SpringBootConfiguration、@EnableAutoConfiguration
2.所具备的特征有:
-
可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs;
-
内嵌Tomcat或Jetty等Servlet容器;
-
提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置;
-
尽可能自动配置Spring容器;
-
提供准备好的特性,如指标、健康检查和外部化配置;
-
绝对没有代码生成,不需要XML配置。
3. 包含的一些注解
1.@Configuration
用于定义配置类,指出该类是 Bean 配置的信息源。
2.@Repository
用于标注数据访问组件,即DAO组件。
使用@Repository注解可以确保DAO或者repositories提供异常转译,这个注解修饰的DAO或者repositories类会被ComponetScan发现并配置,同时也不需要为它们提供XML配置项。
3.@RestController
是REST风格的控制器;它是@Controller和@ResponseBody的合集。
4.@ResponseBody
异步获取json数据,加上@responsebody后,会直接返回json数据。
5.@Bean
相当于XML中的,放在方法的上面,而不是类,意思是产生一个bean,并交给spring管理。
6.@Component
泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
7.@AutoWired
byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
当加上(required=false)时,就算找不到bean也不报错
4.ioc的了解
IOC(Inverse of Control)控制反转是一种程序设计思想。 反转是把对象的创建和管理交给容器完成,然后再交给开发者。
IOC的主要作用是:解耦。
通过反射机制+工厂模式实现IOC。之后可以根据自己的理解更加深入去阐述自己的理解
3.SpringMVC的执行流程
- 用户发送请求
- 前端控制器获得用户请求的URL,发送URL给处理器映射
- 处理器映射将Handler(包装方法信息)返回给前端控制器
- 前端控制器发送Handler给处理器适配器,适配器执行Handler方法
- 执行Handler方法后,返回ModelAndView(逻辑视图)给前端控制器
- 前端控制器将ModelAndView发送给视图解析器,解析出物理视图返回给前端控制器
- 前端控制器渲染视图,发送视图给用户
4.ArrayList 和 LinkedList 的区别是什么?HashMap 和 Hashtable 有什么区别?StringBuild跟StringBuffer的区别
ArrayList和LinkedList的区别
-
数据结构:ArrayList是数组,默认长度是10,扩容因子是0.75,然后扩容1.5倍;LinkedList是双向链表
-
ArrayList查找性能高(因为通过下标快速定位),插入和删除性能低(移动大量数据)
-
LinkedList查找性能低(因为要向前或向后依次查找),插入和删除姓高(只需要修改前后指针,不用移动)
HashTable和HashMap的区别
-
Hashtable是线程安全的,HashMap是非线程安全的
-
HashMap的性能高于Hashtable
-
Hashtable不能接收null作为键和值,HashMap可以
StringBuffer和StringBuilder的区别
- StringBuffer对字符串的操作的方法都加了synchronized,保证线程安全;StringBuilder不保证线程安全,在方法体内需要进行字符串的修改操作,可以new StringBuilder对象,调用StringBuilder对象的append、replace、delete等方法修改字符串。
- 运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer
- 在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
- StringBuilder适用于单线程下在字符缓冲区进行大量操作的情况;StringBuffer适用多线程下在字符缓冲区进行大量操作的情况
5.MyBatis中#{}和${}的区别
#{}应用PreparedStatement的占位符?插入,能防止SQL注入
${}应用字符串的拼接,不能防止SQL注入
6.讲讲Redis,项目中有用过Redis吗?
Redis是一个高性能的内存数据库,以key-value方式存储数据,可以作为缓存使用。
Redis的特点
- 性能高(读的速度是110000次/s,写的速度是81000次/s,单机redis支撑万级并发)
- 支持多种存储类型
- 丰富的特性(发布订阅、事务、过期策略等)
- 支持持久化
- 单线程 (避免上下文切换,线程同步问题)
redis的几种问题跟解决方案
1)缓存击穿
高并发的情况下,短时间内缓存会被穿过,请求直接打到数据库上,可能导致数据库压力过大。
解决方案:对代码上锁(双重检查锁)
2)缓存穿透
高并发的情况下,如果查询不存在的数据,因为缓存和数据库都不存在,请求都会打到数据库上,可能导致系统崩溃。
解决方案:
1) 保存不存在的数据到缓存中,设置一定过期时间
2) 布隆过滤器(直接过滤掉不存在数据的请求) 不能准确判断是否存在数据,能准确判断数据不存在
3)缓存雪崩
高并发的情况下,缓存服务器重启或热点数据同时过期,全部访问数据库,导致数据库宕机
解决方案:
1)配置缓存集群
2)尽量给热点数据设置不一样的过期时间,相对均匀
7.一些常见集合的区别
1、list和set是实现了collection接口的。
2、list可以允许重复的对象。可以插入多个null元素。是一个有序容器,保持了每个元素的插入顺
序,输出的顺序就是插入的顺序。Set不允许重复对象,无序容器,你无法保证每个元素的存储顺
序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序。只允许一个 null 元素。
3、Map不是collection的子接口或者实现类。Map是一个接口。Map 的 每个 Entry 都持有两个对
象,也就是一个键一个值,Map 可能会持有相同的值对象但键对象必须是唯一的。TreeMap 也通
过 Comparator 或者 Comparable 维护了一个排序顺序。Map 里你可以拥有随意个 null 值但最多
只能有一个 null 键。
8.常见的设计模式
1.装饰者设计模式
开闭原则:程序开发过程中,对功能的扩展开放,对功能的修改关闭,提高程序的稳定性
目的:在不修改原有类的代码基础上,对类的功能进行扩展
实现
1.装饰者和被装饰者都要实现相同的接口或继承相同的父类
2.装饰者中定义一个被装饰者的对象
3.给装饰者传入被装饰者对象
4.调用装饰者的方法时,也调用被装饰者的方法,同时进行功能的扩展
2.单例设计模式
目的:保证一个类只有一个实例(对象)
应用场景:
对于某些大的对象,单例模式能节省系统资源
实现 :
- 将所有构造方法定义为private
- 在类中创建一个静态的对象
- 在类中定义一个静态方法来返回该对象
两种单例模式
饿汉式:类中一开始就创建对象,不管后面是否使用对象,都消耗了内存。
懒汉式:类中一开始不创建对象,调用返回对象方法时再创建对象。
3.代理模式
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
作用
1)中介的作用,当调用者不能或不方便调用某个对象时,代理起到中介的作用,帮助调用者间接
的调用对象。
2)符合开闭原则,在不修改原有类代码的前提下,对类的功能进行增强。
代理模式分为两种:
1) 静态代理,在运行前,通过编写代码的方式生成代理类
2) 动态代理,在运行后,通过反射机制生成代理类
静态代理
1)代理者和被代理者都实现相同的接口
2)代理者包含被代理者的对象
3)创建代理对象时传入被代理对象
4)代理者执行方法时,会调用被代理者的方法,同时扩展新的功能
静态代理的问题:一个代理类只能代理一种业务,如果有多种业务,就必须创建大量的代理类。
动态代理
和静态代理不同,动态代理是在运行时,通过反射机制动态生成代理类。开发者不需要手动编写新
的代理类。
动态代理分类
JDK动态代理
JDK自带的,前提是:被代理类必须实现过接口。
实现步骤
- 实现InvocationHandler接口
- 实现invoke方法
- 通过Proxy.newProxyInstance方法返回代理对象
CGLib动态代理
需要引入CGLib依赖,它的原理是:通过反射+继承机制动态生成被代理类的子类,所以被代理类
不能是final的。
实现步骤
- 引入cglib
- 实现MethodInterceptor接口
- 实现intercept方法
- 通过Ehancer返回代理对象
9.消息队列
1.一对一
一个生产者发送消息到一个队列,一个消费者从队列中取消息。
2.工作队列
生产者将消息分发给多个消费者,如果生产者生产了100条消息,消费者1消费50条,消费者2消费50条。
实现能者多劳:
channel.basicQos(1);限制队列一次发一个消息给消费者,等消费者有了反馈,再发下一条
channel.basicAck 消费完消息后手动反馈,处理快的消费者就能处理更多消息
basicConsume 中的参数改为false
3.发布/订阅模式
发布/订阅模式和工作模式的区别是:工作模式只存在一个队列,多个消费者共同消费一个队列中的消息;而发布订阅模式存在多个队列,不同的消费者可以从各自的队列中处理完全相同的消息。
4.路由模式
路由模式的消息队列可以给队列绑定不同的key,生产者发送消息时,给消息设置不同的key,这样交换机在分发消息时,可以让消息路由到key匹配的队列中。
5.主题模式
主题模式和路由模式差不多,在key中可以加入通配符:
* 匹配任意一个单词 com.*
# 匹配.号隔开的多个单词 com.*
10.RabbitMQ的用途
1)解耦
服务之间进行解耦,A服务调用B服务时,需要编写相关的代码,调用情况发生改变时,需要修改
调用的代码
2)异步
传统的同步调用方式,需要等待调用完成,才能进行其它业务
异步调用方法,将消息发送到队列中,就可以返回,执行其它业务,速度大大提升
3)削峰
出现流量激增的情况时,消息队列可以设置消息的最大数量,处理一部分消息,其它消息交给队列
排队处理
11.spring事务配置的具体流程
(1)事务的传播性:@Transactional(propagation=Propagation.REQUIRED)
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
(2)事务的超时性:@Transactional(timeout=30) //默认是30秒
注意这里说的是事务的超时性而不是Connection的超时性,这两个是有区别的
(3)事务的隔离级别:@Transactional(isolation = Isolation.READ_UNCOMMITTED) ,未提交
读,就是一个事务可以读取另一个未提交事务的数据。读取未提交数据(会出现脏读, 不可重复读)
基本不使用;
@Transactional(isolation = Isolation.READ_COMMITTED),已提交读,就是一个事务要等另
一个事务提交后才能读取数据。可以解决脏读,可能会出现不可重复读问题;
@Transactional(isolation = Isolation.REPEATABLE_READ),重复读,就是在开始读取数据
(事务开启)时,不再允许修改操作,重复读可以解决不可重复读问题。不可重复读对应的是修
改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不
是UPDATE操作。
@Transactional(isolation = Isolation.SERIALIZABLE) ,是最高的事务隔离级别,在该级别下,
事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较
耗数据库性能,一般不使用。
12.创建线程的方式
1.继承Thread类
- 继承Thread类
- 重写run方法
- 调用start启动线程
2.实现Runnable接口
- 实现Runnable接口
- 实现run方法
- 创建实现Runnable接口的对象,传入Thread对象中
- 启动线程
3.实现Callable接口
- 实现Callable接口,实现call方法
- 创建Callable对象,传入FutureTask对象
- 创建FutureTask对象,传入Thread对象
- 启动线程
- 调用get方法得到返回结果
4.使用线程池
13.数据库建表的时候int(1)和int(2)的区别
这个int(M)我们可以简单的理解为:
这个长度是为了告诉MYSQL数据库,我们这个字段的存储的数据的宽度为M位数, 当然如果你不是
M位数(只要在该类型的存储范围之内)MYSQL也能正常存储。
设置这个属性后往表时插入数据,系统会自动把uid字段M不够3位的在左侧用0来填充
14.Vector是如何实现的安全的
Vector与ArrayList容器的实现原理是一毛一样的,都是封装了一个数组,并且把数组的扩容、缩小交给容器自己管理。不过Vector支持多线程并发访问,因为修改容器的方法都加上了synchronized关键字修饰(this对象锁,锁住整个容器对象)。由于底层是数组,所以随机访问(通过下标访问)效率高,但是在删除节点、插入节点时比较麻烦,删除节点时需要进行大量元素的前移,插入节点时需要进行大量元素的后移,数组容量有限,还需要进行扩容(整个容器中的元素都要复制一遍),效率比较低。
15.讲讲你知道哪些排序方法,并说下他们的时间复杂度
16.讲讲Elasticsearch
Elasticsearch具备以下特点:
- 分布式,无需人工搭建集群
- Restful风格,一切API都遵循Rest原则,容易上手
- 近实时搜索,数据更新在Elasticsearch中几乎是完全同步的。
- 有多条件的查询方式,自定义排序
ElasticSearch常用的几种查询方式,terms 查询是term的扩展,可以支持多个vlaue匹配,只需要一个匹配就可以了。
1 term查询(精准查询)
term是ES中的精准查询,不会参与ES分词查询。
2 math查询(分词匹配查询)
match查询是按ES分词的倒排表进行查询,而keyword不会被分词,match的需要跟keyword的完全匹配可以。可以用于一般性的匹配查询。
3 fuzzy查询(模糊查询)
fuzzy查询可以用于纠正去拼写的问题,fuzziness是可以允许纠正错误拼写的个数
4 wildcard(通配符查询)
通配符查询允许我们指定一个模式来匹配,而不需要指定完整的trem,匹配的方式类似于match的
分词匹配查询。
?将会匹配如何字符;*将会匹配零个或者多个字符。
5 bool查询(布尔查询)
bool查询本身没有查询功能,而是基于逻辑值使用前面几种查询方式进行组合查询。
17.jq选择器,如何选中元素
- 基本过滤选择器
- 内容过滤选择器
- 可见过滤选择器
- 属性过滤选择器
- 子标签过滤选择器
- 表单属性过滤选择器
- 表单元素过滤选择器
18.Linux常用命令
cd 目录名
ls 目录名(不写就是查看当前目录)
-l 详细列表
-a 所有文件
查看目录详情 ll 目录名
mkdir 目录名
删除目录 rm 目录名
-r 遍历所有子目录
-f 强制删除
创建\打开文件 vi 文件名
三种模式:
命令模式 能删除、复制、粘贴,无法编辑
输入模式 编辑内容
命令行模式 退出、保存文件
操作方式:默认进入命令模式,按i进入输入模式,按esc回到命令模式,按:进入命令行模式
命令模式
x 删除一个字符
dd 删除一行
yy 复制一行
p 粘贴
u 撤销
命令行模式
wq 保存退出
q 退出 (如果有修改,此方式退出会出错)
q! 强制退出
cat 显示整个文件内容(不支持分页)
more 分页显示(只能向前分页查询)
less 分页显示,功能比more强大。(支持前后分页查询)
支持文本查找,/查找内容 向下查询 ; ?查找内容 向上查找内容
退出less模式,点击q
对比vi命令:cat、more、less仅仅是用来进行文本阅读命令,vi用来进行文本编辑的命令。
查询当前文件中是否包含有java单词,如果包含有Java单词的所有行全部显示出来。
cat 文件名 | grep 查询文字
find 查找目录 -name 文件名称
find 查找目录 | grep 名称
which 命令名称
移动文件
mv 原文件 目标文件
复制文件
cp 原文件 目标文件
解压指令:tar -zxvf
压缩指令:tar -zcvf
-z:表示压缩和解压缩的格式为gz压缩文件(gzip)
-c::表示压缩
-x:表示解压缩
-v:表示显示压缩或者解压缩的详细过程。
-f:表示指定压缩或者解压缩的文件,只能放在命令的最后
tar -zcvf demo.tar.gz demo2.txt
tar -cvf 压缩后的文件名称 待压缩的文件
tar -xvf 待解压的解压包名
通过端口查看进程:netstat –apn | grep 8080
通过端口查看进程:lsof -i:3306
通过进程名称查询进程:ps -ef | grep redis
杀死进程:kill -9 PID(进程ID,-9表示强制杀死)
19.mysql跟oracle的区别
一、宏观上:
1、Oracle是大型的数据库而Mysql是中小型数据库;Mysql是开源的,Oracle是收费的,且价格昂贵。
2、Oracle支持大并发,大访问量,是OLTP的最好的工具。
3、安装占用的内存也是有差别,Mysql安装完成之后占用的内存远远小于Oracle所占用的内存,并且Oracle越用所占内存也会变多。
二、微观上:
1、对于事务的支持
Mysql对于事务默认是不支持的,只是有某些存储引擎中如:innodb可以支持;而Oracle对于事务
是完全支持的。
2、并发性
Mysql,既支持表锁,也支持行级锁。表锁,对资源锁定的力度很大,如果一个session对一个表加
锁时间过长,会让其他session无法更新此表的数据。
Oracle使用行级锁,对资源锁定的力度要小很多,只是锁定sql需要的资源,并且加锁是在数据库
中的数据行上,不依赖于索引。所以oracle对并发性的支持要好很多。
3、数据的持久性
Oracle保证提交的事务均可以恢复,因为Oracle把提交的sql操作线写入了在线联机日志文件中,
保存到磁盘上,如果出现数据库或者主机异常重启,重启Oracle可以靠联机在线日志恢复客户提交
的数据。
Mysql默认提交sql语句,但是如果更新过程中出现db或者主机重启的问题,也可能会丢失数据。
4、事务隔离级别
MySQL是repeatable read的隔离级别,而Oracle是read commited的隔离级别,同时二者都支持
serializable串行化事务隔离级别,可以实现最高级别的。
读一致性。每个session提交后其他session才能看到提交的更改。Oracle通过在undo表空间中构造
多版本数据块来实现读一致性,每个session 查询时,如果对应的数据块发生变化,Oracle会在
undo表空间中为这个session构造它查询时的旧的数据块。
MySQL没有类似Oracle的构造多版本数据块的机制,只支持read commited的隔离级别。一个
session读取数据时,其他session不能更改数据,但可以在表最后插入数据。session更新数据
时,要加上排它锁,其他session无法访问数据
5、提交方式
Oracle默认不自动提交,需要手动提交。Mysql默认自动提交。
6、逻辑备份
Mysql逻辑备份是要锁定数据,才能保证备份的数据是一致的,影响业务正常的DML(数据操纵语言
Data Manipulation Language)使用;Oracle逻辑备份时不锁定数据,且备份的数据是一致的。
7、sql语句的灵活性
mysql对sql语句有很多非常实用而方便的扩展,比如limit功能(分页),insert可以一次插入多行数
据;Oracle在这方面感觉更加稳重传统一些,Oracle的分页是通过伪列和子查询完成的,插入数据
只能一行行的插入数据。
8、数据复制
MySQL:复制服务器配置简单,但主库出问题时,丛库有可能丢失一定的数据。且需要手工切换
丛库到主库。
Oracle:既有推或拉式的传统数据复制,也有dataguard的双机或多机容灾机制,主库出现问题
是,可以自动切换备库到主库,但配置管理较复杂。
9、分区表和分区索引
MySQL的分区表还不太成熟稳定;Oracle的分区表和分区索引功能很成熟,可以提高用户访问db
的体验。
10、售后与费用
Oracle是收费的,出问题找客服;Mysql是免费的的,开源的,出问题自己解决。
11、权限与安全
Oracle的权限与安全概念比较传统,中规中矩;MySQL的用户与主机有关,感觉没有什么意义,
另外更容易被仿冒主机及ip有可乘之机。
12、性能诊断方面
Oracle有各种成熟的性能诊断调优工具,能实现很多自动分析、诊断功能。比如awr、addm、
sqltrace、tkproof等 ;MySQL的诊断调优方法较少,主要有慢查询日志。
20.索引的作用
加快查询速度,用户查询时,先找索引,通过索引找到实际数据的位置,再直接定位到实际数据上,极大提高查询速度
缺点:
- 也是数据,需要占存储空间
- 降低增删改的速度
- 创建索引也需要一定的时间
21.深拷贝和浅拷贝的区别
1、浅拷贝:将原对象或原数组的引用直接赋给新对象,新数组,新对象数组只是原对象的一个引
用。
2、深拷贝:创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过
来,是“值”而不是“引用”。
22.sleep和wait的区别
-
调用对象不同
wait() 由锁对象调用
sleep() 由线程调用
-
锁使用不同
执行wait后,自动释放锁
执行sleep后,不会释放锁
-
唤醒机制不同
执行wait后,可以被通知唤醒
执行sleep后,只能等待时间结束后,自动唤醒
23.在jdbc中是使用什么实现占位符
Statement 接口的两个问题:
1.使用 Statement 接口 对象发送的 sql 语句需要再数据库进行一次编译之后成为指令才能执行, 每条 sql 语句都需要编译一次, 这样是很慢的.
2.使用 Statement 接口 操作的 sql 语句需要使用字符串的拼接方式实现, 这样的方式可能存在 sql 注入的安全风险并且拼接字符串比较麻烦.
PreparedStatement 接口的优点:
1.使用该接口操作的 sql 语句会先预先编译成指令在发送给数据库, 数据库就执行指令即可, 这样就提高了一定速度,
2.该接口可以避开 sql 需要使用字符串拼接的方式, 从而解决 sql 注入的安全风险,而是使用占位符 (?) 来代替原来的字符串拼接.
24.讲讲单点登录
-
添加用户服务,实现用户登录(Security)
-
登录成功后,将用户信息转换为JWT格式,保存到Cookie中或者是在页面中,在将Token放入请求头中
-
在网关中添加全局过滤器,对请求进行拦截,获得其中的Token,进行解码,成功就放行
当然还有其他的解决方法,比如说将用户信息存放到Redis中,但是有2个问题
问题1:key的设置,区分前端客户端
问题2:属于有状态登录,在服务器端保存用户信息,需要额外的开销,REST风格不推荐使用有状态登录
同样的Token过期如何处理。
我的解决方法是在用户进行跳转页面的时候重新设置一个JWT的Token,再返回一个新时间的Token,可能比较粗糙,各位大佬可以各抒己见。