Java问答题

1. JDK 和 JRE 有什么区别?

JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发和运行环境,编译器、调试和分析工具等。jdk包含jre。

JRE:Java Runtime Environment 的简称,java 运行时环境,为 java 程序的运行提供了所需环境。jre包含jvm。

 

2. == 和 equals 的区别是什么?

==对于基本类型和引用类型 == 的作用效果是不同的,基本类型比较的是值是否相同,引用类型:比较的是引用是否相同。

equals是Object类的方法,默认实现就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。

 

3. 两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

不对,如果equals为true,则hashCode相同,但如果hashCode相同,不需要保证equals为true

但是,为equals为false的对象生成不同的hashCode可能会提高哈希表的性能。

 

4. final 在 java 中有什么作用?

final 修饰的类不能被继承。

final 修饰的方法(包括静态方法)不能被重写。

final 修饰的变量叫常量,常量必须初始化且不能修改。

 

5. java 中的 Math.round(-1.5) 等于多少?

等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。

 

6. String 属于基础的数据类型吗?

String继承Objcet属于引用类型,但可以直接使用双引号创建String对象而不用new,基础类型有 8 种:byte、boolean、char、short、int、float、long、double。

 

7. java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:String、StringBuffer、StringBuilder。

String 是不可变的字符串,无法对字符串内容进行修改, StringBuffer、StringBuilder 是可变字符串,可以对字符串内容进行修改。

StringBuffer 是线程安全的,StringBuilder 是非线程安全的,StringBuilder 的性能却高于 StringBuffer。

最简便的字符串拼接是使用加号,但性能最好的拼接是使用StringBuilder.append()。

 

8. String str="i"与 String str=new String("i")一样吗?

不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String("i") 则会被分到堆内存中。

 

9. 如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

 

10. String 类的常用方法都有那些?

length,equals,indexOf,lastIndexOf,substring,replace,trim,split,getBytes,charAt,toLowerCase,toUpperCase。

 

11. 抽象类必须要有抽象方法吗?

不一定要有抽象方法。

 

12. 普通类和抽象类有哪些区别?

抽象类使用abstract修饰,可以包含抽象方法,不能直接实例化,普通类反之。

 

13. 抽象类能使用 final 修饰吗?

不能,语法错误编译不通过。

 

14. 接口和抽象类有什么区别?

使用 extends 来继承抽象类,使用 implements 来实现接口。

抽象类可以有构造函数,接口不能有。

抽象类可以有 main 方法,并且我们能运行它,接口不能有 main 方法。

抽象类只能单继承,接口可以多实现。

接口中的方法默认使用 public 修饰,抽象类中的方法可以是任意访问修饰符。

 

15. java 中 IO 流分为几种?

按功能来分:输入流(input)、输出流(output)。

按类型来分:字节流和字符流。

字节流和字符流的区别是:字节流以字节(8bit)为单位输入输出数据,字符流以字符(多少bit与指定的字符编码有关)为单位输入输出数据。

 

16. BIO、NIO、AIO 有什么区别?

BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。

NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。

AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

 

17. Files的常用方法都有哪些?

Files.exists():检测文件路径是否存在。

Files.createFile():创建文件。

Files.createDirectory():创建文件夹。

Files.delete():删除一个文件或目录。

Files.copy():复制文件。

Files.move():移动文件。

Files.size():查看文件个数。

Files.read():读取文件。

Files.write():写入文件。

 

18. java 容器都有哪些?

常用容器的图录:

 

 

19. Collection 和 Collections 有什么区别?

java.util.Collection 是一个顶级集合接口,它提供了对集合对象进行基本操作的通用接口方法,Java 类库中有很多具体的实现,直接继承接口有List与Set。

Collections则是集合类的一个工具类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作,与Collections对应的是操作数据的工具类Arrays。

 

20. List、Set、Map 之间的区别是什么?

list有序可重复,set无序不可重复(treeset是自动排序而不是有序),map是键值对,键不能重复。

 

21. HashMap 和 Hashtable 有什么区别?

最重要的两点:

Hashtable线程安全,HashMap非线程安全,但效率高。

HashMap允许空键值,而Hashtable不允许。

 

22. 如何决定使用 HashMap 还是 TreeMap?

HashMap使用哈希码增删改查都很快,TreeMap使用红黑二叉树比哈希码慢但会自己排序。

除非需要对map进行排序,否则一律使用HashMap。

 

23. 说一下 HashMap 的实现原理?

数组(桶)+链表实现。

根据key的哈希值折算成在数组中的下标,如果该下标没有存值,则直接放入该下标,如果该下标已经有值,则在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾。

JDK 1.8对HashMap的做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率。

 

24. 说一下 HashSet 的实现原理?

HashSet底层由HashMap实现,HashSet的值存放于HashMap的key上,HashMap的value统一为一个常量值。

 

25. ArrayList 和 LinkedList 的区别是什么?

ArrayList底层的数据结构是数组, LinkedList 的底层数据结构是双向循环链表。

ArrayList 增删慢,查询快,LinkedList 反之。

 

26. 如何实现数组和 List 之间的转换?

List转换成为数组:调用ArrayList的toArray方法。

数组转换成为List:调用Arrays的asList方法。

 

27. ArrayList 和 Vector 的区别是什么?

Vector是线程安全的,ArrayList非线程安全但效率要快。

Vector是很古老的类,不建议使用Vector,因为我们可以使用Collections.synchronizedList获取同步列表。

 

28. 数组和 ArrayList 有何区别?

数组可以存储基本类型也可以存储对象,而ArrayList只能存储对象。 

数组创建时必须指定长度,而ArrayList长度不需要指定,可以根据已有数据量自行增加长度。 

数组没有ArrayList众多操作列表的方法。

 

29. 在 Queue 中 poll()和 remove()有什么区别?

poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回空,但是 remove() 失败的时候会抛出异常。

 

30. 哪些集合类是线程安全的?

hashtable,vector

 

31. 迭代器 Iterator 是什么?

迭代器是一种设计模式,用于遍历列表中的对象,开发人员不需要了解该列表的底层结构。

iterator是java里迭代器的一种实现,还有一个迭代器实现是enumeration。

 

32. Iterator 怎么使用?有什么特点?

Java中的iterator功能比较简单,并且只能单向移动:

