java简单的面试题

Java的基本数据类型有8种

分别是:byte(位)、short(短整数)、int(整数)、long(长整数)、float(单精度)、double(双精度)、char(字符)和boolean(布尔值)。

 

JavaScript基本数据类型

String(字符串类型)、Number(数字类型)、Boolean(布尔类型)、Array(数组类型)、Date(日期类型)。

 

JavaScript == 与 === 区别

1、对于 string、number 等基础类型,== 和 === 是有区别的

  • a)不同类型间比较,== 之比较 "转化成同一类型后的值" 看 "值" 是否相等,=== 如果类型不同,其结果就是不等。
  • b)同类型比较,直接进行 "值" 比较,两者结果一样。

2、对于 Array,Object 等高级类型,== 和 === 是没有区别的

进行 "指针地址" 比较

 

JavaScript中typeof和instanceof的区别

typeof 一般只能返回如下几个结果:

number,boolean,string,function,object,undefined。我们可以使用 typeof 来获取一个变量是否存在

instanceof 用于判断一个变量是否某个对象的实例,如 var a=new Array();alert(a instanceof Array);
会返回 true,同时 alert(a instanceof Object) 也会返回 true;这是因为 Array 是 object

 

 JSP中的内置对象

按照内置对象的功能来划分,可以分为以下四类:
1、输出输入对象:request对象、response对象、out对象;
2、通信控制对象:pageContext对象、session对象、application对象;
3、Servlet对象:page对象(JSP页面对象本身)、config对象;
4、错误处理对象:exception对象。
 
 
 

SpringMVC的执行流程

面试:
  1、用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获(捕获);
  2、DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;(查找handler);
  3、 DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller), Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象(执行handler);
  4、DispatcherServlet 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver) (选择ViewResolver);
  5、通过ViewResolver 结合Model和View,来渲染视图,DispatcherServlet 将渲染结果返回给客户端。(渲染返回);

快速记忆技巧:
  核心控制器捕获请求、查找Handler、执行Handler、选择ViewResolver,通过ViewResolver渲染视图并返回;

VUE生命周期钩子函数

  (1)beforeCreate() 实例创建前触发

  (2)created() 实例创建完成,

  (3)beforeMount() 模板渲染前,可以访问数据,模板编译完成,虚拟DOM已经存在

  (4)mounted() 模板渲染完成,可以拿到DOM节点和数据

  (5)beforeUpdate() 更新前

  (6)updated() 更新完成

  (7)activated()   激活前

  (8)deactivated() 激活后

  (9)beforeDestroy()  销毁前

  (10)destroyed()   销毁后

 

数据库分库分表

大表拆分优化
分库分表:

分库:将以前存在于一个数据库实例中的数据拆分成多个数据库实例,部署在不同的服务器上

分表:将以前存在于一张表上的数据拆分成多个表

分库是为了解决服务器资源受单机限制,顶不住高并发的问题,把请求分发到多台服务器降低服务器的压力

分表是为了解决单张表数据量过大,查询效率慢的问题

 

 分库会带来问题:

事务问题:无法保证事务完整性,采用分布式事务

连接join问题:跨库无法join:1.在业务代码上进行关联 2.适当冗余字段

如何分表

垂直分表:把一些不常用的大字段剥离出去

 

水平分表:则是因为一张表内的数据太多了,上文提到数据越多B+树就越高,访问的性能越差,所以进行水平拆分

 

分表带来问题:

排序、count、分页问题:在业务代码上将数据进行排序、count、分页处理

 

get和post的区别

1、传送方式:get通过地址栏传输,post通过报文传输。

2、传送长度:get参数有长度限制(受限于url长度),而post无限制

 

多态,继承,封装

多态:一个接口多种实现(多种子类继承同一个父类,重写父类方法,不同子类对象调用同一方法,可以有不同的实现过程,可以屏蔽不同子类对象之间的差异,写出通用的代码)

使用场景:List list = new ArrayList();   换用其他collection的时候能更快的重构代码,因为在使用ArrayList的独有方法时会很麻烦    ,

xxServiceImpl implements xxService    当service的实现需要修改时,不需要修改原本的实现,可以直接添加另一个实现类,避免了项目重构的风险

继承:子类继承父类,可以使用到父类的方法,属性,能减小代码和数据的冗余度,大大增加程序的重用性

封装:将属性和方法封装为一个类(面向对象编程),隐藏实现细节,只提供接口给外界调用,简化外部调用重用,安全

 

内存泄漏和内存溢出

内存泄漏:申请内存后无法释放内存

内存溢出:申请内存时没有足够的内存供使用

 

AOP面向切面编程(使用的动态代理技术)

日志记录,性能统计,安全控制,事务处理,异常处理等

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,

通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

 

==与equals的区别

==在比较对象时,会比较地址(两个new String 相同对象值,地址不同)

equals比较对象,比较对象的值

 

String ,StringBuilder,StringBuffer(线程安全)区别

String是final修饰的类,每次对其操作都会创建新的String对象,然后将其指针指向新的对象

StringBuilder,StringBuffer都是在原有的对象上进行操作

StringBuffer的每个方法都加上了synchronized,线程安全,而StringBuilder没有,线程不安全

线程不安全性能更高,优先采用StringBuilder

StringBuilder>StringBuffer>String

 

String常用方法

length()  长度

charAt()  指定位置字符

toCharArray()  将字符串转换为数组

indexOf()   字符下标位置

toUpperCase()    转换为大写

split()     分割字符串为数组

equals()    判断字符串值是否一致

equalsIgnoreCase()    忽略大小写判断字符串值是否一致

trim()    去除两边的空格字符

replace()    替换字符

substring()     截取字符串

contains()    是否包含某字符串

startsWith()    是否以某字符串开头

 

 

抽象类和接口的区别

