1.final 关键字的作用?
被 final 修饰的类不可以被继承,
被 final 修饰的方法不可以被重写,
被 final 修饰的变量 不可以被改变.如果修饰引用,那么表示引用不可变,引用指向的内容可变.被 final 修饰的方法,JVM 会尝试将其内联,以提高运行效率,被 final 修饰的常量,在编译阶段会存入常量 池中.
2.abstract class 和 interface 有什么区别?
声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体
现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建
abstract 类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类
的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所
有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行
为的其它类可以在类中实现这些方法。
接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实
现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义
static final 成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行
为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以
在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为
引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,
instanceof 运算符可以用来决定某对象的类是否实现了接口
3. Java 集合类:list、set、queue、map、stack 的特点与用法?
Map
Map 是键值对,键 Key 是唯一不能重复的,一个键对应一个值,值可以重复。
TreeMap 可以保证顺序,HashMap 不保证顺序,即为无序的,Map 中可以将 Key 和 Value 单
独抽取出来,其中 KeySet()方法可以将所有的 keys 抽取成一个 Set,而 Values()方法可以
将 map 中所有的 values 抽取成一个集合。
Set
不包含重复元素的集合,set 中最多包含一个 null 元素,只能用 Iterator 实现单项遍历,
Set 中没有同步方法。
List
有序的可重复集合,可以在任意位置增加删除元素,用 Iterator 实现单向遍历,也可用
ListIterator 实现双向遍历。
Queue
Queue 遵从先进先出原则,使用时尽量避免 add()和 remove()方法,而是使用 offer()来添加
元素,使用 poll()来移除元素,它的优点是可以通过返回值来判断是否成功,LinkedList
实现了 Queue 接口,Queue 通常不允许插入 null 元素。
Stack
Stack 遵从后进先出原则,Stack 继承自 Vector,它通过五个操作对类 Vector 进行扩展,千锋成都 Java 教学部
允许将向量视为堆栈,它提供了通常的 push 和 pop 操作,以及取堆栈顶点的 peek()方法、
测试堆栈是否为空的 empty 方法等。
用法
如果涉及堆栈,队列等操作,建议使用 List。
对于快速插入和删除元素的,建议使用 LinkedList。
如果需要快速随机访问元素的,建议使用 ArrayList。
4.说出 ArrayList,Vector, LinkedList 的存储性能和特性?
ArrayList 和 Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便
增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内
存操作,所以索引数据快而插入数据慢,Vector 由于使用了 synchronized 方法(线程安全),
通常性能上较 ArrayList 差,而 LinkedList 使用双向链表实现存储,按序号索引数据需要
进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。
6. 反射中,Class.forName()和 ClassLoader.loadClass()的区别?
Class.forName(className)方法,内部实际调用的方法是。Class.forName(className,true,classloader);
第 2 个 boolean 参数表示类是否需要初始化, Class.forName(className)默认是需要初始化,
一旦初始化,就会触发目标对象的 static 块代码执行,static 参数也也会被再次初始
化 ,ClassLoader.loadClass(className) 方 法 , 内 部 实 际 调 用 的 方 法 是
ClassLoader.loadClass(className,false);第 2 个 boolean 参数,表示目标对象是否进行链接,
false 表示不进行链接,由上面介绍可以,不进行链接意味着不进行包括初始化等一些列步
骤,那么静态块和静态对象就不会得到执行
8. String、StringBuilder、StringBuffer 区别?
String 字符串常量 不可变 使用字符串拼接时是不同的 2 个空间
StringBuffer 字符串变量可变 线程安全 字符串拼接直接在字符串后追加
StringBuilder 字符串变量可变 非线程安全 字符串拼接直接在字符串后追加
1.StringBuilder 执行效率高于 StringBuffer 高于 String.
2.String 是一个常量,是不可变的,所以对于每一次+=赋值都会创建一个新的对象,
StringBuffer 和 StringBuilder 都是可变的,当进行字符串拼接时采用 append 方
法,在原来的基础上进行追加,所以性能比 String 要高,又因为 StringBuffer 是
线程安全的而 StringBuilder 是线程非安全的,所以 StringBuilder 的效率高于
StringBuffer.
3.对于大数据量的字符串的拼接,采用 StringBuffer,StringBuilder.
9. Hashtable 和 Hashmap 的区别?
1、HashTable 线程安全,HashMap 非线程安全
2、Hashtable 不允许 null 值(key 和 value 都不可以),HashMap 允许 null 值(key 和
value 都可以)。
3、两者的遍历方式大同小异,Hashtable 仅仅比 HashMap 多一个 elements 方法。
18.HashSet 的底层实现是什么?
HashSet 的实现是依赖于 HashMap 的,HashSet 的值都是存储在 HashMap 中的。
在 HashSet 的构造法中会初始化一个 HashMap 对象,HashSet 不允许值重复。
因此,HashSet 的值是作为 HashMap 的 key 存储在 HashMap 中的,当存储的值已经存在时返
回 false。
26. new 一个对象的过程和 clone 一个对象的过程?
new 操作符的本意是分配内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类
型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,
填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以
把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对
象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的
域,填充完成之后,clone 方法返回,一个新的相同的对象被创建,同样可以把这个新对象
的引用发布到外部。
31.Java 创建对象的几种方式?
new 创建新对象;通过反射机制;采用 clone 机制;通过序列化机制
37.深拷贝和浅拷贝的区别是什么?
- 浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用
仍然指向原来的对象.换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象.
- 深拷贝:被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量
将指向被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对
象所引用的对象都复制了一遍.
47. TreeMap、HashMap、LindedHashMap 的区别?
LinkedHashMap 可以保证 HashMap 集合有序。存入的顺序和取出的顺序一致。TreeMap 实现 SortMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排
序的比较器,当用 Iterator 遍历 TreeMap 时,得到的记录是排过序的。HashMap 不保证顺
序,即为无序的,具有很快的访问速度。HashMap 最多只允许一条记录的键为 Null;允许多
条记录的值为 Null;HashMap 不支持线程的同步。
54.运行时异常与一般异常有何异同?
异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能
遇到的异常,是一种常见运行错误。java 编译器要求方法必须声明抛出可能发生的非运行
时异常,但是并不要求必须声明抛出未被捕获的运行时异常。
57.Error 和 Exception 有什么区别?
error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指
望程序能处理这样的情况。exception 表示一种设计或实现问题。也就是说,它表示如果程
序运行正常,从不会发生的情况。
62.Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢?
Set 里的元素是不能重复的,元素重复与否是使用 equals()方法进行判断的。
equals()和==方法决定引用值是否指向同一对象 equals()在类中被覆盖,为的是当两个分离的
对象的内容和类型相配的话,返回真值。
72.用哪两种方式来实现集合的排序?
你可以使用有序集合,如 TreeSet 或 TreeMap,你也可以使用有顺序的的集合,如 list,然
后通过 Collections.sort() 来排序。
75.ArrayList 源码分析?
(1)ArrayList 是一种变长的集合类,基于定长数组实现,使用默认构造方法初始化出来的
容量是 10(1.7 之后都是延迟初始化,即第一次调用 add 方法添加元素的时候才将
elementData 容量初始化为 10)。
(2)ArrayList 允许空值和重复元素,当往 ArrayList 中添加的元素数量大于其底层数组容
量时,其会通过扩容机制重新生成一个更大的数组。ArrayList 扩容的长度是原长度的 1.5 倍
(3)由于 ArrayList 底层基于数组实现,所以其可以保证在 O(1) 复杂度下完成随机查找操
作。
(4)ArrayList 是非线程安全类,并发环境下,多个线程同时操作 ArrayList,会引发不可预
知的异常或错误。
(5)顺序添加很方便
(6)删除和插入需要复制数组,性能差(可以使用 LinkindList)
(7)Integer.MAX_VALUE - 8 :主要是考虑到不同的 JVM,有的 JVM 会在加入一些数据头,当
扩容后的容量大于 MAX_ARRAY_SIZE,我们会去比较最小需要容量和 MAX_ARRAY_SIZE 做比较,
如果比它大, 只能取 Integer.MAX_VALUE,否则是 Integer.MAX_VALUE -8。 这个是从 jdk1.7 开
始才有的
76.HashMap 源码分析?
jdk1.8 之前 list + 链表
jdk1.8 之后 list + 链表(当链表长度到 8 时,转化为红黑树)
HashMap 的扩容因子
默认 0.75,也就是会浪费 1/4 的空间,达到扩容因子时,会将 list 扩容一倍,0.75 是时间与
空间一个平衡值;
77. ConcurrentHashMap 源码分析?
ConcurrentHashMap 所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段
数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线
程访问。有些方法需要跨段,比如 size()和 containsValue(),它们可能需要锁定整个表而而不
仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按
顺序”是很重要的,否则极有可能出现死锁,在 ConcurrentHashMap 内部,段数组是 final 的,
并且其成员变量实际上也是 final 的,但是,仅仅是将数组声明为 final 的并不保证数组成员
也是 final 的,这需要实现上的保证。这可以确保不会出现死锁,因为获得锁的顺序是固定
的。ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成。Segment 是一种可
重入锁 ReentrantLock,在 ConcurrentHashMap 里扮演锁的角色,HashEntry 则用于存储键值
对数据。一个 ConcurrentHashMap 里包含一个 Segment 数组,Segment 的结构和 HashMap
类似,是一种数组和链表结构, 一个 Segment 里包含一个 HashEntry 数组,每个 HashEntry
是一个链表结构的元素,每个 Segment 守护者一个 HashEntry 数组里的元素,当对 HashEntry
数组的数据进行修改时,必须首先获得它对应的 Segment 锁。
5.BIO 和 NIO 和 AIO 的区别以及应用场景?
同步:java 自己去处理 io。
异步:java 将 io 交给操作系统去处理,告诉缓存区大小,处理完成回调。
阻塞:使用阻塞 IO 时,Java 调用会一直阻塞到读写完成才返回。
非阻塞:使用非阻塞 IO 时,如果不能立马读写,Java 调用会马上返回,当 IO 事件分发器通
知可读写时在进行读写,不断循环直到读写完成。
BIO:同步并阻塞,服务器的实现模式是一个连接一个线程,这样的模式很明显的一个缺陷
是:由于客户端连接数与服务器线程数成正比关系,可能造成不必要的线程开销,严重的还
将导致服务器内存溢出。当然,这种情况可以通过线程池机制改善,但并不能从本质上消除
这个弊端。
NIO:在 JDK1.4 以前,Java 的 IO 模型一直是 BIO,但从 JDK1.4 开始,JDK 引入的新的 IO 模
型 NIO,它是同步非阻塞的。而服务器的实现模式是多个请求一个线程,即请求会注册到多
路复用器 Selector 上,多路复用器轮询到连接有 IO 请求时才启动一个线程处理。
AIO:JDK1.7 发布了 NIO2.0,这就是真正意义上的异步非阻塞,服务器的实现模式为多个有
效请求一个线程,客户端的 IO 请求都是由 OS 先完成再通知服务器应用去启动线程处理(回
调)。
应用场景:并发连接数不多时采用 BIO,因为它编程和调试都非常简单,但如果涉及到高并
发的情况,应选择 NIO 或 AIO,更好的建议是采用成熟的网络通信框架 Netty
12.如何实现对象克隆?
有两种方式:
1). 实现 Cloneable 接口并重写 Object 类中的 clone()方法;
2). 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克
隆
1. session 和 cookie 的区别?
session 是存储在服务器端,cookie 是存储在客户端的,所以安全来讲 session 的安全性要比
cookie 高,然后我们获取 session 里的信息是通过存放在会话 cookie 里的 sessionid 获取的。
又由于 session 是存放在服务器的内存中,所以 session 里的东西不断增加会造成服务器的负
担,所以会把很重要的信息存储在 session 中,而把一些次要东西存储在客户端的 cookie 里,
然后 cookie 确切的说分为两大类分为会话 cookie 和持久化 cookie,会话 cookie 确切的说是,
存放在客户端浏览器的内存中,所以说他的生命周期和浏览器是一致的,浏览器关了会话
cookie 也就消失了,然而持久化 cookie 是存放在客户端硬盘中,而持久化 cookie 的生命周
期就是我们在设置 cookie 时候设置的那个保存时间,然后我们考虑一问题当浏览器关闭时
session 会不会丢失,从上面叙述分析 session 的信息是通过会话 cookie 的 sessionid 获取的,
当浏览器关闭的时候会话 cookie 消失所以我们的 sessionid 也就消失了,但是 session 的信息
还存在服务器端,这时我们只是查不到所谓的 session 但它并不是不存在。那么,session 在
什么情况下丢失,就是在服务器关闭的时候,或者是 session 过期(默认时间是 30 分钟),再
或 者 调 用 了 invalidate() 的 或 者 是 我 们 想 要 session 中 的 某 一 条 数 据 消 失 调 用
session.removeAttribute()方法,然后 session 在什么时候被创建呢,确切的说是通过调用
getsession()来创建,这就是 session 与 cookie 的区别.
11.Http 请求的 get 和 post 方法的区别。
1、Get 是向服务器发索取数据的一种请求,而 Post 是向服务器提交数据的一种请求
2、Get 是获取信息,而不是修改信息,类似数据库查询功能一样,数据不会被修改
3、Get 请求的参数会跟在 url 后进行传递,请求的数据会附在 URL 之后,以?分割 URL 和传
输数据,参数之间以&相连,%XX 中的 XX 为该符号以 16 进制表示的 ASCII,如果数据是英文
字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64 加密。
14.为什么要使用 PreparedStatement?
PreparedStatement 接口继承 Statement,PreparedStatement 实例包含已编译的 SQL 语句,所
以其执行速度要快于 Statement 对象。
作为 Statement 的子类, PreparedStatement 继承了 Statement 的所有功能。三种方法
execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数。
在 JDBC 应用中,多数情况下使用 PreparedStatement,原因如下:
代码的可读性和可维护性。Statement 需要不断地拼接,而 PreparedStatement 不会。
PreparedStatement 尽最大可能提高性能。DB 有缓存机制,相同的预编译语句再次被调用
不会再次需要编译。
最重要的一点是极大地提高了安全性。Statement 容易被 SQL 注入,而 PreparedStatement
传入的内容不会和 sql 语句发生任何匹配关系。
16.execute,executeQuery,executeUpdate 的区别是什么?
1、Statement 的 execute(String query)方法用来执行任意的 SQL 查询,如果查询的结果是一个
ResultSet,这个方法就返回 true。如果结果不是 ResultSet,比如 insert 或者 update 查询,它
就 会 返 回 false 。 我 们 可 以 通 过 它 的 getResultSet 方 法 来 获 取 ResultSet , 或 者 通 过
getUpdateCount()方法来获取更新的记录条数。
2、Statement 的 executeQuery(String query)接口用来执行 select 查询,并且返回 ResultSet。
即使查询不到记录返回的 ResultSet 也不会为 null。我们通常使用 executeQuery 来执行查询
语句,这样的话如果传进来的是 insert 或者 update 语句的话,它会抛出错误信息为
“executeQuery method can not be used for update”的 java.util.SQLException。 ,
3、Statement 的 executeUpdate(String query)方法用来执行 insert 或者 update/delete(DML)
语句,或者 什么也不返回,对于 DDL 语句,返回值是 int 类型,如果是 DML 语句的话,它
就是更新的条数,如果是 DDL 的话,就返回 0。
只有当你不确定是什么语句的时候才应该使用 execute()方法,否则应该使用
executeQuery 或者 executeUpdate 方法。
3.synchronized 和 Volatile、CAS 比较?
synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。
volatile 提供多线程共享变量可见性和禁止指令重排序优化。
CAS 是基于冲突检测的乐观锁(非阻塞)
4.线程间通信,wait 和 notify 的理解和使用?
1 wait 和 notify 必须配合 synchronized 关键字使用。
2 wait 方法释放锁,notify 方法不释放锁。
3 还要注意一点 就是涉及到线程之间的通信,就肯定会用到 validate 修饰。
8.什么叫线程安全?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。
如果每次运行结果和单线程运行的结果是一样的,而且其他的变量 的值也和预期的是一样
的,就是线程安全的。一个线程安全的计数器类的同一个实例对象在被多个线程使用的情况
下也不会出现计算失误。很显然你可以将集合类分 成两组,线程安全和非线程安全的。
10.volatile 变量和 atomic 变量有什么不同?
volatile 变量和 atomic 变量看起来很像,但功能却不一样。Volatile 变量可以确保先行关系,
即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用 volatile 修饰 count
变量那么 count++ 操作就不是原子性的。而 AtomicInteger 类提供的 atomic 方法可以让这种
操作具有原子性如 getAndIncrement()方法会原子性 的进行增量操作把当前值加一,其它数
据类型和引用变量也可以进行相似操作。
13.线程池的优点?
1)重用存在的线程,减少对象创建销毁的开销。
2)可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免
堵塞。
3)提供定时执行、定期执行、单线程、并发数控制等功能。
14.volatile 的理解?
volatile 关键字的两层语义
一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后,那么就具备
了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这
新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。
用 volatile 修饰之后,变量的操作:
第一:使用 volatile 关键字会强制将修改的值立即写入主存;
第二:使用 volatile 关键字的话,当线程 2 进行修改时,会导致线程 1 的工作内存中缓存变
量 stop 的缓存行无效(反映到硬件层的话,就是 CPU 的 L1 或者 L2 缓存中对应的缓存行无
效);
第三:由于线程 1 的工作内存中缓存变量 stop 的缓存行无效,所以线程 1 再次读取变量 stop
的值时会去主存读取。
注:指令重排:在不影响单线程执行结果的情况下,调整了代码 顺序
25.Callable 和 Runnable 的区别是什么?
两者都能用来编写多线程,但实现 Callable 接口的任务线程能返回执行结果,而实现 Runnable
接口的任务线程不能返回结果.Callable通常需要和Future/FutureTask结合使用,用于获取异步
计算结果.
27.synchronized 和 Lock 的区别?
主要相同点:Lock 能完成 synchronized 所实现的所有功能
主要不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能。synchronized 会自动
释放锁,而 Lock 一定要求程序员手工释放,并且必须在 finally 从句中释放。
28.ThreadLocal 是什么?有什么作用?
ThreadLocal 是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对
象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,
特别适用于各个线程依赖不通的变量值完成操作的场景。
简单说 ThreadLocal 就是一种以空间换时间的做法,在每个 Thread 里面维护了一个以开地址
法实现的 ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安
全方面的问题了。
31.什么是 FutureTask?
在 Java 并发程序中 FutureTask 表示一个可以取消的异步运算。它有启动和取消运算、查询
运算是否完成和取回运算结果等方法。只有当运算完 成的时候结果才能取回,如果运算尚
未完成 get 方法将会阻塞。一个 FutureTask 对象可以对调用了 Callable 和 Runnable 的对象进
行包 装,由于 FutureTask 也是调用了 Runnable 接口所以它可以提交给 Executor 来执行。
34.什么是自旋
很多 synchronized 里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都
加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然
synchronized 里面的代码执行得非常快,不妨让等待锁的线程不要被阻塞,而是在
synchronized 的边界做忙循环,这就是自旋。如果做了多次忙循环发现还没有获得锁,再阻
塞,这样可能是一种更好的策略。
36.如果提交任务时,线程池队列已满,这时会发生什么?
许多程序员会认为该任务会阻塞直到线程池队列有空位。事实上如果一个任务不能被调度执
行那么 ThreadPoolExecutor’s submit()方法将会抛出一个 RejectedExecutionException 异常。
39.线程池的工作原理,几个重要参数?
ThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTi
me,TimeUnit unit,BlockingQueue workQueue,ThreadFactory
threadFactory,RejectedExecutionHandler handler)
参数说明:
corePoolSize 核心线程数
maximumPoolSize 最大线程数,一般大于等于核心线程数
keepAliveTime 线程存活时间(针对最大线程数大于核心线程数时,非核心线程)
unit 存活时间单位,和线程存活时间配套使用
workQueue 任务队列
threadFactory 创建线程的工程
handler 拒绝策略
42.线程池的拒绝策略都有哪些?
四种拒绝策略
等待队列已经排满了,再也塞不下新任务,同时线程池中线程也已经达到 maximumPoolSize
数量,无法继续为新任务服务,这个时候就需要使用拒绝策略来处理。
RejectedExecutionHandler rejected = null;
rejected = new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务抛出异常,直接
抛出 RejectedExecutionException 异常阻止系统正常运行。
rejected = new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务不异常,直接丢弃任
务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。
rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删,之
后再尝试加入队列, 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交
当前任务。
rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线
程会自己去执行该任务,调用者运行”一种调节机制,该策略既不会丢弃任务,也不会抛出异
常,而是将某些任务回退给调用者,从而降低新任务的流量。
5.单例模式?
单例就是该类只能返回一个实例。
单例所具备的特点:
1.私有化的构造函数
2.私有的静态的全局变量
3.公有的静态的方法
单例分为懒汉式、饿汉式和双层锁式
饿汉式:
public class Singleton1 {
private Singleton1() {};
private static Singleton1 single = new Singleton1();
public static Singleton1 getInstance() {
return single;
}
}
懒汉式:
public class Singleton2 {
private Singleton2() {}千锋成都 Java 教学部
private static Singleton2 single=null;
public tatic Singleton2 getInstance() {
if (single == null) {
single = new Singleton2();
}
return single;
}
}
线程安全:
public class Singleton3 {
private Singleton3() {}
private static Singleton3 single ;
public static Singleton3 getInstance() {
if(null == single){
synchronized(single ){
if(null == single){
single = new Singleton3();
}
}
}
return single;
}
}
- MyBatis 的优点?
1、基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,
SQL 写在 XML 里,解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动
态 SQL 语句,并可重用。
2、与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开
关连接;
3、很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持
的数据库 MyBatis 都支持)。
4、能够与 Spring 很好的集成;
5、提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支
持对象关系组件维护。
3.MyBatis 框架的缺点?
(1)SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句
的功底有一定要求。
(2)SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
4. SpringMVC 工作流程?
1、用户发送请求至前端控制器 DispatcherServlet
2、DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
3、处理器映射器根据请求 url 找到具体的处理器,生成处理器对象及处理器拦截器(如果有
则生成)一并返回给 DispatcherServlet。
4、DispatcherServlet 通过 HandlerAdapter 处理器适配器调用处理器
5、执行处理器(Controller,也叫后端控制器)。
6、Controller 执行完成返回 ModelAndView
7、HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet
8、DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器
9、ViewReslover 解析后返回具体 View
10、DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet 响应用户
5.MyBatis 框架使用的场合?
(1)MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案。千锋成都 Java 教学部
(2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis 将是不错的选
择。
9. SpringIOC 是什么?
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管
理这些对象的整个生命周期。
10. AOP 有哪些实现方式?
实现 AOP 的技术,主要分为两大类:
静态代理 - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理
类,因此也称为编译时增强;
编译时编织(特殊编译器实现)
类加载时编织(特殊的类加载器实现)。
动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
JDK 动态代理、CGLIB
14.Spring 框架中都用到了哪些设计模式?
代理模式,在 AOP 中被使用最多。
单例模式,在 Spring 配置文件中定义 bean 的时候默认的是单例模式。
工厂模式, BeanFactory 用来创建对象的实例。
模板方法, 用来解决重复性代码。
前端控制器,Spring 提供了 DispatcherSerclet 来对请求进行分发。
视图帮助,Spring 提供了一系列的 JSP 标签。
依赖注入,它是惯穿于 BeanFactory/ApplicationContext 接口的核心理念。
31.Mybatis 中#{}和${}的区别是什么?
#{}是预编译处理,${}是字符串替换。
Mybatis 在处理#{}时,会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值;
Mybatis 在处理${}时,就是把${}替换成变量的值。
使用#{}可以有效的防止 SQL 注入,提高系统安全性。
32.Spring 中@Autowire 与@Resource 的区别?
@Autowire :先通过类型再通过名字 找
@Resource :先通过名字再通过类型
35.Spring 的事务传播行为?
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务
中,加入到这个事务中。这是默认的事务传播行为
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。(一个新的
事务将启动,而且如果有一个现有的事务在运行的话,则这个
方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执
行。)
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂
起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,
则执行与 PROPAGATION_REQUIRED 类似的操作。(外层事务抛
出异常回滚,那么内层事务必须回滚,反之内层事务并不影响
外层事务)
3.什么是 redis 持久化?rdb 和 aof 的比较?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
比较:
1、aof 文件比 rdb 更新频率高,优先使用 aof 还原数据。
2、aof 比 rdb 更安全也更大
3、rdb 性能比 aof 好
4、如果两个都配了优先加载 AOF
4.Redis 最适合的场景?
(1)、会话缓存(Session Cache) 最常用的一种使用 Redis 的情景是会话缓存(session cache)。用 Redis 缓存会话比其他存储
(如 Memcached)的优势在于:Redis 提供持久化。
(2)、全页缓存(FPC) 除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。回到一致性问题,即使重启了
Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改 进,类似 PHP 本地 FPC。 再次以 Magento 为例,Magento 提供一个插件来使用 Redis 作为全页缓存后端。 此外,对 WordPress 的用户来说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
(3)、队列 Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis 能作为一个很
好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言(如 Python)
对 list 的 push/pop 操作。
(4),排行榜/计数器 Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted
Set)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结 构。所以,我们要从排序集合中获取到排名最靠前的 10 个用户–我们称之为“user_scores”, 我们只需要像下面一样执行即可:
(5)、发布/订阅 最后(但肯定不是最不重要的)是 Redis 的发布/订阅功能。发布/订阅的使用场景确实非常 多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用 Redis 的发布/订阅功能来建立聊天系统!
7.redis 的淘汰策略有哪些?
noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部 分的写入指令,但 DEL 和几个例外)
allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有 空间存放。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数 据有空间存放。
9.redis 缓存穿透、缓存雪崩、缓存击穿?
缓存穿透:无效 ID,在 redis 缓存中查不到,去查询 DB,造成 DB 压力增大。
解决方法:
1、解决方法 1:布隆过滤器,提供一个很大的 Bit-Map,提供多个 hash 函数,分别对
查询参数值【比如 UUID】,进行求 hash,然后分别对多个 hash 结果,在对应位置对比是否
全为 1 或者某个位置为 0,一旦有一个位置标识为 0,表示本次查询 UUID,不存在于缓存,再
去查询 DB.起到一个再过滤的效果。
2、解决方法 2:把无效的 ID,也在 redis 缓存起来,并设置一个很短的超时时间。
缓存雪崩:缓存同一时间批量失效,导致大量的访问直接访问 DB
解决方法:
在做缓存时候,就做固定失效时间+随机时间段,保证所有的缓存不会同一时间失效
缓存击穿:在缓存失效的时候,会有高并发访问失效的缓存【热点数据】
解决方法:
最简单的解决方法,就是将热点数据设置永不超时!
第二个解决方法:对访问的 Key 加上互斥锁,请求的 Key 如果不存在,则加锁,去数据
库取,新请求过来,如果相同 KEy,则暂停 10s 再去缓存取值;如果 Key 不同,则直接去缓存
取!
12.redis 单线程还能处理速度那么快?
首先,redis 是单进程单线程的 k-v 内存型可持久化数据库。
单线程还能处理速度很快的原因:
1、redis 操作是基于内存的,内存的读写速度非常快
2、正是由于 redis 的单线程模式,避免了线程上下文切换的损耗
3、redis 采用的 IO 多路复用技术,可以很好的解决多请求并发的问题。 多路代表: 多请求,复用代表多个请求重复使用同一个线程。
13.为什么 Redis 的操作是原子性的,怎么保证原子性?
对于 Redis 而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执 行。
Redis 的操作之所以是原子性的,是因为 Redis 是单线程的。
Redis 本身提供的所有 API 都是原子操作,Redis 中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗? 不一定, 将 get 和 set 改成单命令操作,incr 。使用 Redis 的事务,或者使用 Redis+Lua== 的方式实现.
29.Nginx 的调度算法有哪些?
1、sticky:通过 nginx-sticky 模块,来实现 cookie 黏贴的方式将来自同一个客户端的请求发
送到同一个后端服务器上处理,这样一定程度上可以解决多个后端服务器的 session 会话同
步的问题;
2、round-robin(RR):轮询,每个请求按时间顺序依次分配到不同的后端服务器,如果后
端某台服务器死机,自动剔除故障系统,使用户访问不受影响;
3、weight:轮询权重,weight 的值越大分配到的访问概率就越高,主要用于后端每台服务
器性能不均衡的情况下,或者仅仅为在主从的情况下设置不同的权重,达到合理有效的利用
主机资源。
4、least_conn:请求被发送到当前活跃连接最少的 realserver 上,会考虑到 weight 的值;
5、ip_hash:每个请求按照 IP 的哈希结果分配,使来自同一个 IP 的访客固定访问后端服务
器,可以有效的解决动态网页存在的 session 共享问题。
6、fair:比 weight、ip_hash 更加智能的负载均衡算法,fair 算法可以根据页面的大小和加载
时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,相应时间短
的优先分配。nginx 本身不支持 fair,如果需要使用这种调度算法,则必须安装 upstream_fair
模块。
7、url_hash:按访问的 URL 的哈希结果来分配请求,使每个 URL 定向到后端服务器,可以
进一步提高后端缓存服务器的效率。同样,nginx 本身不支持 url_hash,如果需要这种调度
算法,则必须安装 nginx 的 hash 软件包。
30.Nginx 负载均衡调度状态?
常用的状态有:
1、down:表示当前的 server 暂时不参与负载均衡;
2、backup:预留的备份机器。当其他所有的非 backup 机器出现故障或者繁忙的时候,才会
请求 backup 机器,因此这台机器的访问压力最低;
3、max_fails:允许请求失败的次数,默认为 1,当超过最大次数时,返回 proxy_next_upstraem
模块定义的错误;
4、fail_timeout:请求失败超时时间,在经历了 max_fails 次失败后,暂停服务的时间。max_fails
和 fail_timeout 可以一起使用。
32.为什么要用 MQ?
1、解耦:如果多个模块或者系统中,互相调用很复杂,维护起来比较麻烦,但是这个调用
又不是同步调用,就可以运用 MQ 到这个业务中。
2、异步:这个很好理解,比如用户的操作日志的维护,可以不用同步处理,节约响应时间。
3、削峰:在高峰期的时候,系统每秒的请求量达到 5000,那么调用 MySQL 的请求也是
5000,一般情况下 MySQL 的请求大概在 2000 左右,那么在高峰期的时候,数据库就被打
垮了,那系统就不可用了。此时引入 MQ,在系统 A 前面加个 MQ,用户请求先到 MQ,
系统 A 从 MQ 中每秒消费 2000 条数据,这样就把本来 5000 的请求变为 MySQL 可以接
受的请求数量了,可以保证系统不挂掉,可以继续提供服务。MQ 里的数据可以慢慢的把它
消费掉。
55.如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?
这可以使用 DEV 工具来实现。通过这种依赖关系,您可以节省任何更改,嵌入式 tomcat 将
重新启动。Spring Boot 有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。
Java 开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。开发
人员可以重新加载 Spring Boot 上的更改,而无需重新启动服务器。这将消除每次手动部署
更改的需要。Spring Boot 在发布它的第一个版本时没有这个功能。这是开发人员最需要的功
能。DevTools 模块完全满足开发人员的需求。该模块将在生产环境中被禁用。它还提供 H2
数据库控制台以更好地测试应用程序。