(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

(2) 使用next()获得序列中的下一个元素。

(3) 使用hasNext()检查序列中是否还有元素。

(4) 使用remove()将迭代器新返回的元素删除。

Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

 

33. Iterator 和 ListIterator 有什么区别?

Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。 

Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。 

ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

 

35. 并行和并发有什么区别?

并发:不同代码块交替执行;并行:不同代码块同时执行。

并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

 

36. 线程和进程的区别?

进程是操作系统中程序运行和资源分配的基本单位,线程是进程的执行分支,是比进程更小的cpu执行单位,一个进程至少有一个线程,多个线程共享进程的内存资源。

 

37. 守护线程是什么?

守护线程(daemon thread),是运行在后台服务于其他线程的线程,周期性地执行某种任务或等待处理某些发生的事件。

 

38. 创建线程有哪几种方式?

1. 继承Thread类重写run方法。

2. 实现Runnable接口实现run方法,并将Runnable接口实现类的实例作为Thread的构造参数。

3. 实现Callable接口实现run方法,并将Callable接口实现实现类的实例作为和FutureTask的构造参数创建FutureTask对象,然后将FutureTask对象作为Thread的构造参数。这种方式可以获取执行的返回值,获取返回值时会阻塞。

            Callable<String> call = new Callable<String>() {
				@Override
				public String call() throws Exception {
					System.out.println("Running...");
					return "done";
				}
			};
			FutureTask<String> futureTask=new FutureTask<String>(call);
			Thread thread = new Thread(futureTask);
			thread.start();
			//futureTask.get():获取任务执行结果,如果任务还没完成则会阻塞等待直到任务执行完成。如果任务被取消则会抛出CancellationException异常,
            //如果任务执行过程发生异常则会抛出ExecutionException异常,如果阻塞等待过程中被中断则会抛出InterruptedException异常。
            boolean result = (boolean) futureTask.get();
			String result = futureTask.get();
			System.out.println(result);

4.使用线程池。

 

39. 说一下 runnable 和 callable 有什么区别?

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;

Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

 

40. 线程有哪些状态?

传统的线程状态分为五种:创建、就绪、运行、阻塞和死亡。

java线程有六种状态,在java.lang.Thread.State定义,分别是:

1.New/新生(创建),使用new Thread(r)创建一个新线程时,该线程处于新生状态,新生状态会为线程的运行做一些准备。

2.Runnable/可运行(就绪+运行),调用线程的start方法,使该线程处于可运行状态。可运行状态不代表该线程一定在运行,而是它具备运行的条件,它可能正在运行,也可能没有在运行,这完全取决于线程调度控制。

3.Blocked/被阻塞(阻塞),当线程试图获取一个内部的对象锁时,该对象锁被其他线程持有,则该线程进入阻塞状态。

4.Waiting/等待(阻塞),当在调用Object.wait方法、Thread.join方法、java.util.concurrent库中的Lock或Condition时,该线程进入等待状态。

5.Timed waiting/记时等待(阻塞),进入该状态的条件和进入等待状态的条件相同,不过此状态能在指定的时间之后苏醒,所以又有别于等待状态,Thread.sleep(long)和Object.wait(long)方法会使线程进入计时等待状态。

6.Terminated/被终止(死亡),Java没有可直接终止一个线程的方法(stop已经被申明过时不允许使用了,interrrupt方法只是请求中断,不是一定可以中断一个线程),所以只有在程序自然结束或抛出了一个没有捕获的异常时,线程才会进入被终止状态。

使用Thread类的getState()方法可以获取线程状态。

 

41. sleep() 和 wait() 有什么区别?

sleep():是Thread的静态方法,让调用线程进入休眠,让出执行机会给其他线程,等到休眠时间结束后,线程进入就绪状态和其他线程一起竞争cpu执行。它不能释放锁,当一个synchronized块中调用了sleep() 方法,线程虽然进入休眠,但是锁没有被释放,其他线程依然无法执行该synchronized块代码。调用Thread.sleep一定要捕获java.lang.InterruptedException异常(编译异常)。

wait():wait()本质是一个锁释放自己本身的操作方法,因为任何对象都能作为锁,所以放在了Object类中来。调用wait()一定要放到synchronized代码中,否则会报 IllegalMonitorStateException 异常(运行异常)。可以通过notify,notifyAll方法来唤醒等待的线程。

 

42. notify()和 notifyAll()有什么区别?

notify()和notifyAll()本质都是一个锁通知在等待自己的线程,notify是通知等待自己的其中一个线程,notifyAll是通知所有等待自己的线程。被通知的线程会进入这个锁的竞争池,优先级高的会优先持有这个锁。

 

43. 线程的 run()和 start()有什么区别?

start是启动线程的方法,启动线程必须显式调用start,run是具体运行线程代码的线程体,不需要显式调用。

 

44. 创建线程池有哪几种方式?

①. Executors.newFixedThreadPool(int nThreads)

创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。

②. Executors.newCachedThreadPool()

创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。

③. Executors.newSingleThreadExecutor()

这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行。

④. Executors.newScheduledThreadPool(int corePoolSize)

创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。

 

45. 线程池都有哪些状态?

线程池有5种状态:Running、ShutDown、Stop、Tidying、Terminated。

线程池各个状态切换框架图:

 

 

46. 线程池中 submit()和 execute()方法有什么区别?

submit里面其实就是调用的execute,只不过execute没有返回值,submit将run方法的返回值通过Future返回。

 

47. 在 java 程序中怎么保证多线程的运行安全?

线程安全在三个方面体现:

原子性:同一时刻只能由一个线程来运行某段代码或操作某个数据,代码原子性可以使用synchronized来保证,数据原子性可以使用atomic相关类来操作。

i = 2; //原子
j = i; //非原子 (1 读i,2 赋值j)
i++;   //非原子 (1 读i,2 +1 ,3 赋值i)
i = i + 1; //非原子 (1 读i,2 +1 ,3 赋值i)
obj=new Object();//非原子(1 分配对象的内存空间,2构造Object对象,3将内存地址返回给obj,其中构造Object对象包含了许多步骤,如加载类,初始化静态变量,给静态变量赋值,执行静态代码块,初始化实例变量,给实例变量赋值,执行构造代码块和构造函数),这就是为什么单例模式的线程安全写法不是直接在静态变量上new对象。

JMM只实现了基本的原子性,保证了对基本数据类型的读取和赋值操作是原子性操作,像上面i++那样的操作,必须借助于synchronizedLock来保证整块代码的原子性了。线程在释放锁之前,必然会把i的值刷回到主存的。

可见性:一个线程对主内存的修改可以及时地被其他线程看到,当一个变量被volatile修饰时,那么对它的修改会立刻刷新到主存,当其它线程需要读取该变量时,会去内存中读取新值。而普通变量则不能保证这一点。其实通过synchronized和Lock也能够保证可见性,线程在释放锁之前,会把共享变量值都刷回主存,但是synchronized和Lock的开销都更大。

有序性

java遵循java内存模型的happens-before原则来保证有序性。

JMM是允许编译器和处理器对指令重排序的,但是规定了as-if-serial语义,即不管怎么重排序,程序的执行结果不能改变。比如下面的程序段:

double pi = 3.14;    //A
double r = 1;        //B
double s= pi * r * r;//C

上面的语句,可以按照A->B->C执行,结果为3.14,但是也可以按照B->A->C的顺序执行,因为A、B是两句独立的语句,而C则依赖于A、B,所以A、B可以重排序,但是C却不能排到A、B的前面。JMM保证了重排序不会影响到单线程的执行,但无法保证多线程执行结果的正确。

使用volatile可以禁止重排序,可以确保程序的“有序性”,也可以上重量级的synchronized和Lock来保证有序性,它们能保证那一块区域里的代码都是一次性执行完毕的。

 

48. 多线程锁的升级原理是什么?

在Java中,锁共有4种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。

锁升级的图示过程: 

 

 

49. 什么是死锁?

死锁是指两个或两个以上的线程互相持有对方所需要的资源,导致这些线程处于等待状态,都无法继续执行。

死锁不单单发生于多线程,多进程、软件系统甚至人类生活中都可能发生此种情况。

 

50. 怎么防止死锁?

加锁顺序(线程按照一定的顺序加锁)

当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。

如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。

如果一个线程(比如线程3)需要一些锁,那么它必须按照确定的顺序获取锁。它只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。

例如,线程2和线程3只有在获取了锁A之后才能尝试获取锁C(译者注:获取锁A是获取锁C的必要条件)。因为线程1已经拥有了锁A,所以线程2和3需要一直等到锁A被释放。然后在它们尝试对B或C加锁之前,必须成功地对A加了锁。

按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁(译者注:并对这些锁做适当的排序),但总有些时候是无法预知的。

加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

另外一个可以避免死锁的方法是在尝试获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行(加锁超时后可以先继续运行干点其它事情,再回头来重复之前加锁的逻辑)。

ReentrantLock类tryLock(long time, TimeUnit unit)方法可以尝试获取锁,如果超过指定时间仍未获取到锁,则放弃获取锁。

死锁检测

死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。

当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。例如,线程A请求锁7,但是锁7这个时候被线程B持有,这时线程A就可以检查一下线程B是否已经请求了线程A当前所持有的锁。如果线程B确实有这样的请求,那么就是发生了死锁(线程A拥有锁1,请求锁7;线程B拥有锁7,请求锁1)。

当然,死锁一般要比两个线程互相持有对方的锁这种情况要复杂的多。线程A等待线程B,线程B等待线程C,线程C等待线程D,线程D又在等待线程A。线程A为了检测死锁,它需要递进地检测所有被B请求的锁。从线程B所请求的锁开始,线程A找到了线程C,然后又找到了线程D,发现线程D请求的锁被线程A自己持有着。这是它就知道发生了死锁。

一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁(原因同超时类似,不能从根本上减轻竞争)。

一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。

51. ThreadLocal 是什么?有哪些使用场景?

ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是

Synchronized是通过线程等待,牺牲时间来解决访问冲突。ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。

正因为ThreadLocal的线程隔离特性,使他的应用场景相对来说更为特殊一些。当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。

但是在线程生命周期很长时使用ThreadLocal存在内存泄露的风险。

 

52.说一下 synchronized 底层实现原理?

synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性(释放锁时,对修改的变量立刻更新到共享内存)。普通同步方法,锁是当前实例对象,静态同步方法,锁是当前类的class对象,同步方法块,锁是括号里面指定的对象。

底层实现非常复杂,目前只了解java对象头里有保存锁相关信息的Mark Word。

任何一个对象都有一个Monitor与之关联,当且一个Monitor被持有后,它将处于锁定状态。Synchronized在JVM里的实现都是 基于进入和退出Monitor对象来实现方法同步和代码块同步,虽然具体实现细节不一样,但是都可以通过成对的MonitorEnter和MonitorExit指令来实现。

  1. MonitorEnter指令:插入在同步代码块的开始位置,当代码执行到该指令时,将会尝试获取该对象Monitor的所有权,即尝试获得该对象的锁;
  2. MonitorExit指令:插入在方法结束处和异常处,JVM保证每个MonitorEnter必须有对应的MonitorExit;

那什么是Monitor?可以把它理解为 一个同步工具,也可以描述为 一种同步机制,它通常被 描述为一个对象

与一切皆对象一样,所有的Java对象是天生的Monitor,每一个Java对象都有成为Monitor的潜质,因为在Java的设计中 ,每一个Java对象自打娘胎里出来就带了一把看不见的锁,它叫做内部锁或者Monitor锁

也就是通常说Synchronized的对象锁,MarkWord锁标识位为10,其中指针指向的是Monitor对象的起始地址。在Java虚拟机(HotSpot)中,Monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)。

 

53. synchronized 和 volatile 的区别是什么?

volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。

volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。

volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。

volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

 

54. synchronized 和 Lock 有什么区别?

首先synchronized是java内置关键字,Lock是个java类;

synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;

synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;

用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;

synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可);

Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

 

55. synchronized 和 ReentrantLock 区别是什么?

synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上: 

ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁 

ReentrantLock可以获取各种锁的信息

ReentrantLock可以灵活地实现多路通知 

另外,二者的锁机制其实也是不一样的:ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word。

 

56. 说一下 atomic 的原理?