抽象类:方法可以有抽象的,也有非抽象的(具体的实现),有构造器

接口:方法都是抽象的,属性都是常量,默认有public static final修饰

controller调用service接口,接口的实现可以有很多,当切换接口不同的实现类时不需要修改过多的内容,高内聚低耦合

 

自动装箱,自动拆箱,Integer缓存(-128~127)

自动装箱:Integer a = 123;    将基本数据类型转为对象类型

自动拆箱:Integer a == int 123               将对象类型转为基本数据类型进行值比较

-128和127之间的int基本数据类型,装箱时都指向的是同一个内存地址(使用==比较,结果为true)

而不是之间的int数据装箱,会新创建对象,使用不同的内存地址

 

Collection与Collections区别

java工具类  +s 结尾

Collection:集合,List和Set的父类

Collections:集合的工具类,不能实例化,提供了集合的静态方法,例如:

Collections.sort(集合对象);   排序集合
Collections.reverse(集合对象);  倒序集合
Collections.copy(目标集合对象, 集合对象);   复制集合对象的内容到另一集合(目标集合大小>=原集合)
Collections.binarySearch(集合对象, 内容);   查询集合的指定内容的下标

 

List中  ArrayList与LinkedList区别,和互相的优势

ArrayList是数组,存储在连续的内存空间,查找快插入删除慢,扩容机制:默认大小10,位运算扩容1.5倍(old+(old>>1))创建新的数组,转移数据(位运算>>1    二进制向右移1,数值除以2)

LinkedList是双向链表,存储在不连续的内存空间,查找慢插入删除快,因为节点会存储指针使用额外的空间,内存使用更多

特殊情况:

查找第几个:ArrayList占优                     查找内容的下标:都一样(都要循环查找)

插入删除头部中间:LinkedList占优        插入删除尾部:都一样(ArrayList能快速定位到尾部,LinkedList有尾指针)

 

hashmap与hashtable的区别

hashmap:没有使用synchronized,线程不安全,key允许一个null,value允许null

hashtable:使用synchronized锁住整个数组,线程安全,key,value不允许null

 

hashmap的高效并发concurrenthashmap

1.8版本前

concurrenthashmap使用分段锁,将hashmap分成几段,

并发写出现在不同段中并发对结果没有影响,

并发写出现在相同段使用synchronized加锁

1.8版本后

concurrenthashmap使用CAS+synchronized,数组下标位置是否有值,没有就使用CAS插入节点,有就使用synchronized对链表的头节点加锁,锁的颗粒度更小

 

hashmap什么时候链表转化为红黑树?

当数组大小已经超过64并且链表中的元素个数超过默认设定(8个)时,将链表转化为红黑树

不是就数组扩容2倍

 

Set中  HashSet与TreeSet区别

HashSet:元素不重复但无序,使用数组+链表存储数据,使用计算存储对象的hashcode与数组长度-1进行位运算,定位到存储到数组的下标,

如果hash后定位到同一位置,会进行equals比较内容,不同就在数组的同一位置形成链表插入数据,链表过长时(长度8)查找慢,将链表改为红黑数

TreeSet:元素不重复有排序,使用map的key存储数据,之后组织成红黑树进行排序

 

IO流分类

字节流(二进制文件,以Stream结尾):InputStream OutputStream   ,可以处理所有文件

字符流(文本文件):Reader Writer  ,可以简单的处理文本文件的文本内容

 

Serializable序列化与版本号idserialVersionUID

在使用ObjectOutputStream将对象写入磁盘(序列化),ObjectInputStream从磁盘中读取对象(反序列化)和将对象进行网络传输时,

必须implements Serializable,并且添加版本号id:serialVersionUID,

因为将对象进行网络传输和存储到磁盘等介质时,使用的是IO流,只支持字节数组类型的数据,需要将对象数据先转为字节数组(序列化),之后通过将字节数组转为原对象数据(反序列化)进行持久化保存数据

当执行序列化时,磁盘会根据这个类结构生成版本号id:serialVersionUID,

当执行反序列化时,程序会对比磁盘中的版本号id,与当前类的版本号id是否一致,不一致反序列化会失败

使用版本号id,当类的结构(类的属性增加删除)发生改变时,之前序列化的对象依然可以反序列化成功

 

throw和throws的区别

throw,作用在方法内,用于主动抛出异常,报错异常

throws:作用在方法声明上,用于声明该方法可能有该异常,不处理异常

自定义异常需要继承RuntimeException

 

finally

优先级高,会执行return 2

        try{
            ...
        }catch (Exception e){
            return 1;
        }finally {
            return 2;
        }

 

线程5种生命周期

new Runnable Running   block(block wait timewait) terminated

new:新建Thread             Runnable:就绪,thread.start()                Running:运行中,线程运行任务

block:阻塞,线程等待锁释放              wait:使用wait或者join             timewait:使用sleep(100)或wait(100)

terminated:结束,线程执行完毕销毁

 

sleep和wait区别

sleep定义在Thread类上,wait定义在Object类上,因为需要对对象加锁实现多线程互斥,java的锁是对象级别的

sleep不会释放锁,wait会释放锁

sleep可以使用在任何代码块上,wait必须使用在同步方法和同步代码块上(要有synchronized),因为使用在同步代码块上保证了notify唤醒线程必须要在wait之后

 

ThreadLocal是什么,使用场景(确保数据库的connection一致,保证service层业务原子性)

每个线程都有独立的一个map用来保存键(当前thread对象)值对,threadLocal静态对象有get,set方法,获取设置当前线程的设置在threadLocal的值

threadLocal的get,set方法,使用的是map,key存放当前线程的thread对象,value存放设置值

使用场景:

使用ThreadLocal<Connection> threadLocal = new ThreadLocal<>();     保存与数据库连接connection,之后所有的数据库业务层都使用threadLocal 中的connection对象

