2022年Java经典面试题(一)
一、基础篇
1、如何实现对象克隆?
【仅供参考】
实现Cloneable接口,重写 clone() 方法。这种方式是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。如果对象的属性的Class也实现 Cloneable 接口,那么在克隆对象时也会克隆属性,即深拷贝。
结合序列化,深拷贝。
通过org.apache.commons中的工具类BeanUtils和PropertyUtils进行对象复制。
2、Object常用方法有哪些?
【仅供参考】
Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
Object常用方法有:toString()、equals()、hashCode()、clone()等。
3、深拷贝和浅拷贝区别是什么?java基础篇反射机制
【仅供参考】
浅克隆:当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将复制。
4、Java 中操作字符串都有哪些类?它们之间有什么区别?
【仅供参考】
操作字符串的类有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
5、&和&&的区别?
【仅供参考】
&&:逻辑与运算符。当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。
&:逻辑与运算符、按位与运算符。
按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。
逻辑与运算符:& 在用于逻辑与时,和&& 的区别是不具有短路性。所在通常使用逻辑与运算符都会使用 &&,而 & 更多的适用于位运算。
6、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
【仅供参考】
值传递。Java 中只有值传递,对于对象参数,值的内容是对象的引用。
7、== 和 equals 的区别是什么?
【仅供参考】
对于基本类型和引用类型 == 的作用效果是不同的。
基本类型:比较的是值是否相同;
引用类型:比较的是引用是否相同;
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法。
== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。
8、用最有效率的方法计算2乘以8?
【仅供参考】
2 << 3。进阶:通常情况下,可以认为位运算是性能最高的。但是,其实编译器现在已经“非常聪明了”,很多指令编译器都能自己做优化。所以在实际实用中,我们无需特意去追求实用位运算,这样不仅会导致代码可读性很差,而且某些自作聪明的优化反而会误导编译器,使得编译器无法进行更好的优化。
9、BIO、NIO、AIO 有什么区别?
【仅供参考】
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
10、JDK/JRE/JVM三者的关系
【仅供参考】
JVM
英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
所有的java程序会首先被编译为.class的类文件,这种类文件可以在虚拟机上执行。也就是说class文件并不直接与机器的操作系统交互,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解释给本地系统执行。
针对不同的系统有不同的 jvm 实现,有 Linux 版本的 jvm 实现,也有Windows 版本的 jvm 实现,但是同一段代码在编译后的字节码是一样的。这就是Java能够跨平台,实现一次编写,多处运行的原因所在。
JRE
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
JRE是Java的运行环境,并不是一个开发环境,所以没有包含任何开发工具,如编译器和调试器等。
如果你只是想运行Java程序,而不是开发Java程序的话,那么你只需要安装JRE即可。
JDK
JDK目录下有个JRE,也就是JDK中已经集成了 JRE,不用单独安装JRE。
另外,JDK中还有一些好用的工具,如jinfo,jps,jstack等。
JRE = JVM + Java 核心类库
JDK = JRE + Java工具 + 编译器 + 调试器
11、普通类和抽象类有哪些区别?
【仅供参考】
普通类不能包含抽象方法,抽象类可以包含抽象方法。
抽象类不能直接实例化,普通类可以直接实例化。
12、重载(Overload)和重写(Override)的区别?
【仅供参考】
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载:一个类中有多个同名的方法,但是具有有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)。
重写:发生在子类与父类之间,子类对父类的方法进行重写,参数都不能改变,返回值类型可以不相同,但是必须是父类返回值的派生类。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。
13、String 是 Java 基本数据类型吗?
【仅供参考】
不是。Java 中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type)。
基本数据类型:数据直接存储在栈上
引用数据类型区别:数据存储在堆上,栈上只存储引用地址
14、final、finally、finalize有什么区别?
【仅供参考】
final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、
修饰变量表示该变量是一个常量不能被重新赋值。
finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法
finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用
finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
15、try-catch-finally 中哪个部分可以省略?
【仅供参考】
catch 可以省略
更为严格的说法其实是:try只适合处理运行时异常,try+catch适合处理运行时异常+普通异常。
也就是说,如果你只用try去处理普通异常却不加以catch处理,编译是通不过的,因为编译器硬性
规定,普通异常如果选择捕获,则必须用catch显示声明以便进一步处理。而运行时异常在编译时
没有如此规定,所以catch可以省略,你加上catch编译器也觉得无可厚非。
理论上,编译器看任何代码都不顺眼,都觉得可能有潜在的问题,所以你即使对所有代码加上
try,代码在运行期时也只不过是在正常运行的基础上加一层皮。但是你一旦对一段代码加上try,
就等于显示地承诺编译器,对这段代码可能抛出的异常进行捕获而非向上抛出处理。如果是普通异
常,编译器要求必须用catch捕获以便进一步处理;如果运行时异常,捕获然后丢弃并且+finally扫
尾处理,或者加上catch捕获以便进一步处理。
至于加上finally,则是在不管有没捕获异常,都要进行的“扫尾”处理。
16、try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
【仅供参考】
答:会执行,在 return 前执行。
注意:在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的return 语
句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,
然后如果在 finally 中修改了返回值,就会返回修改后的值。显然,在 finally 中返回或者修改返回
值会对程序造成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java
中也可以通过提升编译器的语法检查级别来产生警告或错误。
17、throw 和 throws 的区别是什么?
【仅供参考】
Java 中的异常处理除了包括捕获异常和处理异常之外,还包括声明异常和拋出异常,可以通过
throws 关键字在方法上声明该方法要拋出的异常,或者在方法内部通过 throw 拋出异常对象。
throws 关键字和 throw 关键字在使用上的几点区别如下:
throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常
和非受查异常都可以被抛出。
throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个
方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否
则也要在方法签名中用 throws 关键字声明相应的异常。
18、常见的 RuntimeException 有哪些?
【仅供参考】
ClassCastException(类转换异常)
IndexOutOfBoundsException(数组越界)
NullPointerException(空指针)
ArrayStoreException(数据存储异常,操作数组时类型不一致)
还有IO操作的BufferOverflowException异常
19、运行时异常和一般异常(受检异常)区别是什么?
【仅供参考】
运行时异常包括 RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。 Java 编
译器不会检查运行时异常。
受检异常是Exception 中除RuntimeException 及其子类之外的异常。 Java 编译器会检查受检异常。
RuntimeException异常和受检异常之间的区别:是否强制要求调用者必须处理此异常,如果强
制要求调用者必须进行处理,那么就使用受检异常,否则就选择非受检异常
(RuntimeException)。一般来讲,如果没有特殊的要求,我们建议使用RuntimeException异常。
20、final、finally、finalize有什么区别?
【仅供参考】
final:是修饰符,如果修饰类,此类不能被继承;如果修饰方法和变量,则表示此方法和此变量不能在被改变,只能使用。
finally:是 try{} catch{} finally{} 最后一部分,表示不论发生任何情况都会执行,finally 部分可以省略,但如果 finally 部分存在,则一定会执行 finally 里面的代码。
finalize:是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。
二、容器/多线程/反射/JVM/设计模式
1、List、Set、Map 之间的区别是什么?
【仅供参考】
List、Set、Map 的区别主要体现在两个方面:元素是否有序、是否允许元素重复。List元素有序且允许重复。Set元素无序(AbstractSet和Hashset无序, TreeSet二叉树排序,元素有序),不允许重复。Map元素无序(AbstractMap和Hashmap无序, TreeMap二叉树排序,元素有序),key值必须唯一,value可以重复。
2、怎么确保一个集合不能被修改?
【仅供参考】
可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。
示例代码如下:
List<String> list = new ArrayList<>();
list. add("x");
Collection<String> clist = Collections. unmodifiableCollection(list);
clist. add("y"); // 运行时此行报错
System. out. println(list. size());
3、那在什么时候用链表?什么时候用红黑树?
【仅供参考】
对于插入,默认情况下是使用链表节点。当同一个索引位置的节点在新增后超过8个(阈值8):如果此时数组长度大于等于 64,则会触发链表节点转红黑树节点(treeifyBin);而如果数组长度小于64,则不会触发链表转红黑树,而是会进行扩容,因为此时的数据量还比较小。
对于移除,当同一个索引位置的节点在移除后达到 6 个,并且该索引位置的节点为红黑树节点,会触发红黑树节点转链表节点(untreeify)。
4、迭代器 Iterator 是什么?
【仅供参考】
Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。
5、Java 死锁以及如何避免?
【仅供参考】
Java 中的死锁是一种编程情况,其中两个或多个线程被永久阻塞,Java 死锁情况出现至少两个线程和两个或更多资源。
Java 发生死锁的根本原因是:在申请锁时发生了交叉闭环申请。
6、线程的调度策略
【仅供参考】
线程调度器选择优先级最高的线程运行,但是,如果发生以下情况,就会终止线程的运行:
(1) 线程体中调用了 yield 方法让出了对 cpu 的占用权利
(2) 线程体中调用了 sleep 方法使线程进入睡眠状态
(3) 线程由于 IO 操作受到阻塞
(4) 另外一个更高优先级线程出现
(5) 在支持时间片的系统中,该线程的时间片用完
7、不可变对象对多线程有什么帮助
【仅供参考】
前面有提到过的一个问题,不可变对象保证了对象的内存可见性,对不可变对象的读取不需 要进行额外的同步手段,提升了代码执行效率。
8、volatile 关键字的作用
【仅供参考】
对于可见性,Java 提供了 volatile 关键字来保证可见性。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。从实践角度而言,volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详细的可以参见
java.util.concurrent.atomic 包下的类,比如 AtomicInteger。
9、Thread 调用 start() 方法和调用 run() 方法的区别
【仅供参考】
run():普通的方法调用,在主线程中执行,不会新建一个线程来执行。
start():新启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到 CPU 时间片,就开始执行 run() 方法。
10、Java反射创建对象效率⾼还是通过new创建对象的效率⾼?
【仅供参考】
通过new创建对象的效率⽐较⾼。通过反射时,先找查找类资源,使⽤类加载器创建,过程⽐较繁琐,所以效率较低。
11、实现java反射的类有什么?
【仅供参考】
(1)Class:表⽰正在运⾏的Java应⽤程序中的类和接⼝,注意所有获取对象的信息都需要Class类来实现;
(2)Field:提供有关类和接⼝的属性信息,以及对它的动态访问权限;
(3)Constructor:提供关于类的单个构造⽅法的信息以及它的访问权限;
(4)Method:提供类或接⼝中某个⽅法的信息。
12、反射的实现⽅式都有什么?
【仅供参考】
获取Class对象,有4种⽅法:(1)Class.forName(“类的路径”);(2)类名.class;(3)对象名.getClass();(4)基本类型的包装类,可以调⽤包装类的Type属性来获得该包装类的Class对象。
13、Java反射机制主要提供了哪些功能?
【仅供参考】
在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
14、什么是设计模式?你是否在你的代码里面使用过任何设计模式?
【仅供参考】
设计模式是世界上各种各样程序员用来解决特定设计问题的尝试和测试的方法。设计模式是代码可用性的延伸
15、说一下 JVM 有哪些垃圾回收器?
【仅供参考】
Serial:最早的单线程串行垃圾回收器。
Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。
ParNew:是 Serial 的多线程版本。
Parallel 和 ParNew 收集器类似是多线程的,但Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系统的吞吐量。
Parallel Old 是 Parallel 老生代版本,Parallel使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。
CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。
G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。
16、新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
【仅供参考】
新生代回收器:Serial、ParNew、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。
17、队列和栈是什么?有什么区别?
【仅供参考】
队列和栈都是被用来预存储数据的。
队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。
栈和队列很相似,但它运行对元素进行后进先出进行检索。
18、类加载的过程
【仅供参考】
类加载的过程包括:加载、验证、准备、解析、初始化,其中验证、准备、解析统称为连接。
加载:通过一个类的全限定名来获取定义此类的二进制字节流,在内存中生成一个代表这个类的java.lang.Class对象。
验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
准备:为静态变量分配内存并设置静态变量初始值,这里所说的初始值“通常情况”下是数据类型的零值。
解析:将常量池内的符号引用替换为直接引用。
初始化:到了初始化阶段,才真正开始执行类中定义的 Java 初始化程序代码。主要是静态变量赋值动作和静态语句块(static{})中的语句。
19、说一下 JVM 的主要组成部分?及其作用?
【仅供参考】
类加载器(ClassLoader)
运行时数据区(Runtime Data Area)
执行引擎(Execution Engine)
本地库接口(Native Interface)
「组件的作用:」 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
20、说一下类装载的执行过程?
【仅供参考】
类装载分为以下 5 个步骤:
加载:根据查找路径找到相应的 class 文件然后导入;
检查:检查加载的 class 文件的正确性;
准备:给类中的静态变量分配内存空间;
解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
初始化:对静态变量和静态代码块执行初始化工作。
三、Java Web/Spring/Spring MVC/Hibernate/MyBatis/Spring Boot/Spring Cloud
1、JSP 和 servlet 有什么区别?
【仅供参考】
JSP 是 servlet 技术的扩展,本质上就是servlet 的简易方式。servlet 和 JSP 最主要的不同点在于,servlet 的应用逻辑是在 Java 文件中,并且完全从表示层中的 html 里分离开来,而 JSP 的情况是 Java 和 html 可以组合成一个扩展名为 JSP 的文件。JSP 侧重于视图,servlet主要用于控制逻辑。
2、TCP和UDP的区别
【仅供参考】
1、TCP :面向连接,UDP :面向无连接
2、TCP :传输效率低,UDP :传输效率高(有大小限制,一次限定在64kb之内)
3、TCP:可靠,UDP :不可靠
3、http 响应码 301 和 302 代表的是什么?有什么区别?
【仅供参考】
301:永久重定向。
302:暂时重定向。
它们的区别是,301 对搜索引擎优化(SEO)更加有利;302 有被提示为网络拦截的风险。
4、一次完整的HTTP请求所经历几个步骤?
【仅供参考】
根据域名和 DNS 解析到服务器的IP地址 (DNS + CDN)
通过ARP协议获得IP地址对应的物理机器的MAC地址
浏览器对服务器发起 TCP 3 次握手
建立 TCP 连接后发起 HTTP 请求报文
服务器响应 HTTP 请求,将响应报文返回给浏览器
短连接情况下,请求结束则通过 TCP 四次挥手关闭连接,长连接在没有访问服务器的若干时间后,进行连接的关闭
浏览器得到响应信息中的 HTML 代码, 并请求 HTML 代码中的资源(如js、css、图片等)
浏览器对页面进行渲染并呈现给用户
5、spring 常用的注入方式有哪些?
【仅供参考】
setter 属性注入
构造方法注入
注解方式注入
6、spring mvc 有哪些组件?
【仅供参考】
前置控制器 DispatcherServlet。
映射控制器 HandlerMapping。
处理器 Controller。
模型和视图 ModelAndView。
视图解析器 ViewResolver。
7、为什么要使用 spring?
【仅供参考】
spring 提供 ioc 技术,容器会帮你管理依赖的对象,从而不需要自己创建和管理依赖对象了,更轻松的实现了程序的解耦。
spring 提供了事务支持,使得事务操作变的更加方便。
spring 提供了面向切片编程,这样可以更方便的处理某一类的问题。
更方便的框架集成,spring 可以很方便的集成其他框架,比如MyBatis、hibernate 等。
8、解释一下什么是 aop?
【仅供参考】
aop 是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
简单来说就是统一处理某一“切面”(类)的问题的编程思想,比如统一处理日志、异常等。
9、RequestMapping 和 GetMapping 的不同之处在哪里?
【仅供参考】
RequestMapping 具有类属性的,可以进行 GET,POST,PUT 或者其它的注释中具有的请求方法。GetMapping 是 GET 请求方法中的一个特例。它只是 ResquestMapping 的一个延伸,目的是为了提高清晰度。
10、如何在 Spring Boot 中添加通用的 JS 代码?
【仅供参考】
在源文件夹下,创建一个名为 static 的文件夹。然后,你可以把你的静态的内容放在这里面。
例如,myapp.js 的路径是resourcesstaticjsmyapp.js
11、Spring Boot中的监视器是什么?
【仅供参考】
Spring boot actuator是spring启动框架中的重要功能之一。Spring boot监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。
有几个指标必须在生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。监视器模块公开了一组可直接作为HTTP URL访问的REST端点来检查状态。
12、Spring Cloud 优点?
【仅供参考】
集大成者,Spring Cloud 包含了微服务架构的方方面面。
约定优于配置,基于注解,没有配置文件。
轻量级组件,Spring Cloud 整合的组件大多比较轻量级,且都是各自领域的佼佼者。
开发简便,Spring Cloud 对各个组件进行了大量的封装,从而简化了开发。
开发灵活,Spring Cloud 的组件都是解耦的,开发人员可以灵活按需选择组件。
13、SpringBoot 实现热部署有哪几种方式?
【仅供参考】
主要有两种方式:
1、Spring Loaded
2、Spring-boot-devtools
14、jpa 和 hibernate 有什么区别?
【仅供参考】
jpa 全称 Java Persistence API,是Java 持久化接口规范,hibernate 属于 jpa 的具体实现。
15、Spring Boot 还提供了其它的哪些 Starter Project Options?
【仅供参考】
Spring Boot 也提供了其它的启动器项目包括,包括用于开发特定类型应用程序的典型依赖项。
spring-boot-starter-web-services - SOAP Web Services;
spring-boot-starter-web - Web 和 RESTful 应用程序;
spring-boot-starter-test - 单元测试和集成测试;
spring-boot-starter-jdbc - 传统的 JDBC;
spring-boot-starter-hateoas - 为服务添加 HATEOAS 功能;
spring-boot-starter-security - 使用 SpringSecurity 进行身份验证和授权;
spring-boot-starter-data-jpa - 带有 Hibeernate 的 Spring Data JPA;
spring-boot-starter-data-rest - 使用 Spring Data REST 公布简单的 REST 服务;
16、什么是 spring cloud?
【仅供参考】
spring cloud 是一系列框架的有序集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。
17、创建一个 Spring Boot Project 的最简单的方法是什么?
【仅供参考】
Spring Initializr是启动 Spring Boot Projects 的一个很好的工具。
我们需要做一下几步:
1、登录 Spring Initializr,按照以下方式进行选择:
2、选择 com.in28minutes.springboot 为组
3、选择 studet-services 为组件
4、选择下面的依赖项
Web
Actuator
DevTools
5、点击生 GenerateProject
6、将项目导入 Eclipse。文件 - 导入 - 现有的 Maven 项目
18、Spring Initializr 是创建 Spring Boot Projects 的唯一方法吗?
【仅供参考】
Spring Initiatlizr 让创建 Spring Boot 项目变的很容易,但是,你也可以通过设置一个 maven 项目并添加正确的依赖项来开始一个项目。
在我们的 Spring 课程中,我们使用两种方法来创建项目。
第一种方法是 start.spring.io 。
另外一种方法是在项目的标题为“Basic Web Application”处进行手动设置。
手动设置一个 maven 项目
这里有几个重要的步骤:
1、在 Eclipse 中,使用文件 - 新建 Maven 项目来创建一个新项目
2、添加依赖项。
3、添加 maven 插件。
4、添加 Spring Boot 应用程序类。
到这里,准备工作已经做好!
19、hibernate 是如何工作的?
【仅供参考】
读取并解析配置文件。
读取并解析映射文件,创建 SessionFactory。
打开 Session。
创建事务。
进行持久化操作。
提交事务。
关闭 Session。
关闭 SessionFactory。
20、说一下 MyBatis 的一级缓存和二级缓存?
【仅供参考】
一级缓存:基于 PerpetualCache 的HashMap 本地缓存,它的声明周期是和 SQLSession 一致的,有多个 SQLSession 或者分布式的环境中数据库操作,可能会出现脏数据。当Session flush 或 close 之后,该Session 中的所有 Cache 就将清空,默认一级缓存是开启的。
二级缓存:也是基于 PerpetualCache 的HashMap 本地缓存,不同在于其存储作用域为 Mapper 级别的,如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存,并且二级缓存可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现Serializable 序列化接口(可用来保存对象的状态)。
开启二级缓存数据查询流程:二级缓存 -> 一级缓存-> 数据库。
缓存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
四、Redis/RabbitMQ/Kafka/Zookeeper
1、RabbitMQ 怎么避免消息丢失?
【仅供参考】
把消息持久化磁盘,保证服务器重启消息不丢失。
每个集群中至少有一个物理磁盘,保证消息落入磁盘。
2、要保证消息持久化成功的条件有哪些?
【仅供参考】
声明队列必须设置持久化 durable 设置为 true.
消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。
消息已经到达持久化交换器。
消息已经到达持久化队列。
以上四个条件都满足才能保证消息持久化成功。
3、RabbitMQ 每个节点是其他节点的完整拷贝吗?为什么?
【仅供参考】
不是,原因有以下两个:
存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据;
性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。
4、RabbitMQ 怎么保证消息的稳定性?
【仅供参考】
提供了事务的功能。
通过将 channel 设置为 confirm(确认)模式。
5、RabbitMQ 集群有什么用?
【仅供参考】
集群主要有以下两个用途:
高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用;
高容量:集群可以承载更多的消息量。
6、RabbitMQ 的消息是怎么发送的?
【仅供参考】
首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。
7、RabbitMQ 集群中唯一一个磁盘节点崩溃了会发生什么情况?
【仅供参考】
如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:
不能创建队列
不能创建交换器
不能创建绑定
不能添加用户
不能更改权限
不能添加和删除集群节点
唯一磁盘节点崩溃了,集群是可以保持运行的,但你不能更改任何东西。
8、RabbitMQ 有几种广播类型?
【仅供参考】
direct(默认方式):最基础最简单的模式,发送方把消息发送给订阅方,如果有多个订阅者,默认采取轮询的方式进行消息发送。
headers:与 direct 类似,只是性能很差,此类型几乎用不到。
fanout:分发模式,把消费分发给所有订阅者。
topic:匹配订阅模式,使用正则匹配到消息队列,能匹配到的都能接收到。
9、说一下 zookeeper 的通知机制?
【仅供参考】
客户端端会对某个 znode 建立一个 watcher 事件,当该 znode 发生变化时,这些客户端会收到 zookeeper 的通知,然后客户端可以根据 znode 变化来做出业务上的改变。
10、kafka如何不消费重复数据?比如扣款,我们不能重复的扣?
【仅供参考】
其实还是得结合业务来思考,我这里给几个思路:
比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update一下好吧。
比如你是写Redis,那没问题了,反正每次都是set,天然幂等性。
比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的id,类似订单 id之类的东西,然后你这里消费到了之后,先根据这个id去比如Redis里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个id写Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。
11、什么情况会导致 kafka 运行变慢?
【仅供参考】
cpu 性能瓶颈
磁盘读写瓶颈
网络瓶颈
12、zookeeper 都有哪些功能?
【仅供参考】
集群管理:监控节点存活状态、运行请求等。
主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用zookeeper 可以协助完成这个过程。
分布式锁:zookeeper 提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。zookeeper可以对分布式锁进行控制。
命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。
13、集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?
【仅供参考】
可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。
14、Kafka中的分区器、序列化器、拦截器是否了解?它们之间的处理顺序是什么?
【仅供参考】
拦截器 -> 序列化器 -> 分区器
15、kafka的高可用机制是什么?
【仅供参考】
这个问题比较系统,回答出kafka的系统特点,leader和follower的关系,消息读写的顺序即可。
16、kafka中的broker 是干什么的?
【仅供参考】
broker 是消息的代理,Producers往Brokers里面的指定Topic中写消息,Consumers从Brokers里面拉取指定Topic的消息,然后进行业务处理,broker在中间起到一个代理保存消息的中转站。
17、为什么要使用 kafka,为什么要使用消息队列?
【仅供参考】
缓冲和削峰:上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。
解耦和扩展性:项目开始的时候,并不能确定具体需求。消息队列可以作为一个接口层,解耦重要的业务流程。只需要遵守约定,针对数据编程即可获取扩展能力。
冗余:可以采用一对多的方式,一个生产者发布消息,可以被多个订阅topic的服务消费到,供多个毫无关联的业务使用。
健壮性:消息队列可以堆积请求,所以消费端业务即使短时间死掉,也不会影响主要业务的正常进行。
异步通信:很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
18、Redis 持久化有几种方式?
【仅供参考】
Redis 的持久化有两种方式,或者说有两种策略:
RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。
19、Redis 和 memcache 有什么区别?
【仅供参考】
存储方式不同:memcache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;Redis 有部份存在硬盘上,这样能保证数据的持久性。
数据支持类型:memcache 对数据类型支持相对简单;Redis有复杂的数据类型。
使用底层模型不同:它们之间底层实现方式,以及与客户端之间通信的应用协议不一样,Redis 自己构建了 vm 机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
value 值大小不同:Redis 最大可以达到 512mb;memcache 只有 1mb。
20、怎么保证缓存和数据库数据的一致性?
【仅供参考】
合理设置缓存的过期时间。
新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。
五、MySQL
1、如果数据库误操作, 如何执行数据恢复?
【仅供参考】
数据库在某个时候误操作,就可以找到距离误操作最近的时间节点的bin log,重放到临时数据库里,然后选择误删的数据节点,恢复到线上数据库。
2、说一下 MySQL 的锁?
【仅供参考】
MySQL 在 server 层 和 存储引擎层 都运用了大量的锁
MySQL server 层需要讲两种锁,第一种是MDL(metadata lock) 元数据锁,第二种则 Table Lock 表锁。
MDL 又名元数据锁,那么什么是元数据呢,任何描述数据库的内容就是元数据,比如我们的表结构、库结构等都是元数据。那为什么需要 MDL 呢?
主要解决两个问题:事务隔离问题;数据复制问题
InnoDB 有五种表级锁:IS(意向读锁);IX(意向写锁);S(读);X(写);AUTO-INC
在对表进行select/insert/delete/update语句时候不会加表级锁
IS和IX的作用是为了判断表中是否有已经被加锁的记录
自增主键的保障就是有 AUTO-INC 锁,是语句级别的:为表的某个列添加 AUTO_INCREMENT 属性,之后在插⼊记录时,可以不指定该列的值,系统会⾃动为它赋上单调递增的值。
InnoDB 4 种行级锁
RecordLock:记录锁
GapLock:间隙锁解决幻读;前一次查询不存在的东西在下一次查询出现了,其实就是事务A中的两次查询之间事务B执行插入操作被事务A感知了
Next-KeyLock:锁住某条记录又想阻止其它事务在改记录前面的间隙插入新纪录
InsertIntentionLock:插入意向锁;如果插入到同一行间隙中的多个事务未插入到间隙内的同一位置则无须等待
行锁和表锁的抉择
全表扫描用行级锁
3、MySQL 的内连接、左连接、右连接有什么区别?
【仅供参考】
内连接关键字:inner join;左连接:left join;右连接:right join。
内连接是把匹配的关联数据显示出来;左连接是左边的表全部显示出来,右边的表显示出符合条件的数据;右连接正好相反。
4、如何做 MySQL 的性能优化?
【仅供参考】
为搜索字段创建索引。
避免使用 select *,列出需要查询的字段。
垂直分割分表。
选择正确的存储引擎。
5、MySQL 有哪些自增ID?各自场景是什么?
【仅供参考】
表的自增 ID 达到上限之后,在申请值不会变化,进而导致联系插入数据的时候报主键冲突错误。
row_id 达到上限之后,归 0 在重新递增,如果出现相同的row_id 后写的数据会覆盖之前的数据。
Xid 只需要不在同一个 binlog 文件出现重复值即可,理论上会出现重复值,但概率极小可忽略不计。
InnoDB 的 max_trx_id 递增值每次 MySQL重启会保存起来。
Xid 是由 server 层维护的。InnoDB 内部使用 Xid,就是为了能够在 InnoDB 事务和 server 之间做关联。但是,InnoDB 自己的 trx_id,是另外维护的。
thread_id 是我们使用中最常见的,而且也是处理得最好的一个自增 id 逻辑了。使用了insert_unique算法
6、为什么删除了表,表文件的大小还是没变?
【仅供参考】
数据项删除之后 InnoDB 某个页 page A 会被标记为可复用。
delete 命令把整个表的数据删除,结果就是,所有的数据页都会被标记为可复用。但是磁盘上,文件不会变小。
经过大量增删改的表,都是可能是存在空洞的。这些空洞也占空间所以,如果能够把这些空洞去掉,就能达到收缩表空间的目的。
重建表,就可以达到这样的目的。可以使用 alter table A engine=InnoDB 命令来重建表。
7、索引的三种常见底层数据结构以及优缺点
【仅供参考】
三种常见的索引底层数据结构:分别是哈希表、有序数组和搜索树。
哈希表这种适用于等值查询的场景,比如 memcached 以及其它一些 NoSQL 引擎,不适合范围查询。
有序数组索引只适用于静态存储引擎,等值和范围查询性能好,但更新数据成本高。
N 叉树由于读写上的性能优点以及适配磁盘访问模式以及广泛应用在数据库引擎中。
扩展(以 InnoDB 的一个整数字段索引为例,这个 N 差不多是 1200。棵树高是 4 的时候,就可以存 1200 的 3 次方个值,这已经17 亿了。考虑到树根的数据块总是在内存中的,一个 10 亿行的表上一个整数字段的索引,查找一个值最多只需要访问 3 次磁盘。其实,树的第二层也有很大概率在内存中,那么访问磁盘的平均次数就更少了。)
8、当数据库 crash 后,如何恢复未刷盘的数据到内存中?
【仅供参考】
根据 redo log 和 binlog 的两阶段提交,未持久化的数据分为几种情况:
change buffer 写入,redo log 虽然做了fsync 但未 commit,binlog 未 fsync 到磁盘,这部分数据丢失。
change buffer 写入,redo log fsync 未 commit,binlog 已经fsync 到磁盘,先从 binlog 恢复 redo log,再从 redo log 恢复 change buffer。
change buffer 写入,redo log 和binlog 都已经 fsync,直接从 redo log 里恢复。
9、什么是 WAL 技术,有什么优点?
【仅供参考】
WAL,中文全称是 Write-Ahead Logging,它的关键点就是日志先写内存,再写磁盘。MySQL 执行更新操作后,在真正把数据写入到磁盘前,先记录日志。
好处是不用每一次操作都实时把数据写盘,就算 crash 后也可以通过redo log 恢复,所以能够实现快速响应 SQL 语句。
10、MySQL 是如何保证主备同步?
【仅供参考】
主备关系的建立:
一开始创建主备关系的时候,是由备库指定的,比如基于位点的主备关系,备库说“我要从binlog文件A的位置P”开始同步,主库就从这个指定的位置开始往后发。
而主备关系搭建之后,是主库决定要发给数据给备库的,所以主库有新的日志也会发给备库。
MySQL 主备切换流程:
客户端读写都是直接访问A,而节点B是备库,只要将A的更新都同步过来,到本地执行就可以保证数据是相同的。
当需要切换的时候就把节点换一下,A的节点B的备库
一个事务完整的同步过程:
备库B和主库A建立来了长链接,主库A内部专门线程用于维护了这个长链接。
在备库B上通过changemaster命令设置主库A的IP端口用户名密码以及从哪个位置开始请求binlog包括文件名和日志偏移量
在备库B上执行start-slave命令备库会启动两个线程:io_thread和sql_thread分别负责建立连接和读取中转日志进行解析执行
备库读取主库传过来的binlog文件备库收到文件写到本地成为中转日志
后来由于多线程复制方案的引入,sql_thread演化成了多个线程。
11、MySQL 是如何保证数据不丢失的?
【仅供参考】
只要redolog 和 binlog 保证持久化磁盘就能确保MySQL异常重启后回复数据
在恢复数据时,redolog 状态为 commit 则说明 binlog 也成功,直接恢复数据;如果 redolog 是 prepare,则需要查询对应的 binlog事务是否成功,决定是回滚还是执行。
12、主库出问题如何解决?
【仅供参考】
基于位点的主备切换:存在找同步位点这个问题
MySQL 5.6 版本引入了 GTID,彻底解决了这个困难。那么,GTID 到底是什么意思,又是如何解决找同步位点这个问题呢?
GTID:全局事务 ID,是一个事务在提交的时候生成的,是这个事务的唯一标识;它由两部分组成,格式是:GTID=server_uuid:gno
每个 MySQL 实例都维护了一个 GTID 集合,用来对应“这个实例执行过的所有事务”。
在基于 GTID 的主备关系里,系统认为只要建立主备关系,就必须保证主库发给备库的日志是完整的。因此,如果实例 B 需要的日志已经不存在,A’就拒绝把日志发给 B。
13、MySQL 的并行策略有哪些?
【仅供参考】
按表分发策略:如果两个事务更新不同的表,它们就可以并行。因为数据是存储在表里的,所以按表分发,可以保证两个worker 不会更新同一行。缺点:如果碰到热点表,比如所有的更新事务都会涉及到某一个表的时候,所有事务都会被分配到同一个 worker 中,就变成单线程复制了。
按行分发策略:如果两个事务没有更新相同的行,它们在备库上可以并行。如果两个事务没有更新相同的行,它们在备库上可以并行执行。显然,这个模式要求 binlog 格式必须是 row。缺点:相比于按表并行分发策略,按行并行策略在决定线程分发的时候,需要消耗更多的计算资源。
14、MySQL的一主一备和一主多从有什么区别?
【仅供参考】
在一主一备的双 M 架构里,主备切换只需要把客户端流量切到备库;而在一主多从架构里,主备切换除了要把客户端流量切到备库外,还需要把从库接到新主库上。
15、orderby 排序内部原理?
【仅供参考】
MySQL 会为每个线程分配一个内存(sort-buffer)用于排序该内存大小为 sort_buffer_size;
如果排序的数据量小于 sort_buffer_size,排序就会在内存中完成;
内部排序分为两种
全字段排序:到索引树上找到满足条件的主键ID根据主键ID去取出数据放到sort_buffer然后进行快速排序
rowid排序:通过控制排序的行数据的长度来让sort_buffer中尽可能多的存放数据
如果数据量很大,内存中无法存下这么多,就会使用磁盘临时文件来辅助排序,称为外部排序;
外部排序,MySQL会分为好几份单独的临时文件来存放排序后的数据,一般是磁盘文件中进行归并,然后将这些文件合并成一个大文件;
16、MyISAM 和 InnoDB 实现 B树索引方式的区别是什么?
【仅供参考】
InnoDB 存储引擎:B+ 树索引的叶子节点保存数据本身,其数据文件本身就是索引文件。
MyISAM 存储引擎:B+ 树索引的叶子节点保存数据的物理地址,叶节点的 data 域存放的是数据记录的地址,索引文件和数据文件是分离的。
17、MySQL 索引是怎么实现的?
【仅供参考】
索引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。
具体来说 MySQL 中的索引,不同的数据引擎实现有所不同,但目前主流的数据库引擎的索引都是 B+ 树实现的,B+ 树的搜索效率,可以到达二分法的性能,找到数据区域之后就找到了完整的数据结构了,所有索引的性能也是更好的。
18、什么是覆盖索引和索引下推?
【仅供参考】
覆盖索引:
在某个查询里面,索引 k 已经“覆盖了”我们的查询需求,称为覆盖索引。
覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。
索引下推:
MySQL 5.6 引入的索引下推优化(index condition pushdown), 可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
19、如何获取当前数据库版本?
【仅供参考】
使用 select version() 获取当前 MySQL 数据库版本。
20、什么是两阶段提交?
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/24735.html