在多线程或者并发环境中,我们常常会遇到这种情况 int i=0; i++ 但这种写法是线程不安全的。
为了达到线程安全的目的,我们通常会用synchronized来修饰对应的代码块,还有一种办法就是使用atomic类。
atomic类通过CAS自旋锁操作volatile变量实现数据操作的原子性。因为加锁或使用 synchronized 关键字带来的性能损耗较大,而用 CAS 可以实现乐观锁,它实际上是直接利用了 CPU 层面的指令,所以性能很高。

 

57. 什么是反射?

反射主要是指程序运行时可以访问、检测和修改它本身状态或行为的一种能力。

Java反射机制主要提供了以下功能:

在运行时判断任意一个对象所属的类。

在运行时构造任意一个类的对象。

在运行时判断任意一个类所具有的成员变量和方法。

在运行时调用任意一个对象的方法。 

 

58. 什么是 java 序列化?什么情况下需要序列化?

Java序列化是永久保存对象的机制,一个对象被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。把对象变成字节序列叫序列化,把字节序列变成对象叫反序列化。

在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。类 ObjectInputStream 和 ObjectOutputStream 是高层次的数据流,它们包含反序列化和序列化对象的方法。

要序列化的对象,必须实现Serializable接口。

序列化只能保存实例变量,静态变量无法序列化。

并建议设置版本号:

private static final long serialVersionUID = -3737338076212523007L;

什么情况下需要序列化:

a)把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)用套接字或使用RMI在网络上传输对象的时候;
c)深克隆;

 

59. 动态代理是什么?有哪些应用?

动态代理是代理模式中一种实现,区别于静态代理。

代理模式由三种角色构成:

抽象角色:通过接口或抽象类声明真实角色实现的业务方法。

代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

动态代理和静态代理的区别就在于,静态代理需要自己编写代理角色的代码,而动态代理的代理角色是在程序运行中通过反射自动生成的。

动态代理主要应用在AOP编程中,而AOP编程主要应用在统一的日志、事物、异常、权限等等的处理。

动态代理是AOP编程的实现方式。

 

60. 怎么实现动态代理?

Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {


                if (isExecuteBefore) {
                    before.before(proxy, method, args);// 切入代码
                }

                Object result = null;
                try {
                    result = method.invoke(object, args);
                } catch (Exception e) {
                    if (isExecuteExcep) {
                        excep.excep(proxy, method, args);// 切入代码
                    }
                    if (isIgroreExcep) {
                        e.printStackTrace();
                    } else {
                        throw e;
                    }
                }

                if (isExecuteAfter) {
                    after.after(proxy, method, args);// 切入代码
                }

                return result;
            }
        });

Proxy.newInstance()的工作顺序是根据参数生成class字节码,然后利用加载器加载,并将其实例化产生代理对象,最后返回。

 

61. 为什么要使用克隆?

对一个对象的多个属性进行修改,需要保留原有的属性值,克隆是便捷的实现途径。

 

62. 如何实现对象克隆?

实现Cloneable接口并重写Object类中的clone()方法,方法声明可以改为public,克隆自己使用super.clone()。

实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

 

63. 深克隆和浅克隆区别是什么?

浅克隆复制当前对象属性的基本类型,和引用类型的引用地址,引用的对象和原属性相同。

深克隆复制当前对象属性的基本类型,和引用类型,引用的对象和原属性不同。

 

64. jsp 和 servlet 有什么区别?

jsp是特殊的servlet,web容器最终会将jsp编译成servlet。

jsp直接提供jsp文件给web容器,支持运行时修改发布而不用重启web服务器,servlet需要先编译成class文件打包成jar或war,所以任何修改必须重新编译重启web服务器。

jsp专注于页面显示,servlet专注于逻辑控制。

Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象获取。

 

65. jsp 有哪些内置对象?作用分别是什么?

JSP有9个内置对象:

request:封装客户端的请求,其中包含来自GET或POST请求的参数;

response:封装服务器对客户端的响应;

pageContext:存储页面相关数据,四大作用域之一的page存取值对象;

session:封装用户会话的对象;

application:封装服务器运行环境的对象;

out:输出服务器响应的输出流对象;

config:Web应用的配置对象;

page:JSP页面本身(相当于Java程序中的this);

exception:封装页面抛出异常的对象。


66. 说一下 jsp 的 4 种作用域?

JSP中的四种作用域包括page、request、session和application,具体来说:

page,在当前页面有效,也就是用户请求的页面有效,当当前页面关闭或转到其他页面时,page对象将在响应回馈给客户端后释放。实际操作的时候是使用pageContext内置对象完成的。

request,在当前请求中有效,request可以通过setAttribute()方法实现页面中的信息传递,也可以通过forward()方法进行页面间的跳转。

session,在当前会话中有效。当一个台电脑上的同一浏览器对服务器进行多次访问时,在这多次访问之间传递的信息就是session作用域的范围。它从浏览器发出第一个HTTP请求即可认为会话开始,但是会话结束的时间是不确定的,因为在浏览器关闭时并不会通知服务器,一般Tomcat设置的默认时间为120分钟,也可以通过setMaxInactiveInterval(int)方法进行设置,或是通过invalidate()方法强制结束当前会话。

application,在所有的应用程序中都有效,也就是当服务器开始到服务器结束这段时间,application作用域中存储的数据都是有效的。


67. session 和 cookie 有什么区别?

session是为了标识访问服务器的用户,cookie是浏览器存储一些本地化数据,这些数据在每次请求时都会发送给服务器,服务器在响应时也可以对cookie进行设置。

session的实现使用到了cookie。

 

68. 说一下 session 的工作原理?

第一次访问一个网站,网站会返回一个名为sessionid的cookie,里面的值在服务器里是唯一的,浏览器保存这个cookie和它的值到本地,第二次访问这个网站,浏览器会将这个sessionid的cookie发送给服务器,服务器要去所有session池中找这个sessionid,如果找到了,并且时间没有超过设置的失效时间,那么就会认定这个请求和第一个请求是同一个会话。

 

69. 如果客户端禁止 cookie 能实现 session 还能用吗?

不能,不过可以自己写代码实现类似效果。

 

70. spring mvc 和 struts2 的区别是什么?

Struts2拦截器是Filter(StrutsPrepareAndExecuteFilter)实现的,SpringMVC核心控制器是Servlet(DispatcherServlet)实现的。

Struts2将请求的参数注入到Action实例的属性,SpringMVC将请求的参数注入到Controller方法的参数。

Struts2每次请求都会创建一个Action实例,SpringMVC的Controller默认是单例的。

SpringMVC和spring是无缝集成,可以直接使用Spring框架里的其他组件。

 

71. 如何避免 sql 注入?

PreparedStatement(简单又有效的方法)

使用正则等过滤传入的参数。

 

72. 什么是 XSS 攻击,如何避免?

XSS(Cross Site Script )中文全称叫跨站脚本攻击,类似于 SQL 注入攻击,通过插入恶意脚本,实现对用户游览器的控制,获取用户的一些信息。

拿csdn举例,假设一个用户发表一篇博文,文章内容夹杂有如下代码:

<script>
    var ck=document.cookie;
    //下面使用ajax请求发送cookie到黑客指定服务器
    //具体代码略
</script>

在文章发表成功后,其他用户浏览该文章,那么其他用户的用户名等所有cookie信息都将被窃取,除了cookie,xss还能窃取用户其他信息(得看网站把哪些数据放前端了)。

当然,csdn早就处理过xss问题了,上面代码对应的输出是:

<code class="language-html">&lt;script&gt;
    var ck=document.cookie;
    //下面使用ajax请求发送cookie到黑客指定服务器
    //具体代码略
&lt;/script&gt;</code>

XSS解决方式是:对输入(和URL参数)进行过滤和转义。

 

73. 什么是 CSRF 攻击,如何避免?

CSRF(Cross-site request forgery)中文全称叫跨站请求伪造,举例用户访问了网站a,然后访问网站b,网站b在用户不知情的情况,利用用户在网站a的session未过期,伪造成用户向网站a发送请求。常用于盗取账号、转账、发送虚假消息等。

CSRF与XSS非常不同,攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范。

和重复提交问题有点类似,解决方式也和重复提交一样,通过给关键操作页面设置一个随机码,这个随机码可以放在请求体也可以放在请求头也可以放在url,每次服务器收到关键操作请求,检查这个随机码是否和服务器的一致即可。

除此之外,还可以通过验证请求头的Referer字段判断请求源是否为合法网站,来避免csrf攻击,极端一点可以在关键操作设置图形验证码防止csrf攻击。

 

74. throw 和 throws 的区别?

throws是声明方法可能抛出的异常类型,throw是抛出的一个具体的异常对象。

 

75. final、finally、finalize 有什么区别?

final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。

finally是异常代码块关键字,用于无论是否异常都需要执行的代码。

finalize是Object类的方法,对象在被垃圾回收时会该方法被调用。

 

76. try-catch-finally 中哪个部分可以省略?

try必须要有,catch和finally可以省略其中一个。

 

77. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

答:会执行,并且是在catch所有代码执行之后再执行的。如果finally里也有return,那么以finally的return为准,try和catch的return的代码会执行但不会真正返回值,finally里的return才是最终的真正的返回值。

示例:

public static int a(int[] i) {
			try {
				return 0/0;
			} catch (Exception e) {
				i[0]=i[0]+1;
				System.out.println("catch值:"+i[0]);
				return i[0];
			}finally {
				i[0]=i[0]+1;
				System.out.println("finally值:"+i[0]);
//				return i[0];
			}
		}
		public static void main(String[] args) throws InterruptedException, ExecutionException {
			int[] a={0};
			System.out.println("方法返回值:"+a(a));
			System.out.println("数组实际值:"+a[0]);
		}