保证不同数据库业务层使用同一connection连接,当某一数据库业务(dao层)失败时,能保证service层中其他的相关的数据库业务一起回滚,确保service层业务的原子性

 

类加载的双亲委派机制

启动类加载器(java.lang.*等)《——扩展类加载器(ext扩展包)《——应用程序加载器(程序创建的类)

双亲委派机制

(1):最下层的应用程序加载器收到类加载的请求
(2):把这个请求委托给⽗加载器去完成,⼀直向上委托,直到启动类加载器
(3):启动器加载器检查能不能加载,能就加载(结束);否则,抛出异常,通知⼦加载器进⾏加载
(4):保障类的唯⼀性和安全性以及保证JDK核⼼类的优先加载

 

JSP与Servlet的区别,servlet实现MVC

jsp本质就是servlet,jsp——》翻译——》servlet(java)——》编译——》class文件

jsp能通过写html的方式实现java的内容,拥有一些脚本语言<% java代码 %>  <%= 变量 %>

servlet:out.write("<html>")

servlet通过写java代码实现html的展示

servlet实现MVC

M:数据模型(实体类),V:视图,C:控制器

使用一个servlet实现控制器(C)接受所有请求根据请求,转发到各自不同的使用访问数据源进行操作的servlet作为业务层操作数据模型(M),将获取的数据存放在request中,转发到jsp(V)

 

JSP的四大域对象的作用范围(存储数据空间)

page:作用于单个jsp页面(当前页面)

request:作用于同一请求中(转发有效,重定向无效)

session:作用于同一用户浏览器与服务器会话

context:作用于整个项目应用(使用场景:应用的访问数)

 

servlet的生命周期(对象创建,初始化,service方法,销毁)

servlet是单例的(创建,初始化,销毁一次),由于只有一个对象,同时操作时产生线程安全问题

使用反射创建servlet对象(第一次使用url访问servlet时)——》初始化——》service()(用于转向get,post,put,delete方法)——》doGet()或doPost()——》销毁

 

cookie,session,token区别

session:存储在服务器上,默认30分钟,关闭浏览器不会消失,需要使用到cookie完成操作,而cookie作用范围在相同域名下(前后端分离的系统无法实现登录,需要解决跨域问题)

用户登录成功后,服务器会自动生成      保存当前用户记录标志的key:sessionId的  cookie   返回给客户端

当客户端再次访问时会带上携带了sessionid的cookie访问服务器,实现登录

 

cookie:存储在浏览器上,默认会话级关闭浏览器会消失,非会话级(setMaxAge设置有效时间)可以通过有效期实现七天免登录

其他cookie配置:

httpOnly=true;    设置只能通过后端获取cookie,避免了用户通过修改前端代码(留言版写js代码)获取cookie信息

path='/';   访问路径

domain=’‘     设置cookie的域名,能跨域获取cookie,解决跨域问题

 

token:一个用户标识字符串,在客户端登录成功后,服务器生成一个token通过回调函数返回给客户端,客户端保存token到cookie中,设置前端请求拦截器将token设置在请求头中,每次请求带token访问服务器,服务器判断是否有该token就可以实现登录

(适用于前后端分离系统)

 

jdk、jre、jvm的区别是什么

 

 

 JDK是用于制作程序和Java应用程序的软件开发环境,包含java语言开发工具。Java 开发人员可以在 Windows、macOS、Solaris 和 Linux 上使用,是一个跨平台编程语言。

JRE是java运行时环境,包含jvm和java使用的关键类库,必须使用JRE才能运行java程序

JVM是java虚拟机,它提供运行时环境,驱动 Java 代码或应用程序。将 Java 字节码转换为机器语言,它不能单独安装下载,使用时必须安装JRE配合使用

 

转发与重定向区别

转发:在服务器内部的跳转,只有一次request请求,request中的内容可以进行传递

重定向:在客户端的跳转(网页跳转),有两次request,request中的内容不能传递(百度——》b站),需要使用重定向还要在同一应用中传递数据只能使用session

 

数据三范式

第一范式:列不可分(四川省成都市双流区,可以分为省市区,不满足第一范式)

第二范式:要有主键(每一条数据要有唯一标识)

第三范式:不能有传递依赖(商品表只能有商品类型ID,通过id连接商品类型表,不能有商品类型名,数据冗余)

 

左连接,右连接,内连接

左连接(left join on):连接两表的数据,左表数据都要有,右表的数据对应不上可以用null占位

右连接(right join on):右表数据都要有,左表的数据对应不上可以用null占位

内连接(inner join on):只获取两表对应上的数据,没有null占位

 

sql注入,解决办法

sql注入:通过将不正确的输入值放入输入框,点击提交,能够跳过其他的sql判断条件(where判断),

例如:通过在用户名框填写  ' or 1=1 #  可以直接跳过sql中密码的判断(因为将sql语句改变了,极其危险) select * from user where username = '' or 1=1 # ' password = '';

解决办法:

mybatis中使用#{}进行预编译sql,填写的参数会通过?占位,之后赋值,所以无论填写的是什么,都只会当作参数的值(string类型),不会影响sql语句

不要使用${},因为使用的是字符串拼接,会导致sql注入问题

 

synchronized与Lock的区别

synchronized:是关键字,作用于方法,代码块 ,无需手动释放锁线程结束自动释放锁,修饰对象的方法,锁当前对象(user),修饰类的静态方法,锁当前类的class对象(例如:User.class),修饰代码块可以设置锁对象(synchronized(this))

Lock:是类,作用于代码块,必须要手动释放锁(一般在finally中unlock),有公平锁(根据请求顺序获取锁执行线程),可以中断锁(使等待锁的线程不等待了做其他事),有读写锁(读锁:多个线程可以同时读,写锁:写操作只能一个个线程执行,读写锁互斥:不能同时读写)

 

