风清扬Java面试题突击100道!
https://www.zhihu.com/people/13148848
1. 你对面向对象的理解
- 对比面向对象和面向过程的区别
- 面向对象:是以组织者角度思考,eg:干什么事,找哪些人来做
- 面向过程:是以执行者角度思考,eg:到底怎么干
- 三大特性
- 封装
- 继承
- 多态
2. JDK/JRE/JVM
-
简述三者的关系
- JDK:Java开发工具包,提供了Java的开发环境和运行环境JRE
- JRE:Java运行环境,包括了Java虚拟机和一些基础类库
- JVM:Java虚拟机,执行字节码文件
-
Java如何实现跨平台
- Java源码要编译成class文件,才能被虚拟机执行。一次编译,到处运行
- 我们在不同的平台安装不同的JVM,使用同一套class文件,这样就实现了跨平台
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2RSnvgqj-1620227111528)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210501135614290.png)]
3. ==和equals的区别
- ==比较的值
- 基本数据类型(栈、方法区):比较的是数值
- 引用数据类型(堆):比较的是地址
- equals默认比较的也是地址
- String类重写了Object的equals,实现了比较字面值
package cn.com.gs.common.test;
public class EqualsTest {
public static void main(String[] args) {
// 引用数据类型
String s11 = new String("zs");
String s12 = new String("zs");
System.out.println(s11 == s12);// false
// 基本数据类型,相加时相当于new
String s21 = "zs";
String s22 = "zs";
System.out.println(s21 == s22); // true
String s23 = "zszs";
String s24 = s21 + s22;// 底层使用new StringBuild创建一个新的对象,再赋值
System.out.println(s23 == s24); // false
// 常量
final String s31 = "zs";
final String s32 = "zs";
final String s33 = s31 + s32;//
String s34 = s31 + s32;
final String s35 = s21 + s22;// 底层使用new StringBuild创建一个新的对象
System.out.println(s31 == s32); // true
System.out.println(s23 == s33); // true
System.out.println(s23 == s34); // true
System.out.println(s23 == s35); // false
}
}
4. final关键字
可以修饰类,变量,方法等。
-
修饰类:表示类不可继承;eg:String就是常量类
-
修饰变量:
-
基本数据类型:表示值不可更改
-
引用数据类型:表示引用指向不可更改,但是引用的对象里的属性值还是可变的
final Student student = new Student(1, "Andy"); student.setAge(18);// 这种是可以的 student = new Student(2, "Andy");// 这种是不可以的
-
-
修饰方法:表示方法不可重写
5. String,StringBuild,StringBuffer区别
String | StringBuild | StringBuffer | |
---|---|---|---|
是否可变 | final修饰,每次声明的都是不可变变量 | final修饰,字符串支持动态修改,append方法 | final修饰,字符串支持动态修改 |
线程安全 | / | 线程不安全 | 线程安全,使用synchronized修饰 |
性能 | / | 高 | 低 |
StringBuild使用:在方法内部使用,处理字符串的拼接,此时不会有多线程安全问题。
多线程执行add方法时,每个线程都有自己独有的栈区,资源不共享时不会出问题。
只有多线程访问同一个共享资源时需要考虑安全问题。
public void add() {
StringBuild sb = new StringBuild();
sb.append("zs");
}
6. 接口和抽象类的区别
- 接口
- 类:interface修饰
- 静态常量:public static final修饰
- 方法:public abstract修饰的抽象方法,没有方法体(jdk1.8以后,可以有实现的方法,需要在方法上加上default或static)
- 抽象类
- 类:abstract修饰
- 变量:
- 构造器:不能被实例化,只能被子类调用
- 方法:可以有抽象的,也可以有非抽象的
- 举例:BaseDao
7. 递归算法笔试:
求N的阶乘
公式:N! = (n-1)!*n
例如:3! = 1 * 2 * 3
出口:n0 或 n1,return 1;
求解菲波那切数列的第N个数是几
数字规律:1,1,2,3,4,8,13,21…
8.Integer自动拆箱装箱
// Integer,装箱valueOf方法,数值在-128~127,就返回事前缓存好的数值,其他则new
Integer i1 = 128;// 自动装箱Integer.valueOf(128),128超出范围,则new Integer(128)
Integer i2 = 128;
int i3 = 128;
System.out.println(i1 == i2);// false
System.out.println(i1 == i3);// true,自动拆箱valueOf,比较数值
System.out.println(i2 == i3);// true
Integer i4 = 127;
Integer i5 = 127;
System.out.println(i4 == i5);// true
9. 重写和重载
重写:发生在父子类之间,子类重写父类的方法,同名同参
重载:发生在同一个类中,同名不同参
10. list和set
list:有序,可重复
set:无序,不可重复。无序指输出顺序!=输入顺序,和排序无关
11. ArrayList和LinkedList区别
ArrayList | LinkedList | Vector | |
---|---|---|---|
底层结构 | 数组,连续的内存空间; 长度固定,不够用时自动扩容1.5倍 | 双向链表,不连续的内存空间 | |
速度(不太严谨) | 查找快:因为地址连续 删除/插入慢:因为数据要移动 | 查找慢:因为需要指针一个个寻找 删除/插入快:数据不需要移动,只需要改变前后节点的指针指向即可 | |
线程安全 | 不安全 | 不安全 | 安全,synchronized修饰 |
在查找速度上,需要区分场景:
- 根据位置查:ArrayList > LinkedList
- 根据值查:两者都需遍历,性能不分上下
在插入速度上,需要区分场景:
1. 在头部/中间位置插入:LinkedList > ArrayList
2. 在末尾插入:不分上下
延伸:
确定有1000个对象要存储,使用哪个更省内存?
答:ArrayList更省内存,因为双向链表要存储指针,指针也占内存。
初始化时就分配1000的内存,避免扩容;
ArrayList如何扩容?
第一次创建一个数组,长度为10;
当存第11个的时候,就自动创建一个长度是15的数组;
然后将所有对象迁移到新数组中。
12. Spring的IOC
-
你的理解
控制反转:将创建对象的动作交给Spring容器
解析+赋值
-
应用场景
-
具体怎么实现
-
配置文件实现
怎么加载配置文件?
怎么解析xml? dom4j解析
<bean></bean>
-
注解实现@Autowired
什么时候能找到注解?
答:启动流程:扫描所有包,拿到@controller修饰的类,再找@Autowired修饰的属性,根据类型进行依赖注入
-
13. 如何在双向链表A、B之间插入一个C?
// 先定义C
C.pre = A
C.next = A.next;
// 再从后往前修改指针
A.next.pre = C;
A.next = C
14. HashSet存储原理
HashSet<Object> set = new HashSet<>();
set.add("a");
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
add方法底层还是使用的HashMap来存储,值作为key
延伸:
为什么使用HashSet?
答:要保证数据的唯一性,Set可以保证数据不重复;
怎么保证数据不重复?
答:使用遍历来比对,效率太低。这时出现了Hash算法。
根据需要,重写Object的hashCode方法;
在存储对象时,计算hashCode, 再用hash值和数组长度-1进行位运算,得到要存储的位置。
如果我们要存的位置没有元素,直接存储;
如果有元素,计算equals方法,相等即同一对象不存储,不相等则形成链表(???再优化为红黑树)
为什么采用hash算法?
答:通过hash算法,确认存储的每个对象唯一。
hash算法不可逆,输出值为定值,计算时可减小数据量。
15. ArrayList和Vector
ArrayList:不安全,效率高;
Vector:安全,效率低
16. HashTable&HashMap&ConcurrentHashMap
HashTable:哈希表,本质是数组,数组的元素是链表
HashTable | HashMap | ConcurrentHashMap | |
---|---|---|---|
本质是数组,数组的元素是链表 | 本质是数组,put/get | 分段锁 | |
线程安全 | 安全 | 不安全,多线程操作同一个HashMap,可能出现死锁问题; 解决方式1.0:Collections.synchronizedMap()方法,但是性能依旧有问题,相当于用了HashTable 解决方式2.0:jdk提供了并发包concurrent,兼顾了线程和效率的问题 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XB3uqZbo-1620227111530)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210501181206650.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PljfMw5t-1620227111532)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210501181142735.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OVMk9fs9-1620227111533)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210501181012036.png)]
17. 开发一个自己的栈,你会怎么写?
栈的特点:底层数组,先进后出
入栈:arr[arr.length + 1] = object
出栈:
- pop():取出并移除
- peak():取出
18. 开发一个双向链表的节点类
public class Node<T> {
Node pre;
Node next;
T data;
}
19. IO流
-
分类
- 按方向分类:输入流、输出流
- 按读取单位分:字节流、字符流
-
如何选择字节流和字符流?
- 字节流:上传下载时,不需要具体解析文件内容时,使用字节流
- 字符流:需要具体解析文件内容时,使用字符流
20. serialVersionUID的作用是什么
-
serialVersionUID什么时候出现?
答:在对象序列化到磁盘时,会根据当前类的结构生成serialVersionUID。
如果serialVersionUID由系统生成,可能导致在类更新后,反序列化失败,无法读取以往的版本。
所以一般情况下,我们会手动在类里面写一个固定的serialVersionUID值。
-
有什么作用?
答:用于反序列化时比对。反序列化时,先根据当前类的结构生成一个版本ID,和磁盘的版本ID进行比对,一致则反序列化成功,否则反序列化失败。
-
什么时候需要序列化和反序列化?
答:序列化:当想要把类信息写入磁盘时;反序列化:要从磁盘读取信息时;
21. Java的异常
Throwable:
- Error:一般是JVM异常,有可能是我们写的有问题
- StackOverError栈内存溢出
- Exception:
- runtimeException运行时异常:编译时无法发现,一般是程序写的并不严谨导致的。
- 数组下标越界
- 空指针
- 类型转换异常
- 数字格式异常
- 算法异常
- 非运行时异常:
- IOException
- SQLException
- FileNotFoundException
- NoSuchFileException
- NoSuchMethodException
- runtimeException运行时异常:编译时无法发现,一般是程序写的并不严谨导致的。
22. throw和throws的区别
throw:作用在方法内,用于主动抛出一个异常
throws:作用在方法声明上,可以抛出多个异常
一般我们的异常都是一级一级往上抛,最终通过异常处理机制统一处理。(方便打印日志及响应信息)。
自定义异常怎么写?
框架的异常最终为什么要继承RuntimeException?
答:因为框架制定了一系列规则,我们可能因为不会使用而导致出异常,并不属于逻辑错误。
23. 创建线程的方式
- 继承Thread:重写run方法
- 实现Runnable接口:重写run方法
- 实现Callable接口(可以获取线程执行之后的返回值):重写call方法
其实后两种,严格来说是一个可执行的任务,还是需要创建Thread对象来执行,比如new Thread(runnable).start();
实际开发中,我们一般采用线程池,统一管理,节约资源。
延伸:
启动线程的方式
线程池的使用
24. main方法,不是一个人在战斗
main也是一个线程,同时还有一个GC垃圾回收线程
25. 线程的五大状态
新建new、就绪runnable、运行running、阻塞(blocked、waiting、timed waiting)、死亡terminated
详述共6种状态:getState()
new、runnable、blocked、waiting、timed waiting、terminated
blocked:当线程进入synchronize同步代码块或同步方法,但是没有获取到锁,会进入blocked状态, 直到其他线程释放锁,当前线程拿到锁,进入就绪runnable状态。
waiting:当线程调用wait()或join()时,线程进入waiting状态,
当线程调用notify()/notifyAll(),或者join的线程执行结束后,进入就绪runnable状态。
timed waiting:当线程调用wait(time)或sleep(time)时,线程进入timed waiting状态,
当线程调用notify()/notifyAll(),或者线程休眠结束后,进入就绪runnable状态。
26. 谈谈你对线程安全的理解
描述:当多线程访问同一个对象时,如果不用额外的同步控制,就能得到正确的结果,那我们就说这个对象是线程安全的。
如何做到线程安全?
答:常见的是使用synchronize加锁,例如StringBuffer
27. sleep和wait的区别
sleep | wait | |
---|---|---|
所属 | 属于Thread类,Thread.sleep() | 属于Object类,o.wait() |
锁 | 不释放锁,休眠结束自动进入就绪状态 | 释放锁,等待notify()/notifyAll()唤醒 |
使用范围 | 任意代码 | 只能放在同步代码块或同步方法 |
为什么wait要定义在Object上,而不定义在Thread上?
答:Java的锁是对象级别的,我们需要对象锁来实现多线程的互斥效果
为什么wait必须用在同步代码块或同步方法中?
答:因为wait方法需要被唤醒,需要按照先wait、再唤醒的顺序来执行。用同步来保证顺序。
如果写在非同步的地方,其他线程提前调用了notify方法,那么当前线程就无法被唤醒。
28. 谈谈你对ThreadLocal的理解
ThreadLocal为每个线程创建一个副本,实现在线程的上下文传递对象。
每个线程都有一个对应的Map区域,存放键值对,键为threadLocal,在线程上下文都可获取到。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQIE09Qj-1620227111535)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210502171035909.png)]
29. ThreadLocal的使用
使用实例:在操作数据库时,service调用两个dao,事务要统一控制。
每一个dao层的方法,使用同一个connection连接,由底层进行connection的传递。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eAicQxJq-1620227111535)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210502175005881.png)]
30. 类加载机制
什么是类加载机制?
答:JVM使用Java类的流程:
- Java源文件—>编辑—>class文件
- 类加载器ClassLoader读取class文件,并将其转为java.lang.Class实例。有这个实例,JVM就可以进行创建对象和调用方法等操作了。
class来源有哪些?
答:来源有三种
- Java自带的核心类库,在$JAVA_HOME/jre/lib下,最著名的rt.jar
- Java自带的扩展类,在$JAVA_HOME/jre/lib/ext下
- 我们自己开发的类,以及我们用的第三方jar,位于WEB-INF/lib下
有哪些类加载器?
答:对于不同来源的class,有不同的加载器:
-
Java核心类:由BootstrapClassLoader加载。这个加载器被称为“根加载器或引导加载器”;
BootstrapClassLoader不继承ClassLoader,是JVM内部实现的。
我们无法通过Class类的getClassLoader获取。
-
Java扩展类:由ExtClassLoader加载,被称为“扩展类加载器”;
-
自己开发的类和第三方类库:由AppClassLoader加载,被称为“系统类加载器”;
// BootstrapClassLoader不继承ClassLoader,是JVM内部实现的
String string = new String();
System.out.println(string.getClass().getClassLoader());// null
// 我们自己的Student类
Student student = new Student();
System.out.println(student.getClass().getClassLoader());// AppClassLoader
System.out.println(student.getClass().getClassLoader().getParent());// ExtClassLoader
System.out.println(student.getClass().getClassLoader().getParent().getParent());// null
面试场景:
我现在编写一个类,类全名为java.lang.String, 我们这个类能否替换掉JDK的String类?
答:不能。因为类加载的双亲委派机制。双亲是指AppClassLoader还有两个上级。
在加载class时,AppClassLoader委派给ExtClassLoader,ExtClassLoader又委派给BootstrapClassLoader,BootstrapClassLoader找到该类就使用该类,找不到就让委托人ExtClassLoader自己去找。
所以我们new String()时,是由BootstrapClassLoader找到了JDK的String,无法找到我们自定义的String。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7x1VjuWf-1620227111536)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210502183953634.png)]
31. Ajax的工作原理
异步交互、XMLHttpRequest对象、回调函数
传统模式和Ajax模式对比:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7fMzfBma-1620227111537)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210502210638895.png)]
32. JavaScript的原型机制
JavaScript的原型有一个关键的作用:用来扩展其原有类的特性,从而使框架更易用。
eg:给String类扩展一个hello方法
var str = "abc";
String.prototype.hello = function(){
alert("通过原型的方式扩展原有类的方法或属性")
}
str.hrllo();
33. JSP和Servlet的区别
-
JSP本质是一个Servlet,JSP—>翻译—>Servlet—>编译—>class文件
-
JSP主要是写HTML页面视图,也支持Java代码;Servlet主要写java逻辑代码
jsp翻译成java文件,存储路径tomcat/work/Catilina/localhost/examples/org/apache/jsp/…
34. Servlet生命周期
Servlet是单例的,只初始化一次,用完销毁。
生命周期:创建对象—>初始化—>service()—>doGet/doPost()—>销毁
创建对象的时机
- 默认是第一次访问该Servlet时创建
- 也可以通过web.xml改变创建时机,比如容器启动时就创建,DispatcherServlet。
<!--DispatcherServlet:项目启动就创建-->
<load-on-startup>1</load-on-startup>
<!-- springMVC配置 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
需要考虑线程安全吗?
需要:因为Servlet是单例的,被多个线程共享,需要考虑安全问题。
35. session和cookie的区别
session | cookie | |
---|---|---|
存储位置 | 服务端 | 客户端 |
存储格式 | key-value形式,value存对象 | key-value形式,value存字符串 |
存储大小 | 受服务器内存控制 | 一般来说,最大4k |
生命周期 | 服务器控制,默认30分钟 | 客户端控制,分两种情况: 1.会话级cookie(默认):随浏览器关闭而消失,例如存储sessionId的cookie; 2.非会话级cookie:通过有效期控制的失效时间,例如7天免登录功能;setMaxAge来设置有效期 |
关闭浏览器后,session会消失吗?
答:不会,session由服务器控制,与客户端无关。
为什么会产生session的使用?
答:因为http是无状态的协议,服务器需要有一种机制来存储用户的登录信息,以此来校验用户访问接口的权限。
36. 转发和重定向的区别
转发:发生在服务器端内部的跳转,对于用户来说,只有一次请求;
重定向:发生在客户端的跳转,对于用户来说,发了两次请求。
37. 谈谈MVC三层架构
三层架构:控制层、业务层、数据层。
- 控制层:负责对外提供接口;SpringMVC
- 业务层:编写具体的业务逻辑;Spring
- 数据层:和数据库交互;Mybatis、SpringJdbc
MVC:将控制层分为模型、视图和控制器。
- Model:代表一个数据对象;
- View:代表一个可视化的页面,比如HTML、JSP
- Controller:控制器作用在模型和视图上,用于接收数据以及返回对应的视图。
SpringMVC执行流程:
1.springmvc将所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责对请求进行真正的处理工作。
2.DispatcherServlet查询HandlerMapping,找到处理请求的Controller。
3.DispatcherServlet将请求提交到目标Controller。
4.Controller进行业务逻辑处理后,会返回一个ModelAndView。
5.Dispathcher查询ViewResolver视图解析器,找到ModelAndView对象指定的视图对象。
6.将页面渲染到浏览器客户端。
38. JSP九大内置对象
request、response、session、application
page、pageContext、config、out、exception
39. JSP的4大域对象
域 | 作用范围 |
---|---|
HttpServletRequest | 只能在同一次请求中使用 |
HttpSession | 只能在同一个会话使用 |
PageContext | 只能在当前jsp页面使用 |
ServletContext | 只能在同一个web应用使用 |
40. 并发和并行的区别
并发:同一个CPU执行多个任务,按时间片交替执行
并行:在多个CPU,同时处理多个任务
41. 数据库三大范式
第一范式:列不可分;
第二范式:要有主键;
第三范式:不可存在传递依赖;
举例:商品表和商品类别表。
商品表要关联商品类别,存储一个商品类别ID字段即可;
如果此时存储了一个商品类别Name字段,即存在了传递依赖,造成了冗余,违反了第三范式。
反范式设计:(反第三范式)
上例中,违反了第三范式,正贴合了反范式思想。
反范式是为了提高查询效率,以冗余换时间,将多表关联转为单表查询,提高查询效率。
经典示例:订单表。订单表要存储订单人、订单地址等信息,而不能通过多表关联。
42. 数据库聚合函数
count、sum、max、min、avg
select count(*) from t_student; // 学生总数
select sum(age) from t_student; // 年龄总和
select max(age) from t_student; // 年龄最大的学生
select min(age) from t_student; // 年龄最小的学生
select avg(age) from t_student; // 平均年龄
count(*)和count(字段)的效率问题
43. 左连接、右连接、内连接
左连接(left join):以左表为基础,查询右边满足关联条件的信息,查询结果条数=左表记录数
右连接(right join):以右表为基础,查询左边满足关联条件的信息,查询结果条数=右表记录数
内连接(inner join):查询两表满足关联条件的信息,查询结果条数不确定
44. 如何避免SQL注入
SQL注入,是指通过字符串拼接的方式,构成了一种特殊的,和预期相悖的查询语句
常用注入字符 #(Mysql)、–(Oracle)
# 原语句
select * from t_user where username = ? and password = ?
# 用户输入username值为“or 1=1 #”,密码随便输入,即可登录
# 拼接完的SQL如下
select * from t_user where username = "or 1=1 #" and password = ?
相当于
select * from t_user where username = "or 1=1
解决方案:
使用预处理PreparedStatement对象,好处有:
- 解决SQL注入问题
- 预先编译,执行效率提高
MyBatis如何解决注入?
Mybatis在写语句时,使用#占位可解决注入,而?不行
45. JDBC如何控制对事务的控制
JDBC通过Connection来控制事务,代码如下
try {
connection.setAutoCommint(false);
// dosomeing
connection.commit();
} catch(Exception e) {
connection.rollback();
}
事务的边界要放在业务层,因为业务层可能会调用多个dao,要用一个事物来控制。
46. 事务的特点
原子性:事务中包含的操作要么一起成功,要么一起失败
一致性:数据库中的数据,在事务操作前后,都应该满足业务规则约束。比如A给B转账,转账前后,A和B的账户总金额应该是一致的。
隔离性:一个事务的执行,不能受其他事务的干扰。
持久性:事务一旦提交,结果便是永久性的。即便宕机,依然可以靠事务日志完成数据持久化。
事务日志包括回滚日志和重做日志。
当我们修改数据时,首先会将数据库变化信息记录到重做日志中,然后再对数据库中的数据进行修改。这样即便数据库崩溃,我们也可以通过重做日志进行数据恢复。
47. 事务的4大隔离级别
- read uncommited读未提交:可能会导致脏读、幻读、不可重复读;
- read commited读已提交:避免脏读,可能会导致幻读、不可重复读;
- repeatable read可重复读:避免脏读、不可重复读,可能会导致幻读;
- serializable串行化:避免脏读,幻读、不可重复读,但性能影响较大;
Oracle默认隔离级别:读已提交;
MySQL默认隔离级别:可重复读;
场景:夫妻两人在不同的地方用同一张卡在消费,老婆看上了一件衣服3000块,查询余额有4000块钱,于是刷卡结账,与此同时丈夫请客吃饭,花了2000块钱,导致老婆结账时失败。
脏读:
幻读:
不可重复读:查询余额4000,但是当真正结账时发现只剩2000了,重复读的数据不一致,这就叫不可重复读。
48. synchronized和lock的区别
synchronized | lock | |
---|---|---|
作用位置 | 作用在代码块、方法 | 作用在代码块 |
获取锁机制 | 不需要手动获取锁,进入修饰的代码块、方法自动获取锁 | 需要手动加锁lock() |
释放锁机制 | 发生异常时会自动解锁 | 需要手动解锁unlock(),否则会死锁,一般写在finally里 |
synchronized加锁对象:
- 代码块:可自定义锁住对象,比如synchronized(this){xxx}
- 静态方法:锁住类
- 普通方法:锁住对象
49. TCP和UDP协议
传输层协议
TCP建立连接:三次握手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1hlXTTW9-1620227111539)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503140128851.png)]
TCP断开连接:四次挥手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-74PXpTKH-1620227111540)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503143553230.png)]
50. 什么是死锁?如何避免死锁?
线程1锁住了资源a,线程2锁住了资源b,这时线程1需要访问资源b,线程2也需要访问资源a,双方都在等待对方释放锁,从而发生死锁。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wDAjLS8q-1620227111541)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503144909567.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nEL8a83s-1620227111542)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503144839932.png)]
如何避免死锁?
- 避免嵌套加锁;
- 可以采用tryLock(timeout)的方式,超时后主动退出,避免死锁;
- 降低锁的粒度,不要几个功能共用一把锁
51. 反射
反射是一种能力,能够在程序运行时,动态的获取当前类的所有属性和所有方法,可以动态执行方法,动态给属性赋值等操作。
举例:@Autowired根据反射自动实现赋值
原理:注解解析程序,扫描指定包下哪些属性加了@Autowired注解,加了注解的就去容器中,根据类型找到相应的实现,进行赋值。
52. 谈谈对Spring的认识
- Spring的特性IOC、AOP
- IOC:解耦
- AOP:业务增强
Spring Data和Spring web模块,更方便继承其他主流框架。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XhYi9xui-1620227111542)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503162025948.png)]
53. Spring的bean作用域
- bean默认是单例的
- 原型:每次调用bean的时候,都会创建一个新的对象
- request:一次请求创建一个对象
- session:一个会话中bean共享
bean生命周期
54. Spring的bean是线程安全的吗
默认情况下,bean是单例的,在多线程下如果操作对象,就需要考虑线程安全???
风清扬老师讲:bean是单例的,但是bean无状态,就是没有存储数据,没有通过数据的状态来作为下一步操作的依据,从这点看,是线程安全的。
不懂
56. 事务的传播特性和Spring支持的特性
事务的边界在service层。
如果一个事务内,调用了另外一个有事务的service方法,此时我们需要把这个事务控制在最外层,就发生了事务的传播。
Spring支持的特性
- propagation_required:支持当前事务,如果当前没有事务,就新建一个事务
- propagation_supports:支持当前事务,如果当前没有事务,就以非事务方式进行
- propagation_mandatory:支持当前事务,如果当前没有事务,就抛出异常
- propagation_requires_new:新建事务,如果当前存在事务,就把当前事务挂起
- propagation_not_supported:以非事务方式执行,如果当前存在事务,就把当前事务挂起
- propagation_never:以非事务方式执行,如果当前没有事务,就抛出异常
57. 什么是悲观锁,什么是乐观锁?
悲观锁:利用数据库本身的锁机制来实现,会锁记录。
select * from t_table where id = 1 for update
举例:synchronized
乐观锁:不锁记录,采用CAS(compare and swap)模式,采用version字段作为判断依据。
version字段可以+1,也可采用时间戳
select *,version from t_table where id = 1
得到version = 1,以此查询结果作为更新的限定条件,更新时更新version字段
update t_table set version = version + 1 where id = 1 and version = 1
为什么不采用其他字段呢?
首先,我们不能使用业务字段,业务字段有可能出现ABA的情况
58. Mybatis缓存机制
缓存,主要作用是为了提高查询效率,减少和数据库的交互,减轻数据库的压力。
适用于读多写少的场景。
默认开启一级缓存。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFuVQXDk-1620227111544)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503171126975.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6K87pF8B-1620227111544)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503171215721.png)]
一级缓存总结
- 默认开启
- 作用域在SqlSession,SqlSession关闭重新打开后,会重新查询数据库
- 执行过查询条件,就会把结果放进一级缓存
- 对数据库有任何更新操作,都会清空一级缓存!注意,任何更新操作指增删改。
二级缓存:默认关闭,图有问题
- 默认关闭
- 作用域是SqlSessionFactory
- sqlsession关闭后,才会将结果放进二级缓存
- 对数据库有任何更新操作,都会清空二级缓存!注意,任何更新操作指增删改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7x3w87Rr-1620227111545)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503171950193.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-77VG0HQX-1620227111546)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503172054551.png)]
代码验证:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qlf4uQOq-1620227111547)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503172257444.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1P0LhJQB-1620227111547)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503172318960.png)]
发现sqlsession关闭后,才会将结果放进二级缓存。
59. Mybatis分页方式
分为物理分页和逻辑分页。
- 物理分页(正常人都用):从数据库查出指定条数的数据。eg:分页插件PageHelper
- 逻辑分页:使用Mybatis自带的RowBounds进行分页,一次性查出多条数据,再检索分页中的数据。具体一次性查询多少条数据,受封装jdbc配置的fetch-size决定。
分页插件能实现的原理:
- Mybatis提供了一个拦截器接口Interceptor
- 分页插件实现Interceptor接口,重写intercept()方法
- 在方法内部,拦截到待执行的SQL,然后根据不同的方言、分页参数,重新生成带分页的SQL,执行重写后的SQL,从而实现分页
60. 从在浏览器输入地址到响应,都经历了什么?
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oouHLsXe-1620227111548)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503180413159.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ePm07qwc-1620227111549)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503180251904.png)]
61. Synchronized底层原理
通过两个指令实现。
JDK1.6之前:
JDK1.6之后:提供三种锁,偏向锁、轻量级锁和重量级锁。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P7mBOFna-1620227111549)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503182019858.png)]
synchronized如何保证可见性?
可见性指多个线程对共享变量要可见。
线程工作时,分为本地内存和主内存。获取到锁之后,都从主内存读;当释放锁后,将本地内存的值写到主内存。下一个线程就能从主内存拿到最新的值,从而实现可见性。
62. synchronized和volatile的区别
synchronized | volatile轻量级线程同步机制 | |
---|---|---|
作用位置 | 代码块、方法 | 变量 |
作用 | 可以保证变量修改的可见性和原子性,可能会造成线程阻塞。 | 可以保证变量修改的可见性,但无法保证原子性,不会造成线程阻塞 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rMCEQcka-1620227111550)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210503183536238.png)]
微服务
63. 什么是微服务?
64. 为什么要做微服务?
65. 怎么做服务拆分?
根据业务边界拆分,一般分为公共服务和业务服务。
例如:短信服务、邮件服务等
SpringBoot
1. 启动流程
示例代码:重点@SpringBootApplication,SpringApplication.run()
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
每个SpringBoot程序都有一个主入口,也就是main方法,main里面调用SpringApplication.run()启动整个spring-boot程序,该方法所在类需要使用@SpringBootApplication注解。
@SpringBootApplication = (默认属性)@Configuration + @EnableAutoConfiguration + @ComponentScan。
@ComponentScan:自动扫描并加载符合条件的组件(比如@Component和@Repository等)或者bean定义,最终将这些bean定义加载到IoC容器中。
-
我们可以通过basePackages等属性来细粒度的定制@ComponentScan自动扫描的范围,如果不指定,则默认Spring框架实现会从声明@ComponentScan所在类的package进行扫描。
-
所以SpringBoot的启动类最好是放在root package下,因为默认不指定basePackages。
-
但是一般满足不了我们的需求,还是需要自己指定扫描包路径
@EnableAutoConfiguration:开启自动配置。他实现的关键在于引入了AutoConfigurationImportSelector,其核心逻辑为selectImports方法,逻辑大致如下:
- 从配置文件META-INF/spring.factories加载所有可能用到的自动配置类(spring-boot-autoconfig包);
- 去重,并将exclude和excludeName属性携带的类排除;
- 过滤,将满足条件(@Conditional)的自动配置类返回;
2. Springboot使用JavaConfig整合MVC
在spring.factories配置文件下,预先写好了WebMvcAutoConfiguration类,
Spring
1. JavaConfig技术之web容器启动为什么能够调用Spring框架下的方法呢?
Servlet3.0以后提供了SPI技术
规范:需要在项目根目录下WETA-INF/services文件夹下提供一个配置文件,配置文件名为javax.servlet.ServletContainerInitializer,在配置文件中指定实现了ServletContainerInitializer接口的类路径。
我们用的Spring框架,在spring-web包下已经实现了该规范,如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RYnU1E7Y-1620227111550)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210505213012350.png)]
SpringServletContainerInitializer.java使用了@HandlesTypes({WebApplicationInitializer.class}),意味着对规范进行了扩展,提供了一个WebApplicationInitializer接口。启动时会自动将WebApplicationInitializer的实现类扫描成一个list,遍历执行各个类的onStartup方法。
后续我们想写的容器启动就加载的类,实现WebApplicationInitializer接口重写onStartup方法即可。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.web;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
public interface WebApplicationInitializer {
void onStartup(ServletContext var1) throws ServletException;
}