打印:

catch值:1
finally值:2
方法返回值:1
数组实际值:2

去掉finally里的return的注释:

public static int a(int[] i) {
			try {
				return 0/0;
			} catch (Exception e) {
				i[0]=i[0]+1;
				System.out.println("catch值:"+i[0]);
				return i[0];
			}finally {
				i[0]=i[0]+1;
				System.out.println("finally值:"+i[0]);
				return i[0];
			}
		}
		public static void main(String[] args) throws InterruptedException, ExecutionException {
			int[] a={0};
			System.out.println("方法返回值:"+a(a));
			System.out.println("数组实际值:"+a[0]);
		}

打印:

catch值:1
finally值:2
方法返回值:1
数组实际值:2

 

78. 常见的异常类有哪些?

NullPointerException,ClassNotFoundException,ClassCastException,NoSuchMethodException,FileNotFoundException,IOException,NumberFormatException,ParseException,SQLException,ArrayIndexOutOfBoundsException。

 

79. http 响应码 301 和 302 代表的是什么?有什么区别?

301,302 都是重定向http状态码,区别在于301永久性转移,302暂时性转移。

附http状态码:

状态码状态码英文名称中文描述
100Continue继续。客户端应继续其请求
101Switching Protocols切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
 
200OK请求成功。一般用于GET与POST请求
201Created已创建。成功请求并创建了新的资源
202Accepted已接受。已经接受请求,但未处理完成
203Non-Authoritative Information非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204No Content无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205Reset Content重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206Partial Content部分内容。服务器成功处理了部分GET请求
 
300Multiple Choices多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301Moved Permanently永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302Found临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303See Other查看其它地址。与301类似。使用GET和POST请求查看
304Not Modified未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305Use Proxy使用代理。所请求的资源必须通过代理访问
306Unused已经被废弃的HTTP状态码
307Temporary Redirect临时重定向。与302类似。使用GET请求重定向
 
400Bad Request客户端请求的语法错误,服务器无法理解
401Unauthorized请求要求用户的身份认证
402Payment Required保留,将来使用
403Forbidden服务器理解请求客户端的请求,但是拒绝执行此请求
404Not Found服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405Method Not Allowed客户端请求中的方法被禁止
406Not Acceptable服务器无法根据客户端请求的内容特性完成请求
407Proxy Authentication Required请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408Request Time-out服务器等待客户端发送的请求时间过长,超时
409Conflict服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
410Gone客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411Length Required服务器无法处理客户端发送的不带Content-Length的请求信息
412Precondition Failed客户端请求信息的先决条件错误
413Request Entity Too Large由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414Request-URI Too Large请求的URI过长(URI通常为网址),服务器无法处理
415Unsupported Media Type服务器无法处理请求附带的媒体格式
416Requested range not satisfiable客户端请求的范围无效
417Expectation Failed服务器无法满足Expect的请求头信息
 
500Internal Server Error服务器内部错误,无法完成请求
501Not Implemented服务器不支持请求的功能,无法完成请求
502Bad Gateway作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503Service Unavailable由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504Gateway Time-out充当网关或代理的服务器,未及时从远端服务器获取请求
505HTTP Version not supported服务器不支持请求的HTTP协议的版本,无法完成处理

 

80. forward 和 redirect 的区别?

转发是服务器做出的处理,浏览器并不知情,浏览器地址栏未改变,实际只发送了一次请求;重定向是服务器和浏览器联合做出的处理,服务器将新地址发给浏览器,浏览器重写请求新地址,浏览器地址栏会发生改变,并实际发送了两次请求。

 

81. 简述 tcp 和 udp的区别?

tcp是基于一对一连接的协议(数据流协议),三次握手建立连接,四次挥手关闭连接,udp是基于无连接广播式的协议(数据报协议)。

tcp保证数据的正确性可靠性,udp不保证数据的正确性和可靠性。

tcp效率低,udp效率高。

 

82. tcp 为什么要三次握手,两次不行吗?为什么?

只有三次才能验证客户端接收数据的能力有没有问题。

第一次握手是为了验证客户端发送数据的能力,第二次握手是为了验证服务端接收数据和发送数据的能力,第三次握手是为了验证客户端接收数据的能力。在收到客户端发来的第三次握手,服务端才能确认客户端接收数据的能力有没有问题。

 

83. 说一下 tcp 粘包是怎么产生的?

接收方粘包问题是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

发送方粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据negal优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

附negal优化算法介绍:

会将数据量小的,且时间间隔较短的数据一次性发给对方。

 

84. OSI 的七层模型都有哪些?

应用层(http,ftp,smtp,pop),表示层。会话层,传输层(tcp,udp),网络层(ip),数据链路层,物理层。

附七层网络模型和四层网络模型对照表:

 

85. get 和 post 请求有哪些区别?

get参数只能拼接在url上,post可以放url也可以放请求体中。

url长度有限制,所以get参数有限制,post参数放请求体没有大小限制。

get参数放在url上显示在浏览器地址栏不友好不安全,post没有此问题。

get只能接受ASCII字符类型的参数,post不受限制。

get请求在浏览器回退时不会重复提交,post会重复提交。

 

86. 如何实现跨域?

方式一:

利用img、script、iframe标签的地址不受域名限制的原理,请求跨域的服务。jquery里的jsonp就是用的script标签,而且使用了jquery里的jsonp,那么async只能为true,method只能是get。

方式二:

修改服务器的cors设置,允许指定域名或所有域名访问。

方式三:

在浏览器能请求的服务器上请求跨域的服务,并将服务结果返回给浏览器,从而完成代理访问跨域服务。

其他:

WebSocket支持跨域请求。

修改document.domain跨子域。

iframe+window.postMessage(),本质还是利用iframe地址不受域名限制的原理。

 

88. 说一下你熟悉的设计模式?

单例模式:类的实例只有一个的设计模式,实现方式是私有化构造方法,提供一个公有的静态方法获取唯一的实例化对象。

线程不安全的写法:

public class Singleton {
    private static Singleton singleton = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return singleton;
    }
}

线程安全的写法:

public class Singleton {  
   private static Singleton instance;  
   private Singleton (){}  
   public static synchronized Singleton getInstance() {  
       if (instance == null) {  
           instance = new Singleton();  
       }  
       return instance;  
   }  
}

 

观察者模式

对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式有两大角色,一个是主题(被观察者、主角),一个是观察者,然后,主题又派生出抽象主题,和实际主题,观察者又派生出抽象观察者和实际观察者。

我个人认为,抽象主题是可以省略。

观察者模式结构图:

 

观察者模式特别适合事件的通知,比如Swing的各种事件都使用到了观察者模式。

 

装饰者模式

对已有的业务逻辑进一步的封装,使其增加额外的功能。

与代理模式比较像,但区别在于代理模式是为了隐藏原对象,而装饰模式是为了给原对象增加功能,目的不一样。

代理模式调用方不需要知道原对象,而装饰模式调用方需要自己构造原对象并传入。

Java中的IO流就使用了装饰者模式。

 

适配器模式

将两种完全不同的事物联系到一起,就像现实生活中的变压器。

适配器模式既可以作为类结构型模式,也可以作为对象结构型模式

java8接口有默认实现,适配器模式作为类适配器已经不需要了。

 

工厂模式

简单工厂模式

抽象工厂模式:

 

代理模式

隐藏真实的对象,提供代理对象给调用方调用,代理类和真实对象实现同一个接口,并可以增加新的功能。

和装饰模式很像,但区别在于代理模式是为了隐藏原对象,而装饰模式是为了给原对象增加功能,目的不一样。

代理模式调用方不需要知道原对象,而装饰模式调用方需要自己构造原对象并传入。

 

89. 简单工厂和抽象工厂有什么区别?

 

 

90. 为什么要使用 spring?

spring使用依赖注入实现对调用方与被调用方的解耦,实现对业务对象的统一管理。

spring使用动态代理提供了面向切面编程的能力,事物,异常处理,权限控制,日志处理这些代码都可以与业务类脱离,统一由一个地方处理,避免了冗余代码的出现提升了开发效率。

相对ejb而言,spring是轻量的非侵入式的容器。

 

91. 解释一下什么是 aop?

AOP是面向切面编程,是面向对象编程的补充。

AOP是为了解决散布在系统中冗余代码,将这些散布的冗余代码统一提构出来,以横切的形式插入执行。

避免了代码的冗余,提升了开发效率,利于系统维护。

 

92. 解释一下什么是 ioc?

控制反转是影响广泛的设计模式,其主要目的就是为了管理对象之间的关系,为对象之间解除耦合,把对象生命周期的管理和关系的管理这些和对象个体无关的公共任务交给公共容器处理。

控制反转有的好处:每一个模块只专注于它自己的任务;每一个模块都假定自己与其它模块无关,无需操心其它模块是如何实现和做什么的;更换某个模块对其它模块无影响。


依赖注入是容器全权负责组件装配,它把对象传递给需要的对象,依赖注入DI(Dependency Injection)是控制反转IoC(Inversion of Control)的一种实现方式,另一种实现方式是依赖查找。

 

93. spring 有哪些主要模块?

