目录
mysql面试题
内连接查询的是两张表的交集,两张表都有的才查出来。
左外连接是查询左表中的所有,右表中满足条件的查询出来。
右外连接是查询右表中的所有,左表中满足条件的查询出来。
索引是一种数据结构,是数据库管理系统中一个排序的数据结构,可以快速的查询数据库表中的数据,通常由B+树或hash表。
索引优点 | 索引缺点 |
可以大大加快数据的检索速度 | 创建索引和维护索引需要耗费时间,对表中的数据进行增加、删除和修改的时候,索引需要动态的维护,会降低 (增/删/改/查的速度)的执行效率 |
可以在查询过程中,使用优化器,提高系统性能 | 索引需要占领物理空间 |
创建索引的原则
1.常作为查询条件的字段建立索引,where子句中的列,或者连接子句中指定的列
2.为经常需要排序、分组操作的字段建立索引
3.更新频繁的字段不适合创建索引
4.不能太有效区分的列不适合做索引列
5.对于定义为text、image和bit的数据类型的列不要建立索引
mysql使用自增主键的好处
1.自增主键按顺序存放,增删数据速度快,对于检索非常有利
2.数字型,占用空间小,易排序
事务
特性 | 说明 |
原子性 | 事务是最小的执行单位,不允许分割。事务包含的所有操作要么全部成功,要么全部失败。 |
一致性 | 事务执行之前和执行之后都必须处于一致性状态。举例:拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。 |
隔离性 | 隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间是相互隔离的 |
持久性 | 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下,也不会丢失提交事务的操作。 |
脏读 | 一个事务读取到另外一个事务未提交的数据。 举例:一个事务1读取了被另一个事务2修改但还未提交的数据。由于某种异常事务2回滚,则事务1读取的是无效数据。 |
不可重复读 | 一个事务读取到同一条记录2次,得到的结果不一致,这可能是两次查询过程中间,另一个事务更新了这条数据。 |
幻读 | 幻读发生在两个完全相同的查询,得到的结果不一致。这可能是两次查询过程中间,另一个事务增加或者减少了行记录。 |
不可重复读的重点是修改,幻读的重点在于新增或者删除
隔离级别 | 说明 |
读未提交 | 一个事务可以读取到另一个事务更新但未提交的数据。可能会导致脏读、不可重复读、幻读。 |
读已提交 | 一个事务提交后才能被其它事务读取到,可以阻止脏读,但是不可重复读或幻读仍有可能发生。 |
可重复读 | 对同一记录的多次读取结果都是一致的,除非数据是被本身事务所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
可串行化 | 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。 |
Redis面试题
Redis是一种非关系型键值对数据库.
Redis的键必须是字符串,值支持五种数据类型(字符串、列表、散列表、集合、有序集合)
优点 | 缺点 |
读写性能优异,支持数据持久化 | 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。 |
数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构 | Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂 |
支持主从复制,主机会自动将数据同步到从机,可以进行读写分离 | Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。 |
支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。 | 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。 |
Redis为什么这么快
完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。
数据结构简单,对数据操作也简单
采用单线程,避免了不必要的上下文切换和竞争,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
数据类型 | 可以存储的值 | 操作 | 应用场景 |
string | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作对整数和浮点数执行自增或者自减操作 | 做简单的键值对缓存 |
list | 列表 | 从两端压入或者弹出元素对单个或者多个元素进行修剪,只保留一个范围内的元素 | 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的数据 |
hash | 包含键值对的无序散列表 | 添加、获取、移除单个键值对获取所有键值对 检查某个键是否存在 | 结构化的数据,比如一个对象 |
set | 无序集合 | 添加、获取、移除单个元素检查一个元素是否存在于集合中 计算交集、并集、差集从集合里面随机获取元素 | 交集、并集、差集的操作,比如交集,可以把两个人的粉丝列表整一个交集 |
zset | 有序集合 | 添加、获取、删除元素根据分值范围或者成员来获取元素 计算一个键的排名 | 去重但可以排序,如获取排名前几名的用户 |
JAVA基础知识面试题
Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行
Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。
修饰符 | 当前类 | 同包 | 子类 | 其他包 |
private | √ | × | × | × |
default | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
&和&&的区别
&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true 整个表达式的值才是 true。&&之所以称为短路运算,是因为如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。
final、finally、finalize区别
final是一个修饰符关键字,可以修饰类、方法、变量,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
finally是一个异常处理的关键字,一般作用在try-catch-finally代码块中,在处理异常的时候,通常我们将一定要执行的代码放在finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
finalize是属于Object类的一个方法,该方法一般由垃圾回收器来调用,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收
this | super |
普通的直接引用,this相当于是指向当前对象本身。 | 普通的直接引用,super相当于是指向当前对象的父类引用,这样就可以用super.xxx来引用父类的成员。 |
当形参与成员名字重名,用this来区分 | 子类中的成员变量或方法与父类中的成员变量或方法同名时,用super进行区分 |
引用本类的构造函数 | 引用父类构造函数 |
static主要意义是在于创建独立于具体对象的变量或者方法。即使没有创建对象,也能使用属性和调用方法!
break | continue | return |
结束当前循环体 | 跳出本次循环,继续执行下次循环 | 结束当前的方法,直接返回 |
面向过程 | 面向对象 |
优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。 | 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护 |
缺点:没有面向对象易维护、易复用、易扩展 | 缺点:性能比面向过程低 |
面向对象的特征主要有以下几个方面:
抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
其中Java 面向对象编程三大特性:封装 继承 多态
封装:封装是把一个对象的属性私有化,隐藏内部的实现细节,同时提供一些可以被外界访问属性的方法。通过封装可以使程序便于使用,提高复用性和安全性
继承:继承是使用已存在的类的定义作为基础,建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过继承可以提高代码复用性。继承是多态的前提。
关于继承如下 3 点请记住
-
子类拥有父类非 private 的属性和方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
多态性:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。多态提高了程序的扩展性。一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。
Java实现多态有三个必要条件:继承、重写、向上转型。
-
继承:在多态中必须存在有继承关系的子类和父类。
-
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
-
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备调用父类和子类的方法的技能。
抽象类 | 接口 | |
相同点 | 1.接口和抽象类都不能实例化 2.都位于继承的顶端,用于被其他类实现或继承 3.都包含抽象方法,其子类都必须重写这些抽象方法 | |
不同点 | 1.抽象类使用abstract关键字声明 2.子类使用extends关键字来继承抽象类。如果一个类继承了抽象类,那么该子类必须实现抽象类的所有抽象方法 3.抽象类可以有构造器 4.抽象类中的方法可以是任意访问修饰符 5.抽象类的字段声明可以是任意的 6.一个类最多只能继承一个抽象类 | 1.接口使用interface关键字声明 2.子类使用implements关键字来实现接口。如果一个类实现了接口,那么该子类必须实现父接口的所有方法。 3.接口不能有构造器 4.接口方法默认修饰符是public。并且不允许定义为 private 或者 protected 5.接口的字段默认都是 static 和 final 的 6.一个类可以实现多个接口 |
成员变量 | 局部变量 | |
作用域 | 作用范围是整个类 | 在方法或者语句块内有效 |
存储位置和生命周期 | 随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中 | 在方法被调用的时候存在,方法调用完会自动释放,存储在栈内存中 |
初始值 | 有默认初始值 | 没有默认初始值,使用前必须赋值 |
使用原则 | 就近原则,首先在局部位置找,有就使用;接着在成员位置找 |
重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分
重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。
如果两个对象相等,则hashcode一定也是相同的
两个对象相等,对两个对象分别调用equals方法都返回true
两个对象有相同的hashcode值,它们不一定是相等的
因此,当重写equals
方法后有必要将hashCode
方法也重写,这样做才能保证不违背hashCode
方法中“相同对象必须有相同哈希值”的约定。
输入流 | 输出流 |
InputStream字节输入流 | OutputStream字节输出流 |
Reader字符输入流 | Writer字符输出流 |
UDP | TCP | |
是否连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠传输,不使用流量控制和拥塞控制 | 可靠传输,使用流量控制和拥塞控制 |
连接对象个数 | 支持一对一,一对多,多对一和多对多交互通信 | 只能是一对一通信 |
传输方式 | 面向报文 | 面向字节流 |
首部开销 | 首部开销小,仅8字节 | 首部最小20字节,最大60字节 |
场景 | 适用于实时应用(IP电话、视频会议、直播等) | 适用于要求可靠传输的应用,例如文件传输 |
三次握手
-
第一次握手
:客户端要向服务端发起连接请求,首先客户端随机生成一个起始序列号ISN(比如是100),那客户端向服务端发送的报文段包含SYN标志位(也就是SYN=1),序列号seq=100。 -
第二次握手
:服务端收到客户端发过来的报文后,发现SYN=1,知道这是一个连接请求,于是将客户端的起始序列号100存起来,并且随机生成一个服务端的起始序列号(比如是300)。然后给客户端回复一段报文,回复报文包含SYN和ACK标志(也就是SYN=1,ACK=1)、序列号seq=300、确认号ack=101(客户端发过来的序列号+1)。 -
第三次握手
:客户端收到服务端的回复后发现ACK=1并且ack=101,于是知道服务端已经收到了序列号为100的那段报文;同时发现SYN=1,知道了服务端同意了这次连接,于是就将服务端的序列号300给存下来。然后客户端再回复一段报文给服务端,报文包含ACK标志位(ACK=1)、ack=301(服务端序列号+1)、seq=101(第一次握手时发送报文是占据一个序列号的,所以这次seq就从101开始,需要注意的是不携带数据的ACK报文是不占据序列号的,所以后面第一次正式发送数据时seq还是101)。当服务端收到报文后发现ACK=1并且ack=301,就知道客户端收到序列号为300的报文了,就这样客户端和服务端通过TCP建立了连接。
四次挥手
-
第一次挥手
:当客户端的数据都传输完成后,客户端向服务端发出连接释放报文(当然数据没发完时也可以发送连接释放报文并停止发送数据),释放连接报文包含FIN标志位(FIN=1)、序列号seq=1101(100+1+1000,其中的1是建立连接时占的一个序列号)。需要注意的是客户端发出FIN报文段后只是不能发数据了,但是还可以正常收数据;另外FIN报文段即使不携带数据也要占据一个序列号。 -
第二次挥手
:服务端收到客户端发的FIN报文后给客户端回复确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=1102(客户端FIN报文序列号1101+1)、序列号seq=2300(300+2000)。此时服务端处于关闭等待状态,而不是立马给客户端发FIN报文,这个状态还要持续一段时间,因为服务端可能还有数据没发完。 -
第三次挥手
:服务端将最后数据(比如50个字节)发送完毕后就向客户端发出连接释放报文,报文包含FIN和ACK标志位(FIN=1,ACK=1)、确认号和第二次挥手一样ack=1102、序列号seq=2350(2300+50)。 -
第四次挥手
:客户端收到服务端发的FIN报文后,向服务端发出确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=2351、序列号seq=1102。注意客户端发出确认报文后不是立马释放TCP连接,而是要经过2MSL(最长报文段寿命的2倍时长)后才释放TCP连接。而服务端一旦收到客户端发出的确认报文就会立马释放TCP连接,所以服务端结束TCP连接的时间要比客户端早一些。
为什么为什么TCP连接的时候是3次?2次不可以吗
-
因为需要考虑连接时丢包的问题,如果只握手2次,第二次握手时如果服务端发给客户端的确认报文段丢失,此时服务端已经准备好了收发数(可以理解服务端已经连接成功)据,而客户端一直没收到服务端的确认报文,所以客户端就不知道服务端是否已经准备好了(可以理解为客户端未连接成功),这种情况下客户端不会给服务端发数据,也会忽略服务端发过来的数据。
如果是三次握手,即便发生丢包也不会有问题,比如如果第三次握手客户端发的确认ack报文丢失,服务端在一段时间内没有收到确认ack报文的话就会重新进行第二次握手,也就是服务端会重发SYN报文段,客户端收到重发的报文段后会再次给服务端发送确认ack报文。
-
为什么TCP连接的时候是3次,关闭的时候却是4次
- 因为只有在客户端和服务端都没有数据要发送的时候才能断开TCP。而客户端发出FIN报文时只能保证客户端没有数据发了,服务端还有没有数据发客户端是不知道的。而服务端收到客户端的FIN报文后只能先回复客户端一个确认报文来告诉客户端我服务端已经收到你的FIN报文了,但我服务端还有一些数据没发完,等这些数据发完了,服务端才能给客户端发FIN报文(所以不能一次性将确认报文和FIN报文发给客户端,就是这里多出来了一次)。
Get | Post | |
安全性 | Get是不安全的,因为在传输过程,数据被放在请求的URL中 | Post的所有操作对用户来说都是不可见的,相对安全 |
url数据大小 | Get请求提交的url中的数据受浏览器和服务器的限制,防止有人恶意发送请求 | Post请求url数据没有大小限制 |
表单字符集 | Get限制Form表单的数据集的值必须为ASCII字符 | Post支持整个ISO10646字符集 |
TCP数据包数量 | GET产生一个TCP数据包 | POST产生两个TCP数据包 |
执行效率 | Get执行效率比Post快 | Post执行效率比Get慢 |
cookie | cookie是由Web服务器保存在用户浏览器上的小文件(key-value格式),包含用户相关的信息。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户身份。 |
session | session是依赖Cookie实现的。session是服务器端对象 session 是浏览器和服务器会话过程中,服务器分配的一块储存空间。服务器默认为浏览器在cookie中设置 sessionid,浏览器在向服务器请求过程中传输 cookie 包含 sessionid ,服务器根据 sessionid 获取出会话中存储的信息,然后确定会话的身份信息。 |
Token | Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。 使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。 |
session与token区别 |
1.session机制存在服务器压力增大,CSRF跨站伪造请求攻击,扩展性不强等问题; 2.session存储在服务器端,token存储在客户端 3.token提供认证和授权功能,作为身份认证,token安全性比session好 4.session这种会话存储方式方式只适用于客户端代码和服务端代码运行在同一台服务器上,token适用于项目级的前后端分离(前后端代码运行在不同的服务器下) |
cookie与session区别 |
1.存储位置与安全性:cookie数据存放在客户端上,安全性较差,session数据放在服务器上,安全性相对更高 2.存储空间:单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie,session无此限制 3.占用服务器资源:session一定时间内保存在服务器上,当访问增多,占用服务器性能,考虑到服务器性能方面,应当使用cookie |
Servlet生命周期 |
Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化; 请求到达时调用Servlet的service()方法,service()方法会根据需要调用与请求对应的doGet或doPost等方法; 当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的destroy()方法。 init方法和destory方法只会执行一次,service方法客户端每次请求Servlet都会执行。Servlet中有时会用到一些需要初始化与销毁的资源,因此可以把初始化资源的代码放入init方法中,销毁资源的代码放入destroy方法中,这样就不需要每次处理客户端的请求都要初始化与销毁资源。 |
Servlet执行流程 |
web客户向Servlet容器发出HTTP请求; Servlet容器解析web的HTTP请求. Servlet容器创建一个HttpRequest对象,在这个对象中封装了http请求信息; Servlet容器创建一个HttpResponse对象; Servlet容器(如果访问的该servlet不是在服务器启动时创建的,则先创建servlet实例并调用init()方法初始化对象)调用HttpServlet的service()方法,把HttpRequest和HttpResponse对象为service方法的参数传给HttpServlet对象; HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息; HttpServlet调用HttpResponse的有关方法,生成响应数据; Servlet容器把HttpServlet的响应结果传给web客户. |
forward | redirect | |
实际发生地址不同,地址栏不同 | 转发是发生在服务器的,转发是由服务器进行跳转的,浏览器的地址栏是没有发生变化的,实现转发只是一次的http请求 | 重定向是由浏览器进行跳转的,浏览器的地址会发生变化的,由浏览器进行的页面跳转,实现重定向会发出两个http请求,request域对象是无效的,因为它不是同一个request对象 |
访问范围不一样 | 转发是服务器跳转只能去往当前web应用的资源 | 重定向是浏览器跳转,可以去往任何的资源 |
传递数据的类型不同 | 转发的request对象可以传递各种类型的数据,包括对象 | 重定向只能传递字符串 |
跳转的时间不同 | 转发:执行到跳转语句时就会立刻跳转 | 重定向:整个页面执行完之后才执行跳转 |
典型的应用场景 | forward一般用于用户登陆的时候根据角色转发到相应的模块 | redirect一般用于用户注销登陆时返回主页面和跳转到其它的网站等 |
Http请求步骤
根据域名和 DNS 解析到服务器的IP地址 (DNS + CDN)
通过ARP协议获得IP地址对应的物理机器的MAC地址
浏览器对服务器发起 TCP 3 次握手
建立 TCP 连接后发起 HTTP 请求报文
服务器响应 HTTP 请求,将响应报文返回给浏览器
短连接情况下,请求结束则通过 TCP 四次挥手关闭连接,长连接在没有访问服务器的若干时间后,进行连接的关闭
浏览器得到响应信息中的 HTML 代码, 并请求 HTML 代码中的资源(如js、css、图片等)
浏览器对页面进行渲染并呈现给用户
状态码 | 类别 | 描述 |
1xx | Informational(信息状态码) | 信息,服务器收到请求,需要请求者继续执行操作 |
2xx | Success(成功状态码) | 成功,操作被成功接收并处理 |
3xx | Redirection(重定向状态码 | 重定向,需要进一步的操作以完成请求 |
4xx | Client Error(客户端错误状态码) | 客户端错误,请求包含语法错误或无法完成请求 |
5xx | Server Error(服务器错误状态码) | 服务器错误,服务器在处理请求的过程中发生了错误 |
字符型常量和字符串常量的区别
形式上:字符常量是单引号引起的一个字符,字符串常量是双引号引起的若干个字符
含义上:字符常量相当于一个整形值(ASCII值),可以参加表达式运算,字符串常量代表一个地址值(该字符串在内存中存放位置)
占用内存大小:字符常量只占两个字节,字符串常量占若干个字节
String、StringBuilder、StringBuffer区别
String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。
线程安全
String中的对象是不可变的,也就可以理解为常量,线程安全。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能
每次对String 类型对象进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。
StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
使用场景
在字符串内容不经常发生变化的业务场景,优先使用String类,例如常量声明、少量的字符串拼接操作等。
在单线程环境下,频繁地进行字符串的操作,建议使用StringBuilder,例如SQL语句拼装、JSON封装等。
在多线程环境下,频繁地进行字符串的操作,建议使用StringBuffer,例如XML解析、HTTP参数解析与封装。
运行时异常 | 编译时异常 |
Java 编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。 | |
NullPointerException空指针异常、 ArrayIndexOutBoundException数组下标越界异常、 ClassCastException类型转换异常、 ArithmeticExecption算术异常 |
throws 关键字和 throw 关键字在使用上的几点区别如下:
-
throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常。
-
throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。调用该方法的方法必须包含可处理异常的代码,否则也要在方法声明中用 throws 关键字声明相应的异常。
Spring面试题
Spring的核心是IOC(依赖注入)和AOP(面向切面编程)
Spring优点 | Spring缺点 |
1.方便解耦,简化开发 Spring就是一个大工厂,可以将所有对象的创建和依赖关系的维护,交给Spring管理。 2.AOP编程的支持 Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。 3.声明式事务的支持 只需要通过配置就可以完成对事务的管理,而无需手动编程。 4.方便程序的测试 Spring对Junit4支持,可以通过注解方便的测试Spring程序。 5.方便集成各种优秀框架 Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架的直接支持 6.降低JavaEE API的使用难度 Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低 | 1.Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低 2.Spring依赖反射,反射影响性能 3.使用门槛升高,入门Spring需要较长时间 |
Spring框架中都用到了哪些设计模式
1.工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例
2.单例模式:Bean默认为单例模式
3.代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
4.模板方法:用来解决代码重复的问题。比如:RestTemplate, JmsTemplate, JpaTemplate
5.观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被动更新,如Spring中listener的实现--ApplicationListene
什么是Spring IOC容器
控制反转它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对对象组件控制权的转移,从程序代码本身转移到了外部容器
控制反转有什么作用
1.管理对象的创建和依赖关系的维护
2.解耦,由容器去维护具体的对象
3.托管了类的整个生命周期
构造器注入 | setter注入 | |
部分注入 | 没有部分注入 | 有部分注入 |
覆盖setter属性 | 不会覆盖 setter 属性 | 会覆盖 setter 属性 |
任意修改是否创建新实例 | 任意修改都会创建一个新实例 | 任意修改不会创建一个新实例 |
适用场景 | 适用于设置很多属性 | 适用于设置少量属性 |
Aop(面向切面编程)
作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),通过面向切面编程减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。常用于权限认证、日志、事务处理等
通知类型 | 说明 | 使用场景 |
前置通知(Before) | 在目标方法被执行之前调用通知 | |
后置通知(After) | 在目标方法被执行之前调用通知 | 记录日志(方法已经调用,但不一定成功) |
返回通知(After-returning) | 在目标方法正常返回后执行通知,如果方法没有正常返返回,例如抛出异常,则返回通知不会执行,可以通过配置得到目标方法的返回值 | 记录日志(方法已经成功调用) |
异常通知(After-throwing) | 在目标方法抛出异常后调用通知 | 异常处理 |
环绕通知(Around) | 通知包裹了目标方法,在目标方法调用之前和调用之后执行自定义的行为 | 事务权限控制 |
SpringBean的生命周期
1.Spring对bean进行实例化
2.Spring将值和bean的引用注入到bean对应的属性中
3.如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法
4.如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
5.如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来
6.如果bean实现了BeanPostProcessor接口,Spring将调用它们的post-ProcessBeforeInitialization()方法
7.如果bean实现了InitializingBean接口,Spring将调用它们的after-PropertiesSet()方法。类似地,如果bean使用initmethod声明了初始化方法,该方法也会被调用
8.此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁
9.如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用
使用@Autowired注解自动装配bean的过程是怎样的
在使用@Autowired注解之前需要在Spring配置文件进行配置<context:annotation-config />
标签,然后在启动spring IoC时,容器就会自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean
1.如果查询结果刚好为一个,就将该bean装配给@Autowired指定的属性
2.如果查询的结果不止一个,会抛出异常,需要配合@Qualifier注解根据名称来查找
3.如果配合@Qualifier注解根据名称来查找的结果为空,会抛出异常,可以将@Autowire注解的required属性设置为false
@Autowired | @Resource |
按照类型装配 | 按照名称进行装配 |
默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false |
@Controller用于标注控制层组件
@Service用于标注业务层组件
@Repository用于标注数据访问层组件,即DAO组件
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
Spring框架的事务管理有哪些优点
为不同的事务API 如 JTA,JDBC,Hibernate,JPA 和JDO,提供一个不变的编程模式。
为编程式事务管理提供了一套简单的API而不是一些复杂的事务API
支持声明式事务管理。
和Spring各种数据访问抽象层很好得集成
SpringMVC面试题
Spring MVC是一个基于Java,实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把模型-视图-控制器分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。
SpringMvc优点
1.可以支持各种视图技术,而不仅仅局限于JSP
2.与Spring框架集成
3.清晰的角色分配:前端控制器(dispatcherServlet) , 处理器映射器(handlerMapping),处理器适配器(HandlerAdapter),视图解析器(ViewResolver)
4.支持各种请求资源的映射策略
SpringMvc工作流程
1.用户发送请求至前端控制器DispatcherServlet
2.DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handler
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响应用户
SpringBoot面试题
SpringBoot优点
1.容易上手,提升开发效率,为 Spring 开发提供一个更快、更广泛的入门体验
2.开箱即用,远离繁琐的配置
3.没有代码生成,也不需要XML配置
4.提供了一系列大型项目通用的非业务性功能
5.避免大量的 Maven 导入和各种版本冲突
@SpringBootApplication相当于@SpringBootConfiguration+@ComponentScan+@EnableAutoConfiguration
@SpringBootConfiguration 通过与 @Bean 结合完成Bean的 JavaConfig 配置
@ComponentScan 通过范围扫描的方式,扫描特定注解注释的类,将其注册到Spring容器
EnableAutoConfiguration 通过 spring.factories 的配置,并结合 @Condition 条件,完成bean的注册
bootstrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud Config 或者 Nacos 中会用到它。且 bootstrap 里面的属性不能被覆盖
application由ApplicatonContext 加载,用于 spring boot 项目的自动化配置
Spring-boot-starter-parent 有什么作用
定义了 Java 编译版本为 1.8 。
使用 UTF-8 格式编码。
继承自 spring-boot-dependencies,这个里边定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写依赖时才不需要写版本号。
执行打包操作的配置。
自动化的资源过滤。
自动化的插件配置。
针对 application.properties 和 application.yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev.properties 和 application-dev.yml
Spring Boot 项目最终打包成的 jar 是可执行 jar ,这种 jar 可以直接通过 java -jar xxx.jar
命令来运行,这种 jar 不可以作为普通的 jar 被其他项目依赖,即使依赖了也无法使用其中的类
SpringCloud面试题
优点 | 缺点 |
1.产出于Spring大家族,Spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善 2.组件丰富,功能齐全。Spring Cloud 为微服务架构提供了非常完整的支持。例如、配置管理、服务发现、断路器、微服务网关等 3.Spring Cloud 社区活跃度很高,教程很丰富,遇到问题很容易找到解决方案 4.服务拆分粒度更细,耦合度比较低,有利于资源重复利用,有利于提高开发效率 5.可以更精准的制定优化服务方案,提高系统的可维护性 6.减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发 7.微服务可以是跨平台的,可以用任何一种语言开发 8.适于互联网时代,产品迭代周期更短 | 1.微服务过多,治理成本高,不利于系统维护 2.分布式系统开发的成本高 |
SpringBoot和SpringCloud的区别
SpringBoot专注于快速、方便的开发单个微服务个体,SpringCloud关注全局的服务治理框架。SpringCloud将SpringBoot开发的一个个单体微服务整合并管理起来,为各个微服务之间提供配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等集成服务
SpringBoot可以离开SpringCloud独立开发项目, 但是SpringCloud离不开SpringBoot ,属于依赖的关系。
Mybatis面试题
是一个半ORM(对象关系映射)框架。避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
优点 | 缺点 |
基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL写在XML里,解除SQL与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL语句,并可重用 | SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求 |
与JDBC相比,减少了50%以上的代码量,消除了JDBC大量冗余的代码,不需要手动开关连接 | |
很好的与各种数据库兼容 | SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库 |
提供映射标签,支持对象与数据库的字段映射;提供对象关系映射标签,支持对象关系组件维护 | |
能够与Spring很好的集成 |
MyBatis的工作原理
1.读取 MyBatis 配置文件:mybatis-config.xml 为 MyBatis 的全局配置文件,包含了 MyBatis 行为的设置和属性信息,例如数据库连接信息和映射文件。
2.加载映射文件mapper.xml。映射文件即 SQL 映射文件,该文件中配置了操作数据库的 SQL 语句,需要在 MyBatis 配置文件 mybatis-config.xml 中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
3.构造会话工厂:通过 MyBatis 的环境等配置信息构建会话工厂 SqlSessionFactory。
4.创建会话对象:由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 语句的所有方法。
5.Executor 执行器:MyBatis 底层定义了一个 Executor 接口来操作数据库,它将根据 SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
6.MappedStatement 对象:在 Executor 接口的执行方法中有一个 MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等信息。
7.输入参数映射:输入参数类型可以是 Map、List 等集合类型,也可以是基本数据类型和 POJO 类型。输入参数映射过程类似于 JDBC 对 preparedStatement 对象设置参数的过程。
8.输出结果映射:输出结果类型可以是 Map、 List 等集合类型,也可以是基本数据类型和 POJO 类型。输出结果映射过程类似于 JDBC 对结果集的解析过程。
#{}和${}的区别
1.#{}是占位符,预编译处理,可以防止SQL注入;${}是拼接符,字符串替换,没有预编译处理,不能防止SQL注入。
2.Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值;Mybatis在处理${}时,是原值传入,就是把${}替换成变量的值,相当于JDBC中的Statement编译
使用MyBatis的mapper接口调用时有哪些要求
1.Mapper.xml文件中的namespace即是mapper接口的全限定类名。
2.Mapper接口方法名和mapper.xml中定义的sql语句id一一对应。
3.Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql语句的parameterType的类型相同。
4.Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql语句的resultType的类型相同。
Mybatis的动态sql是做什么的,都有哪些动态sql
Mybatis动态sql可以让我们在xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种动态sql标签
trim|where|set|foreach|if|choose|when|otherwise|bind。
集合面试题
集合和数组的区别
数组是固定长度的;集合是可变长度的
数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型
数组是Java语言中内置的数据类型,是线性排列的,执行效率和类型检查都比集合快,集合提供了众多的属性和方法,方便操作
List | Set | Map |
ArrayList: object数组 LinkedList:双向循环链表 Vector:Object数据 | HashSet:基于HashMap实现,底层采用HashMap的key来保存元素 | HashMap由数组+链表组成 数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的 |
集合和数组的区别ArrayList、LinkedList、Vector 有何区别
这三者都是实现集合框架中的 List 接口,也就是所谓的有序集合,因此具体功能也比较近似,比如都提供搜索、添加或者删除的操作,都提供迭代器以遍历其内容等功能。
-
数据结构实现:ArrayList 和 Vector 是动态数组的数据结构实现,而 LinkedList 是双向循环链表的数据结构实现。
-
随机访问效率:ArrayList 和 Vector 比 LinkedList 在根据索引随机访问的时候效率要高,因为 LinkedList 是链表数据结构,需要移动指针从前往后依次查找。
-
增加和删除效率:在非尾部的增加和删除操作,LinkedList 要比 ArrayList 和 Vector 效率要高,因为 ArrayList 和 Vector 增删操作要影响数组内的其他数据的下标,需要进行数据搬移。因为 ArrayList 非线程安全,在增删元素时性能比 Vector 好。
-
内存空间占用:一般情况下LinkedList 比 ArrayList 和 Vector 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,分别是前驱节点和后继节点
-
线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;Vector 使用了 synchronized 来实现线程同步,是线程安全的。
-
扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍容量,而 ArrayList 只会增加 50%容量。
-
使用场景:在需要频繁地随机访问集合中的元素时,推荐使用 ArrayList,希望线程安全的对元素进行增删改操作时,推荐使用Vector,而需要频繁插入和删除操作时,推荐使用 LinkedList。
HashMap | HashSet | |
父接口 | 实现了Map接口 | 实现Set接口 |
存储数据 | 存储键值对 | 仅存储对象 |
添加元素 | 调用put()向map中添加元素 | 调用add()方法向Set中添加元素 |
计算哈希值 | HashMap使用键(Key)计算hashcode | HashSet使用对象来计算hashcode值,对于两个对象来说hashcode可能相同,需要用equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false |
获取元素的速度 | HashMap相对于HashSet较快,因为它是使用唯一的键获取对象 | HashSet较HashMap来说比较慢 |
collection和collections有什么区别
java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List、Set,Queue。
java.util.Collections 则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
Vue面试题
Java虚拟机面题
jvm包含两个系统和两个组件
类装载(Class loader) | 根据给定的全限定名称来装载class文件到运行时数据区中的方法区 |
执行引擎(Execution engine) | 执行字节码中的指令 |
本地接口(Native Interface) | 是其它编程语言交互的接口 |
运行时数据区域(Runtime data area) | 这就是我们常说的JVM的内存 |
首先通过编译器把 Java 代码转换成字节码,类加载器(ClassLoader)再把字节码加载到内存中,将其放在运行时数据区(Runtime data area)的方法区内,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的解释器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
java堆 | Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存 |
方法区 | 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据 |
java虚拟机栈 | 用于存储局部变量表、操作数栈、动态链接、方法出口等信息 |
本地方法栈 | 与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的 |
程序计数器 | 当前线程所执行的字节码的行号指示器,字节码解释器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成 |
堆栈的区别
堆 | 栈 | |
物理地址 | 物理地址分配是不连续的,因此性能慢些。在GC的时候需要考虑到不连续的分配,所以有各种垃圾回收算法。 | 栈使用的是数据结构中的栈,具有先进后出的规则,物理地址分配是连续的,因此性能快 |
分配内存时机 | 堆因为是不连续的,所以分配的内存是在运行期 确认的,因此大小不固定。一般堆大小远远大于栈。 | 栈是连续的,所以分配的内存大小要在编译期 就确认,大小是固定的。 |
存放的内容 | 堆存放的是对象的实例和数组。此区域更关注的是数据的存储 | 栈存放:局部变量,操作数栈,返回结果。此区域更关注的是程序方法的执行。 |
程序的可见度 | 堆对于整个应用程序都是共享的、可见的。 | 栈对当前线程是可见的,是线程私有。他的生命周期和线程相同。 |
JVM垃圾回收算法
标记-清除算法 | 标记无用对象,然后进行清除回收。 缺点:效率不高,无法清除垃圾碎片。 |
复制算法 | 按照容量划分两个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的那块内存空间清理掉。 缺点:内存使用率不高,只有原来的一半。 |
标记-整理算法 | 标记无用对象,让所有存活的对象都向一端移动,然后清除掉端边界以外的内存。 |
分代收集算法 | 根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代采用复制算法,老年代采用标记整理算法。 |