B站风清扬-Java面试总结

风清扬Java面试题突击100道!

https://www.zhihu.com/people/13148848

1. 你对面向对象的理解

  1. 对比面向对象和面向过程的区别
  • 面向对象:是以组织者角度思考,eg:干什么事,找哪些人来做
  • 面向过程:是以执行者角度思考,eg:到底怎么干
  1. 三大特性
  • 封装
  • 继承
  • 多态

2. JDK/JRE/JVM

  1. 简述三者的关系

    • JDK:Java开发工具包,提供了Java的开发环境和运行环境JRE
    • JRE:Java运行环境,包括了Java虚拟机和一些基础类库
    • JVM:Java虚拟机,执行字节码文件
  2. Java如何实现跨平台

    • Java源码要编译成class文件,才能被虚拟机执行。一次编译,到处运行
    • 我们在不同的平台安装不同的JVM,使用同一套class文件,这样就实现了跨平台

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2RSnvgqj-1620227111528)(C:\Users\Sharm\AppData\Roaming\Typora\typora-user-images\image-20210501135614290.png)]

3. ==和equals的区别

  1. ==比较的值
    • 基本数据类型(栈、方法区):比较的是数值
    • 引用数据类型(堆):比较的是地址
  2. 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区别

StringStringBuildStringBuffer
是否可变final修饰,每次声明的都是不可变变量final修饰,字符串支持动态修改,append方法final修饰,字符串支持动态修改
线程安全/线程不安全线程安全,使用synchronized修饰
性能/

StringBuild使用:在方法内部使用,处理字符串的拼接,此时不会有多线程安全问题。

多线程执行add方法时,每个线程都有自己独有的栈区,资源不共享时不会出问题。

只有多线程访问同一个共享资源时需要考虑安全问题。

public void add() {
    StringBuild sb = new StringBuild();
    sb.append("zs");
}

6. 接口和抽象类的区别

  1. 接口
    • 类:interface修饰
    • 静态常量:public static final修饰
    • 方法:public abstract修饰的抽象方法,没有方法体(jdk1.8以后,可以有实现的方法,需要在方法上加上default或static)
  2. 抽象类
    • 类: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区别

ArrayListLinkedListVector
底层结构数组,连续的内存空间;
长度固定,不够用时自动扩容1.5倍
双向链表,不连续的内存空间
速度(不太严谨)查找快:因为地址连续
删除/插入慢:因为数据要移动
查找慢:因为需要指针一个个寻找
删除/插入快:数据不需要移动,只需要改变前后节点的指针指向即可
线程安全不安全不安全安全,synchronized修饰

在查找速度上,需要区分场景:

  1. 根据位置查:ArrayList > LinkedList
  2. 根据值查:两者都需遍历,性能不分上下

在插入速度上,需要区分场景:

1. 在头部/中间位置插入:LinkedList  > ArrayList
2. 在末尾插入:不分上下

延伸:

确定有1000个对象要存储,使用哪个更省内存?

答:ArrayList更省内存,因为双向链表要存储指针,指针也占内存。

​ 初始化时就分配1000的内存,避免扩容;

ArrayList如何扩容?

第一次创建一个数组,长度为10;

当存第11个的时候,就自动创建一个长度是15的数组;

然后将所有对象迁移到新数组中。

12. Spring的IOC

  1. 你的理解

    控制反转:将创建对象的动作交给Spring容器

    解析+赋值

  2. 应用场景

  3. 具体怎么实现

    • 配置文件实现

      怎么加载配置文件?

      怎么解析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:哈希表,本质是数组,数组的元素是链表

HashTableHashMapConcurrentHashMap
本质是数组,数组的元素是链表本质是数组,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流

  1. 分类

    • 按方向分类:输入流、输出流
    • 按读取单位分:字节流、字符流
  2. 如何选择字节流和字符流?

    • 字节流:上传下载时,不需要具体解析文件内容时,使用字节流
    • 字符流:需要具体解析文件内容时,使用字符流

20. serialVersionUID的作用是什么

  1. serialVersionUID什么时候出现?

    答:在对象序列化到磁盘时,会根据当前类的结构生成serialVersionUID。

    如果serialVersionUID由系统生成,可能导致在类更新后,反序列化失败,无法读取以往的版本。

    所以一般情况下,我们会手动在类里面写一个固定的serialVersionUID值。

  2. 有什么作用?

    答:用于反序列化时比对。反序列化时,先根据当前类的结构生成一个版本ID,和磁盘的版本ID进行比对,一致则反序列化成功,否则反序列化失败。

  3. 什么时候需要序列化和反序列化?

    答:序列化:当想要把类信息写入磁盘时;反序列化:要从磁盘读取信息时;