Spring框架至今已集成了20多个模块。这些模块主要被分如下图所示的核心容器、数据访问/集成,、Web、AOP、工具、消息和测试模块。


94. spring 常用的注入方式有哪些?

xml注入:构造方法注入,set方法注入,属性注入

注解注入:@Autowired、@Inject、@Resource。

附@Autowired、@Inject、@Resource区别:

@Autowired从容器查找时,先匹配类型,再匹配名称,如果匹配到多个则根据beanid来区分(也就是说需要使用@Qualifier),不然会报错。

@Inject和@Autowired查找顺序一致,只是@Inject不能设置required属性,并且@Inject不属于spring,而是jsr-330定义的规范,因此推荐使用@Inject代替@Autowired。

@Resource先查找名称,再查找类型,和@Autowired只是查找顺序不一样,其他都一样。

 

95. spring 中的 bean 是线程安全的吗?

Spring容器本身并没有提供Bean的线程安全策略,所以spring 中的 bean不是线程安全的。

 

96. spring 支持几种 bean 的作用域?

singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例

prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例,相当于new

request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。只有在Web应用中使用Spring时,该作用域才有效

session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效

globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。同样只有在Web应用中使用Spring时,该作用域才有效

其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。

如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。

 

97. spring 自动装配 bean 有哪些方式?

spring中可以设置default-autowire属性来控制自动装配的方式,可以设置为byName、byType、constructor和autodetect;

 

98. spring 事务实现方式有哪些?

spring支持编程式事务和声明式事务两种方式。

编程式事务使用TransactionTemplate或者直接使用底层的PlatformTransactionManager,spring推荐使用TransactionTemplate。

声明式事务建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。

 

99. 说一下 spring 的事务隔离?

隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:

TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。

TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读,因此很少使用该隔离级别。比如PostgreSQL实际上并没有此级别。

TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。

TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读。

TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

 

附数据库事物隔离级别:

(1)read uncommitted 读未提交
最低的隔离级别,一个事务可以读到另一个事务未提交的结果,该级别下所有的并发事务问题都会发生。

(2)read committed 读已提交
任何事务只可以读取其他事务已提交的数据,可以解决脏读问题。

(3)repeatable read 可重复读
在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。该级别可以解决脏读、不可重复读,但存在可能将未提交的记录查询出来,而出现幻读问题。

(4)serializable 可串行化
事务串行化执行(强制排序后按顺序执行),隔离级别最高,该级别下所有的并发事务问题都不会发生,但会导致大量超时现象和锁竞争。


附事务不同隔离级别引发事务并发问题:

事务隔离级别脏读不可重复读幻读
读未提交(read uncommitted)
读已提交(read committed)
可重复读(repeatable read)
可串行化(serializable)


附事务并发问题:

脏读:一个事务读取到其他事务未提交的数据。
如,事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A提交失败,事务B读到的就是脏数据。

不可重复读:在同一个事务中,对于同一份数据读取到的结果不一致。
如,事务A中执行了两个查询,但在事务A的执行过程中,事务B对要查询的数据进行了修改并提交,此时会造成事务A前面的查询是事务B修改之前的数据,事务A后面的查询是事务B修改之后的数据。
不可重复读出现的原因就是事务并发修改记录,要避免这种情况,最简单的方法就是对要修改的数据加行锁,这会导致锁竞争加剧,影响性能。另一种方法是通过MVCC可以在无锁的情况下,避免不可重复读。

幻读:在同一个事务中,同一个查询多次返回的结果不一致。
和不可重复度类似,区别在于幻读的关注的是整个表(增删数据),不可重复度关注的是具体的数据(修改数据)。
幻读是由于并发事务增删数据导致的,这个不能像不可重复读通过加行锁解决,因为对于新增的数据根本无法加行锁。需要将事务串行化,或锁表,才能避免幻读。
 

100. 说一下 spring mvc 运行流程?

大致流程是,用户的请求如果符合web.xml的配置则被核心控制器(DispatcherServlet)拦截,然后交给映射管理器(HandlerMapping)查询url映射的处理器(Controller),找到对应的处理器后将请求交给处理器进行处理(调用对应的方法,将请求参数传入),并得到处理器的返回值(ModelAndView),再交给视图解析器(ViewResolver)进行解析,解析完了最后到交给视图渲染(JSP或freemaker),渲染完后将最终得到的html返回给用户。

附Spring MVC运行流程图:

 

 

101. spring mvc 有哪些组件?

Spring MVC的核心组件:

DispatcherServlet:中央控制器,把请求给转发到具体的控制类

HandlerMapping:映射管理器

Controller:处理器

ViewResolver:视图解析器

Interceptors :拦截器

 

102. @RequestMapping 的作用是什么?

RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。

如果Controller类没有此注解,则表示该类匹配项目的根url。

RequestMapping注解有六个属性:

value:指定请求的实际地址,指定的地址可以是URI Template 模式(后面将会说明);

method:指定请求的method类型, GET、POST、PUT、DELETE等;

consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;

produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;

params: 指定request中必须包含某些参数值是,才让该方法处理。

headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。

 

103. @Autowired 的作用是什么?

@Autowired是实现自动注入的一个spring注解,用于将spring容器中的bean自动装配到对象的属性中。

@Autowired从容器查找时,先匹配类型,再匹配名称,如果匹配到多个则根据beanid来区分(也就是说需要使用@Qualifier),不然会报错。

可以用@Inject、@Resource替换@Autowired。

 

104. 什么是 spring boot?

springboot是一个为了简化spring项目的繁琐配置的脚手架。

基于约定优于配置的理念,设置了众多默认配置项,开发者只需要修改自己关注的那部分配置即可。

springboot内置web服务器tomcat和jetty,web项目可以直接用main方法运行。

使用插拔式starter集成其他框架,starter负责配置好与spring整合相关的配置和相关依赖(jar和jar版本),使用者无需关心框架整合带来的问题(比如版本冲突)。

 

105. 为什么要用 spring boot?

简化了配置,只需要一个核心的application.properties配置文件,即可解决大多数配置,并且很多配置springboot都有设置默认值。

简化了部署,spring boot内部集成了web服务器tomcat和jetty,可以直接打包成jar用java -jar运行。

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

使用插拔式starter集成其他框架,starter负责配置好与spring整合相关的配置和相关依赖(jar和jar版本),使用者无需关心框架整合带来的问题(比如版本冲突)。

 

106. spring boot 核心配置文件是什么?

application.properties或application.yml。

 

107. spring boot 配置文件有哪几种类型?它们有什么区别?

核心配置文件可以为.properties或.yml。

properties是没有层次结构的键值对,yml是有层次结构的键值对。

个人意见是不要用yml,原因是容易弄错空格数以及容易将tab和空格混淆。

 

108. spring boot 有哪些方式可以实现热部署?

spring-boot-devtools

加入依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-devtools</artifactId>
	<optional>true</optional>
</dependency>

然后点eclipse>project>build automatically>勾选。

devtools的热部署其实是轻量的重启服务器,并且在pom.xml新增依赖时,依然需要手动进行update maven,不然会报错找不到新增依赖的class。

spring loaded

和devtools差不多,目前本人没用过。

 

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的规范。

 

110. 什么是 spring cloud?

Spring Cloud是一个服务于分布式系统的云服务框架,是为了解决分布式系统的各种问题,以及简化开发工作的云服务工具。

Spring Cloud本身是由众多开源的云服务工具组件整合而成。

Spring Cloud许多组件都是可插拔的,可以根据需要自行替换。

Spring Cloud基于Spring Boot,可以开箱即用的使用Spring Cloud各个组件,可以快速构建分布式系统。

 

Spring Cloud常用功能组件:

配置管理

服务注册与发现

断路器

智能路由

服务间调用

负载均衡

微代理

控制总线

一次性令牌

全局锁

领导选举

分布式会话

集群状态

分布式消息

 

111. spring cloud 断路器的作用是什么?

在分布式架构中,断路器作用类似于电路中的保险丝,当某个服务单元发生故障后,通过断路器的故障监控,向调用方返回一个错误响应,而不是长时间的等待。避免线程因调用故障服务被长时间占用,也避免了故障在分布式系统中的雪崩蔓延。

Spring Cloud断路器的实现是Netflix Hystrix。

 

112. spring cloud 的核心组件有哪些?

①. 服务发现——Netflix Eureka

一个RESTful服务,用来定位运行在AWS地区(Region)中的中间层服务。由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。

②. 客服端负载均衡——Netflix Ribbon

Ribbon,主要提供客户侧的软件负载均衡算法。Ribbon客户端组件提供一系列完善的配置选项,比如连接超时、重试、重试算法等。Ribbon内置可插拔、可定制的负载均衡组件。

③. 断路器——Netflix Hystrix

断路器可以防止一个应用程序多次试图执行一个操作,即很可能失败,允许它继续而不等待故障恢复或者浪费 CPU 周期,而它确定该故障是持久的。断路器模式也使应用程序能够检测故障是否已经解决。如果问题似乎已经得到纠正,应用程序可以尝试调用操作。

④. 服务网关——Netflix Zuul

类似nginx,反向代理的功能,不过netflix自己增加了一些配合其他组件的特性。

⑤. 分布式配置——Spring Cloud Config

这个还是静态的,得配合Spring Cloud Bus实现动态的配置更新。

 

113. 为什么要使用 hibernate?

Hibernate对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。