浏览器输入url发出请求到页面加载完毕,经历了什么

1.检查输入的URL格式是否正确

2.浏览器解析   域名,端口,请求路径

3.页面有缓存使用缓存,没有缓存发出请求

4.DNS解析域名转为ip(www.baidu.com——>  23.122.3.1)

5.浏览器与服务器建立TCP连接(三次握手)

6.浏览器向服务器发出http请求

7.服务器处理http请求,返回数据给浏览器

8.浏览器收到数据,根据html源码渲染页面

 

计算机网络架构

应用层(www,http,DNS)

传输层(TCP,UDP)

网络层:通过许多的路由中转站将数据中转,使不同地区的ip能够相连(IP,路由器)

数据链路层:将比特组装成帧,点对点的传输(帧)(IEEE,MAC,网桥,交换机)

物理层:通过介质传输比特(bit)(同轴电缆,光纤,中继器,集线器,网关)

 

TCP与UDP区别(传输层)

TCP:面向连接,提供可靠的传输协议,传输前需要建立连接(三次握手),以字节流方式传输,传输慢

UDP:面向无连接,提供不可靠的传输协议,传输不用建立连接,以数据报文段的方式传输,传输块

 

TCP建立连接的三次握手

客户端想要与服务端建立连接,        客户端发送给服务端SYN(SYN握手信号-------随机数x)

服务端确认收到建立连接信号,               服务端发送给客户端SYN,ACK(SYN+1---------x+1 ,ACK----------随机数y)

客户端确认服务端的建立连接,客户端发送给服务端(SYN+1---------x+1 ,ACK+1----------y+1)

为什么要三次握手

第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常

第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常

第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常

所以三次握手就能确认双发收发功能都正常,缺一不可。

SYN是客户端到服务端的发送信号,返回了代表客户端到服务端发送通道没问题

ACK是服务端到客户端的发送信号,没有ACK无法知道服务端是否能发送成功

 

TCP断开连接的四次挥手

TCP四次挥手

断开一个 TCP 连接则需要“四次挥手”:

  • 客户端-发送一个 FIN,用来关闭客户端到服务器的数据传送
  • 服务器-收到这个 FIN,它发回一 个 ACK,确认序号为收到的序号加1 。和 SYN 一样,一个 FIN 将占用一个序号
  • 服务器-关闭与客户端的连接,发送一个FIN给客户端
  • 客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加1

为什么要四次挥手

举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,

但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。

 

TCP如何保证可靠性

1:合适分割数据为数据块

2:数据块编号,接受方接受到数据块后,接受方对所有数据块进行排序

3:校验和:通过发送方生成校验码,接收方通过校验码和接受到的数据进行运算,确认数据在传输过程中是否变化,如果变化就丢弃该数据

4:滑动窗口:利用滑动窗口控制发送端的发送速率,因为接收端可能会来不及接收发送来的大量数据,数据会丢失

5:自动重传:发一组数据,之后接收方确认收到后,再发另一组数据,如果一段时间内接收方没确认,会重新发送数据

 

HTTP状态码

 

 

 

TCP长连接与短连接

长连接:保持tcp连接不断开,持续获取资源,不会永远连接,会有设置时长

短连接:tcp连接后获取到资源后断开

 

HTTP和HTTPS 的区别

端口 :HTTP的URL由“http://”起始且默认使用端口80,而HTTPS的URL由“https://”起始且默认使用端口443。

http:运行在TCP协议之上,以明文的方式传输发送到服务器的内容,数据传输没有任何的加密,不安全,可能被截取信息,获取到用户的重要信息

 

https:运行在SSL协议(安全套接字协议)之上,SSL协议运行在TCP协议之上,服务器会将SSL安全证书以及服务器公钥发送给客户端,客户端将数据通过公钥加密(对称加密),

之后发送给服务器,服务器通过服务器私钥解密数据(非对称加密)(私钥和公钥是一对通过数学计算得出的钥匙,通过公钥加密的数据只有相对应的私钥解密数据)

只有当服务器的私钥丢失时,可能被截取数据

https缺点:网页加载慢,SSL证书贵,作用有限只能防数据被截取

 

死锁,解决办法

是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

产生死锁必要原因:

1.互斥条件,资源只能有一个线程占用                                                            2.请求保持条件,在自己已有一个资源的情况下,请求其他资源被阻塞,会一直等待不释放已有资源

3.不剥夺条件,其他线程不能抢夺线程中的独占资源,只能自己主动释放      4.循环等待条件,线程间互相等待对方释放资源(乱序获取资源的锁,A线程A资源,B线程B资源  都要对方的资源)

解决办法:破坏其中一种产生原因就能解决

1.破坏互斥条件,使用CAS算法(乐观锁),不产生锁,资源不会被占用。