21. Java的异常

Throwable:

  • Error:一般是JVM异常,有可能是我们写的有问题
    • StackOverError栈内存溢出
  • Exception:
    • runtimeException运行时异常:编译时无法发现,一般是程序写的并不严谨导致的。
      • 数组下标越界
      • 空指针
      • 类型转换异常
      • 数字格式异常
      • 算法异常
    • 非运行时异常:
      • IOException
      • SQLException
      • FileNotFoundException
      • NoSuchFileException
      • NoSuchMethodException

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

img

详述共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的区别

sleepwait
所属属于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类的流程:

  1. Java源文件—>编辑—>class文件
  2. 类加载器ClassLoader读取class文件,并将其转为java.lang.Class实例。有这个实例,JVM就可以进行创建对象和调用方法等操作了。
class来源有哪些?

答:来源有三种

  1. Java自带的核心类库,在$JAVA_HOME/jre/lib下,最著名的rt.jar
  2. Java自带的扩展类,在$JAVA_HOME/jre/lib/ext下
  3. 我们自己开发的类,以及我们用的第三方jar,位于WEB-INF/lib下
有哪些类加载器?

答:对于不同来源的class,有不同的加载器:

  1. Java核心类:由BootstrapClassLoader加载。这个加载器被称为“根加载器或引导加载器”;

    ​ BootstrapClassLoader不继承ClassLoader,是JVM内部实现的。

    ​ 我们无法通过Class类的getClassLoader获取。

  2. Java扩展类:由ExtClassLoader加载,被称为“扩展类加载器”;

  3. 自己开发的类和第三方类库:由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()—>销毁

创建对象的时机
  1. 默认是第一次访问该Servlet时创建
  2. 也可以通过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的区别

sessioncookie
存储位置服务端客户端
存储格式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的账户总金额应该是一致的。

隔离性:一个事务的执行,不能受其他事务的干扰。

持久性:事务一旦提交,结果便是永久性的。即便宕机,依然可以靠事务日志完成数据持久化。

​ 事务日志包括回滚日志和重做日志。

​ 当我们修改数据时,首先会将数据库变化信息记录到重做日志中,然后再对数据库中的数据进行修改。这样即便数据库崩溃,我们也可以通过重做日志进行数据恢复。

image-20210503131956990

47. 事务的4大隔离级别

  • read uncommited读未提交:可能会导致脏读、幻读、不可重复读;
  • read commited读已提交:避免脏读,可能会导致幻读、不可重复读;
  • repeatable read可重复读:避免脏读、不可重复读,可能会导致幻读;
  • serializable串行化:避免脏读,幻读、不可重复读,但性能影响较大;

Oracle默认隔离级别:读已提交;

MySQL默认隔离级别:可重复读;

场景:夫妻两人在不同的地方用同一张卡在消费,老婆看上了一件衣服3000块,查询余额有4000块钱,于是刷卡结账,与此同时丈夫请客吃饭,花了2000块钱,导致老婆结账时失败。

脏读:

幻读:

不可重复读:查询余额4000,但是当真正结账时发现只剩2000了,重复读的数据不一致,这就叫不可重复读。

48. synchronized和lock的区别

synchronizedlock
作用位置作用在代码块、方法作用在代码块
获取锁机制不需要手动获取锁,进入修饰的代码块、方法自动获取锁需要手动加锁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)]

如何避免死锁?

  1. 避免嵌套加锁;
  2. 可以采用tryLock(timeout)的方式,超时后主动退出,避免死锁;
  3. 降低锁的粒度,不要几个功能共用一把锁

51. 反射

反射是一种能力,能够在程序运行时,动态的获取当前类的所有属性和所有方法,可以动态执行方法,动态给属性赋值等操作。

举例:@Autowired根据反射自动实现赋值

原理:注解解析程序,扫描指定包下哪些属性加了@Autowired注解,加了注解的就去容器中,根据类型找到相应的实现,进行赋值。

52. 谈谈对Spring的认识

  1. 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生命周期

img

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的区别

synchronizedvolatile轻量级线程同步机制
作用位置代码块、方法变量
作用可以保证变量修改的可见性和原子性,可能会造成线程阻塞。可以保证变量修改的可见性,但无法保证原子性,不会造成线程阻塞

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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;
}

2. 编程题:使用JavaConfig技术,实现无xml的SSM框架

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值