Hibernate是一个优秀的ORM实现,将对象与关系表通过配置进行映射,从而实现使用面向对象编程操作数据。

hibernate屏蔽了各个数据库的的差异,切换数据库变得简单易行。

hibernate使用hql替换sql,开发人员不必了解各个数据库特性和技术细节。

 

114. 什么是 ORM 框架?

对象关系映射(Object-Relational Mapping),面向对象的开发方法是当今企业级应用开发环境中的主流开发方法,关系数据库是企业级应用环境中永久存放数据的主流数据存储系统。对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系。因此,对象-关系映射(ORM)系统一般以中间件的形式存在,主要实现程序对象到关系数据库数据的映射。

 

115. hibernate 中如何在控制台查看打印的 sql 语句?

hibernate的hibernate.cfg.xml文件:

<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>

spring的applicationContext.xml文件:

<property name="hibernateProperties">
    <props>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.format_sql">true</prop>  
    </props>
</property>

spring boot的application.properties文件:

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true

 

116. hibernate 有几种查询方式?

hql查询

Query query = session.createQuery("from User");

sql查询

SQLQuery query = session.createSQLQuery("select * from user")

QBC查询

Criteria criteria = session.createCriteria(User.class)

 

117. hibernate 实体类可以被定义为 final 吗?

可以但不好,因为Hibernate会使用代理模式在延迟关联的情况下提高性能。

实体类如果已经提构出了实体类的接口,那么使用final也是没有关系的,因为有接口的话,hibernate会使用java接口代理来代理实体类,生成的代理类是实现接口的,不需要继承实体。如果没有提构实体类接口,那么hibernate是使用cglib类代理来实现代理,生成的代理类必须继承实体类,如果是final也就无法继承。

 

118. 在 hibernate 中使用 Integer 和 int 做映射有什么区别?

integer有null值,int没有null值,所以不要在hibernate中用int来对应数据库的字段。

如果一定要用int,需要在hbm映射文件中设置其unsaved-value属性为0,否则数据库有null值时会报错。

 

119. hibernate 是如何工作的?

hibernate流程:

读取并解析hibernate.cfg.xml配置文件,Configuration config = new Configuration().configure()

读取并解析映射文件,<mapping resource="xxx.hbm.xml"/>

创建SessionFactory,SessionFactory sf = config.buildSessionFactory()

创建Session即打开一个数据库连接,Session session = sf.openSession();

开启事物(如果需要的话),Transaction tx = session.beginTransaction();

增删改查等数据库操作,例如session.createQuery、session.createCriteria等

提交或回滚事务(如果有事物的话),tx.commit()

关闭Session,session.close()

 

120. get()和 load()的区别?

load() 是先返回代理对象,代理对象中只存储了ID值,在使用到非ID属性时,才去查询数据库。

get() 是立即查询数据库。

 

121. 说一下 hibernate 的缓存机制?

Hibernate中的缓存分为一级缓存和二级缓存。

一级缓存就是  Session 级别的缓存,在事务范围内有效,是内置的不能被卸载。

二级缓存是 SesionFactory级别的缓存,从应用启动到应用结束有效,默认不启用,需要手动开启。二级缓存是插拔式的,可以根据需要自行替换hibernate的自带的二级缓存。

hibernate的自带的二级缓存是不支持分布式缓存的,可以使用memcahe、redis等中央缓存来代替二级缓存。

什么样的数据适合存放到第二级缓存中?

经常使用,很少修改,允许延时。

 

122. hibernate 对象有哪些状态?

hibernate里对象有三种状态:

Transient(瞬时):对象刚new出来,尚未与Hibernate Session 关联的对象。

Persistent(持久):在数据库中有对应的记录,并拥有一个持久化标识(identifier),与Hibernate Session 已关联的对象 , Hibernate会检测到处于持久状态的对象的任何改动以便及时更新到数据库。

Detached(脱管):与持久(Persistent)对象关联的Session被关闭后,对象就变为脱管(Detached)的。对脱管(Detached)对象的引用依然有效,对象可继续被修改。脱管(Detached)对象如果重新关联到某个新的 Session 上, 会再次转变为持久(Persistent)的(在Detached其间的改动将被持久化到数据库)

 

123. 在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?

openSession是每次都打开一个新的session。

getCurrentSession是获取当前正在使用且没有关闭的Session,如果没有,则打开一个新Session。

 

124. hibernate 实体类必须要有无参构造函数吗?为什么?

必须,因为hibernate框架会调用这个默认构造方法来构造实例对象。

 

125. mybatis 中 #{}和 ${}的区别是什么?

#{}是预编译处理,mybatis会将#{}替换为?号,使用PreparedStatement来赋值。

${}是字符串替换,${}会被具体的字符串值替换后拼接在sql上。

#{}是安全的可以防止sql注入,${}不安全,开发中不要使用${}。

 

126. mybatis 有几种分页方式?

sql分页

即自己在sql中编写分页语句。

RowBounds分页

不需要在 sql 语句中拼写sql语句,mybatis 会自动拼接 sql ,添加 limit,在 mapper 接口层,传参时传入 RowBounds(int offset, int limit) 对象,即可完成分页。

注意:由于 java 允许的最大整数为 2147483647,所以 limit 能使用的最大整数也是 2147483647,一次性取出大量数据可能引起内存溢出,所以在大数据场合慎重使用RowBounds分页。

拦截器分页

没用过。

 

128. mybatis 逻辑分页和物理分页的区别是什么?

物理分页即每次查询指定页的数据,筛选指定页的数据这个工作在数据库完成。

逻辑分页即查询全部的数据,筛选指定页的数据这个工作在应用端完成。

开发中一定要避免使用逻辑分页,不要将属于数据库端的压力转移到应用端来,就算数据量很小也不要使用。

 

129. mybatis 是否支持延迟加载?延迟加载的原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

延迟加载的原理是,使用动态代理(java接口代理或cglib类代理)创建实体类的代理对象,一开始查询出来的数据都是这些代理对象,里面只有一个id数据,当获取非id属性值时,会触发代理对象set方法里的‘钩子’,这些钩子会去查询数据库,hibernate的延时加载也是这样弄的。

 

130. 说一下 mybatis 的一级缓存和二级缓存?

一级缓存:作用域为Session的本地缓存,默认开启。相同的查询语句会返回第一次查询的缓存值。

二级缓存:存储作用域为 Mapper Namespace,默认不开启,若开启需自定义存储源(如Ehcache)并且缓存实体类需要实现Serializable序列化接口。

二级缓存会导致:

映射语句文件中的所有select语句将会被缓存。

映射语句文件中的所欲insert、update和delete语句会刷新缓存。

缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。

根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。

缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用

缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。

 

131. mybatis 和 hibernate 的区别有哪些?

(1)mybatis是一个不完全的ORM框架,它需要开发人员自行编写sql,无法做到数据库无关性,切换数据库代价很大,但因为sql语句可以自己写,所以显得很灵活,sql语句也可以根据需要进行优化。

(2)hibernate是一个完整的ORM框架,使用hql替代sql,可以用面向对象的方式去操作数据库,数据库无关性好,但因为sql是hibernate帮我生成的,所以sql没法优化。

 

132. mybatis 有哪些执行器(Executor)?

mybatis有三种基本的执行器(Executor):

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。

BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

 

133. mybatis 分页插件的实现原理是什么?

分页插件的基本原理是使用Mybatis提供的拦截器接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

 

134. mybatis 如何编写一个自定义插件?

Mybatis自定义插件针对Mybatis四大对象(Executor、StatementHandler 、ParameterHandler 、ResultSetHandler )进行拦截,具体拦截方式为: 

Executor:拦截执行器的方法(log记录) 

StatementHandler :拦截Sql语法构建的处理 

ParameterHandler :拦截参数的处理 

ResultSetHandler :拦截结果集的处理 

 

Mybatis自定义插件必须实现Interceptor接口

public interface Interceptor {    Object intercept(Invocation invocation) throws Throwable;    Object plugin(Object target);    void setProperties(Properties properties);}

intercept方法:拦截器具体处理逻辑方法 

plugin方法:根据签名signatureMap生成动态代理对象 

setProperties方法:设置Properties属性

自定义插件demo:

// ExamplePlugin.java@Intercepts({@Signature(  type= Executor.class,  method = "update",  args = {MappedStatement.class,Object.class})})public class ExamplePlugin implements Interceptor {  public Object intercept(Invocation invocation) throws Throwable {  Object target = invocation.getTarget(); //被代理对象  Method method = invocation.getMethod(); //代理方法  Object[] args = invocation.getArgs(); //方法参数  // do something ...... 方法拦截前执行代码块  Object result = invocation.proceed();  // do something .......方法拦截后执行代码块  return result;  }  public Object plugin(Object target) {    return Plugin.wrap(target, this);  }  public void setProperties(Properties properties) {  }}

一个@Intercepts可以配置多个@Signature,@Signature中的参数定义如下: 

type:表示拦截的类,这里是Executor的实现类;

method:表示拦截的方法,这里是拦截Executor的update方法;

args:表示方法参数。

 

135. rabbitmq 的使用场景有哪些?

①. 跨系统的异步通信,所有需要异步交互的地方都可以使用消息队列。就像我们除了打电话(同步)以外,还需要发短信,发电子邮件(异步)的通讯方式。