2.破坏请求保持条件,线程不使用   synchronized   使用   Lock锁  ,其中可以设置 获取锁的等待时长,等待超时就释放自己已有资源,重试线程。(可能导致活锁

3.破坏循环等待条件,将获取资源的锁排序,必须按照顺序获取锁(先获取A资源再获取B资源),A线程获取A资源,B线程需要按照顺序  获取A资源阻塞,A线程能顺利获取B资源

检测死锁方法:

使用java自带的    jvisualvm(杰维u ven)检测定位发生死锁的代码行

 

数据库存储过程,优缺点

一组为了完成特定功能的SQL语句集。经编译后存储在数据库中。

优点:不需要在网络上传递多个sql语句,速度快,只有管理员能创建存储过程,指定用户使用存储过程,用户不能看到内部实现

缺点:只有管理员才能创建存储过程,换其他数据库时需要重新编写大量的存储过程,存储过程相对编程困难需要专业人士编写,维护困难

 

mybatis使用sqlsession连接数据库操作

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//SqlSessionFactory初始化
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//获取SqlSession
SqlSession session = factory.openSession();

mybatis的一级二级缓存

mybatis缓存:在查询成功后,查询的内容会保存在缓存中,之后的相关查询可以通过缓存获取结果,不需要通过数据库查询。

一级缓存:默认开启,作用域在   SqlSession    ,任何的增删改数据库后,一级缓存会被清空

二级缓存:默认开启但是不生效,采用map实现,最大存储1024个查询内容,缓存满了后使用LRU回收机制(清除最长时间未使用对象),

作用域在   SqlSessionFactory   ,通过SqlSession工厂创建的新的SqlSession也能查询到相同二级缓存内容        ,可以通过第三方缓存   redis  保存二级缓存内容

 

springboot一级缓存使用:

1.service中开启事务@Transactional,因为使用数据库连接池,每次查询自动commit,使用不同的SqlSession,使用事务后使用的是同一SqlSession

 

springboot 二级缓存使用:

1.实体类实现Serializable接口

2.mybatis配置文件添加(这一步可以不要,默认开启了,只是没有生效,之后的一步使二级缓存生效)

<settings>
    <setting name = "cacheEnabled" value = "true" />
</settings>

3.mapper.xml添加

<cache/>

4.二级缓存根据namespace生效,如果有两表连接,因为其namespace不同,二级缓存不同,如果修改其中一个表内容,另一个缓存不会更新,二表之后的连接查询会查不到更新后的数据,需要

在使用连表查询的另一个mapper.xml中添加,将两表作用域合成一个

<cache-ref namespace="com.zhengxl.mybatiscache.mapper.UserOrderMapper"/>

 

mybatis分页方式

逻辑分页:使用mybatis的RowBounds进行分页,会查询所有数据,再根据代码将数据分页

物理分页:使用分页插件PageHelper只查指定数量的数据,原理:使用拦截器将sql请求拦截,并且在sql请求中添加   limit  数据库分页语句,再在数据库中查询分页内容

分页插件PageHelper

1.引入依赖

2.mybtis配置文件配置分页拦截器,或者spring配置文件配置,二选一

3.mapper中使用,在使用startPage后的下一个查询语句会进行分页,如果需要多个查询语句也需要多个startPage

PageHelper.startPage(1, 10);        //第一页的10条数据
List<Country> list = countryMapper.select();

 

sql的分页limit到最后一页查询慢怎么解决

因为limit是先根据where取出所有符合条件的数据,遍历到  offset遍历量后,可能遍历很久,之后根据size分页大小取出相应数量的数据

解决办法1:使用where id>具体值  先将符合条件的数据减少

解决办法2:先通过使用覆盖索引查询id之后连接查询所有字段,select id,a,b,c  from y inner join(select id from x  limit 10000,5)

解决办法3:使用between and 代替limit   前提必须知道具体位置

 

mybatis中  #{},$ {}  区别

#{}是预编译处理,$ {}是字符串替换。

mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;

mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。

使用 #{} 可以有效的防止SQL注入,提高系统安全性。

 

mybatis使用模糊查询

使用concat拼接字符串

like concat(‘%’,#{empname},'%')

使用双引号

like "%"#{empname}"%"

向mapper传递参数

在java传递参数时传递   String username = "%" + user.getUsername() + "%";

 

过滤器和拦截器区别,使用场景

过滤器:依附于servlet,它可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。能获取到request请求,url,并对其进行处理,处理完成后移交给下一个过滤器,直到把请求发给目标

使用场景:web项目中参数校验,参数统一编码,参数修改

拦截器:依赖于web框架,基于java的反射机制的动态代理,因为依赖spring框架可以依赖注入service,实现业务逻辑添加,生效范围:在servlet转发到post,get方法后,到controller之前,拦截器会生效拦截到controller的方法,在controller之前或之后加入某些操作

使用场景:项目安全范围,例如:校验用户是否登录

 

 

 

重载与重写

重载 发生在同一个类中,方法名相同,参数的类型、个数、顺序不同;方法的返回值和修饰符对是不是重载方法没有影响。

重写 发生在父子类中,方法名和参数相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;

如果父类方法访问修饰符为 private 或者 final 则子类就不能重写该方法。

反射(动态获取类的信息),优点缺点

反射就是动态找到一个类的信息(可以获得类名,构造器,方法),反射经常被用在框架上,例如:@Autowired自动注入注解,利用反射机制动态获取类的信息,并创建一个对象赋值给使用注解的对象中

Class.forName("类的全包名");

对象.getClass();

Class clazz = 类.class;

通过Class对象可以获取类的构造方法,属性和方法

clazz.getConstructors();

clazz.getField("name");

clazz.getDeclaredMethod("hello",String.class);    前面是方法名,后面是方法需要的参数,用于定位具体的方法

优点:

一个接口多个实现类,切换实现类时修改代码少,只需要修改使用类一处地方,之后通过反射获得对象,代码简单灵活

通过任一类知道类的方法,属性,    通过任一对象能随意调用它的方法

缺点:

性能差:反射包括一些动态类型,JVM无法优化该代码,相比非反射的代码运行效率低

功能错误:反射允许使用一些私有的方法属性(private),可能会造成意料外的错误

不易读:使用反射的代码会更加复杂,代码不是正常逻辑,可能会有维护问题

 

Spring使用技术(IOC和AOP)

IOC:控制反转,不需要手动创建对象,都由spring容器通过反射机制创建对象(@AutoWired),帮助我们自动管理不同层级的依赖对象

主要作用:解耦,面向接口编程,因为不同层级的对象(service层依赖dao层)不会耦合在一起(Userservice service = new UserserviceImpl();  ),

在新的需求到来时需要另一个实现类(Userservice service = new UserserviceImpl2();  ),不需要大幅度修改所有使用之前实现类的地方,只需要修改@service的位置就行了

AOP:面向切面编程,将非核心业务抽离出来形成单独的方法,实现核心业务(商品信息crud)和非核心业务的解耦

主要作用:使用动态代理的设计模式,将    日志记录,性能统计,安全控制,事务处理,异常处理  的操作    放入代理方中达成额外的操作

 

Spring bean是线程安全的吗

spring bean默认是单例的,但当bean无状态(没有存储数据)时,是线程安全的,例如:大部分的controller,service,dao

如果bean的其中的属性被修改,即有状态(存储数据),线程是不安全的,需要使用ThreadLocal,@Scope("prototype"),保证线程访问不同的bean对象

 

Spring bean的作用域有哪些

singleton(单例):在创建容器时创建一个bean实例,调用bean时只会调用创建好的那个bean实例,Bean以单例方式存在,bean作用域范围的默认值

prototype:              每次从容器中调用Bean时,都会创建并返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。

request:                 每次HTTP请求都会创建一个新的Bean

session:                 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。

可以在bean类上使用,规定作用域

@Scope("prototype")

 

SpringBoot的自动装配原理

Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,

这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,通过@Bean导入到Spring容器中,以Properties结尾命名的类是和配置文件进行绑定的。

它能通过这些以Properties结尾命名的类中取得在全局配置文件中配置的属性,我们可以通过修改配置文件对应的属性来修改自动配置的默认值,来完成自定义配置

 

spring 事务传播机制(一个service调用另一个service,如何保证失败后两个service中的dao操作全部回滚)

PROPAGATION_REQUIRED(require)

Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行

PROPAGATION_SUPPORT(support)

内层需要外层service有事务,没有就非事务运行

PROPAGATION_MANDATORY(mandatory)

与NEVER相反,如果外层没有事务,则抛出异常

PROPAGATION_NOT_SUPPORT

该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码

PROPAGATION_NEVER

该传播机制不支持外层事务,即如果外层有事务就抛出异常

 

 

springboot的事务管理(将service作为事务,使用的AOP技术,将service执行前后进行代理处理当捕获异常并进行回滚,默认只能拦截RuntimeException和Error)

springboot启动类添加

@EnableTransactionManagement

在service方法上使用注解设为事务

@Transactional

@Transactional()中可以设的参数      使用,隔开参数

 

isolation=Isolation.DEFAULT     设置隔离级别

propagation=Propagation.REQUIRED      指定事务传播机制(require为默认,外层service有事务,内层就加入,没有就创建事务,外层内层都是使用同一事务,失败一起回滚)

readOnly=true      service事务只能读,不能更新删除

timeout=30      设置超时时间

rollbackFor=Exception.class    指定遇到哪类异常会回滚(默认只能拦截RuntimeException和Error)

noRollbackFor=Exception.class      指定遇到哪类异常不回滚

 

spring 悲观锁,乐观锁

悲观锁:利用数据库的行锁机制,使用事务(@Transactional 和 sql语句加上 for update  数据库排他锁)进行加锁操作,for update 在查询条件有主键的具体内容而不是范围,开启的是行锁,其余查询到的情况下,开启的是表锁

乐观锁:使用CAS模式,使用version,每次对数据的更新操作都会使version+1,不要使用自由变化的变量,可能出现ABA问题(两个A操作后的版本一样,实际操作后锁的版本应该不一样)

CAS sql 语句:

update set version = version+1 where version=#{oldversion}

 

 

 

JDK 动态代理(静态代理,加上反射机制)

使用步骤

创建接口及实现类

实现代理处理器:implement InvokationHandler ,实现 invoke(Proxy proxy,Method method,Object[] args) 方法(这里的方法的内容是需要被代理的方法,和代理自己的方法

通过 Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) (其中的参数都可以通过java 的反射获取·)获得代理类

通过代理类调用方法。

使用场景:

使用一个代理类实现所有的代理功能,在调用方法时,会自动使用代理方法,这是AOP的基本原理

 

 

mysql的基本语句的执行顺序

1from->2join->3on->4where->5groupby->6having->7select->8orderby->9limit

 

mysql的6种日志

查询日志

慢查询日志

事务日志

错误日志

二进制日志

中继日志(主从结构,用来复制主节点数据)

 

redis事务(multi .... exec)

在准备阶段失败,整个事务操作失败

在事务执行时候某任务失败,只会失败那个任务,其他的都会正常执行

 

 

jdbc对比数据库连接池的缺点

jdbc需要频繁地建立与数据库的连接 , 使用完毕之后需要断开连接(释放资源) , 会消耗大量的时间以及资源

数据源建立多个数据库连接, 这些连接保存在连接池中 , 当应用需要连接数据库时, 直接从池中获取空闲的连接对象 , 使用完毕之后, 将连接放回池中

 

Mysql数据库引擎InnoDB,MyISAM区别,使用场景

MyISAM存储引擎,表级锁定,有较高查询速度,读写互相阻塞,不支持事务处理与外键和行级锁

使用场景:没有事务,读取频繁,写入少

InnoDB存储引擎,具有提交回滚机制,崩溃恢复机制

使用场景:并发大,可靠性要求高,要求使用事务

 

事务的四特性

1 、原子性。事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做

2 、一致性。数据库的数据在事务操作前后都必须满足业务规则约束,例如:A转账给B,AB的总额在前后要一致

3 、隔离性。一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。(隔离级别问题)

4 、持久性。也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的,

即使数据库宕机,也能通过事务日志(开始事务操作前写入事务内容)完成没有完成的事务操作,完成数据持久化

并发运行事务可能出现问题

脏读:           读取修改未提交的数据

不可重复读:读取修改已提交的数据

幻读:           读取增加已提交的数据

解决事务并发问题(隔离级别)

读未提交:读取未提交的数据

读已提交:读取已提交的数据(解决:脏读)

可重复读:读取后,其他事务无法修改只能读取(解决:脏读,不可重复读)

序列化:    只能串行化运行事务(解决:脏读,不可重复读,幻读)

 

为什么使用数据索引能提高效率

数据索引的存储是有序的

在有序的情况下,通过索引查询一个数据是无需遍历索引记录的(索引查找mysql  直接定位m,之后找y,再找sql)

 

有哪些索引

物理存储角度

聚集索引(索引顺序 和 数据存储在磁盘的物理顺序 一致)

非聚集索引

逻辑角度

唯一索引(索引的列 值要唯一,不允许有空值)

单列索引

组合索引

主键索引(主键自动创建唯一索引,不允许有空值)

前缀索引(数据太长了,只根据前面几位字符建索引,index(x_name(1)) )

 

建索引的原则

对值唯一的字段(学号)建   唯一索引  (重复度低)

对   重复度低  的字段    建索引

对经常需要   作为查询条件(where ),排序(order by),分组(group by),联合(union) 的字段建    索引

限制索引个数     (索引也是一张表,会占据磁盘空间,并且在修改表时索引的更新很浪费时间)

字段存储的  数据太长   ,建   前缀索引

尽量的扩展索引,不要新建索引(a索引,拓展到,ab组合索引)

删除   很少使用的索引

 

索引失效的情况

查询出太多记录(大于15%),不会使用索引

like后直接跟通配符在前的查询(like ‘%xss’),索引失效

最左前缀原则:组合索引abc    只会走abc  ab   a 索引       ,<   和   >  后的索引失效

对索引列使用函数,索引失效,可以将   函数索引列  作为索引

对索引列进行运算(+,-,*,/,!,<> 一起用  等),索引失效   (where a <> 1  改为 a<1 or a>1 )

查询条件索引列出现隐式转换(varchar -> int),索引失效

查询 is null ,is not null  ,索引失效

 

创建线程的四种方法,及优缺点

实现Runnable接口,重写run方法,使用实现Runnable接口的对象   构造   Thread对象,之后调用Thread对象的start方法

实现Callable接口,允许泛型返回值,重新call方法,使用实现Callable接口的对象   构造   Thread对象,之后调用Thread对象的start方法

继承Thread类,重写run方法,创建了   继承了Thread类的对象   之后调用  MyThread对象的start方法

ThreadPoolExecutor 线程池创建线程,创建线程池对象,线程池对象调用excute方法   执行     实现Runnable接口,重写run方法的匿名类对象

 

实现接口都是可以继续继承其他的类,编程较复杂

继承Thread类不能再继承其他的类,编程简单

上面的都可能导致OOM堆内存溢出(不停的创建线程)

而线程池可以统一管理,调优,监控线程,降低资源消耗,减少响应速度(直接取出线程使用)

 

线程池运行原理,参数,拒绝策略,核心线程数怎么设置合理

原理

参数

核心线程数

最大线程数

任务缓存阻塞队列长度  能最多缓存多少任务

当缓存队列满了,线程达到最大线程数,任务到来,使用什么拒绝策略,拒绝此任务执行

除核心线程的    其他线程最长空闲时间,超过会销毁线程

空闲时间单位(毫秒,秒。。)

使用什么线程工厂(一般使用默认)

拒绝策略

丢弃任务抛出异常

丢弃任务不抛出异常

丢弃队列前面的任务,重新尝试执行被拒绝任务

由调用该任务的线程处理该任务(自己的事自己做)

核心线程数怎么设置多少(cpu核心+1,cpu核心*2)

cpu密集型任务,线程使用率高,开多了线程会线程上下线切换,cpu核心+1

io密集性任务,线程使用率低,多开线程  可以在等待io时有其他线程处理其他任务,cpu核心*2

线程工作时间长,减少线程,线程等待时间长,增加线程

 

线程池为什么使用阻塞队列 保存任务

阻塞队列相比普通队列能保存更多的任务,因为队列满了之后阻塞队列能将任务阻塞,从而保留更多想要进队列的任务

阻塞队列在队列是空的没有任务时,可以阻塞获取任务的核心线程,使线程wait,释放cpu资源,在有任务时唤醒线程执行任务,一直维持核心线程的存活,可能导致内存溢出OOM,在不使用线程池时,销毁线程池

怎么阻塞线程

sleep        不释放锁,一段时间内阻塞线程

yield         不释放锁,重新加入抢夺锁的队伍

suspend   不释放锁,阻塞线程     resume唤醒线程

wait              释放锁,阻塞线程     notify唤醒线程

 

synchronized底层原理,及其优化,偏向锁 轻量级锁 重量级锁,锁升级

synchronized使用monitorenter和monitorexit指令进行同步,现阶段monitor使用三种实现锁的方式(偏向锁 轻量级锁 重量级锁),锁会根据情况逐步升级(锁升级),锁能升级不能降级

偏向锁(没有线程竞争锁):适用于自己线程获取自己的锁,锁对象的    对象头有一个    threadid字段,默认为空,当锁住时存储上锁线程的id,线程结束释放资源,再设为空

轻量级锁(少线程竞争锁,锁时间短):线程获取锁时,自旋判断     threadid字段    是否为自己线程的id,是就获取锁,不是就升级为轻量级锁,但是自旋过长会导致CPU空转消耗

重量级锁(大量线程竞争锁,锁时间长):一定次数内自旋获取锁对象失败,升级为重量锁,把除了拥有锁的线程都阻塞,防止CPU空转

锁升级好处:减少锁带来的性能消耗

 

synchronized保证可见性(主内存和两个线程工作内存数据一致)

当获取锁时,从主内存读取数据,当线程结束释放锁时,将工作内存数据写入主内存,实现可见性

 

synchronized与volatile的区别

synchronized:修饰方法,代码块,会上锁会阻塞线程,可以保证可见性,原子性(线程挨着执行),不能保证有序性

volatile:修饰变量,不会上锁不会阻塞线程,可以保证可见性(修改立即更新主存,其他工作内存失效,获取主存数据),有序性(内存屏障:禁止指令重排),不能保证原子性

 

CAS算法是什么,使用场景

对比和设置

一种乐观锁,会设置运行主版本,之后各个线程  对比 自己的线程版本 和 运行主版本 是否一致,

一致才运行线程进行线程对共享变量的修改,

不一致就修改自己的线程版本 进入到下一次的版本对比

使用场景:

优化线程的上下文切换(因为加锁和释放锁会导致比较多的上下文切换)

解决死锁问题

不过可能一直发生自旋(一直失败重试,导致活锁)

 

通信BIO和NIO区别

BIO:为每个连接创建一个线程

NIO:一个线程服务多个连接(多路IO复用),连接数太多,效率下降

Netty:封装了NIO,在其基础上实现了主从处理组,bossgroup,workgroup

 

微服务同步异步通信方式

同步:dubbo 的RPC方式,springcloud 的Restful http方式

异步:MQ消息队列,调用方无需等待执行方处理结果

 

dubbo和springcloud通信区别

dubbo:RPC方式,基于TCP长连接方式

springcloud:restful  http方式(通过http方式访问资源),基于TCP短连接方式

 

zookeeper和eureka注册中心区别

zookeeper:存储服务信息结构为树状结构(和文件夹差不多),创建临时节点(服务故障,节点删除),监听机制:观察节点信息是否变化,变化时,通知dubbo(本地默认有服务列表缓存,zookeeper断了也能访问服务)重新抓取注册服务列表

eureka:使用ConcurrentHashmap线程安全其性能兼顾的map存储注册服务信息

 

微服务中共享资源的控制,分布式锁(zookeeper可靠性高,集群情况一致性高)

因为synchronized只能控制一个JVM,但微服务中多机架构是多个JVM,不能实现共享资源控制,需要使用分布式锁

数据库:建lock表,设lock字段,上锁时为1,解锁时为0

redis:setnx(没有才创建),上锁时setnx lock value 成功,解锁时 delete lock,避免死锁:设置过期时间,避免释放别人的锁:释放锁前判断是否为自己的锁

zookeeper:上锁创建节点lock,解锁删除节点lock,避免死锁:设置为临时节点(上锁的挂了会删除节点),因为zookeeper集群情况下一致性很高,创建锁节点后立即更新到从机上,不存在  访问不同zookeeper机器的锁不同

 

接口幂等性(多次重复调用接口,保证只返回一次结果)

解决方法:

数据库使用唯一字段,用户账户在重复申请时,在数据库存在该值会报错,报错后只需要查询一次返回结果,解决重复申请账户

使用token,每次的提交需要带上token,验证token相同时,执行操作并更新token,重复提交验证token相同失败,保证只执行一次

悲观锁,乐观锁,分布式锁

并发不高的系统,可以使用select+insert的方式,先查询是否操作过,判断之后进行操作

对外开放的api保证操作幂等性,source来源+seq序列号做唯一索引,防止多次操作

 

分布式事务(解决事务不在同一服务器上的问题)

分布式事务问题:例如,库存业务和订单业务不在同一服务器上,有这样的事务,用户买东西,库存减少并产生订单信息,由于不在同一服务器,会违背事务的ACID原则,可能库存减少了但由于网络问题没有产生订单信息

解决方法:

数据库层面:

1、2PC(两阶段提交,准备,提交):有一个协调者(队长),协调多个个业务系统的任务,  2PC图片

客户端向协调者发起事务操作——》协调者通知业务进行准备——》业务准备成功返回协调者准备完成(没成功就返回没完成)——》协调者通知业务开始执行操作(有业务没有准备完成通知中止操作)——》业务开始执行操作(被通知中止后中止操作)

2、3PC(三阶段提交,准备,预提交,提交):比2PC多一个预提交阶段,3PC图片

协调者准备阶段询问是否有条件能接收该事务,预提交阶段的引入起到了一个统一状态的作用,在预处理阶段表明所有参与者都已经回应了,

引入了超时机制,参与者就不会傻等了,如果是等待提交命令超时,那么参与者就会提交事务了,因为都到了这一阶段了大概率是提交的,如果是等待预提交命令超时,那该干啥就干啥了,反正本来啥也没干。

业务层面:

3、TCC(try预留,confirm确认,cancel撤销):预留(资源的预留与锁定),确认(执行操作),撤销(取消预留进行的操作)TCC图片

事务管理者先对所有操作进行try预留,成功后confirm确认执行操作,有一个预留失败撤销之前的预留操作

TCC对业务的侵入较大和业务紧耦合,难点在于业务上的定义,对于每一个操作你都需要定义三个动作分别对应Try - Confirm - Cancel,需要根据特定的场景和业务逻辑来设计相应的操作

4、本地消息表:将操作和消息(两个处于同一服务器)放入一个事务里,保证消息一定会进入数据库,操作执行完成后消息更改为完成状态,没完成定时任务读取未完成状态的消息并执行对应的操作,容忍数据暂时不一致,保证了数据的最终一致性,

5、使用消息中间件:生产者给消息中间件发送事务通知,之后一个操作执行完成后通知消息中间件是完成还是回滚,如果回滚丢弃事务消息,如果完成通知消费者进行之后的操作,完成后消费信息

 

posted @ 2021-09-10 19:34  低调的。。。  阅读(132)  评论(0编辑  收藏  举报