一. java基础
.1. JDK和JJRE有什么区别?
1.jdk是Java开发的工具包, 主要面向开发人员;
2. jdk包含了jre. 同时页包含了编译java源代码的javac, 还包含了Java调试和分析的工具;
3. jdk是Java开发的核心, 包含了jre运行环境, java工具和java基础类等;
4. jre是java运行环境, 面向Java程序的使用者,而不是开发者;
5. JRE是可以在其上运行、测试和传输应用程序的Java平台。它包括Java虚拟机(jvm)、Java核心类库和支持文件。它不包含开发工具(JDK)–编译器、调试器和其它工具。
2. == 和 equals 的区别是什么?
-
功能不同:
== 是判断两个变量的或者实例是不是指向同一个内存空间;
.equals 是判断两个变量或者实力指向内存空间的值是否相同;
-
定义不同:
.equals是Java中的一个方法;
== 在Jav中只是一个运算符;
3. 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?
不对,两个对象的 hashCode()相同,equals()不一定 true。
举例:
因为当年龄为1和4的时候除以3的余数都是1,所以他们的hashCode是相同的。但是他们的equals是不相同的。
4. final 在 java 中有什么作用?
在Java中,final可以修饰 类 / 方法/ 变量(成员变量和局部变量)
-
在修饰类的时候, 说明这个类不能被继承, 一般用的会比较少
-
修饰方法:
原因1: 被final修饰的方法,不能被子类所重写(但是可以重载),表示这个方法是最终的;
解决办法:子类可以重新定义这个方法, 相同的方法申明. 此时不会产生重写和final的矛盾;
-
修饰变量: final成员变量,表示常量,只能赋值一次. 赋值后无法在改变;
final基本数据类型的值时,一旦发生初始化后便不能在发生变化;
fianl 修饰引用类型的时候,在初始化之后,就不能在指向其他的对象了,但是该引用所指向的内容是可以发生变化的, 因为引用的值是一个地址,final是要求值的, 而地址的值不会改变;
5. java 中的 Math.round(-1.5) 等于多少?
–java中四舍五入的原理是在现有参数上加0.5,.那么就是-1
6. String 属于基础的数据类型吗?
不是,是最基本的引用类型,Java中的基本类型有8个,byte,short,int ,long,float,double ,boolean,char;
7. java 中操作字符串都有哪些类?它们之间有什么区别?
String / StringBuffer / Stringbulider
-
String : 是被final修饰的,而且String类返回的都是一个new String 一个新的引用对象,只要创建就得需要在内存中重新创建内存
-
StringBuffer:在方法上添加的锁,所以保证线程安全,.但是效率相对会小一点, 可以new其对象.调用其中方法,可以用过修改参数值(append),不会重新在创建对象
-
StringBuilder: 不保证线程的安全,但是效率高,通过new对象,调用其中的方法,可以修改其字符串.
eg.StringBuffer中replace()方法
public synchronized StringBuffer replace(int start, int end, String str) { super.replace(start, end, str); return this; }
StringBuilder中replace():
public StringBuilder replace(int start, int end, String str) { super.replace(start, end, str); return this; }
8. String str="i"与 String str=new String(“i”)一样吗?
不一样,内存分配方式不一样,java虚拟机会将一个分配到常量池,一个会分到堆内存中;
9.如何将字符串反转?
1.将字符串转换为字符数组后,在通过遍历调换位置,最后在转换成字符串格式
public static String reversal(String str) {
char[] array = str.toCharArray();
for (int x = 0, y = array.length - 1; x < y; x++, y--) {
char temp = array[x];
array[x] = array[y];
array[y] = temp;
}
return new String(array);
- 把字符串转换成StringBuffer格式后,调用其中的reverse()方法实现反转,在转成String对象
public static String reverse(String str){
StringBuffer sb = new StringBuffer(str);
StringBuffer res = sb.reverse();
return res.toString();
}
3.调用String的charAt()方法.然后从最后到前依此取出来,再逐一添加到StringBuffer中,再转换成String
public static String reverse1(String str){
StringBuffer sb = new StringBuffer();
for (int x = str.length() -1; x >= 0; x--) {
sb.append(str.charAt(x));
}
return sb.toString();
}
10.String 类的常用方法都有那些?
length / trim / split / indexOf / cahrAt / equals / repalce / substring / toUpperCase / toLowerCase / …
11.抽象类必须要有抽象方法吗?
1.一个类被abstract修饰就是抽象类,抽象类不是实例化,
2.包含抽象方法的类,必须是抽象类, 但是抽象类可以没有抽象方法;
12.普通类和抽象类有哪些区别?
- 抽象类不能被实例化
- 抽象类可以有抽象方法,抽象方法只需申明,无需实现
- 含有抽象方法的类必须申明为抽象类
- 抽象类的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
- 抽象方法不能被声明为静态
- 抽象方法不能用 private 修饰
- 抽象方法不能用 final 修
13.抽象类能使用 final 修饰吗?
不能,抽象类是用来定义规范的,同来被继承的,final修饰代表不可继承;
14.接口和抽象类有什么区别?
- 抽象类可以dao有构造方法源,接口中不能有构造方法bai。
- 抽象类du中可以有普通成员变zhi量,接口dao中没有普通成员变量
- 抽象类中可以包含静态方法,接口中不能包含静态方法
- 一个类可以实现多个接口,但只能继承一个抽象类。
- 接口可以被多重实现,抽象类只能被单一继承
- 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法
15.java 中 IO 流分为几种?
-
按照流的流向分,可以分为输入流和输出流;
-
按照操作单元划分,可以划分为字节流和字符流;
-
按照流的角色划分为节点流和处理流。
节点流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eiTV2VYJ-1596706790915)(C:\Users\zj\AppData\Roaming\Typora\typora-user-images\image-20200730155402626.png)]
- (1)File 文件流。对文件进行读、写操作 :FileReader、FileWriter、FileInputStream、FileOutputStream。、
- (2)Memory
1)从/向内存数组读写数据: CharArrayReader与 CharArrayWriter、ByteArrayInputStream与ByteArrayOutputStream。
2)从/向内存字符串读写数据 StringReader、StringWriter、StringBufferInputStream。 - (3)Pipe管道流。 实现管道的输入和输出(进程间通信): PipedReader与PipedWriter、PipedInputStream与PipedOutputStream。
处理流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LrzOLirZ-1596706790959)(C:\Users\zj\AppData\Roaming\Typora\typora-user-images\image-20200730155515578.png)]
(1)Buffering缓冲流:在读入或写出时,对数据进行缓存,以减少I/O的次数:BufferedReader与BufferedWriter、BufferedInputStream与BufferedOutputStream。
- (2)Filtering 滤流:在数据进行读或写时进行过滤:FilterReader与FilterWriter、FilterInputStream与FilterOutputStream。
- (3)Converting between Bytes and Characters 转换流:按照一定的编码/解码标准将字节流转换为字符流,或进行反向转换(Stream到Reader):InputStreamReader、OutputStreamWriter。
- (4)Object Serialization 对象流 :ObjectInputStream、ObjectOutputStream。
- (5)DataConversion数据流: 按基本数据类型读、写(处理的数据是Java的基本类型(如布尔型,字节,整数和浮点数)):DataInputStream、DataOutputStream 。
- (6)Counting计数流: 在读入数据时对行记数 :LineNumberReader、LineNumberInputStream。
- (7)Peeking Ahead预读流: 通过缓存机制,进行预读 :PushbackReader、PushbackInputStream。
- (8)Printing打印流: 包含方便的打印方法 :PrintWriter、PrintStream。
16.BIO、NIO、AIO 有什么区别?
BIO: 同步并阻塞,一个连接一个线程, 适用连接少,并且固定的架构;
NIO: 同步非阻塞.一个请求一个线程, 适用连接数目多,但是连接比较短的架构;
AIO: 异步非阻塞. 一个有效请求一个线程, 适用来连接数目多,并且连接比较长的
17.Files的常用方法都有哪些?
Files.exists() 检测文件路径是否存在
Files.createFile()创建文件
Files.createDirectory()创建文件夹
Files.delete() 删除文件或者目录
Files.copy() 复制文件
Files.move() 移动文件
Files.size()查看文件个数
Files.read() 读取文件
Files.write()写入文件
二. 容器
18.java 容器都有哪些?
数组,String,java.util下的集合容器
数组长度限制为 Integer.Integer.MAX_VALUE;
String的长度限制: 底层是char 数组 长度 Integer.MAX_VALUE 线程安全的
List:存放有序,列表存储,元素可重复
Set:无序,元素不可重复
Map:无序,元素可重复
19.Collection 和 Collections 有什么区别?
1、java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
List,Set,Queue接口都继承Collection。
直接实现该接口的类只有AbstractCollection类,该类也只是一个抽象类,提供了对集合类操作的一些基本实现。List和Set的具体实现类基本上都直接或间接的继承了该类。
2、java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态方法(对集合的搜索、排序、线程安全化等),大多数方法都是用来处理线性表的。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
20.List、Set、Map 之间的区别是什么?
List:可以允许重复对象, 可以插入多个null元素, 是一个有序容器
Set:不允许重复对象, 只允许一个null元素, 无序容器
Map:
Map不是Collection的子接口或实现类。Map是一个接口
Map 的每个Entry都特有两个对象,也就是一个键一个值,Map可能会持有相同的值对象但键对象必须是唯一的
Map里可以拥有随意个null值但最多只能有一个null键
21.HashMap 和 Hashtable 有什么区别?
1.继承父类不同:
HashMap继承自AbstractMap类。但二者都实现了Map接口。
Hashtable继承自Dictionary类,Dictionary类是一个已经被废弃的类(见其源码中的注释)。父类都被废弃,自然而然也没人用它的子类Hashtable了。
2.hashMap线程不安全,hashtable线程安全
3.包含的contains方法不同,map没有,table有
4.MAP允许有null值,table不允许有null 值;
5.计算hash值方式不同,
6.扩容方式不同,map是2倍,table是2倍+1;
7.解决hash冲突方式不同. map是冲突数量小于8以链表,超过会转换成红黑树储存.table都是用链表
22.如何决定使用 HashMap 还是 TreeMap?
TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排列的;TreeMap的实现也是基于红黑树结构。
而HashMap<K,V>的Key值实现散列hashCode(),分布是散列的均匀的,不支持排序,数据结构主要是桶(数组),链表或红黑树。
所以,查询的时候使用HashMap,增加、快速创建的时候使用TreeMap。
23.说一下 HashMap 的实现原理?
1.hashMap是非同步实现了Map接口,所以包含了所有接口的方法,并且允许有null键和null值 ,同时也不能保证顺序,
2.HashMap的数据结构: 是数组和,链表的结合体,通过其中的key的hash值,在于数组长度取到的模.计算出在数组的下标位置,如果该位置目前为空,直接储存,如果该位置有别的对象已经储存,那么,就在就发生了哈希冲突,也叫哈希碰撞,那么就通过链表的方式储存,后加入的键值对会代替原来的键值对位置,成为新的链表节点.当哈希碰撞次数达到8的时候,就会在适用红黑树进行数据的储存.
24.说一下 HashSet 的实现原理?
底层就是HashMap中的key,没有顺序,也不可以重复,根据hashcode值,得到数组中的下标位置
25.ArrayList 和 LinkedList 的区别是什么?
1.数据结构不同. 一个是数组结构, 一个是链表结构
2.效率不同, 数组结构,因为有下标,所以适合查询的场合, 链表结构的,所以操作的效率会更好
3.自由性不同. 一个需要手动设置固定大小容量,通过下标来控制.LinkedListed能够随动态数据的变化而变化
26.如何实现string,数组和 List 之间的转换?
1.数组转 List ,使用 JDK 中 java.util.Arrays 工具类的 asList 方法
2.List 转数组,使用 List 的toArray方法。
27.ArrayList 和 Vector 的区别是什么?
1.ArrayList: 都有初始容量大小,采用线性储存空间,当元素超过初始容量大小时,.会以50%扩容,节约资源 为了保证其性能,所以线程是不安全的, 因为底层是数组,所以更加适合查询,和遍历,不适合插入和删除
2.Vector也有初始容量,按照翻倍扩容,资源浪费,但是他的方法都是同步方法,所以线程是安全的,若有多个线程访问,适用Vector就不需要在考虑线程是否安全;
28.Array 和 ArrayList 有何区别?
Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
Array大小是固定的,ArrayList的大小是动态变化的。
ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢
29.在 Queue 中 poll()和 remove()有什么区别?
poll()和remove()都将移除并且返回对头,但是在poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。
30.哪些集合类是线程安全的?
vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。
statck:堆栈类,先进后出
hashtable:就比hashmap多了个线程安全
enumeration:枚举,相当于迭代器
31.迭代器 Iterator 是什么?
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。
32.Iterator 怎么使用?有什么特点?
- java.lang.Iterable 接口被 java.util.Collection 接口继承,java.util.Collection 接口的 iterator() 方法返回一个 Iterator 对象
- next() 方法获得集合中的下一个元素
- hasNext() 检查集合中是否还有元素
- remove() 方法将迭代器新返回的元素删除
- forEachRemaining(Consumer<? super E> action) 方法,遍历所有元素
33.Iterator 和 ListIterator 有什么区别?
- ListIterator 继承 Iterator
- ListIterator 比 Iterator多方法
1) add(E e) 将指定的元素插入列表,插入位置为迭代器当前位置之前
2) set(E e) 迭代器返回的最后一个元素替换参数e
3) hasPrevious() 迭代器当前位置,反向遍历集合是否含有元素
4) previous() 迭代器当前位置,反向遍历集合,下一个元素
5) previousIndex() 迭代器当前位置,反向遍历集合,返回下一个元素的下标
6) nextIndex() 迭代器当前位置,返回下一个元素的下标
- 使用范围不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子类
- ListIterator 有 add 方法,可以向 List 中添加对象;Iterator 不能
- ListIterator 有 hasPrevious() 和 previous() 方法,可以实现逆向遍历;Iterator不可以
- ListIterator 有 nextIndex() 和previousIndex() 方法,可定位当前索引的位置;Iterator不可以
- ListIterator 有 set()方法,可以实现对 List 的修改;Iterator 仅能遍历,不能修改
34.怎么确保一个集合不能被修改?
我们可以采用Collections包下的unmodifiableMap方法,通过这个方法返回的map,是不可以修改的。他会报 java.lang.UnsupportedOperationException错。
同理:Collections包也提供了对list和set集合的方法。
Collections.unmodifiableList(List)
Collections.unmodifiableSet(Set)
三、多线程
35.并行和并发有什么区别?
1.并发是指一个处理器同时处理多个任务。并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。
2.并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。就好像两个人各拿一把铁锨在挖坑,一小时后,每人一个大坑。所以无论从微观还是从宏观来看,二者都是一起执行的。
36.线程和进程的区别?
1.一个软件的运行,最少需要一个进程支撑,一个进程又可以启动多个线程来执行
2.线程是进程的实际单位,也是操作系统可以调度的最少单位,
3.如果一个进程,只包含一个线程,叫做单线程
4.如果一个进程中有多条执行路径(包含2条以上线程) 叫做多线程
37.守护线程是什么?
- 守护线程:专门用于服务其他的线程,如果非守护线程(即用户自定义线程)都执行完毕,程序终止,那么jvm就会退出(即停止运行)——此时,连jvm都停止运行了,守护线程当然也就停止执行了。
- 反过来说,只要任何非守护线程还在运行,程序就不会终止。
- 换一种说法:如果有用户自定义线程存在的话,jvm就不会退出——此时,守护线程也不能退出,也就是它还要运行,干嘛呢,就是为了执行垃圾回收的任务。(java中的垃圾回收线程就是特殊的守护线程)
38.创建线程有哪几种方式?
1、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
2、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
3、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
39.说一下 runnable 和 callable 有什么区别?
1.相同点:
- 两者都是接口;(废话)
- 两者都可用来编写多线程程序;
- 两者都需要调用Thread.start()启动线程;
2.不同点:
- 两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;
- Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛;
注意:
- Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!
40.线程有哪些状态?
有5中状态 1)新建new --2)可运行状态—>3)运行状态—>4)阻塞状态(等待 / 同步 / 其他阻塞)—>5)死亡状态 dead
41.sleep() 和 wait() 有什么区别?
1、这两个方法来自不同的类分别是Thread和Object,sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。
2、sleep()是线程类(Thread)的方法,不涉及线程通信,调用时会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;wait()是Object的方法,用于线程间的通信,调用时会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才进入对象锁定池准备获得对象锁进入运行状态。
3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)。
4、sleep()方法必须捕获异常InterruptedException,而wait()\notify()以及notifyAll()不需要捕获异常。
42.notify()和 notifyAll()有什么区别?
1.如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
2.当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争
3.优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
43.线程的 run()和 start()有什么区别?
1.调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。
2.一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。
44.创建线程池有哪几种方式?
1、newCachedThreadPool(),它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过60秒,则被终止并移除缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用SynchronousQueue作为工作队列。
2、newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列中等待空闲线程出现;如果工作线程退出,将会有新的工作线程被创建,以补足指定数目nThreads。
3、newSingleThreadExecutor(),它的特点在于工作线程数目限制为1,操作一个无界的工作队列,所以它保证了所有的任务都是被顺序执行,最多会有一个任务处于活动状态,并且不予许使用者改动线程池实例,因此可以避免改变线程数目。
4、newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
5、newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。
45.线程池都有哪些状态?
- running:线程池一旦被创建,就处于 RUNNING 状态,任务数为 0,能够接收新任务,对已排队的任务进行处理。
- shutdown:不接收新任务,但能处理已排队的任务。调用线程池的 shutdown() 方法,线程池由 RUNNING 转变为 SHUTDOWN 状态。
- stop:不接收新任务,不处理已排队的任务,并且会中断正在处理的任务。调用线程池的 shutdownNow() 方法,线程池由(RUNNING 或 SHUTDOWN ) 转变为 STOP 状态。
- tidying:
- SHUTDOWN 状态下,任务数为 0, 其他所有任务已终止,线程池会变为 TIDYING 状态,会执行 terminated() 方法。线程池中的 terminated() 方法是空实现,可以重写该方法进行相应的处理。
- 线程池在 SHUTDOWN 状态,任务队列为空且执行中任务为空,线程池就会由 SHUTDOWN 转变为 TIDYING 状态。
- 线程池在 STOP 状态,线程池中执行中任务为空时,就会由 STOP 转变为 TIDYING 状态。
- terminated:线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会由 TIDYING 转变为 TERMINATED 状态。
46.线程池中 submit()和 execute()方法有什么区别?
1、接收的参数不一样
2、submit有返回值,而execute没有
用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。
然后我就可以把所有失败的原因综合起来发给调用者。
个人觉得cancel execution这个用处不大,很少有需要去取消执行的。
而最大的用处应该是第二点。
3、submit方便Exception处理
意思就是如果你在你的task里会抛出checked或者unchecked exception,
而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。
47.在 java 程序中怎么保证多线程的运行安全?
存在问题的因素有:
原子性:一个或者多个操作在 CPU 执行的过程中不被中断的特性
可见性:一个线程对共享变量的修改,另外一个线程能够立刻看到
有序性:程序执行的顺序按照代码的先后顺序执行
导致原因:
缓存导致的可见性问题
线程切换带来的原子性问题
编译优化带来的有序性问题
解决办法:
JDK Atomic开头的原子类、synchronized(java底层的锁)、LOCK(jdk里面的锁),,可以解决原子性问题
synchronized、volatile、LOCK,可以解决可见性问题
Happens-Before 规则可以解决有序性问题
48.多线程锁的升级原理是什么?
JVM优化synchronized的运行机制,当JVM检测到不同的竞争状态时,就会根据需要自动切换到合适的锁,这种切换就是锁的升级。升级是不可逆的,也就是说只能从低到高,也就是偏向–>轻量级–>重量级,不能够降级
锁级别:无锁->偏向锁->轻量级锁->重量级锁
无锁: 没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失败的线程会不断重试直到修改成功。
偏向锁:偏向第一个加锁的线程,该线程不会主动释放锁.只有当有别的线程访问的时候,才会被释放,如果当偏向锁线程暂停时,判断锁对象是否锁定,若线程不活动了,就撤销锁,若活动,则,升级为轻量锁
轻量锁:轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B 会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。
重量级锁:指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。
49.什么是死锁?
-
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
-
死锁发生的原因?
- 互斥条件:即当资源被一个线程使用(占有)时,别的线程不能使用
- 请求和保持条件:即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
- 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
- 环路等待条件:指在发生死锁时,存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
50.怎么防止死锁?
- 尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
- 尽量使用 Java. util. concurrent 并发类代替自己手写锁。
- 尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
- 尽量减少同步的代码块。
打破四个必要条件之一:
- 打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。
- 打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。
- 打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。
- 打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。
51.ThreadLocal 是什么?有哪些使用场景?
1.ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些
2.ThreadLocal的应用场景?
在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。最常见的ThreadLocal使用场景为用来解决数据库连接、Session管理等
52.说一下 synchronized 底层实现原理?
Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
53.synchronized 和 volatile 的区别是什么?
区别:
(1)、volatile只能作用于变量,使用范围较小。synchronized可以用在变量、方法、类、同步代码块等,使用范围比较广。
(2)、volatile只能保证可见性和有序性,不能保证原子性。而可见性、有序性、原子性synchronized都可以包证。
(3)、volatile不会造成线程阻塞。synchronized可能会造成线程阻塞。
volatile关键字的作用
其实volatile关键字的作用就是保证了可见性和有序性(不保证原子性),如果一个共享变量被volatile关键字修饰,那么如果一个线程修改了这个共享变量后,其他线程是立马可知的。为什么是这样的呢?比如,线程A修改了自己的共享变量副本,这时如果该共享变量没有被volatile修饰,那么本次修改不一定会马上将修改结果刷新到主存中,如果此时B去主存中读取共享变量的值,那么这个值就是没有被A修改之前的值。如果该共享变量被volatile修饰了,那么本次修改结果会强制立刻刷新到主存中,如果此时B去主存中读取共享变量的值,那么这个值就是被A修改之后的值了。
volatile能禁止指令重新排序,在指令重排序优化时,在volatile变量之前的指令不能在volatile之后执行,在volatile之后的指令也不能在volatile之前执行,所以它保证了有序性。
synchronized关键字的作用
synchronized提供了同步锁的概念,被synchronized修饰的代码段可以防止被多个线程同时执行,必须一个线程把synchronized修饰的代码段都执行完毕了,其他的线程才能开始执行这段代码。
因为synchronized保证了在同一时刻,只能有一个线程执行同步代码块,所以执行同步代码块的时候相当于是单线程操作了,那么线程的可见性、原子性、有序性(线程之间的执行顺序)它都能保证了。
54.synchronized 和 Lock 有什么区别
-
来源:
lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现;
-
异常是否释放锁:
synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)
-
是否响应中断
lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;
-
是否知道获取锁
Lock可以通过trylock来知道有没有获取锁,而synchronized不能;
-
Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)
-
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
-
synchronized使用Object对象本身的wait 、notify、notifyAll调度机制,而Lock可以使用Condition进行线程之间的调度,
55.synchronized 和 ReentrantLock 区别是什么?
synchronized 竞争锁时会一直等待;ReentrantLock 可以尝试获取锁,并得到获取结果
synchronized 获取锁无法设置超时;ReentrantLock 可以设置获取锁的超时时间
synchronized 无法实现公平锁;ReentrantLock 可以满足公平锁,即先等待先获取到锁
synchronized 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll();ReentrantLock 控制等待和唤醒需要结合 Condition 的 await() 和 signal()、signalAll() 方法
synchronized 是 JVM 层面实现的;ReentrantLock 是 JDK 代码层面实现
synchronized 在加锁代码块执行完或者出现异常,自动释放锁;ReentrantLock 不会自动释放锁,需要在 finally{} 代码块显示释放
56.说一下 atomic 的原理?
直接操作内存,使用Unsafe 这个类
2、使用 getIntVolatile(var1, var2) 获取线程间共享的变量
3、采用CAS的尝试机制(核心所在)
4、使用Atomic ,是在硬件上、寄存器进行阻塞,而不是在线程、代码上阻塞。
5、这个是通俗说法ABA的问题
四、反射
57.什么是反射?
主要是指程序可以访问、检测和修改它本身状态或行为的一种能力
1.在运行时判断任意一个对象所属的类。
2.在运行时构造任意一个类的对象。
3.在运行时判断任意一个类所具有的成员变量和方法。甚至是私有属性和方法
4.在运行时调用任意一个对象的方法。
58.什么是 java 序列化?什么情况下需要序列化?
序列化:将 Java 对象转换成字节流的过程。永久的保存到磁盘的过程,
反序列化:将字节流转换成 Java 对象的过程。
当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。
59.动态代理是什么?有哪些应用?
动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法。
默认的是CGLIB代理, 动态代理类,继承了目标类.
jdk提供的动态代理,和目标类共同实现了同一接口,.
应用场景如:
- 统计每个 api 的请求耗时
- 统一的日志输出
- 校验被调用的 api 是否已经登录和权限鉴定
- Spring的 AOP 功能模块就是采用动态代理的机制来实现切面编程
60.怎么实现动态代理?
无需声明代理类。是使用反射和字节码的技术,在运行期创建指定接口或类的子类(即动态代理类)以及其实例对象的技术。通过动态代理技术可以无侵入地对代码进行增强。
一般有两种:JDK 原生动态代理是基于接口实现: 动态代理类和被代理类必须继承同一个接口。动态代理只能对接口中声明的方法进行代理。
cglib 是基于继承当前类的子类实现: CGLib(Code Generation Library)是一个基于ASM的字节码生成库。它允许我们在运行时对字节码进行修改或动态生成。CGLib通过继承被代理类的方式实现代理。
五、对象拷贝
61. 为什么要使用克隆?
克隆的对象可能包含一些已经修改过的属性,而new出来的对象属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就需要使用克隆方法了。
62. 如何实现对象克隆?
- 实现 Cloneable 接口并重写Object类中的clone() 方法。
- 实现 Serializable 接口,通过对象的序列化和反序列化来实现克隆,可以实现真正的深度克隆。
63. 深拷贝和浅拷贝区别是什么?
- 浅克隆:当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有被复制。
- 深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将被复制。
六、Java Web
64. jsp 和 servlet 有什么区别?
分析:其实就是当你通过 http 请求一个 JSP 页面是,首先 Tomcat 会调用 service()方法将JSP编译成为 Servlet,然后执行 Servlet。
相同点: jsp经编译后就变成了servlet,jsp本质就是servlet,jvm只能识别java的类,不能识别jsp代码,web容器将jsp的代码编译成jvm能够识别的java类。
不同点: JSP侧重视图,Sevlet主要用于控制逻辑。
Servlet中没有内置对象 。
JSP中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到。
65. jsp 有哪些内置对象?作用分别是什么?
1.HttpServletRequet类的Request对象:代表请求对象,主要用于接受客户端通过HTTP协议连接传输服务器端的数据。
2.HttpSevletResponse类的Response对象:代表响应对象,主要用于向客户端发送数据。
3.JspWriter类的out对象:主要用于向客户端输出数据,out的基类是jspWriter
4.HttpSession类的session对象:主要用来分别保存每个月的信息与请求关联的会话;会话状态的维持是web应用开发者必须面对的问题。
5.ServletContext类的application对象:主要用于保存用户信息,代码片段的运行环境;它是一个共享的内置对象,即一个容器中的多个用户共享一个application
,故其保存的信息被所有用户所共享。
6.PageContext类的PageContext对象:管理网页属性,为jsp页面包装页面的上下文,管理对属于jsp的特殊可见部分中已经命名对象的访问,它的创建和初始化都是由容器来完成的。
7.ServletConfig类的Config对象:代码片段配置对象,标识Servlet的配置。
8.Object类的Page对象,处理jsp页面,是object类的一个实例,指的是jsp实现类的实例
9.Exception对象:处理jsp文件执行时发生的错误和异常,只有在错误页面里才使用,前提是在页面指令里要有isErrorPage=true。
66.说一下 jsp 的 4 种作用域?
1.application 在所有应用程序中有效
2.session 在当前会话中有效
3.request 在当前请求中有效
4.page 在当前页面有效
67.session 和 cookie 有什么区别?
1、存储位置不同
cookie的数据信息存放在客户端浏览器上。
session的数据信息存放在服务器上。
2、存储容量不同
单个cookie保存的数据<=4KB,一个站点最多保存20个Cookie。
对于session来说并没有上限,但出于对服务器端的性能考虑,session内不要存放过多的东西,并且设置session删除机制。
3、存储方式不同
cookie中只能保管ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据。
session中能够存储任何类型的数据,包括且不限于string,integer,list,map等。
4、隐私策略不同
cookie对客户端是可见的,别有用心的人可以分析存放在本地的cookie并进行cookie欺骗,所以它是不安全的。
session存储在服务器上,对客户端是透明对,不存在敏感信息泄漏的风险。
5、有效期上不同
开发可以通过设置cookie的属性,达到使cookie长期有效的效果。
session依赖于名为JSESSIONID的cookie,而cookie JSESSIONID的过期时间默认为-1,只需关闭窗口该session就会失效,因而session不能达到长期有效的效果。
6、服务器压力不同
cookie保管在客户端,不占用服务器资源。对于并发用户十分多的网站,cookie是很好的选择。
session是保管在服务器端的,每个用户都会产生一个session。假如并发访问的用户十分多,会产生十分多的session,耗费大量的内存。
7、浏览器支持不同
假如客户端浏览器不支持cookie:
cookie是需要客户端浏览器支持的,假如客户端禁用了cookie,或者不支持cookie,则会话跟踪会失效。关于WAP上的应用,常规的cookie就派不上用场了。
运用session需要使用URL地址重写的方式。一切用到session程序的URL都要进行URL地址重写,否则session会话跟踪还会失效。
假如客户端支持cookie:
cookie既能够设为本浏览器窗口以及子窗口内有效,也能够设为一切窗口内有效。
session只能在本窗口以及子窗口内有效。
8、跨域支持上不同
cookie支持跨域名访问。
session不支持跨域名访问。
68.说一下 session 的工作原理?
首先,Servlet有getSession(),先获取到Session,往Session里放属性,底层服务器一看到你用了getSession(),
它会马上生成一个32长度的随机串,然后再创建一个Session对象,然后以这个32位的随机字符串作为key,以新创建的Session对象作为value,
放到Session列表中,Session列表是一个Map,当用户发出第一次请求的时候,服务器把32位的这个字符串包装成了一个Cookie(JSessionID=32位的随机字符串),发送给客户端浏览器,浏览器接收到Cookie会保存到浏览器的缓存,当客户端再次发送请求的时候,目的是从刚才那个Session中读取数据,它首先会把浏览器缓存中的Cookie(JSessionID=32位的随机字符串)放到请求头部信息中,然后发送给服务器,服务器接收到这个Cookie以后,会拿到这个JSessionID的值,也就是32位的随机字符串,然后到Session列表中去进行查找,找到这个key,就找到了这个key所对应的value,也就是存放有域属性的那个Session,就可以读取到数据了.
69.如果客户端禁止 cookie 能实现 session 还能用吗?
一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。
如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。
但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session。
通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。
70.spring mvc 和 struts 的区别是什么?
- 底层实现机制
struts2:filter
springmvc:servlet
- 运行效率
struts2:底层是Servlet,参数基于属性封装,如果配置单例,会出现线程安全问题,所以配置多例
springmvc:底层是Servlet,单例
- 参数封装
struts2:基于属性封装
springmvc:基于方法进行封装
71.如何避免 sql 注入?
原因:程序没有有效过滤用户的输入,使攻击者成功的向服务器提交恶意的 SQL 脚本,
方法:
1.严格限制 Web 应用的数据库的操作权限,给连接数据库的用户提供满足需要的最低权限,最大限度的减少注入攻击对数据库的危害
2.校验参数的数据格式是否合法(可以使用正则或特殊字符的判断)
3.对进入数据库的特殊字符进行转义处理,或编码转换
4.预编译 SQL(Java 中使用 PreparedStatement),参数化查询方式,避免 SQL 拼接
5.发布前,利用工具进行 SQL 注入检测
6.报错信息不要包含 SQL 信息输出到 Web 页面
- PreparedStatement(简单又有效的方法)
- 使用正则表达式过滤传入的参数
- 字符串过滤
- JSP中调用该函数检查是否包函非法字符
- JSP页面判断代码
72.什么是 XSS 攻击,如何避免?
即跨站脚本攻击(Cross Site Scripting),它是 web 程序中常见的漏洞。
攻击者往 web 页面里插入恶意的 HTML 代码(Javascript、css、html 标签等),当用户浏览该页面时,嵌入其中的 HTML 代码会被执行,从而达到恶意攻击用户的目的。如盗取用户 cookie 执行一系列操作,破坏页面结构、重定向到其他网站等。
- web 页面中可由用户输入的地方,如果对输入的数据转义、过滤处理
- 后台输出页面的时候,也需要对输出内容进行转义、过滤处理(因为攻击者可能通过其他方式把恶意脚本写入数据库)
- 前端对 html 标签属性、css 属性赋值的地方进行校验
73.什么是 CSRF 攻击,如何避免?
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任;
- CSRF 漏洞进行检测的工具,如 CSRFTester、CSRF Request Builder…
- 验证 HTTP Referer 字段
- 添加并验证 token
- 添加自定义 http 请求头
- 敏感操作添加验证码
- 使用 post 请求
七、异常
74.throw 和 throws 的区别?
throw:
表示方法内抛出某种异常对象
如果异常对象是非 RuntimeException 则需要在方法申明时加上该异常的抛出 即需要加上 throws 语句 或者 在方法体内 try catch 处理该异常,否则编译报错
执行到 throw 语句则后面的语句块不再执行
throws:
方法的定义上使用 throws 表示这个方法可能抛出某种异常
需要由方法的调用者进行异常处理
75.final、finally、finalize 有什么区别?
final可以用来修饰类、方法、变量,分别有不同的意义所在,final修饰的class代表不可继续扩展,final修饰的变量代表不可修改,final修饰的方法代表不可重写。
finally则是java保证某一段重点代码一定要被执行的修饰符,例如:我们需要用try块让JDBC保证连接,保证unlock锁等动作
finalize是基础类java.lang.Object的一个方法,它的设计目的是为了保证对象在垃圾回收之前完成特定资源的回收
一般不推荐使用,简单的说就是无法保证它什么时候执行,执行是否符合预期,使用不当会影响性能,导致死锁,挂起等
76.try-catch-finally 中哪个部分可以省略?
catch和finally可以省去一个,例如,在需要线程休眠的时候,有的时候finally也会不执行,例如5/0
77.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
会执行,在return之前执行
} catch (ArithmeticException e) {
a = 30;
return a;
/*
* return a 在程序执行到这一步的时候,这里不是return a 而是 return 30;这个返回路径就形成了
* 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40
* 再次回到以前的路径,继续走return 30,形成返回路径之后,这里的a就不是a变量了,而是常量30
*/
} finally {
a = 40;
}
} catch (ArithmeticException e) {
a = 30;
return a;
/*
* return a 在程序执行到这一步的时候,这里不是return a 而是 return 30;这个返回路径就形成了
* 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40
* 再次回到以前的路径,继续走return 30,形成返回路径之后,这里的a就不是a变量了,而是常量30
*/
} finally {
a = 40;
return a; //如果这样,就又重新形成了一条返回路径,由于只能通过1个return返回,所以这里直接返回40
}
78.常见的异常类有哪些?
(1)NullPointerException 当应用程序试图访问空对象时,则抛出该异常。
(2)SQLException 提供关于数据库访问错误或其他错误信息的异常。
(3)IndexOutOfBoundsException指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
(4)NumberFormatException当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
(5)FileNotFoundException当试图打开指定路径名表示的文件失败时,抛出此异常。
(6)IOException当发生某种I/O异常时,抛出此异常。此类是失败或中断的I/O操作生成的异常的通用类。
(7)ClassCastException当试图将对象强制转换为不是实例的子类时,抛出该异常。
(8)ArrayStoreException试图将错误类型的对象存储到一个对象数组时抛出的异常。
(9)IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
(10)ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
(11)NegativeArraySizeException如果应用程序试图创建大小为负的数组,则抛出该异常。
(12)NoSuchMethodException无法找到某一特定方法时,抛出该异常。
(13)SecurityException由安全管理器抛出的异常,指示存在安全侵犯。
(14)UnsupportedOperationException当不支持请求的操作时,抛出该异常。
(15)RuntimeExceptionRuntimeException 是那些可能在Java虚拟机正常运行期间抛出的异常的超类。
八、网络
79.http 响应码 301 和 302 代表的是什么?有什么区别?
http 协议的 301 和 302 状态码都代表重定向。浏览器请求某url收到这两个状态码时,都会显示和跳转到 Response Headers 中的Location。即在浏览器地址输入 url A,却自动跳转到url B。
区别:
301 表示被请求 url 永久转移到新的 url;302 表示被请求 url 临时转移到新的 url。
301 搜索引擎会索引新 url 和新 url 页面的内容;302 搜索引擎可能会索引旧 url 和 新 url 的页面内容。
302 的返回码可能被别人利用,劫持你的网址。因为搜索引擎索引他的网址,他返回 302 跳转到你的页面。
80.forward 和 redirect 的区别?
**forward又叫转发:**一次请求一次响应,服务器端内部地址跳转,
*redirect叫做重定向😗*两次请求,两次响应,类似如客户端重新发送请求
81.简述 tcp 和 udp的区别?
tcp是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。一个TCP连接必须要经过三次“对话”才能建立起来。使用TCP协议传输数据,TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。当数据从A端传到B端后,B端会发送一个确认包(ACK包)给A端,告知A端数据我已收到!
UDP协议是无连接的数据传输协议并且无重传机制,会发生丢包、收到重复包、乱序等情况;
1.基于连接与无连接。
2.UDP不提供可靠性,不能保证数据能够到达目的地。
3.对系统资源的要求(TCP较多,UDP少)。
4.UDP结构较简单。
5.TCP面向字节流模式,TCP会保证服务端按顺序接收到全部的字节流,UDP面向数据报模式,不保证顺序性。
当数据传输的性能必须让位于数据传输的完整性、可控制性和可靠性时,选择TCP协议.但是性能没有UDP高.相反可以选择高性能的UDP协议;
82.tcp 为什么要三次握手,两次不行吗?为什么?
两次握手只能保证单向连接是畅通的。
这样的两次握手过程, A 向 B 打招呼得到了回应,即 A 向 B 发送数据,B 是可以收到的。
但是 B 向 A 打招呼,A 还没有回应,B 没有收到 A 的反馈,无法确保 A 可以收到 B 发送的数据。
- 只有经过第三次握手,才能确保双向都可以接收到对方的发送的 数据。
83.说一下 tcp 粘包是怎么产生的?
1、什么是 tcp 粘包?
发送方发送的多个数据包,到接收方缓冲区首尾相连,粘成一包,被接收。
2、原因
TCP 协议默认使用 Nagle 算法可能会把多个数据包一次发送到接收方。
应用程读取缓存中的数据包的速度小于接收数据包的速度,缓存中的多个数据包会被应用程序当成一个包一次读取。
3、处理方法
发送方使用 TCP_NODELAY 选项来关闭 Nagle 算法
数据包增加开始符和结束,应用程序读取、区分数据包。
在数据包的头部定义整个数据包的长度,应用程序先读取数据包的长度,然后读取整个长度的包字节数据,保证读取的是单个包且完整。
84.OSI 的七层模型都有哪些?
应用层: 提供为应用软件而设的接口,以设置与另一应用软件之间的通信
表达层: 数据转换,转成与接收者系统格式兼容兼容的格式
会话层: 负责在数据传输中设置和维护计算机网络中两台计算机之间的通信连接
传输层: 把传输表头(TH)加至数据以形成数据包。传输表头包含了所使用的协议等发送信息
网络层: 决定数据的路径选择和转寄,将网络表头(NH)加至数据包,以形成分组。网络表头包含了网络数据
数据链路层: 负责网络寻址、错误侦测和改错。当表头和表尾被加至数据包时,会形成帧。数据链表头(DLH)是包含了物理地址和错误侦测及改错的方法。数据链表尾(DLT)是一串指示数据包末端的字符串
物理层: 在局部局域网上传送数据帧(data frame),它负责管理计算机通信设备和网络媒体之间的互通。包括了针脚、电压、线缆规范、集线器、中继器、网卡、主机接口卡等
85.get 和 post 请求有哪些区别?
-
GET在浏览器回退时是无害的,而POST会再次提交请求。
-
GET产生的URL地址可以被Bookmark,而POST不可以。
-
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
-
GET请求只能进行url编码,而POST支持多种编码方式
-
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
-
GET请求在URL中传送的参数是有长度限制的,而POST么有。
-
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
-
GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
-
GET参数通过URL传递,POST放在Request body中。
85.1: restFul风格?
一般在工作中有两种用法:
1.动态获取url路径中的参数
2.以统一的url地址,不同的请求参数,执行不同的业务调用
一般都会添加请求类型,为了安全
86.如何实现跨域?
1、jsonp
利用了 script 不受同源策略的限制
缺点:只能 get 方式,易受到 XSS攻击
2、CORS(Cross-Origin Resource Sharing),跨域资源共享
当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头 origin;
后端在接受到请求后确定响应后会在后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin;
浏览器判断响应中的 Access-Control-Allow-Origin 值是否和当前的地址相同,匹配成功后才继续响应处理,否则报错
缺点:忽略 cookie,浏览器版本有一定要求
3、代理跨域请求
前端向发送请求,经过代理,请求需要的服务器资源
缺点:需要额外的代理服务器
4、Html5 postMessage 方法
允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递
缺点:浏览器版本要求,部分浏览器要配置放开跨域限制
5、修改 document.domain 跨子域
相同主域名下的不同子域名资源,设置 document.domain 为 相同的一级域名
缺点:同一一级域名;相同协议;相同端口
6、基于 Html5 websocket 协议
websocket 是 Html5 一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,允许跨域请求
缺点:浏览器一定版本要求,服务器需要支持 websocket 协议
7、document.xxx + iframe
通过 iframe 是浏览器非同源标签,加载内容中转,传到当前页面的属性中
缺点:页面的属性值有大小限制
87.说一下 JSONP 实现原理?
1).利用javascript中的src属性实现跨域
<script type="text/javascript" src="http://manage.jt.com/test.json"></script>
2).自定义回调函数 function callback(){}
function hello(data){
alert(data.name);
}
3).将返回值结果 进行特殊的格式封装 callback(JSON数据)
hello({"id":"1","name":"tomcat猫"})
九、设计模式
88.说一下你熟悉的设计模式?
1.单例设计模式:饥汉时,饿汉式,
3.dubbo远程调用设计模式
4.动态代理模式
5.工厂模式
89.简单工厂和抽象工厂有什么区别?
1、创建对象不同。bai创建对象时,“工厂模式du”使用Factory模式替代使用new创建对象;“简单工厂模式”使用fw模式建立对象;“抽象工厂模式”则使用迭代模式创建对象。
2、定义变量不同。“工厂模式”不用事先定义变量,使用时随时引用便可。“简单工厂模式”,使用参数或者配置文件等事先定义好的变量,然后利用分支判断初始化具体产品类并返回。“抽象工厂模式则”不符合“开发-封闭”原则,每次增加产品,都需要修改类方法。
3、接口数量不同。“工厂模式”有两至三个接口。“简单工厂模式”只有一个接口。抽象工厂模式理论上具有无限个接口。
十、Spring/Spring MVC
90.为什么要使用 spring?
spring 是一个开源的轻量级 JavaBean 容器框架。使用 JavaBean 代替 EJB ,并提供了丰富的企业应用功能,降低应用开发的复杂性。
轻量:非入侵性的、所依赖的东西少、资源占用少、部署简单,不同功能选择不同的 jar 组合
容器:工厂模式实现对 JavaBean 进行管理,通过控制反转(IOC)将应用程序的配置和依赖性与应用代码分开
松耦合:通过 xml 配置或注解即可完成 bean 的依赖注入
AOP:通过 xml 配置 或注解即可加入面向切面编程的能力,完成切面功能,如:日志,事务…的统一处理
方便集成:通过配置和简单的对象注入即可集成其他框架,如 Mybatis、Hibernate、Shiro…
丰富的功能:JDBC 层抽象、事务管理、MVC、Java Mail、任务调度、JMX、JMS、JNDI、EJB、动态语言、远程访问、Web Service…
91.解释一下什么是 aop?
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
AOP是Spring提供的关键特性之一。AOP即面向切面编程,是OOP编程的有效补充。
使用AOP技术,可以将一些系统性相关的编程工作,独立提取出来,独立实现,然后通过切面切入进系统。
从而避免了在业务逻辑的代码中混入很多的系统相关的逻辑——比如权限管理,事物管理,日志记录等等。
这些系统性的编程工作都可以独立编码实现,然后通过AOP技术切入进系统即可。从而达到了 将不同的关注点分离出来的效果。
92.解释一下什么是 ioc?
控制反转:所谓控制反转就是把对象(bean)对象和维护对象(bean)之间的关系的权利转移到Sqring容器中去了(ApplicationContext.xml)而程序本身不在维护了
93.spring 有哪些主要模块?
Spring有七大功能模块,分别是Spring Core,AOP,ORM,DAO,MVC,WEB,Context。
1,Spring Core
Core模块是Spring的核心类库,Spring的所有功能都依赖于该类库,Core主要实现IOC功能,Sprign的所有功能都是借助IOC实现的。
2,AOP
AOP模块是Spring的AOP库,提供了AOP(拦截器)机制,并提供常用的拦截器,供用户自定义和配置。
3,ORM
Spring 的ORM模块提供对常用的ORM框架的管理和辅助支持,Spring支持常用的Hibernate,ibtas,jdao等框架的支持,Spring本身并不对ORM进行实现,仅对常见的ORM框架进行封装,并对其进行管理
4,DAO模块
Spring 提供对JDBC的支持,对JDBC进行封装,允许JDBC使用Spring资源,并能统一管理JDBC事物,并不对JDBC进行实现。(执行sql语句)
5,WEB模块
WEB模块提供对常见框架如Struts1,WEBWORK(Struts 2),JSF的支持,Spring能够管理这些框架,将Spring的资源注入给框架,也能在这些框架的前后插入拦截器。
6,Context模块
Context模块提供框架式的Bean访问方式,其他程序可以通过Context访问Spring的Bean资源,相当于资源注入。
7,MVC模块
WEB MVC模块为Spring提供了一套轻量级的MVC实现,在Spring的开发中,我们既可以用Struts也可以用Spring自己的MVC框架,相对于Struts,Spring自己的MVC框架更加简洁和方便。
94.spring 常用的注入方式有哪些?
Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:构造方法注入,setter 注入,基于注解的注入。
95.spring 中的 bean 是线程安全的吗?
线程安全这个问题,要从单例与原型Bean分别进行说明。
原型Bean
对于原型Bean,每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
单例Bean
对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
spring单例,为什么controller、service和dao确能保证线程安全?
Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理。
实际上大部分时间Bean是无状态的(比如Dao) 所以说在某种程度上来说Bean其实是安全的。
但是如果Bean是有状态的 那就需要开发人员自己来进行线程安全的保证,最简单的办法就是改变bean的作用域 把 "singleton"改为’‘protopyte’ 这样每次请求Bean就相当于是 new Bean() 这样就可以保证线程的安全了。
有状态就是有数据存储功能
无状态就是不会保存数据
96.spring 支持几种 bean 的作用域?
Spring框架支持以下五种bean的作用域:
singleton :bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
global-session:在一个全局的HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
缺省的Spring bean 的作用域是Singleton.
97.spring 自动装配 bean 有哪些方式?
no:默认值,表示没有自动装配,应使用显式 bean 引用进行装配。
byName:它根据 bean 的名称注入对象依赖项。
byType:它根据类型注入对象依赖项。 构造函数:通过构造函数来注入依赖项,需要设置大量的参数。
autodetect:容器首先通过构造函数使用 autowire 装配,如果不能,则通过 byType 自动装配。
98.spring 事务实现方式有哪些?
Spring提供了编程式事务和声明式事务两种实现方式,
编程式事务允许用户在代码中精确定义事务的边界,
而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。
简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;
而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。
99.说一下 spring 的事务隔离?
spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
ISOLATIONREADUNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);isolation readun commintted
ISOLATIONREADCOMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;isokation read committed
ISOLATIONREPEATABLEREAD:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
isolation repeatable read
ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
isolation_serializable
100.说一下 spring mvc 运行流程?
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 将渲染结果返回给客户端。(渲染返回)
101.spring mvc 有哪些组件?
前端控制器(DispatcherServlet)
处理器映射器(HandlerMapping)
处理器适配器(HandlerAdapter)
拦截器(HandlerInterceptor)
语言环境处理器(LocaleResolver)
主题解析器(ThemeResolver)
视图解析器(ViewResolver)
文件上传处理器(MultipartResolver)
异常处理器(HandlerExceptionResolver)
数据转换(DataBinder)
消息转换器(HttpMessageConverter)
请求转视图翻译器(RequestToViewNameTranslator)
页面跳转参数管理器(FlashMapManager)
处理程序执行链(HandlerExecutionChain)
102.@RequestMapping 的作用是什么?
@RequestMapping是一个用来处理请求地址映射的注解,可用于类或者方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
103.@Autowired 的作用是什么?
@Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
十一、Spring Boot/Spring Cloud
104.什么是 spring boot?
Spring Boot 是所有基于 Spring 开发的项目的起点。Spring Boot 的设计是为了让你尽可能快的跑起来 Spring 应用程序并且尽可能减少你的配置文件。简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架(不知道这样比喻是否合适)
105.为什么要用 spring boot?
Spring boot是一个脚手架(而非框架),构建于Spring框架(Framework)基础之上,基于快速构建理念,提供了自动配置功能,可实现其开箱即用特性(创建完一个基本的项目以后,可零配置或者少量配置即可运行我们的项目),其核心主要有如下几个方面:
起步依赖(Starter Dependency)。
自动配置(Auto Configuration)。
健康检查(Actator)-监控。
106.spring boot 核心配置文件是什么?
Spring Boot 有两种类型的配置文件,application 和 bootstrap 文件
Spring Boot会自动加载classpath目前下的这两个文件,文件格式为 properties 或 yml 格式
*.properties 文件是 key=value 的形式
*.yml 是 key: value 的形式
*.yml 加载的属性是有顺序的,但不支持 @PropertySource 注解来导入配置,一般推荐用yml文件,看下来更加形象
bootstrap 配置文件是系统级别的,用来加载外部配置,如配置中心的配置信息,也可以用来定义系统不会变化的属性.bootstatp 文件的加载先于application文件
application 配置文件是应用级别的,是当前应用的配置文件
107.spring boot 配置文件有哪几种类型?它们有什么区别?
1.SpringBoot的核心配置文件有哪些?
SpringBoot的核心配置文件有application和bootstarp配置文件。
2.他们的区别是什么?
application文件主要用于Springboot自动化配置文件。
bootstarp文件主要有以下几种用途:
使用Spring Cloud Config注册中心时 需要在bootStarp配置文件中添加链接到配置中心的配置属性来加载外部配置中心的配置信息。
一些固定的不能被覆盖的属性
一些加密/解密的场景
都有什么格式?
.properties 和 .yml
.yml采取的是缩进的格式 不支持@PeopertySource注解导入配置
108.spring boot 有哪些方式可以实现热部署?
1、模板热部署
在SpringBoot中,模板引擎的页面默认是开启缓存的,如果修改了页面的内容,则刷新页面是得不到修改后的页面的,因此我们可以在application.properties中关闭模版引擎的缓存,
2、使用调试模式Debug实现热部署
此种方式为最简单最快速的一种热部署方式,运行系统时使用Debug模式,无需装任何插件即可,但是无发对配置文件,方法名称改变,增加类及方法进行热部署,使用范围有限。
3、spring-boot-devtools
在Spring Boot 项目中添加 spring-boot-devtools依赖即可实现页面和代码的热部署。如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
此种方式的特点是作用范围广,系统的任何变动包括配置文件修改、方法名称变化都能覆盖,但是后遗症也非常明显,它是采用文件变化后重启的策略来实现了,主要是节省了我们手动点击重启的时间,提高了实效性,在体验上回稍差。
spring-boot-devtools 默认关闭了模版缓存,如果使用这种方式不用单独配置关闭模版缓存。
4、Spring Loaded
此种方式与Debug模式类似,适用范围有限,但是不依赖于Debug模式启动,通过Spring Loaded库文件启动,即可在正常模式下进行实时热部署。此种需要在 run confrgration 中进行配置。
5、JRebel
Jrebel是Java开发最好的热部署工具,对Spring Boot 提供了极佳的支持,JRebel为收费软件,试用期14天。,可直接通过插件安装。
109.jpa 和 hibernate 有什么区别?
JPA Java Persistence API,是Java EE 5的标准ORM接口,也是ejb3规范的一部分。
Hibernate,当今很流行的ORM框架,是JPA的一个实现,但是其功能是JPA的超集。
JPA和Hibernate之间的关系,可以简单的理解为JPA是标准接口,Hibernate是实现。那么Hibernate是如何实现与JPA的这种关系的呢。Hibernate主要是通过三个组件来实现的,及hibernate-annotation、hibernate-entitymanager和hibernate-core。
hibernate-annotation是Hibernate支持annotation方式配置的基础,它包括了标准的JPA annotation以及Hibernate自身特殊功能的annotation。
hibernate-core是Hibernate的核心实现,提供了Hibernate所有的核心功能。
hibernate-entitymanager实现了标准的JPA,可以把它看成hibernate-core和JPA之间的适配器,它并不直接提供ORM的功能,而是对hibernate-core进行封装,使得Hibernate符合JPA的规范。
Jpa是一种规范,而Hibernate是它的一种实现。除了Hibernate,还有EclipseLink(曾经的toplink),OpenJPA等可供选择,所以使用Jpa的一个好处是,可以更换实现而不必改动太多代码。
110.什么是 spring cloud?
Spring Cloud是在Spring Boot 基础上构建的,用于快速构建分布式系统的通用模式的工具集.
使用Spring Cloud开发的应用程序非常适合在Docker或者Pass上部署,所以又叫云原生应用(Cloud Native Application). 云原生可以简单的理解为面向云环境的软件架构;
Spring Cloud有以下特点:
- 约定优于配置
- 适用于各种环境.开发,部署在PC Server 或各种云环境均可
- 隐藏了组件的复杂性,并提供声明式,无xml的配置方式
- 开箱即用
- 轻量级的组件. Spring Cloud整合的组件大多比较轻量.例如Eureka,Zuul,等等
- 组件丰富,功能齐全. Spring Cloud 为微服务架构提供了非常完整的支持.例如,配置管理,服务发现,断路器,微服务网关等;
- 选型中立,丰富. 例如,Spring Cloud 支持使用Eureka,Zookeeper或Consul实现服务发现.
- 灵活.Spring Cloud的组成部分是解耦的,开发人员可按需灵活挑选技术选型.
111.spring cloud 断路器的作用是什么?
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
112.spring cloud 的核心组件有哪些?
- Eureka:服务注册于发现。
- Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。
- Ribbon:实现负载均衡,从一个服务的多台机器中选择一台。
- Hystrix:提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
- Zuul:网关管理,由 Zuul 网关转发请求给对应的服务。
十二、Hibernate
113. 为什么要使用 hibernate?
- hibernate 是对 jdbc 的封装,大大简化了数据访问层的繁琐的重复性代码。
- hibernate 是一个优秀的 ORM 实现,很多程度上简化了 DAO 层的编码功能。
- 可以很方便的进行数据库的移植工作。
- 提供了缓存机制,是程序执行更改的高效。
114. 什么是 ORM 框架?
ORM(Object Relation Mapping)对象关系映射,是把数据库中的关系数据映射成为程序中的对象。
使用 ORM 的优点:提高了开发效率降低了开发成本、开发更简单更对象化、可移植更强。
115. hibernate 中如何在控制台查看打印的 SQL 语句?
在 Config 里面把 hibernate. show_SQL 设置为 true 就可以。但不建议开启,开启之后会降低程序的运行效率。
116. hibernate 有几种查询方式?
三种:hql、原生 SQL、条件查询 Criteria。
117. hibernate 实体类可以被定义为 final 吗?
实体类可以定义为 final 类,但这样的话就不能使用 hibernate 代理模式下的延迟关联提供性能了,所以不建议定义实体类为 final。
118. 在 hibernate 中使用 Integer 和 int 做映射有什么区别?
Integer 类型为对象,它的值允许为 null,而 int 属于基础数据类型,值不能为 null。
119. hibernate 是如何工作的?
- 读取并解析配置文件。
- 读取并解析映射文件,创建 SessionFactory。
- 打开 Session。
- 创建事务。
- 进行持久化操作。
- 提交事务。
- 关闭 Session。
- 关闭 SessionFactory。
120. get()和 load()的区别?
- 数据查询时,没有 OID 指定的对象,get() 返回 null;load() 返回一个代理对象。
- load()支持延迟加载;get() 不支持延迟加载。
121. 说一下 hibernate 的缓存机制?
hibernate 常用的缓存有一级缓存和二级缓存:
一级缓存:也叫 Session 缓存,只在 Session 作用范围内有效,不需要用户干涉,由 hibernate 自身维护,可以通过:evict(object)清除 object 的缓存;clear()清除一级缓存中的所有缓存;flush()刷出缓存;
二级缓存:应用级别的缓存,在所有 Session 中都有效,支持配置第三方的缓存,如:EhCache。
122. hibernate 对象有哪些状态?
- 临时/瞬时状态:直接 new 出来的对象,该对象还没被持久化(没保存在数据库中),不受 Session 管理。
- 持久化状态:当调用 Session 的 save/saveOrupdate/get/load/list 等方法的时候,对象就是持久化状态。
- 游离状态:Session 关闭之后对象就是游离状态。
123. 在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?
- getCurrentSession 会绑定当前线程,而 openSession 则不会。
- getCurrentSession 事务是 Spring 控制的,并且不需要手动关闭,而 openSession 需要我们自己手动开启和提交事务。
124. hibernate 实体类必须要有无参构造函数吗?为什么?
hibernate 中每个实体类必须提供一个无参构造函数,因为 hibernate 框架要使用 reflection api,通过调用 ClassnewInstance() 来创建实体类的实例,如果没有无参的构造函数就会抛出异常。
十三、Mybatis
125.mybatis 中 #{}和 ${}的区别是什么?
#{}是预编译处理,${}是字符串替换。
(1)mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值。
(2)mybatis在处理 时 , 就 是 把 {}时,就是把 时,就是把{}替换成变量的值。
(3)使用#{}可以有效的防止SQL注入,提高系统安全性。原因在于:预编译机制。预编译完成之后,SQL的结构已经固定,即便用户输入非法参数,也不会对SQL的结构产生影响,从而避免了潜在的安全风险。
(4)预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。
126.mybatis 有几种分页方式?
1.数组分页
原理:进行数据库查询操作时,获取到数据库中所有满足条件的记录,保存在应用的临时数组中,再通过List的subList方法,获取到满足条件的所有记录。
2.借助Sql语句进行分页
在了解到通过数组分页的缺陷后,我们发现不能每次都对数据库中的所有数据都检索。然后在程序中对获取到的大量数据进行二次操作,这样对空间和性能都是极大的损耗。所以我们希望能直接在数据库语言中只检索符合条件的记录,不需要在通过程序对其作处理。这时,Sql语句分页技术横空出世。
实现:通过sql语句实现分页也是非常简单的,只是需要改变我们查询的语句就能实现了,即在sql语句后面添加limit分页语句。
3.拦截器分页
上面提到的数组分页和sql语句分页都不是我们今天讲解的重点,今天需要实现的是利用拦截器达到分页的效果。自定义拦截器实现了拦截所有以ByPage结尾的查询语句,并且利用获取到的分页相关参数统一在sql语句后面加上limit分页的相关语句,一劳永逸。不再需要在每个语句中单独去配置分页相关的参数了。。
4.RowBounds实现分页
原理:通过RowBounds实现分页和通过数组方式分页原理差不多,都是一次获取所有符合条件的数据,然后在内存中对大数据进行操作,实现分页效果。只是数组分页需要我们自己去实现分页逻辑,这里更加简化而已。
存在问题:一次性从数据库获取的数据可能会很多,对内存的消耗很大,可能导致性能变差,甚至引发内存溢出。
适用场景:在数据量很大的情况下,建议还是适用拦截器实现分页效果。RowBounds建议在数据量相对较小的情况下使用。
127.RowBounds 是一次性查询全部结果吗?为什么?
RowBounds 表面是在“所有”数据中检索数据,其实并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next()的时候,去查询更多的数据。就好比你去自动取款机取 10000 元,但取款机每次最多能取 2500 元,所以你要取 4 次才能把钱取完。只是对于 jdbc 来说,当你调用 next()的时候会自动帮你完成查询工作。这样做的好处可以有效的防止内存溢出。
128.mybatis 逻辑分页和物理分页的区别是什么?
1.物理分页
物理分页就是数据库本身提供了分页方式,如MySQL的limit,oracle的rownum ,好处是效率高,不好的地方就是不同数据库有不同的搞法。
2.逻辑分页
逻辑分页利用游标分页,好处是所有数据库都统一,坏处就是效率低
3.分页结论:
1.物理分页速度上并不一定快于逻辑分页,逻辑分页速度上也并不一定快于物理分页。
2.物理分页总是优于逻辑分页:没有必要将属于数据库端的压力加诸到应用端来,就算速度上存在优势,然而其它性能上的优点足以弥补这个缺点。
3.在分页工作前,有必要了解使用数据库本身的一些sql语句特点更好的分页
129.mybatis 是否支持延迟加载?延迟加载的原理是什么?
- mybatis支持延迟加载
- 适用场景
- 一对一,多对一 立即加载
- 一对多,多对多 延迟加载
130.说一下 mybatis 的一级缓存和二级缓存?
一级缓存也叫sqlSession级别的缓存 ,也就是在同一个sqlSession内执行两次多次相同结果的查询语句,只会在第一次时发 出sql查询数据库的数据,然后之后每次从一级缓存中查询数据返回。
2.二级缓存
是mapper级别的缓存,也就是多个sqlSession之间可以实现数据的共享。
注意:1 当执行insert、delete、update时清除二级缓存,因为一级缓存都不存在了,何来二级缓存。2 当没有session.close()时,没有二级缓存,因为压根没有写入,
3 ,没有开启相关配置文件,没有二级缓存,因为其默认是关闭的
131.mybatis 和 hibernate 的区别有哪些?
132.mybatis 有哪些执行器(Executor)?
133.mybatis 分页插件的实现原理是什么?
首先,在MyBatis内部定义了一个拦截器接口
所有的插件都要实现该接口,来,我们看看这个接口的定义
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
那么其中一个关键的方法就是intercept,从而实现拦截
分页插件的原理就是使用MyBatis提供的插件接口,实现自定义插件,在插件的拦截方法内,拦截待执行的SQL,然后根据设置的dialect(方言),和设置的分页参数,重写SQL ,生成带有分页语句的SQL,执行重写后的SQL,从而实现分页
所以原理还是基于拦截器
134.mybatis 如何编写一个自定义插件?
- Executor :拦截内部执行器,它负责调用StatementHandler 操作数据库,并把结果集通过 ResultSetHandler 进行自动映射,另外它还处理了二级缓存的操作。
- StatementHandler :拦截 SQL 语法构建的处理,它是MyBatis 直接和数据库执行 SQL脚本的对象,另外它也实现了 MyBatis一级缓存
- ParamenterHandler :拦截参数的处理。
- ResultSetHandler :拦截结果集的处理。
十四、RabbitMQ
135.rabbitmq 的使用场景有哪些?
136.rabbitmq 有哪些重要的角色?
137.rabbitmq 有哪些重要的组件?
138.rabbitmq 中 vhost 的作用是什么?
139.rabbitmq 的消息是怎么发送的?
140.rabbitmq 怎么保证消息的稳定性?
- 提供了事务的功能。
- 通过将 channel 设置为 confirm(确认)模式。
141.rabbitmq 怎么避免消息丢失?
1 消息持久化
2 ACK确认机制
3 设置集群镜像模式
4 消息补偿机制
142.要保证消息持久化成功的条件有哪些?
1声明队列必须设置持久化 durable 设置为 true. 2
消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。
3消息已经到达持久化交换器。 4
消息已经到达持久化队列。
以上四个条件都满足才能保证消息持久化成功。
143.rabbitmq 持久化有什么缺点?
1 持久化的缺地就是降低了服务器的吞吐量,
2 因为使用的是磁盘而非内存存储,
3 从而降低了吞吐量。可尽量使用 ssd 硬盘来缓解吞吐量的问题。
144.rabbitmq 有几种广播类型?
三种广播模式:
1 fanout: 所有bind到此exchange的queue都可以接收消
(纯广播,绑定到RabbitMQ的接受者都能收到消息);
2 direct: 通过routingKey和exchange决定的那个唯一的queue可以接收消息;
3 topic: 所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息;
145.rabbitmq 怎么实现延迟消息队列?
1 通过消息过期后进入死信交换器,
再由交换器转发到延迟消费队列,实现延迟功能;
2 使用 RabbitMQ-delayed-message-exchange 插件实现延迟功能。
146.rabbitmq 集群有什么用?
- 高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用;
- 高容量:集群可以承载更多的消息量。
147.rabbitmq 节点的类型有哪些?
1 磁盘节点:消息会存储到磁盘。
2 内存节点:消息都存储在内存中,重启服务器消息丢失,性能高于磁盘类型
148.rabbitmq 集群搭建需要注意哪些问题?
-
各节点之间使用“–link”连接,此属性不能忽略。
-
各节点使用的 erlang cookie 值必须相同,此值相当于“秘钥”的功能,用于各节点的认证。
-
整个集群中必须包含一个磁盘节点。
149.rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?
不是,原因有以下两个:
-
存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,
这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据; -
性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,
那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。
150.rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?
如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:
不能创建队列
不能创建交换器
不能创建绑定
不能添加用户
不能更改权限
不能添加和删除集群节点
唯一磁盘节点崩溃了,集群是可以保持运行的,但你不能更改任何东西
151.rabbitmq 对集群节点停止顺序有要求吗?
RabbitMQ 对集群的停止的顺序是有要求的,
应该先关闭内存节点,最后再关闭磁盘节点。如果顺序恰好相反的话,可能会造成消息的丢失。
十五、Kafka
152. kafka 可以脱离 zookeeper 单独使用吗?为什么?
kafka 不能脱离 zookeeper 单独使用,因为 kafka 使用 zookeeper 管理和协调 kafka 的节点服务器。
153. kafka 有几种数据保留的策略?
kafka 有两种数据保存策略:按照过期时间保留和按照存储的消息大小保留。
154. kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?
这个时候 kafka 会执行数据清除工作,时间和大小不论那个满足条件,都会清空数据。
155. 什么情况会导致 kafka 运行变慢?
- cpu 性能瓶颈
- 磁盘读写瓶颈
- 网络瓶颈
156. 使用 kafka 集群需要注意什么?
- 集群的数量不是越多越好,最好不要超过 7 个,因为节点越多,消息复制需要的时间就越长,整个群组的吞吐量就越低。
- 集群数量最好是单数,因为超过一半故障集群就不能用了,设置为单数容错率更高。
十六、Zookeeper
157.zookeeper 是什么?
ZooKeeper 是一个高性能、集中化、分布式应用程序协调服务,主要是用来解决分布式应用中用户经常遇到的一些数据管理问题,例如,数据发布/订阅、命名服务、分布式协调通知、集群管理、Master 选举、分布式锁和分布式队列等功能。其中,Master 选举是 ZooKeeper 最典型的应用场景。
158.zookeeper 都有哪些功能?
1、 命名服务
2、 配置管理
3、 集群管理
4、 分布式锁
5、队列管理
159.zookeeper 有几种部署模式?
单机部署:一般用来检验Zookeeper基础功能,熟悉ZK各种基础操作及特性;
伪集群部署:在单台机器上部署集群,方便在本地验证集群模式下的各种功能;
集群部署:一般在生产环境使用,具备一致性、分区容错性;
161.集群中为什么要有主节点?
在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,
其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,所以就需要主节点
162.集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?
可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。
163.说一下 zookeeper 的通知机制?
客户端端会对某个 znode 建立一个 watcher 事件,当该 znode 发生变化时,这些客户端会收到 zookeeper 的通知,然后客户端可以根据 znode 变化来做出业务上的改变。
十七、MySql
164.数据库的三范式是什么?
第一范式:强调的是列的原子性,就是列不能够再分成其他几列;
第二范式:一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖主键的一部分;
第三范式:任何非主属性不依赖于其他非主属性。
165.一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?
- 数据库引擎如果是 MyISAM ,那 id 就是 8。
- 数据库引擎如果是 InnoDB,那 id 就是 6。
InnoDB 表只会把自增主键的最大 id 记录在内存中,所以重启之后会导致最大 id 丢失。
166.如何获取当前数据库版本?
使用 select version() 获取当前 MySQL 数据库版本。
167.说一下 ACID 是什么?
- Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
- Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
- Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
- Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
168.char 和 varchar 的区别是什么?
- char(n) :固定长度类型,比如订阅 char(10),当你输入"abc"三个字符的时候,它们占的空间还是 10 个字节,其他 7 个是空字节。
char 优点:效率高;缺点:占用空间;适用场景:存储密码的 md5 值,固定长度的,使用 char 非常合适。
- varchar(n) :可变长度,存储的值是每个值占用的字节再加上一个用来记录其长度的字节的长度。
所以,从空间上考虑 varcahr 比较合适;从效率上考虑 char 比较合适,二者使用需要权衡。
169.float 和 double 的区别是什么?
- float 最多可以存储 8 位的十进制数,并在内存中占 4 字节。
- double 最可可以存储 16 位的十进制数,并在内存中占 8 字节。
170.mysql 的内连接、左连接、右连接有什么区别?
内连接关键字:inner join;左连接:left join;右连接:right join。
内连接是把匹配的关联数据显示出来;左连接是左边的表全部显示出来,右边的表显示出符合条件的数据;右连接正好相反。
171.mysql 索引是怎么实现的?
索引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。
具体来说 MySQL 中的索引,不同的数据引擎实现有所不同,但目前主流的数据库引擎的索引都是 B+ 树实现的,B+ 树的搜索效率,可以到达二分法的性能,找到数据区域之后就找到了完整的数据结构了,所有索引的性能也是更好的。
172.怎么验证 mysql 的索引是否满足需求?
使用 explain 查看 SQL 是如何执行查询语句的,从而分析你的索引是否满足需求。
explain 语法:explain select * from table where type=1。
173.说一下数据库的事务隔离?
MySQL 的事务隔离是在 MySQL. ini 配置文件里添加的,在文件的最后添加:transaction-isolation = REPEATABLE-READ
可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。
-
- READ-UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)。
- READ-COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读)。
- REPEATABLE-READ:可重复读,默认级别,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读)。
- SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
- READ-UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读)。
脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
不可重复读 :是指在一个事务内,多次读同一数据。
幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。
174.说一下 mysql 常用的引擎?
- InnoDB 引擎:mysql 5.1 后默认的数据库引擎,提供了对数据库 acid 事务的支持,并且还提供了行级锁和外键的约束,它的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候,InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎是不支持全文搜索,同时启动也比较的慢,它是不会保存表的行数的,所以当进行 select count(*) from table 指令的时候,需要进行扫描全表。由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。
- MyIASM 引擎:不提供事务的支持,也不支持行级锁和外键。因此当执行插入和更新语句时,即执行写操作的时候需要锁定这个表,所以会导致效率会降低。不过和 InnoDB 不同的是,MyIASM 引擎是保存了表的行数,于是当进行 select count(*) from table 语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的,可以将 MyIASM 作为数据库引擎的首选。
175.说一下 mysql 的行锁和表锁?
MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。
-
- 表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低。
- 行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高。
- 表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低。
176.说一下乐观锁和悲观锁?
- 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
- 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。
数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。
177.mysql 问题排查都有哪些手段?
- 使用 show processlist 命令查看当前所有连接信息。
- 使用 explain 命令查询 SQL 语句执行计划。
- 开启慢查询日志,查看慢查询的 SQL。
178.如何做 mysql 的性能优化?
- 为搜索字段创建索引。
- 避免使用 select *,列出需要查询的字段。
- 垂直分割分表。
- 选择正确的存储引擎。
十八、Redis
179. Redis 是什么?都有哪些使用场景?
Redis 是一个使用 C 语言开发的高速缓存数据库。
Redis 使用场景:
-
- 记录帖子点赞数、点击数、评论数;
- 缓存近期热帖;
- 缓存文章详情信息;
- 记录用户会话信息。
- 记录帖子点赞数、点击数、评论数;
180. Redis 有哪些功能?
- 数据缓存功能
- 分布式锁的功能
- 支持数据持久化
- 支持事务
- 支持消息队列
181. Redis 和 memcache 有什么区别?
- 存储方式不同:memcache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;Redis 有部份存在硬盘上,这样能保证数据的持久性。
- 数据支持类型:memcache 对数据类型支持相对简单;Redis 有复杂的数据类型。
- 使用底层模型不同:它们之间底层实现方式,以及与客户端之间通信的应用协议不一样,Redis 自己构建了 vm 机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
- value 值大小不同:Redis 最大可以达到 512mb;memcache 只有 1mb。
182. Redis 为什么是单线程的?
因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。
关于 Redis 的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。
而且单线程并不代表就慢 nginx 和 nodejs 也都是高性能单线程的代表。
183. 什么是缓存穿透?怎么解决?
缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
184. Redis 支持的数据类型有哪些?
Redis 支持的数据类型:string(字符串)、list(列表)、hash(散列表)、set(集合)、zset(有序集合)。
185. Redis 支持的 Java 客户端都有哪些?
支持的 Java 客户端有 Redisson、jedis、lettuce 等。
186. jedis 和 Redisson 有哪些区别?
- jedis:提供了比较全面的 Redis 命令的支持。
- Redisson:实现了分布式和可扩展的 Java 数据结构,与 jedis 相比 Redisson 的功能相对简单,不支持排序、事务、管道、分区等 Redis 特性。
187. 怎么保证缓存和数据库数据的一致性?
- 合理设置缓存的过期时间。
- 新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。
188. Redis 持久化有几种方式?
Redis 的持久化有两种方式,或者说有两种策略:
-
- RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
- AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。指令
- RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
189. Redis 怎么实现分布式锁?
Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。
占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。
190. Redis 分布式锁有什么缺陷?
Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。
191. Redis 如何做内存优化?
尽量使用 Redis 的散列表,把相关的信息放到散列表里面存储,而不是把每个字段单独存储,这样可以有效的减少内存使用。比如将 Web 系统的用户对象,应该放到散列表里面再整体存储到 Redis,而不是把用户的姓名、年龄、密码、邮箱等字段分别设置 key 进行存储。
192. Redis 淘汰策略有哪些?
- volatile-lru:从已设置过期时间的数据集(server. db[i]. expires)中挑选最近最少使用的数据淘汰。
- volatile-ttl:从已设置过期时间的数据集(server. db[i]. expires)中挑选将要过期的数据淘汰。
- volatile-random:从已设置过期时间的数据集(server. db[i]. expires)中任意选择数据淘汰。
- allkeys-lru:从数据集(server. db[i]. dict)中挑选最近最少使用的数据淘汰。
- allkeys-random:从数据集(server. db[i]. dict)中任意选择数据淘汰。
- no-enviction(驱逐):禁止驱逐数据。
193. Redis 常见的性能问题有哪些?该如何解决?
- 主服务器写内存快照,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以主服务器最好不要写内存快照。
- Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,主从库最好在同一个局域网内。
十九、JVM
194. 说一下 JVM 的主要组成部分?及其作用?
- 类加载器(ClassLoader)
- 运行时数据区(Runtime Data Area)
- 执行引擎(Execution Engine)
- 本地库接口(Native Interface)
组件的作用: 首先类加载器(ClassLoader)会把字节码文件(.class)加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。运行时数据区(Runtime Data Area)作用详细见下一点。
195. 说一下 JVM 运行时数据区?
不同虚拟机的运行时数据区可能略微有所不同,但都会遵从 Java 虚拟机规范, Java 虚拟机规范规定的区域分为以下 5 个部分:
-
- 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;
- Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
- 本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;
- Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;
- 方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
- 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;
196. 说一下堆栈的区别?
- 功能方面:堆是用来存放对象的,栈是用来执行程序的。
- 共享性:堆是线程共享的,栈是线程私有的。
- 空间大小:堆大小远远大于栈。
197. 队列和栈是什么?有什么区别?
队列和栈都是被用来预存储数据的。
队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。
栈和队列很相似,但它运行对元素进行后进先出进行检索。
198. 什么是双亲委派模型?
在介绍双亲委派模型之前先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。
类加载器分类:
-
- 启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
- 其他类加载器:
- 扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;
- 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
- 启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
199. 说一下类装载的执行过程?
类装载分为以下 5 个步骤:
-
- 加载:根据查找路径找到相应的 class 文件然后导入;
- 检查:检查加载的 class 文件的正确性;
- 准备:给类中的静态变量分配内存空间;
- 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
- 初始化:对静态变量和静态代码块执行初始化工作。
- 加载:根据查找路径找到相应的 class 文件然后导入;
200. 怎么判断对象是否可以被回收?
一般有两种方法来判断:
-
- 引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
- 可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
- 引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
201. Java 中都有哪些引用类型?
- 强引用:发生 gc 的时候不会被回收。
- 软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
- 弱引用:有用但不是必须的对象,在下一次GC时会被回收。
- 虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知。
202. 说一下 JVM 有哪些垃圾回收算法?
- 标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
- 标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
- 复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
- 分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
203. 说一下 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 选项。
204. 详细介绍一下 CMS 垃圾回收器?
CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。
CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。
205. 新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?
- 新生代回收器:Serial、ParNew、Parallel Scavenge
- 老年代回收器:Serial Old、Parallel Old、CMS
- 整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。
206. 简述分代垃圾回收器是怎么工作的?
分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。
新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,它的执行流程如下:
-
- 把 Eden + From Survivor 存活的对象放入 To Survivor 区;
- 清空 Eden 和 From Survivor 分区;
- From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
- 把 Eden + From Survivor 存活的对象放入 To Survivor 区;
每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。
老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。
207. 说一下 JVM 调优的工具?
JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。
-
- jconsole:用于对 JVM 中的内存、线程和类等进行监控;
- jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。
- jconsole:用于对 JVM 中的内存、线程和类等进行监控;
208. 常用的 JVM 调优的参数都有哪些?
- -Xms2g:初始化推大小为 2g;
- -Xmx2g:堆最大内存为 2g;
- -XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
- -XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
- –XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
- -XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
- -XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
- -XX:+PrintGC:开启打印 gc 信息;
- -XX:+PrintGCDetails:打印 gc 详细信息。