②. 多个应用之间的耦合,由于消息是平台无关和语言无关的,而且语义上也不再是函数调用,因此更适合作为多个应用之间的松耦合的接口。基于消息队列的耦合,不需要发送方和接收方同时在线。在企业应用集成(EAI)中,文件传输,共享数据库,消息队列,远程过程调用都可以作为集成的方法。

③. 应用内的同步变异步,比如订单处理,就可以由前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于同步通常意味着阻塞,而大量线程的阻塞会降低计算机的性能。

④. 消息驱动的架构(EDA),系统分解为消息队列,和消息制造者和消息消费者,一个处理流程可以根据需要拆成多个阶段(Stage),阶段之间用队列连接起来,前一个阶段处理的结果放入队列,后一个阶段从队列中获取消息继续处理。

⑤. 应用需要更灵活的耦合方式,如发布订阅,比如可以指定路由规则。

⑥. 跨局域网,甚至跨城市的通讯(CDN行业),比如北京机房与广州机房的应用程序的通信。

 

136. rabbitmq 有哪些重要的角色?

RabbitMQ 中重要的角色有:生产者、消费者和代理:

  • 生产者:消息的创建者,负责创建和推送数据到消息服务器;

  • 消费者:消息的接收方,用于处理数据和确认消息;

  • 代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。

 

137. rabbitmq 有哪些重要的组件?

  • ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用。

  • Channel(信道):消息推送使用的通道。

  • Exchange(交换器):用于接受、分配消息。

  • Queue(队列):用于存储生产者的消息。

  • RoutingKey(路由键):用于把生成者的数据分配到交换器上。

  • BindingKey(绑定键):用于把交换器的消息绑定到队列上。

 

138. rabbitmq 中 vhost 的作用是什么?

vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ  server。其内部均含有独立的 queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的 vhost 中)。 

 

139. rabbitmq 的消息是怎么发送的?

首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。

 

140. rabbitmq 怎么保证消息的稳定性?

  • 提供了事务的功能。

  • 通过将 channel 设置为 confirm(确认)模式。

 

141. rabbitmq 怎么避免消息丢失?

  1. 消息持久化

  2. ACK确认机制

  3. 设置集群镜像模式

  4. 消息补偿机制

 

142. 要保证消息持久化成功的条件有哪些?

  1. 声明队列必须设置持久化 durable 设置为 true.

  2. 消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。

  3. 消息已经到达持久化交换器。

  4. 消息已经到达持久化队列。

 

以上四个条件都满足才能保证消息持久化成功。

 

143. rabbitmq 持久化有什么缺点?

持久化的缺地就是降低了服务器的吞吐量,因为使用的是磁盘而非内存存储,从而降低了吞吐量。可尽量使用 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 节点的类型有哪些?

  • 磁盘节点:消息会存储到磁盘。

  • 内存节点:消息都存储在内存中,重启服务器消息丢失,性能高于磁盘类型。

 

148. rabbitmq 集群搭建需要注意哪些问题?

  • 各节点之间使用“--link”连接,此属性不能忽略。

  • 各节点使用的 erlang cookie 值必须相同,此值相当于“秘钥”的功能,用于各节点的认证。

  • 整个集群中必须包含一个磁盘节点。

 

149. rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?

不是,原因有以下两个:

  1. 存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据;

  2. 性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。

 

150. rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?

如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:

  • 不能创建队列

  • 不能创建交换器

  • 不能创建绑定

  • 不能添加用户

  • 不能更改权限

  • 不能添加和删除集群节点

 

唯一磁盘节点崩溃了,集群是可以保持运行的,但你不能更改任何东西。

 

151. rabbitmq 对集群节点停止顺序有要求吗?

RabbitMQ 对集群的停止的顺序是有要求的,应该先关闭内存节点,最后再关闭磁盘节点。如果顺序恰好相反的话,可能会造成消息的丢失。

 

152. kafka 可以脱离 zookeeper 单独使用吗?为什么?

kafka 不能脱离 zookeeper 单独使用,因为 kafka 使用 zookeeper 管理和协调 kafka 的节点服务器。

 

153. kafka 有几种数据保留的策略?

kafka 有两种数据保存策略:按照过期时间保留和按照存储的消息大小保留。

 

154. kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?

这个时候 kafka 会执行数据清除工作,时间和大小不论那个满足条件,都会清空数据。

 

155. 什么情况会导致 kafka 运行变慢?

  • cpu 性能瓶颈

  • 磁盘读写瓶颈

  • 网络瓶颈

 

156. 使用 kafka 集群需要注意什么?

  • 集群的数量不是越多越好,最好不要超过 7 个,因为节点越多,消息复制需要的时间就越长,整个群组的吞吐量就越低。

  • 集群数量最好是单数,因为超过一半故障集群就不能用了,设置为单数容错率更高。

 

157. zookeeper 是什么?

zookeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 google chubby 的开源实现,是 hadoop 和 hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

 

158. zookeeper 都有哪些功能?

  • 集群管理:监控节点存活状态、运行请求等。

  • 主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用 zookeeper 可以协助完成这个过程。

  • 分布式锁:zookeeper 提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。zookeeper可以对分布式锁进行控制。

  • 命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。

 

159. zookeeper 有几种部署模式?

zookeeper 有三种部署模式:

  • 单机部署:一台集群上运行;

  • 集群部署:多台集群运行;

  • 伪集群部署:一台集群启动多个 zookeeper 实例运行。

 

160. zookeeper 怎么保证主从节点的状态同步?

zookeeper 的核心是原子广播,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 zab 协议。 zab 协议有两种模式,分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,zab 就进入了恢复模式,当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 server 具有相同的系统状态。

 

161. 集群中为什么要有主节点?

在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,所以就需要主节点。

 

162. 集群中有 3 台服务器,其中一个节点宕机,这个时候 zookeeper 还可以使用吗?

可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。

 

163. 说一下 zookeeper 的通知机制?

客户端端会对某个 znode 建立一个 watcher 事件,当该 znode 发生变化时,这些客户端会收到 zookeeper 的通知,然后客户端可以根据 znode 变化来做出业务上的改变。

 

164. 数据库的三范式是什么?

第一范式:每列都是不可再分。

反例:user(id,name,age,height_weight),height_weight可以拆分成身高和体重。

第二范式:在第一范式的基础上,要有主键,所有列都完全依赖主键。

反例:主要发生在联合主键时。

第三范式:在第二范式的基础上,所有列都直接依赖主键,不能间接依赖主键。

反例:

user(id,name,age,dept_id,dept_name,dept_detail)

dept_name,dept_detail依赖于dept_id,而dept_id依赖于id,所以dept_name,dept_detail间接依赖于id。

三大范式是数据库表设计的参考,可以根据情况来遵循。

用容易理解的话说,第一范式是不能搞连体人,第二范式是要听指挥,别听一半,第三范式是要听直接指挥,别人传过的不算。

 

165. 一张自增表里面总共有 17 条数据,删除了最后 2 条数据,重启 mysql 数据库,又插入了一条数据,此时 id 是几?

表类型如果是 MyISAM ,那 id 就是 18。

表类型如果是 InnoDB,那 id 就是 15。

InnoDB 表只会把自增主键的最大 id 记录在内存中,所以重启之后会导致最大 id 丢失。

 

166. mysql如何获取当前数据库版本?

使用 select version() 获取当前 MySQL 数据库版本。

 

167. 说一下 ACID 是什么?

原子性(Atomicity):
一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。实现事务的原子性,要支持回滚操作,在某个操作失败后,回滚到事务执行之前的状态。

一致性(Consistency):
事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态(正确的状态)。这种特性称为事务的一致性。假如数据库的状态满足所有的完整性约束,就说该数据库是一致的。
什么叫正确的状态呢?就是当前的数据满足预定的约束条件就叫做正确的状态。实际上它依赖于应用层来实现,也就是依赖于开发者来实现。数据库是利用AID(特别是A)来支持一致性的,所以说一致性与原子性紧密相关。
事务可以不同程度的一致性:
强一致性:读操作可以立即读到提交的更新操作。
弱一致性:提交的更新操作,不一定立即会被读操作读到,此种情况会存在一个不一致窗口,指的是读操作可以读到最新值的一段时间。
最终一致性:是弱一致性的特例。事务更新一份数据,最终一致性保证在没有其他事务更新同样的值的话,最终所有的事务都会读到之前事务更新的最新值。如果没有错误发生,不一致窗口的大小依赖于:通信延迟,系统负载等。
单调一致性:如果一个进程已经读到一个值,那么后续不会读到更早的值。
会话一致性:保证客户端和服务器交互的会话过程中,读操作可以读到更新操作后的最新值。

隔离性(Isolation):
由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,到底是另一个事务执行之前的状态还是中间某个状态,相互之间存在什么影响,是可以通过隔离级别的设置来控制的。
事务可以不同程度的隔离性(隔离级别):
读未提交、读已提交、可重复读、可串行化。

持久性(Durability):
事务结束后,事务处理的结果必须能够得到固化,不可回滚,即写入数据库文件中即使机器宕机数据也不会丢失,它对于系统的影响是永久性的。
 

168. mysql的char 和 varchar 的区别是什么?

char(n) :固定长度,多余的用空字节填充,效率高但更占用空间。

varchar(n) :可变长度,多余的用不填充,效率低但少占用空间。

 

169.mysql的 float 和 double 的区别是什么?

都是浮点类型,和其他语言一样,float 最多可以存储 8 位的十进制数,double 最多可以存储 16 位的十进制数。

 

170. 内连接、左连接、右连接有什么区别?

内连接只取两边都符合条件的数据,左连接是取左表的所有数据和右表符合条件的数据,右连接是取右表的所有数据和左表符合条件的数据。

 

171. mysql 索引是怎么实现的?

索引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。

具体来说 MySQL 中的索引,不同的数据引擎实现有所不同,但目前主流的数据库引擎的索引都是 B+ 树实现的,B+ 树的搜索效率,可以到达二分法的性能。

 

172. 怎么验证 mysql 的索引是否满足需求?

使用 explain 查看 SQL 是如何执行查询语句的,从而分析你的索引是否满足需求。

explain 语法:explain select * from table where type=1。

 

173. 说一下数据库的事务隔离?

隔离级别是指并发事物之间不同程度的隔离性,总共分为四级:

(1)read uncommitted 读未提交
最低的隔离级别,一个事务可以读到另一个事务未提交的结果,该级别下所有的并发事务问题都会发生。

(2)read committed 读已提交
任何事务只可以读取其他事务已提交的数据,可以解决脏读问题。

(3)repeatable read 可重复读
在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。该级别可以解决脏读、不可重复读,但存在可能将未提交的记录查询出来,而出现幻读问题。

(4)serializable 可串行化
事务串行化执行(强制排序后按顺序执行),隔离级别最高,该级别下所有的并发事务问题都不会发生,但会导致大量超时现象和锁竞争。
MySQL 支持四种事务隔离级别:read uncommitted、read committed、repeatable read、serializable。

mysql查看和修改事物隔离级别:

select @@tx_isolation;--查看当前会话隔离级别
select @@global.tx_isolation;--查看系统当前隔离级别
set session transaction isolatin level repeatable read;--设置当前会话隔离级别
set global transaction isolation level repeatable read;--设置系统当前隔离级别

oracle数据库支持read committed和serializable,默认隔离级别是read committed。

 

174. 说一下 mysql 常用的引擎?

InnoDB 引擎:InnoDB 引擎提供了对数据库 acid 事务的支持,并且还提供了行级锁和外键的约束,它的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候,InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎是不支持全文搜索,同时启动也比较的慢,它是不会保存表的行数的,所以当进行 select count(*) from table 指令的时候,需要进行扫描全表。由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。

MyIASM 引擎:MySQL 的默认引擎,但不提供事务的支持,也不支持行级锁和外键。因此当执行插入和更新语句时,即执行写操作的时候需要锁定这个表,所以会导致效率会降低。不过和 InnoDB 不同的是,MyIASM 引擎是保存了表的行数,于是当进行 select count(*) from table 语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的,可以将 MyIASM 作为数据库引擎的首选。

 

175. 说一下 mysql 的行锁和表锁?

表锁就是锁定整张表,行锁就是锁定部分行。这是根据所锁的对象不同来进行的分类,还有一种分类是根据读写操作来分为读锁和写锁。

表锁:锁开销小,不会死锁,易锁冲突,并发量低。

行锁:锁开销大,会死锁,不易锁冲突,并发量高。

MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。

 

176. 说一下乐观锁和悲观锁?

悲观锁:对于数据被外界修改持悲观态度,认为数据随时会修改,所以整个数据处理中需要将数据加锁。悲观锁一般都是依靠关系数据库提供的锁机制,事实上关系数据库中的行锁、表锁不论是读写锁都是悲观锁。

乐观锁:对于数据被外界修改持乐观态度,认为每次自己操作数据的时候没有人回来修改它,所以不去加锁,但是在更新的时候会去判断在此期间数据有没有被修改,需要用户自己去实现。既然都有数据库提供的悲观锁可以方便使用为什么要使用乐观锁呢?对于读操作远多于写操作的时候,大多数都是读取,这时候一个更新操作加锁会阻塞所有读取,降低了吞吐量。最后还要释放锁,锁是需要一些开销的,我们只要想办法解决极少量的更新操作的同步问题。换句话说,如果是读写比例差距不是非常大或者你的系统没有响应不及时,吞吐量瓶颈问题,那就不要去使用乐观锁,它增加了复杂度,也带来了额外的风险。

 

177. mysql 问题排查都有哪些手段?

使用 show processlist 命令查看当前所有连接信息。

使用 explain 命令查询 SQL 语句执行计划。

开启慢查询日志slow_query_log,查看查询慢的SQL日志。

 

178. 如何做 mysql 的性能优化?

数据量大的情况下,为外键字段、经常作为查询条件的字段添加索引。

sql语句尽量避免索引失效引发全表扫描。

避免使用 select *,列出需要查询的字段。

数据量过多可以考虑横向拆表。

数据量大时要使用分页查询,应用端尽量避免查询全部数据。

limit有性能问题,需要通过加子查询优化。


SELECT
    *
FROM
    table t
INNER JOIN (
    SELECT
        id--这里必须要使用有索引的字段,这优化limit最核心的关键
    FROM
        table
    WHERE
        xxx_id = 143381
    LIMIT 800000,20
) t1 ON t.id = t1.id

附索引失效情况:

1、最佳左前缀原则——如果索引了多列,要遵守最左前缀原则。指的是查询要从索引的最左前列开始并且不跳过索引中的列。

2、不在索引列上做任何操作(计算,函数,(自动或者手动)类型装换),会导致索引失效而导致全表扫描

3、存储引擎不能使用索引中范围条件右边的列,范围之后索引失效。(< ,> between and)

4、mysql使用不等于(!= 或者<>)的时候,无法使用索引,会导致索引失效

5、mysql中使用is not null 或者 is null会导致无法使用索引

6、mysql中like查询是以%开头,索引会失效变成全表扫描,覆盖索引。

7、mysql中,如果条件中有or,即使其中有条件带索引也不会使用。要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引

8、如果mysql使用全表扫描要比使用索引快,则不会使用到索引

 

179. redis 是什么?都有哪些使用场景?

Redis是一个基于内存亦可持久化的日志型、Key-Value数据库,性能优秀。

Redis 使用场景:

数据缓存

限时业务

计数业务

延时业务

高并发环境下读写数据

 

180. redis 有哪些功能?

  • 数据缓存功能

  • 分布式锁的功能

  • 支持数据持久化

  • 支持事务

  • 支持消息队列

 

181. redis 和 memecache 有什么区别?

  • memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型

  • redis的速度比memcached快很多

  • redis可以持久化其数据

 

182. redis 为什么是单线程的?

因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。

关于 Redis 的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。

而且单线程并不代表就慢 nginx 和 nodejs 也都是高性能单线程的代表。

 

183. 什么是缓存穿透?怎么解决?

缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

 

184. redis 支持的数据类型有哪些?

string、list、hash、set、zset。

 

185. redis 支持的 java 客户端都有哪些?

Redisson、Jedis、lettuce等等,官方推荐使用Redisson。

 

186. jedis 和 redisson 有哪些区别?

Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。

Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

 

187. 怎么保证缓存和数据库数据的一致性?

合理设置缓存的过期时间。

新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。

 

188. redis 持久化有几种方式?

Redis 的持久化有两种方式,或者说有两种策略:

RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。

AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。

 

189. redis 怎么实现分布式锁?

Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。

占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。

 

190. redis 分布式锁有什么缺陷?

Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。

 

191. redis 如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。

比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的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 主从复制的性能问题,为了主从复制的速度和连接的稳定性,主从库最好在同一个局域网内。

 

194. 说一下 jvm 的主要组成部分?及其作用?

类加载器(ClassLoader)

运行时数据区(Runtime Data Area)

执行引擎(Execution Engine)

本地库接口(Native Interface)

组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

 

195. 说一下 jvm 运行时数据区?

程序计数器

虚拟机栈

本地方法栈

方法区

 

有的区域随着虚拟机进程的启动而存在,有的区域则依赖用户进程的启动和结束而创建和销毁。

 

196. 说一下堆栈的区别?

1. 栈内存存储的是局部变量而堆内存存储的是实体;

2. 栈内存的更新速度要快于堆内存,因为局部变量的生命周期很短;

3. 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。

 

197. 队列和栈是什么?有什么区别?

队列和栈都是被用来预存储数据的。

队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。

栈和队列很相似,但它运行对元素进行后进先出进行检索。

 

198. 什么是双亲委派模型?

在介绍双亲委派模型之前先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。

 

类加载器分类:

  • 启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;

  • 其他类加载器:

  • 扩展类加载器(Extension ClassLoader):负责加载<java_home style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; outline: 0px !important;">\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;</java_home>

  • 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

 

199. 说一下类加载的执行过程?

类加载分为以下 5 个步骤:

  1. 加载:根据查找路径找到相应的 class 文件然后导入;

  2. 检查:检查加载的 class 文件的正确性;

  3. 准备:给类中的静态变量分配内存空间;

  4. 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;

  5. 初始化:对静态变量和静态代码块执行初始化工作。

 

200. 怎么判断对象是否可以被回收?

一般有两种方法来判断:

  • 引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;

  • 可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。

 

201. java 中都有哪些引用类型?

  • 强引用

  • 软引用

  • 弱引用

  • 虚引用(幽灵引用/幻影引用)

 

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。

每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。

老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

 

207. 说一下 jvm 调优的工具?

JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。

  • jconsole:用于对 JVM 中的内存、线程和类等进行监控;

  • jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。

 

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 详细信息。

 

©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页