Download as pdf or txt
Download as pdf or txt
You are on page 1of 485

互联网 Java 工程师面试题

内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、
Redis、MySQL、Spring、Spring Boot、Spring Cloud、RabbitMQ、Kafka、
Linux 等技术栈

目录

互联网 Java 工程师面试题..........................................................................................1


........................................................................................................................................1
MyBatis 面试题........................................................................................................ 33
1、什么是 Mybatis?............................................................................... 33
2、Mybaits 的优点:............................................................................... 33
3、MyBatis 框架的缺点:....................................................................... 34
4、MyBatis 框架适用场合:................................................................... 34
5、MyBatis 与 Hibernate 有哪些不同?.............................................. 35

第 1 页 共 485 页
6、#{}和${}的区别是什么?..................................................................... 35
7、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?........... 35
8、 模糊查询 like 语句该怎么写?...........................................................36
9、通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,
这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,
方法能重载吗?..........................................................................................37
10、Mybatis 是如何进行分页的?分页插件的原理是什么?.............38
11、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?都有
哪些映射形式?..........................................................................................38
12、如何执行批量插入?........................................................................... 39
13、如何获取自动生成的(主)键值?........................................................ 40
14、在 mapper 中如何传递多个参数?.................................................. 40
15、Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?.......42
16、Xml 映射文件中,除了常见的 select|insert|updae|delete 标签之
外,还有哪些标签?..................................................................................42
18、为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动的区别
在哪里?......................................................................................................43
19、 一对一、一对多的关联查询 ?..................................................... 43
20、MyBatis 实现一对一有几种方式?具体怎么操作的?...................45
21、MyBatis 实现一对多有几种方式,怎么操作的?............................45
22、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?46
23、Mybatis 的一级、二级缓存:............................................................46
24、什么是 MyBatis 的接口绑定?有哪些实现方式?........................47
25、使用 MyBatis 的 mapper 接口调用时有哪些要求?................... 47
26、Mapper 编写有哪几种方式?.........................................................47
27、简述 Mybatis 的插件运行原理,以及如何编写一个插件。........50
ZooKeeper 面试题...................................................................................................50
1. ZooKeeper 面试题?.......................................................................... 50
2. ZooKeeper 提供了什么?................................................................... 51
3. Zookeeper 文件系统............................................................................51
4. ZAB 协议?............................................................................................ 52

第 2 页 共 485 页
5. 四种类型的数据节点 Znode.............................................................. 52
6. Zookeeper Watcher 机制 -- 数据变更通知..................................53
7. 客户端注册 Watcher 实现.................................................................. 54
8. 服务端处理 Watcher 实现.................................................................. 55
9. 客户端回调 Watcher........................................................................... 55
10. ACL 权限控制机制.............................................................................. 56
11. Chroot 特性.........................................................................................57
12. 会话管理.............................................................................................. 57
13. 服务器角色.......................................................................................... 58
14. Zookeeper 下 Server 工作状态..................................................... 58
15. 数据同步.............................................................................................. 59
16. zookeeper 是如何保证事务的顺序一致性的?..............................61
17. 分布式集群中为什么会有 Master?................................................61
18. zk 节点宕机如何处理?......................................................................61
19. zookeeper 负载均衡和 nginx 负载均衡区别................................. 62
20. Zookeeper 有哪几种几种部署模式?............................................. 62
21. 集群最少要几台机器,集群规则是怎样的?.................................... 62
22. 集群支持动态添加机器吗?.............................................................. 62
23. Zookeeper 对节点的 watch 监听通知是永久的吗?为什么不是永
久的?............................................................................................................63
24. Zookeeper 的 java 客户端都有哪些?............................................63
25. chubby 是什么,和 zookeeper 比你怎么看?..............................64
26. 说几个 zookeeper 常用的命令。....................................................64
27. ZAB 和 Paxos 算法的联系与区别?.................................................64
28. Zookeeper 的典型应用场景............................................................. 64
Dubbo 面试题.......................................................................................................... 68
1、为什么要用 Dubbo?......................................................................... 68
2、Dubbo 的整体架构设计有哪些分层?.............................................. 69
3、默认使用的是什么通信框架,还有别的选择吗?............................. 70
4、服务调用是阻塞的吗?....................................................................... 70
5、一般使用什么注册中心?还有别的选择吗?................................... 70

第 3 页 共 485 页
6、默认使用什么序列化框架,你知道的还有哪些?........................... 71
7、服务提供者能实现失效踢出是什么原理?....................................... 71
8、服务上线怎么不影响旧版本?........................................................... 71
9、如何解决服务调用链过长的问题?................................................... 71
10、说说核心的配置有哪些?................................................................. 71
11、Dubbo 推荐用什么协议?..............................................................72
12、同一个服务多个注册的情况下可以直连某一个服务吗?............. 72
13、画一画服务注册与发现的流程图?................................................. 73
14、Dubbo 集群容错有几种方案?......................................................73
15、Dubbo 服务降级,失败重试怎么做?..........................................74
16、Dubbo 使用过程中都遇到了些什么问题?..................................74
17、Dubbo Monitor 实现原理?......................................................... 74
18、Dubbo 用到哪些设计模式?..........................................................75
19、Dubbo 配置文件是如何加载到 Spring 中的?........................... 76
20、Dubbo SPI 和 Java SPI 区别?................................................... 77
21、Dubbo 支持分布式事务吗?..........................................................77
22、Dubbo 可以对结果进行缓存吗?..................................................77
23、服务上线怎么兼容旧版本?............................................................. 78
24、Dubbo 必须依赖的包有哪些?.......................................................78
25、Dubbo telnet 命令能做什么?......................................................78
26、Dubbo 支持服务降级吗?..............................................................79
27、Dubbo 如何优雅停机?..................................................................79
28、Dubbo 和 Dubbox 之间的区别?...............................................79
29、Dubbo 和 Spring Cloud 的区别?.............................................80
30、你还了解别的分布式框架吗?......................................................... 81
Elasticsearch 面试题............................................................................................... 81
1、elasticsearch 了解多少,说说你们公司 es 的集群架构,索引数据
大小,分片有多少,以及一些调优手段 。............................................81
2、elasticsearch 的倒排索引是什么...................................................... 83
3、elasticsearch 索引数据多了怎么办,如何调优,部署................. 84
4、elasticsearch 是如何实现 master 选举的.......................................85

第 4 页 共 485 页
5、详细描述一下 Elasticsearch 索引文档的过程................................. 86
6、详细描述一下 Elasticsearch 搜索的过程?..................................... 87
7、Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法.......... 88
8、lucence 内部结构是什么?................................................................88
9、Elasticsearch 是如何实现 Master 选举的?...................................89
10、Elasticsearch 中的节点(比如共 20 个),其中的 10 个选了一个
master,另外 10 个选了另一个 master,怎么办?............................90
11、客户端在和集群连接时,如何选择特定的节点执行请求的?..... 90
12、详细描述一下 Elasticsearch 索引文档的过程。...........................90
13、详细描述一下 Elasticsearch 更新和删除文档的过程。...............92
14、详细描述一下 Elasticsearch 搜索的过程。...................................92
15、在 Elasticsearch 中,是怎么根据一个词找到对应的倒排索引的?
......................................................................................................................94
16、Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法?....94
17、对于 GC 方面,在使用 Elasticsearch 时要注意什么?............... 96
18、Elasticsearch 对于大数据量(上亿量级)的聚合如何实现?....97
19、在并发情况下,Elasticsearch 如果保证读写一致?....................97
20、如何监控 Elasticsearch 集群状态?...............................................97
21、介绍下你们电商搜索的整体技术架构。......................................... 98
22、介绍一下你们的个性化搜索方案?................................................. 98
23、是否了解字典树?............................................................................. 98
24、拼写纠错是如何实现的?...............................................................100
Memcached 面试题.............................................................................................. 102
1、Memcached 是什么,有什么作用?.............................................102
2、Memcached 服务分布式集群如何实现?.....................................104
3、Memcached 服务特点及工作原理是什么?.................................105
4、简述 Memcached 内存管理机制原理?........................................105
5、memcached 是怎么工作的?.........................................................107
6、memcached 最大的优势是什么?.................................................107
7、memcached 和 MySQL 的 query................................................. 107
8、memcached 和服务器的 local cache(比如 PHP 的 APC、mmap

第 5 页 共 485 页
文件等)相比,有什么优缺点?............................................................108
9、memcached 的 cache 机制是怎样的?........................................ 109
10、memcached 如何实现冗余机制?...............................................109
11、memcached 如何处理容错的?...................................................110
12、如何将 memcached 中 item 批量导入导出?........................... 110
13、如果缓存数据在导出导入之间过期了,您又怎么处理这些数据呢?
....................................................................................................................111
14、memcached 是如何做身份验证的?...........................................111
15、memcached 的多线程是什么?如何使用它们?.......................112
16、memcached 能接受的 key 的最大长度是多少?...................... 112
17、memcached 最大能存储多大的单个 item?............................. 113
18、memcached 能够更有效地使用内存吗?...................................113
19、什么是二进制协议,我该关注吗?...............................................113
20 、 memcached 的 内 存 分 配 器 是 如 何 工 作 的 ? 为 什 么 不 适 用
malloc/free!?为何要使用 slabs?................................................... 114
21、memcached 是原子的吗?...........................................................114
22、如何实现集群中的 session 共享存储?.......................................115
23、memcached 与 redis 的区别?................................................... 116
Redis 面试题........................................................................................................... 118
1、什么是 Redis?....................................................................................118
2、Redis 的数据类型?..........................................................................119
3、使用 Redis 有哪些好处?.................................................................119
4、Redis 相比 Memcached 有哪些优势?........................................ 120
5、Memcache 与 Redis 的区别都有哪些?....................................... 120
6、Redis 是单进程单线程的?..............................................................120
7、一个字符串类型的值能存储最大容量是多少?............................. 120
8、Redis 的持久化机制是什么?各自的优缺点?..............................121
9、Redis 常见性能问题和解决方案:..................................................122
10、redis 过期键的删除策略?.............................................................122
11、Redis 的回收策略(淘汰策略)?..................................................123
12、为什么 edis 需要把所有数据放到内存中?................................. 124

第 6 页 共 485 页
13、Redis 的同步机制了解么?........................................................... 124
14、Pipeline 有什么好处,为什么要用 pipeline?.......................... 124
15、是否使用过 Redis 集群,集群的原理是什么?.......................... 125
16、Redis 集群方案什么情况下会导致整个集群不可用?............... 125
17、Redis 支持的 Java 客户端都有哪些?官方推荐用哪个?......... 125
18、Jedis 与 Redisson 对比有什么优缺点?..................................... 125
19、Redis 如何设置密码及验证密码?............................................... 126
20、说说 Redis 哈希槽的概念?.......................................................... 126
21、Redis 集群的主从复制模型是怎样的?....................................... 126
22、Redis 集群会有写操作丢失吗?为什么?................................... 126
23、Redis 集群之间是如何复制的?................................................... 126
24、Redis 集群最大节点个数是多少?............................................... 127
25、Redis 集群如何选择数据库?....................................................... 127
26、怎么测试 Redis 的连通性?.......................................................... 127
27、怎么理解 Redis 事务?.................................................................. 127
28、Redis 事务相关的命令有哪几个?............................................... 127
29、Redis key 的过期时间和永久有效分别怎么设置?....................128
30、Redis 如何做内存优化?............................................................... 128
31、Redis 回收进程如何工作的?....................................................... 128
32、都有哪些办法可以降低 Redis 的内存使用情况呢?.................. 128
33、Redis 的内存用完了会发生什么?............................................... 128
34、一个 Redis 实例最多能存放多少的 keys?List、Set、Sorted Set
他们最多能存放多少元素?....................................................................129
35、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证
redis 中的数据都是热点数据?............................................................. 129
36、Redis 最适合的场景?................................................................... 130
37、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以某个固定
的已知的前缀开头的,如果将它们全部找出来?................................131
38、如果有大量的 key 需要设置同一时间过期,一般需要注意什么?132
39、使用过 Redis 做异步队列么,你是怎么用的?.......................... 132
40、使用过 Redis 分布式锁么,它是什么回事?.............................. 133

第 7 页 共 485 页
MySQL 面试题........................................................................................................133
1、MySQL 中有哪几种锁?...................................................................134
2、MySQL 中有哪些不同的表格?.......................................................134
3、简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别............... 134
4、MySQL 中 InnoDB 支持的四种事务隔离级别名称,以及逐级之间
的区别?....................................................................................................136
5、CHAR 和 VARCHAR 的区别?........................................................136
6、主键和候选键有什么区别?............................................................. 136
7、myisamchk 是用来做什么的?.......................................................137
8、如果一个表有一列定义为 TIMESTAMP,将发生什么?.............137
9、你怎么看到为表格定义的所有索引?............................................. 137
10、LIKE 声明中的%和_是什么意思?................................................ 138
11、列对比运算符是什么?...................................................................138
12、BLOB 和 TEXT 有什么区别?........................................................ 138
13、MySQL_fetch_array 和 MySQL_fetch_object 的区别是什么?138
14、MyISAM 表格将在哪里存储,并且还提供其存储格式?..........139
15、MySQL 如何优化 DISTINCT?..................................................... 139
16、如何显示前 50 行?........................................................................ 139
17、可以使用多少列创建索引?...........................................................140
18、NOW()和 CURRENT_DATE()有什么区别?.................... 140
19、什么是非标准字符串类型?...........................................................140
20、什么是通用 SQL 函数?................................................................. 140
21、MySQL 支持事务吗?.................................................................... 141
22、MySQL 里记录货币用什么字段类型好........................................ 142
23、MySQL 有关权限的表都有哪几个?............................................ 142
24、列的字符串类型可以是什么?.......................................................142
25、MySQL 数据库作发布系统的存储,一天五万条以上的增量,预计
运维三年,怎么优化?...............................................................................143
26、锁的优化策略...................................................................................143
27、索引的底层实现原理和优化...........................................................144
28、什么情况下设置了索引但无法使用...............................................144

第 8 页 共 485 页
29、实践中如何优化 MySQL................................................................ 144
30、优化数据库的方法...........................................................................145
31、简单描述 MySQL 中,索引,主键,唯一索引,联合索引的区别,
对数据库的性能有什么影响(从读写两方面)....................................146
32、数据库中的事务是什么?.................................................................146
33、SQL 注入漏洞产生的原因?如何防止?...................................... 147
34、为表中得字段选择合适得数据类型...............................................148
35、存储时期...........................................................................................148
36、对于关系型数据库而言,索引是相当重要的概念,请回答有关索
引的几个问题:........................................................................................149
37、解释 MySQL 外连接、内连接与自连接的区别........................... 150
38、Myql 中的事务回滚机制概述........................................................ 150
39、SQL 语言包括哪几部分?每部分都有哪些操作关键字?.......... 151
40、完整性约束包括哪些?...................................................................151
41、什么是锁?.......................................................................................152
42、什么叫视图?游标是什么?...........................................................152
43、什么是存储过程?用什么来调用?...............................................153
44、如何通俗地理解三个范式?...........................................................153
45、什么是基本表?什么是视图?.......................................................154
46、试述视图的优点?...........................................................................154
47、 NULL 是什么意思......................................................................... 154
48、主键、外键和索引的区别?...........................................................154
49、你可以用什么来确保表格里的字段只接受特定范围里的值?.....155
50、说说对 SQL 语句优化有哪些方法?(选择几条)..................... 156
Java 并发编程(一)............................................................................................. 156
1、在 java 中守护线程和本地线程区别?............................................156
2、线程与进程的区别?......................................................................... 157
3、什么是多线程中的上下文切换?..................................................... 157
4、死锁与活锁的区别,死锁与饥饿的区别?..................................... 158
5、Java 中用到的线程调度算法是什么?............................................ 158
6、什么是线程组,为什么在 Java 中不推荐使用?........................... 159

第 9 页 共 485 页
7、为什么使用 Executor 框架?...........................................................159
8、在 Java 中 Executor 和 Executors 的区别?................................ 159
9、如何在 Windows 和 Linux 上查找哪个线程使用的 CPU 时间最长?
....................................................................................................................160
10、什么是原子操作?在 Java Concurrency API 中有哪些原子类
(atomic classes)?..................................................................................160
11、Java Concurrency API 中的 Lock 接口(Lock interface)是什么?
对比同步它有什么优势?........................................................................161
12、什么是 Executors 框架?.............................................................. 162
13、什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队
列来实现生产者-消费者模型?..............................................................162
14、什么是 Callable 和 Future?.......................................................... 163
15、什么是 FutureTask?使用 ExecutorService 启动任务。........... 164
16、什么是并发容器的实现?...............................................................164
17、多线程同步和互斥有几种实现方法,都是什么?.......................164
18、什么是竞争条件?你怎样发现和解决竞争?...............................165
19、你将如何使用 thread dump?你将如何分析 Thread dump?165
20、为什么我们调用 start()方法时会执行 run()方法,为什么我们不能
直接调用 run()方法?............................................................................. 173
21、Java 中你怎样唤醒一个阻塞的线程?..........................................173
22、在 Java 中 CycliBarriar 和 CountdownLatch 有什么区别?...173
23、什么是不可变对象,它对写并发应用有什么帮助?...................174
24、什么是多线程中的上下文切换?...................................................175
25、Java 中用到的线程调度算法是什么?..........................................175
26、什么是线程组,为什么在 Java 中不推荐使用?.........................176
27、为什么使用 Executor 框架比使用应用创建和管理线程好?.... 176
28、java 中有几种方法可以实现一个线程?...................................... 177
29、如何停止一个正在运行的线程?...................................................177
30、notify()和 notifyAll()有什么区别?............................................. 177
31、什么是 Daemon 线程?它有什么意义?.....................................178
32、java 如何实现多线程之间的通讯和协作?.................................. 178

第 10 页 共 485 页
33、什么是可重入锁(ReentrantLock)?....................................... 178
34、当一个线程进入某个对象的一个 synchronized 的实例方法后,其
它线程是否可进入此对象的其它方法?................................................179
35、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?...........179
36、SynchronizedMap 和 ConcurrentHashMap 有什么区别?..181
37、CopyOnWriteArrayList 可以用于什么应用场景?................... 181
38、什么叫线程安全?servlet 是线程安全吗?................................... 182
39、volatile 有什么用?能否用一句话说明下 volatile 的应用场景?183
40、为什么代码会重排序?...................................................................183
41、在 java 中 wait 和 sleep 方法的不同?....................................... 183
42、用 Java 实现阻塞队列.....................................................................185
43、一个线程运行时发生异常会怎样?...............................................185
44、如何在两个线程间共享数据?.......................................................186
45、Java 中 notify 和 notifyAll 有什么区别?.................................186
46、为什么 wait, notify 和 notifyAll 这些方法不在 thread 类里面?
....................................................................................................................186
47、什么是 ThreadLocal 变量?..........................................................186
48、Java 中 interrupted 和 isInterrupted 方法的区别?............. 187
49、为什么 wait 和 notify 方法要在同步块中调用?........................187
50、为什么你应该在循环中检查等待条件?.........................................188
51、Java 中的同步集合与并发集合有什么区别?..............................188
52、什么是线程池? 为什么要使用它?.............................................188
53、怎么检测一个线程是否拥有锁?...................................................188
54、你如何在 Java 中获取线程堆栈?.................................................188
56、Thread 类中的 yield 方法有什么作用?..................................... 189
57、Java 中 ConcurrentHashMap 的并发度是什么?.................... 189
58、Java 中 Semaphore 是什么?......................................................190
59、Java 线程池中 submit() 和 execute()方法有什么区别?....... 190
60、什么是阻塞式方法?.......................................................................190
61、Java 中的 ReadWriteLock 是什么?...........................................190
62、volatile 变量和 atomic 变量有什么不同?.............................. 191

第 11 页 共 485 页
63、可以直接调用 Thread 类的 run ()方法么?................................ 191
64、如何让正在运行的线程暂停一段时间?.......................................191
65、你对线程优先级的理解是什么?...................................................191
66 、 什 么 是 线 程 调 度 器 (Thread Scheduler) 和 时 间 分 片 (Time
Slicing )?................................................................................................ 192
67、你如何确保 main()方法所在的线程是 Java 程序最后结束的线程?
....................................................................................................................192
68、线程之间是如何通信的?...............................................................193
69、为什么线程通信的方法 wait(), notify()和 notifyAll()被定义在
Object 类里?.........................................................................................193
70、为什么 wait(), notify()和 notifyAll ()必须在同步方法或者同步块
中被调用?................................................................................................193
71、为什么 Thread 类的 sleep()和 yield ()方法是静态的?............193
72、如何确保线程安全?.......................................................................194
73、同步方法和同步块,哪个是更好的选择?...................................194
74、如何创建守护线程?.......................................................................194
75、什么是 Java Timer 类?如何创建一个有特定时间间隔的任务?195
Java 并发编程(二)............................................................................................. 195
1、并发编程三要素?............................................................................. 195
2、实现可见性的方法有哪些?............................................................. 195
3、多线程的价值?................................................................................. 196
4、创建线程的有哪些方式?................................................................. 196
5、创建线程的三种方式的对比?......................................................... 196
6、线程的状态流转图............................................................................. 197
7、Java 线程具有五中基本状态............................................................ 197
8、什么是线程池?有哪几种创建方式?............................................. 198
9、四种线程池的创建:......................................................................... 199
10、线程池的优点?...............................................................................199
11、常用的并发工具类有哪些?...........................................................199
12、CyclicBarrier 和 CountDownLatch 的区别............................... 199
13、synchronized 的作用?.................................................................200

第 12 页 共 485 页
14、volatile 关键字的作用.................................................................... 200
15、什么是 CAS...................................................................................... 200
16、CAS 的问题...................................................................................... 201
17、什么是 Future?............................................................................. 201
18、什么是 AQS..................................................................................... 202
19、AQS 支持两种同步方式:............................................................. 202
20、ReadWriteLock 是什么................................................................. 202
21、FutureTask 是什么......................................................................... 203
22、synchronized 和 ReentrantLock 的区别................................... 203
23、什么是乐观锁和悲观锁...................................................................203
24、线程 B 怎么知道线程 A 修改了变量..............................................204
25、synchronized、volatile、CAS 比较...........................................204
26、sleep 方法和 wait 方法有什么区别?............................................ 204
27、ThreadLocal 是什么?有什么用?...............................................204
28、为什么 wait()方法和 notify()/notifyAll()方法要在同步块中被调用
....................................................................................................................205
29、多线程同步有哪几种方法?...........................................................205
30、线程的调度策略...............................................................................205
31、ConcurrentHashMap 的并发度是什么...................................... 206
32、Linux 环境下如何查找哪个线程使用 CPU 最长..........................206
33、Java 死锁以及如何避免?..............................................................206
34、死锁的原因.......................................................................................206
35、怎么唤醒一个阻塞的线程...............................................................207
36、不可变对象对多线程有什么帮助...................................................207
37、什么是多线程的上下文切换...........................................................207
38、如果你提交任务时,线程池队列已满,这时会发生什么...........207
39、Java 中用到的线程调度算法是什么..............................................208
40 、 什 么 是 线 程 调 度 器 (Thread Scheduler) 和 时 间 分 片 (Time
Slicing)?..................................................................................................208
41、什么是自旋.......................................................................................208
42、Java Concurrency API 中的 Lock 接口(Lock interface)是什么?

第 13 页 共 485 页
对比同步它有什么优势?........................................................................209
43、单例模式的线程安全性...................................................................209
44、Semaphore 有什么作用................................................................209
45、Executors 类是什么?................................................................... 210
46、线程类的构造方法、静态块是被哪个线程调用的.......................210
47、同步方法和同步块,哪个是更好的选择?.....................................210
48、Java 线程数过多会造成什么异常?..............................................210
Java 面试题(一)................................................................................................. 211
1、面向对象的特征有哪些方面?......................................................... 211
2、访问修饰符 public,private,protected,以及不写(默认)时的区别?
....................................................................................................................212
3、String 是最基本的数据类型吗?....................................................213
4、float f=3.4;是否正确?.................................................................... 213
5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
....................................................................................................................214
6、Java 有没有 goto?..........................................................................214
7、int 和 Integer 有什么区别?........................................................... 214
8、&和&&的区别?...............................................................................218
9、解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。
....................................................................................................................219
10、Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?220
11、switch 是否能作用在 byte 上,是否能作用在 long 上,是否能
作用在 String 上?.................................................................................. 220
12、用最有效率的方法计算 2 乘以 8?............................................... 221
13、数组有没有 length()方法?String 有没有 length()方法?...... 222
14、在 Java 中,如何跳出当前的多重嵌套循环?.............................223
15、构造器(constructor)是否可被重写(override)?............. 223
16、两个对象值相同(x.equals(y) == true),但却可有不同的 hash
code,这句话对不对?.......................................................................... 223
17、是否可以继承 String 类?............................................................. 224
18、当一个对象被当作参数传递到一个方法后,此方法可改变这个对

第 14 页 共 485 页
象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传
递?............................................................................................................225
19、String 和 StringBuilder、StringBuffer 的区别?.....................226
20、重载(Overload)和重写(Override)的区别。重载的方法能否
根据返回类型进行区分?........................................................................227
21、描述一下 JVM 加载 class 文件的原理机制?..............................228
22、char 型变量中能不能存贮一个中文汉字,为什么?.................229
23、抽象类(abstract class)和接口(interface)有什么异同?.230
24、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不
同?............................................................................................................230
25、Java 中会存在内存泄漏吗,请简单描述。.................................234
26、抽象的(abstract)方法是否可同时是静态的(static),是否可同
时是本地方法(native),是否可同时被 synchronized 修饰?.....236
27、阐述静态变量和实例变量的区别。...............................................236
28、是否可以从一个静态(static)方法内部发出对非静态(non-static)
方法的调用?............................................................................................237
29、如何实现对象克隆?.......................................................................237
30、GC 是什么?为什么要有 GC?......................................................242
31、String s = new String(“xyz”);创建了几个字符串对象?.....244
32 、 接 口 是 否 可 继 承 ( extends ) 接 口 ? 抽 象 类 是 否 可 实 现
(implements)接口?抽象类是否可继承具体类(concrete class)?
....................................................................................................................244
33、一个”.java”源文件中是否可以包含多个类(不是内部类)?有
什么限制?................................................................................................245
34、Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否
可以实现接口?........................................................................................245
35、内部类可以引用它的包含类(外部类)的成员吗?有没有什么限
制?............................................................................................................245
36、Java 中的 final 关键字有哪些用法?...........................................245
37、指出下面程序的运行结果...............................................................246
38、数据类型之间的转换:...................................................................247

第 15 页 共 485 页
39、如何实现字符串的反转及替换?...................................................248
40、怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的字符串?
....................................................................................................................248
41、日期和时间:...................................................................................249
42、打印昨天的当前时刻。...................................................................251
43、比较一下 Java 和 JavaSciprt。.....................................................252
44、什么时候用断言(assert)?........................................................253
45、Error 和 Exception 有什么区别?................................................254
46、try{}里有一个 return 语句,那么紧跟在这个 try 后的 finally{}里
的代码会不会被执行,什么时候被执行,在 return 前还是后?........255
47、Java 语言如何进行异常处理,关键字:throws、throw、try、catch、
finally 分别如何使用?........................................................................... 256
48、运行时异常与受检异常有何异同?...............................................257
49、列出一些你常见的运行时异常?...................................................258
50、阐述 final、finally、finalize 的区别。........................................258
51、类 ExampleA 继承 Exception,类 ExampleB 继承 ExampleA。259
52、List、Set、Map 是否继承自 Collection 接口?....................... 260
53、阐述 ArrayList、Vector、LinkedList 的存储性能和特性。.....261
54、Collection 和 Collections 的区别?............................................262
55、List、Map、Set 三个接口存取元素时,各有什么特点?.........262
56、TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具
类中的 sort()方法如何比较元素?........................................................ 262
57、Thread 类的 sleep()方法和对象的 wait()方法都可以让线程暂停执
行,它们有什么区别?..............................................................................267
58、线程的 sleep()方法和 yield()方法有什么区别?........................ 268
59、当一个线程进入一个对象的 synchronized 方法 A 之后,其它线
程是否可进入此对象的 synchronized 方法 B?.................................268
60、请说出与线程同步以及线程调度相关的方法。...........................268
61、编写多线程程序有几种实现方式?...............................................276
62、synchronized 关键字的用法?.....................................................278
63、举例说明同步和异步。...................................................................278

第 16 页 共 485 页
64、启动一个线程是调用 run()还是 start()方法?............................ 278
65、什么是线程池(thread pool)?................................................ 279
66、线程的基本状态以及状态之间的关系?.......................................280
67、简述 synchronized 和 java.util.concurrent.locks.Lock 的异同?
....................................................................................................................281
68、Java 中如何实现序列化,有什么意义?......................................282
69、Java 中有几种类型的流?..............................................................282
70、写一个方法,输入一个文件名和一个字符串,统计这个字符串在
这个文件中出现的次数。........................................................................284
71、如何用 Java 代码列出一个目录下所有的文件?.........................286
72、用 Java 的套接字编程实现一个多线程的回显(echo)服务器。288
73、XML 文档定义有几种形式?它们之间有何本质区别?解析 XML
文档有哪几种方式?................................................................................295
74、你在项目中哪些地方用到了 XML?............................................. 296
75、阐述 JDBC 操作数据库的步骤。................................................... 296
76、Statement 和 PreparedStatement 有什么区别?哪个性能更好?
....................................................................................................................298
77、使用 JDBC 操作数据库时,如何提升读取数据的性能?如何提升
更新数据的性能?....................................................................................299
78、在进行数据库编程时,连接池有什么作用?...............................300
79、什么是 DAO 模式?........................................................................300
80、事务的 ACID 是指什么?............................................................... 301
82、JDBC 能否处理 Blob 和 Clob?....................................................306
83、简述正则表达式及其用途。...........................................................308
84、Java 中是如何支持正则表达式操作的?......................................308
85、获得一个类的类对象有哪些方式?...............................................309
88、如何通过反射调用对象的方法?...................................................313
90、简述一下你了解的设计模式。.......................................................317
91、用 Java 写一个单例类。.................................................................318
92、什么是 UML?.................................................................................319
93、UML 中有哪些常用的图?.............................................................320

第 17 页 共 485 页
95、用 Java 写一个折半查找。.............................................................324
Java 面试题(二)................................................................................................. 326
多线程、并发及线程的基础问题............................................................328
1、Java 中能创建 volatile 数组吗?................................................. 328
2、volatile 能使得一个非原子操作变成原子操作吗?..................... 328
3、volatile 修饰符的有过什么实践?................................................. 328
4、volatile 类型变量提供什么保证?................................................. 329
5、10 个线程和 2 个线程的同步代码,哪个更容易写?................ 329
6、你是如何调用 wait()方法的?使用 if 块还是循环?为什么?329
有经验程序员的 Java 面试题............................................................... 330
8、什么是 Busy spin?我们为什么要使用它?................................. 330
9、Java 中怎么获取一份线程 dump 文件?....................................331
10、Swing 是线程安全的?................................................................. 331
11、什么是线程局部变量?...................................................................331
12、用 wait-notify 写一段代码来解决生产者-消费者问题?........ 331
13、用 Java 写一个线程安全的单例模式(Singleton)?.............332
14、Java 中 sleep 方法和 wait 方法的区别?...............................332
15、什么是不可变对象(immutable object)?Java 中怎么创建一
个不可变对象?........................................................................................332
16、我们能创建一个包含可变对象的不可变对象吗?.......................333
数据类型和 Java 基础面试问题........................................................... 333
17、Java 中应该使用什么数据类型来代表价格?.............................333
18、怎么将 byte 转换为 String?..................................................... 333
19、Java 中怎样将 bytes 转换为 long 类型?..............................333
20、我们能将 int 强制转换为 byte 类型的变量吗?如果该值大于
byte 类型的范围,将会出现什么现象?............................................. 334
21、存在两个类,B 继承 A,C 继承 B,我们能将 B 转换为 C 么?
如 C = (C) B;........................................................................................ 334
22、哪个类包含 clone 方法?是 Cloneable 还是 Object?.......334
23、Java 中 ++ 操作符是线程安全的吗?.......................................334
23、不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,

第 18 页 共 485 页
然后存储回内存,这个过程可能会出现多个线程交差。....................335
24、a = a + b 与 a += b 的区别...................................................... 335
25、我能在不进行强制转换的情况下将一个 double 值赋值给 long
类型的变量吗?........................................................................................335
26、3*0.1 == 0.3 将会返回什么?true 还是 false?.....................336
27、int 和 Integer 哪个会占用更多的内存?.................................. 336
28、为什么 Java 中的 String 是不可变的(Immutable)?.......336
JVM 底层 与 GC(Garbage Collection) 的面试问题................. 336
31、64 位 JVM 中,int 的长度是多数?.........................................337
32、Serial 与 Parallel GC 之间的不同之处?...................................337
33、32 位和 64 位的 JVM,int 类型变量的长度是多数?.......... 337
34、Java 中 WeakReference 与 SoftReference 的区别?......... 337
35、WeakHashMap 是怎么工作的?................................................337
36、JVM 选项 -XX:+UseCompressedOops 有什么作用?为什么要
使用?........................................................................................................338
37、怎样通过 Java 程序来判断 JVM 是 32 位 还是 64 位?... 338
38、32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?....... 338
39、JRE、JDK、JVM 及 JIT 之间有什么不同?.............................. 339
3 年工作经验的 Java 面试题............................................................... 339
40、解释 Java 堆空间及 GC?........................................................... 339
JVM 底层面试题及答案......................................................................... 339
41、你能保证 GC 执行吗?................................................................. 339
42、怎么获取 Java 程序使用的内存?堆使用的百分比?...............339
43、Java 中堆和栈有什么区别?.........................................................340
关于内存的的面试问题和答案................................................................340
Java 基本概念面试题............................................................................. 340
44、“a==b”和”a.equals(b)”有什么区别?............................... 340
45、a.hashCode() 有什么用?与 a.equals(b) 有什么关系?....... 340
46、final、finalize 和 finally 的不同之处?....................................341
47、Java 中的编译期常量是什么?使用它又什么风险?.................341
Java 集合框架的面试题......................................................................... 341

第 19 页 共 485 页
48、List、Set、Map 和 Queue 之间的区别(答案)........................ 341
49、poll() 方法和 remove() 方法的区别?......................................342
50、Java 中 LinkedHashMap 和 PriorityQueue 的区别是什么?342
51、ArrayList 与 LinkedList 的不区别?......................................... 342
52、用哪两种方式来实现集合的排序?...............................................342
53、Java 中怎么打印数组?.................................................................343
54、Java 中的 LinkedList 是单向链表还是双向链表?..................343
55、Java 中的 TreeMap 是采用什么树实现的?(答案)................. 343
56、Hashtable 与 HashMap 有什么不同之处?........................... 343
57、Java 中的 HashSet,内部是如何工作的?............................... 344
58、写一段代码在遍历 ArrayList 时移除一个元素?...................... 344
59、我们能自己写一个容器类,然后使用 for-each 循环码?.......344
60、ArrayList 和 HashMap 的默认大小是多数?..........................344
61、有没有可能两个不相等的对象有有相同的 hashcode?.......... 345
62、两个相同的对象会有不同的的 hash code 吗?....................... 345
63、我们可以在 hashcode() 中使用随机数字吗?..........................345
64、Java 中,Comparator 与 Comparable 有什么不同?........ 345
Java IO 和 NIO 的面试题.................................................................... 346
66、在我 Java 程序中,我有三个 socket,我需要多少个线程来处理?
....................................................................................................................346
67、Java 中怎么创建 ByteBuffer?...................................................346
68、Java 中,怎么读写 ByteBuffer ?.............................................346
69、Java 采用的是大端还是小端?.....................................................346
70、ByteBuffer 中的字节序是什么?.................................................346
71、Java 中,直接缓冲区与非直接缓冲器有什么区别?.................347
72、Java 中的内存映射缓存区是什么?.............................................347
73、socket 选项 TCP NO DELAY 是指什么?................................347
74、TCP 协议与 UDP 协议有什么区别?.........................................347
75、Java 中,ByteBuffer 与 StringBuffer 有什么区别?(答案).. 347
Java 最佳实践的面试问题..................................................................... 348
76、Java 中,编写多线程程序的时候你会遵循哪些最佳实践?.....348

第 20 页 共 485 页
77、说出几点 Java 中使用 Collections 的最佳实践......................348
78、说出至少 5 点在 Java 中使用线程的最佳实践。.................... 348
79、说出 5 条 IO 的最佳实践(答案)................................................. 349
80、列出 5 个应该遵循的 JDBC 最佳实践.......................................349
81、说出几条 Java 中方法重载的最佳实践?...................................350
Date、Time 及 Calendar 的面试题.................................................. 350
82、在多线程环境下,SimpleDateFormat 是线程安全的吗?..... 350
83、Java 中如何格式化一个日期?如格式化为 ddMMyyyy 的形式?
....................................................................................................................350
84、Java 中,怎么在格式化的日期中显示时区?.............................351
85、Java 中 java.util.Date 与 java.sql.Date 有什么区别?........ 351
86、Java 中,如何计算两个日期之间的差距?.................................351
87、Java 中,如何将字符串 YYYYMMDD 转换为日期?............. 352
单元测试 JUnit 面试题..........................................................................352
89、如何测试静态方法?(答案)............................................................ 352
90、怎么利用 JUnit 来测试一个方法的异常?................................. 352
91、你使用过哪个单元测试库来测试你的 Java 程序?...................352
92、@Before 和 @BeforeClass 有什么区别?..............................352
编程和代码相关的面试题........................................................................353
93、怎么检查一个字符串只包含数字?解决方案...............................353
94、Java 中如何利用泛型写一个 LRU 缓存?................................. 353
95、写一段 Java 程序将 byte 转换为 long?................................353
95、在不使用 StringBuffer 的前提下,怎么反转一个字符串?....353
97、Java 中,怎么获取一个文件中单词出现的最高频率?.............353
98、如何检查出两个给定的字符串是反序的?...................................354
99、Java 中,怎么打印出一个字符串的所有排列?.........................354
100、Java 中,怎样才能打印出数组中的重复元素?...................... 354
101、Java 中如何将字符串转换为整数?.......................................... 354
102、在没有使用临时变量的情况如何交换两个整数变量的值?.... 355
关于 OOP 和设计模式的面试题...........................................................355
103、接口是什么?为什么要使用接口而不是直接使用具体类?.... 355

第 21 页 共 485 页
104、Java 中,抽象类与接口之间有什么不同?.............................. 355
105、除了单例模式,你在生产环境中还用过什么设计模式?........ 356
106、你能解释一下里氏替换原则吗?...................................................356
107) 什么情况下会违反迪米特法则?为什么会有这个问题?......... 356
108、适配器模式是什么?什么时候使用?........................................ 356
109、什么是“依赖注入”和“控制反转”?为什么有人使用?.... 356
110、抽象类是什么?它与接口有什么区别?你为什么要使用过抽象
类?............................................................................................................357
111、构造器注入和 setter 依赖注入,那种方式更好?..................357
112、依赖注入和工程模式之间有什么不同?.................................... 357
113、适配器模式和装饰器模式有什么区别?.................................... 358
114、适配器模式和代理模式之前有什么不同?................................ 358
115、什么是模板方法模式?................................................................ 358
116、什么时候使用访问者模式?........................................................ 358
117、什么时候使用组合模式?............................................................ 358
118、继承和组合之间有什么不同?.................................................... 359
119、描述 Java 中的重载和重写?.................................................... 359
120、Java 中,嵌套公共静态类与顶级类有什么不同?.................. 359
121、 OOP 中的 组合、聚合和关联有什么区别?..........................359
122、给我一个符合开闭原则的设计模式的例子?............................ 360
123、抽象工厂模式和原型模式之间的区别?.................................... 360
Java 面试中其他各式各样的问题......................................................... 361
125、嵌套静态类与顶级类有什么区别?............................................ 361
126、你能写出一个正则表达式来判断一个字符串是否是一个数字吗?
....................................................................................................................361
127、Java 中,受检查异常 和 不受检查异常的区别?.................. 362
128、Java 中,throw 和 throws 有什么区别................................ 362
129、Java 中,Serializable 与 Externalizable 的区别?............ 362
130、Java 中,DOM 和 SAX 解析器有什么不同?...................... 363
131、说出 JDK 1.7 中的三个新特性?.............................................. 363
132、说出 5 个 JDK 1.8 引入的新特性?........................................ 363

第 22 页 共 485 页
133、Java 中,Maven 和 ANT 有什么区别?............................... 363
Spring 面试题(一)............................................................................................. 364
1、一般问题............................................................................................. 364
1.1、不同版本的 Spring Framework 有哪些主要功能?...............364
1.2、什么是 Spring Framework?.....................................................365
1.3、列举 Spring Framework 的优点。...........................................365
1.4、Spring Framework 有哪些不同的功能?.................................365
1.5、Spring Framework 中有多少个模块,它们分别是什么?.....365
1.6、什么是 Spring 配置文件?......................................................... 368
1.7、Spring 应用程序有哪些不同组件?........................................... 368
1.8、使用 Spring 有哪些方式?......................................................... 368
2、依赖注入(Ioc)............................................................................... 369
2.1、什么是 Spring IOC 容器?......................................................... 369
2.2、什么是依赖注入?..........................................................................369
2.3、可以通过多少种方式完成依赖注入?..........................................370
2.4、区分构造函数注入和 setter 注入。........................................... 370
2.5、spring 中有多少种 IOC 容器?.................................................370
2.6、区分 BeanFactory 和 ApplicationContext。........................371
2.7、列举 IoC 的一些好处。............................................................... 371
2.8、Spring IoC 的实现机制。............................................................371
3、Beans..................................................................................................373
3.1、什么是 spring bean?................................................................. 373
3.2、spring 提供了哪些配置方式?....................................................373
3.3、spring 支持集中 bean scope?................................................375
3.4、spring bean 容器的生命周期是什么样的?............................. 375
3.5、什么是 spring 的内部 bean?.................................................. 376
3.6、什么是 spring 装配......................................................................377
3.7、自动装配有哪些方式?..................................................................377
3.8、自动装配有什么局限?..................................................................378
4、注解..................................................................................................... 378
4.1、什么是基于注解的容器配置..........................................................378

第 23 页 共 485 页
4.2、如何在 spring 中启动注解装配?..............................................379
4.3、@Component, @Controller, @Repository, @Service 有何区
别?............................................................................................................379
4.4、@Required 注解有什么用?...................................................... 380
4.5、@Autowired 注解有什么用?....................................................380
4.6、@Qualifier 注解有什么用?....................................................... 381
4.7、@RequestMapping 注解有什么用?.......................................382
5、数据访问............................................................................................. 382
5.1、spring DAO 有什么用?..............................................................382
5.2、列举 Spring DAO 抛出的异常。............................................... 383
5.3、spring JDBC API 中存在哪些类?............................................. 383
5.4、使用 Spring 访问 Hibernate 的方法有哪些?...................... 384
5.5、列举 spring 支持的事务管理类型..............................................384
5.6、spring 支持哪些 ORM 框架...................................................... 384
6、AOP.....................................................................................................385
6.1、什么是 AOP?............................................................................... 385
6.2、什么是 Aspect?...........................................................................385
6.3、什么是切点(JoinPoint)............................................................ 386
6.4、什么是通知(Advice)?............................................................. 386
6.5、有哪些类型的通知(Advice)?................................................. 387
6.6、指出在 spring aop 中 concern 和 cross-cutting concern 的
不同之处。................................................................................................387
6.7、AOP 有哪些实现方式?............................................................... 387
6.8、Spring AOP and AspectJ AOP 有什么区别?........................388
6.9、如何理解 Spring 中的代理?..................................................... 388
6.10、什么是编织(Weaving)?.......................................................389
7、MVC....................................................................................................389
7.1、Spring MVC 框架有什么用?.....................................................389
7.2、描述一下 DispatcherServlet 的工作流程................................ 389
7.3、介绍一下 WebApplicationContext.......................................... 391
Spring 面试题(二)............................................................................................. 392

第 24 页 共 485 页
1、什么是 spring?.................................................................................. 392
2、使用 Spring 框架的好处是什么?...................................................392
3、Spring 由哪些模块组成?..................................................................392
4、核心容器(应用上下文) 模块。......................................................393
5、BeanFactory – BeanFactory 实现举例。.................................. 393
6、XMLBeanFactory............................................................................. 394
7、解释 AOP 模块...................................................................................394
8、解释 JDBC 抽象和 DAO 模块。...................................................... 394
9、解释对象/关系映射集成模块。....................................................... 394
10、解释 WEB 模块。........................................................................... 394
12、Spring 配置文件............................................................................. 395
13、什么是 Spring IOC 容器?........................................................... 395
14、IOC 的优点是什么?.......................................................................395
15、ApplicationContext 通常的实现是什么?................................... 395
16、Bean 工厂和 Application contexts 有什么区别?................ 396
17、一个 Spring 的应用看起来象什么?............................................ 396
依赖注入....................................................................................................396
18、什么是 Spring 的依赖注入?........................................................ 396
19、有哪些不同类型的 IOC(依赖注入)方式?...............................397
20、哪种依赖注入方式你建议使用,构造器注入,还是 Setter 方法注
入?............................................................................................................397
Spring Beans...........................................................................................397
21.什么是 Spring beans?......................................................................397
22、一个 Spring Bean 定义 包含什么?..........................................398
23、如何给 Spring 容器提供配置元数据?......................................... 398
24、你怎样定义类的作用域?.................................................................398
25、解释 Spring 支持的几种 bean 的作用域。.................................399
26、Spring 框架中的单例 bean 是线程安全的吗?............................399
27、解释 Spring 框架中 bean 的生命周期。.....................................399
28、哪些是重要的 bean 生命周期方法?你能重载它们吗?........... 400
29、什么是 Spring 的内部 bean?......................................................400

第 25 页 共 485 页
30、在 Spring 中如何注入一个 java 集合?......................................400
31、什么是 bean 装配?......................................................................... 401
32、什么是 bean 的自动装配?........................................................... 401
33、解释不同方式的自动装配 。.........................................................401
34.自动装配有哪些局限性 ?..................................................................402
35、你可以在 Spring 中注入一个 null 和一个空字符串吗?.......... 402
Spring 注解.............................................................................................. 402
36、什么是基于 Java 的 Spring 注解配置? 给一些注解的例子...... 403
37、什么是基于注解的容器配置?.........................................................403
38、怎样开启注解装配?.......................................................................403
39、@Required 注解........................................................................... 403
40、@Autowired 注解.........................................................................404
41、@Qualifier 注解............................................................................ 404
Spring 数据访问...................................................................................... 404
42.在 Spring 框架中如何更有效地使用 JDBC?...................................404
43、JdbcTemplate................................................................................ 404
44、Spring 对 DAO 的支持.................................................................. 405
45、使用 Spring 通过什么方式访问 Hibernate?.............................. 405
46、Spring 支持的 ORM.......................................................................405
47.如何通过 HibernateDaoSupport 将 Spring 和 Hibernate 结合起
来?............................................................................................................406
48、Spring 支持的事务管理类型......................................................... 406
49、Spring 框架的事务管理有哪些优点?......................................... 406
50、你更倾向用那种事务管理类型?...................................................407
Spring 面向切面编程(AOP)..............................................................407
51、解释 AOP......................................................................................... 407
52、Aspect 切面....................................................................................407
52、在 Spring AOP 中,关注点和横切关注的区别是什么?..........407
54、连接点...............................................................................................408
55、通知...................................................................................................408
56、切点...................................................................................................408

第 26 页 共 485 页
57、什么是引入?.....................................................................................409
58、什么是目标对象?.............................................................................409
59、什么是代理?.....................................................................................409
60、有几种不同类型的自动代理?.......................................................409
61、什么是织入。什么是织入应用的不同点?...................................409
62、解释基于 XML Schema 方式的切面实现。................................ 410
63、解释基于注解的切面实现...............................................................410
Spring 的 MVC.......................................................................................410
64、什么是 Spring 的 MVC 框架?..................................................... 410
65、DispatcherServlet......................................................................... 410
66、WebApplicationContext............................................................. 411
67、什么是 Spring MVC 框架的控制器?..........................................411
68、@Controller 注解..........................................................................411
69、@RequestMapping 注解............................................................411
微服务 面试题..........................................................................................................411
1、您对微服务有何了解?..................................................................... 411
2、微服务架构有哪些优势?................................................................. 413
3。微服务有哪些特点?......................................................................... 414
4、设计微服务的最佳实践是什么?..................................................... 415
5、微服务架构如何运作?..................................................................... 415
6、微服务架构的优缺点是什么?......................................................... 416
7、单片,SOA 和微服务架构有什么区别?........................................417
8、在使用微服务架构时,您面临哪些挑战?..................................... 418
9、SOA 和微服务架构之间的主要区别是什么?................................418
10、微服务有什么特点?.......................................................................419
11、什么是领域驱动设计?...................................................................419
12、为什么需要域驱动设计(DDD)?..............................................420
13、什么是无所不在的语言?...............................................................420
14、什么是凝聚力?...............................................................................421
15、什么是耦合?...................................................................................421
16、什么是 REST / RESTful 以及它的用途是什么?......................... 421

第 27 页 共 485 页
17、你对 Spring Boot 有什么了解?.................................................. 421
18、什么是 Spring 引导的执行器?.................................................... 422
19、什么是 Spring Cloud?.................................................................422
20、Spring Cloud 解决了哪些问题?.................................................423
21、在 Spring MVC 应用程序中使用 WebMvcTest 注释有什么用处?
....................................................................................................................423
22。你能否给出关于休息和微服务的要点?.......................................424
23、什么是不同类型的微服务测试?...................................................424
24、您对 Distributed Transaction 有何了解?................................ 424
25、什么是 Idempotence 以及它在哪里使用?............................... 425
26、什么是有界上下文?.......................................................................425
27、什么是双因素身份验证?...............................................................425
28、双因素身份验证的凭据类型有哪些?...........................................426
29、什么是客户证书?...........................................................................427
30、PACT 在微服务架构中的用途是什么?........................................427
31、什么是 OAuth?............................................................................. 427
32、康威定律是什么?...........................................................................428
33、合同测试你懂什么?.......................................................................428
34、什么是端到端微服务测试?...........................................................429
35、Container 在微服务中的用途是什么?....................................... 429
36、什么是微服务架构中的 DRY?......................................................430
37、什么是消费者驱动的合同(CDC)?.......................................... 430
38、Web,RESTful API 在微服务中的作用是什么?....................... 431
39、您对微服务架构中的语义监控有何了解?...................................431
40、我们如何进行跨功能测试?...........................................................431
41、我们如何在测试中消除非决定论?...............................................431
42、Mock 或 Stub 有什么区别?........................................................ 432
43、您对 Mike Cohn 的测试金字塔了解多少?................................ 432
44、Docker 的目的是什么?................................................................ 433
45、什么是金丝雀释放?.......................................................................434
46、什么是持续集成(CI)?............................................................... 434

第 28 页 共 485 页
47、什么是持续监测?...........................................................................434
48、架构师在微服务架构中的角色是什么?.......................................434
49、我们可以用微服务创建状态机吗?...............................................435
50、什么是微服务中的反应性扩展?...................................................435
Linux 面试题........................................................................................................... 435
1、绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目
录用什么表示? 切换目录用什么命令?................................................436
2、怎么查看当前进程?怎么执行退出?怎么查看当前路径?......... 436
3、怎么清屏?怎么退出当前命令?怎么执行睡眠?怎么查看当前用户
id?查看指定帮助用什么命令?............................................................436
4、Ls 命令执行什么功能? 可以带哪些参数,有什么区别?......... 437
5、建立软链接(快捷方式),以及硬链接的命令。...............................437
6、目录创建用什么命令?创建文件用什么命令?复制文件用什么命
令?............................................................................................................437
7、查看文件内容有哪些命令可以使用?............................................. 438
8、随意写文件命令?怎么向屏幕输出带空格的字符串,比如”hello
world”?...................................................................................................439
9、终端是哪个文件夹下的哪个文件?黑洞文件是哪个文件夹下的哪个
命令?........................................................................................................439
10、移动文件用哪个命令?改名用哪个命令?...................................439
11、复制文件用哪个命令?如果需要连同文件夹一块复制呢?如果需
要有提示功能呢?....................................................................................440
12、删除文件用哪个命令?如果需要连目录及目录下文件一块删除
呢?删除空文件夹用什么命令?............................................................440
13、Linux 下命令有哪几种可使用的通配符?分别代表什么含义?.440
14、用什么命令对一个文件的内容进行统计?(行号、单词数、字节数)
....................................................................................................................441
15、Grep 命令有什么用? 如何忽略大小写? 如何查找不含该串的行?
....................................................................................................................441
16、Linux 中进程有哪几种状态?在 ps 显示出来的信息中,分别用
什么符号表示的?....................................................................................441

第 29 页 共 485 页
17、怎么使一个命令在后台运行?.........................................................442
18、利用 ps 怎么显示所有的进程? 怎么利用 ps 查看指定进程的信
息?............................................................................................................443
19、哪个命令专门用来查看后台任务?.................................................443
20、把后台任务调到前台执行使用什么命令?把停下的后台任务在后台
执行起来用什么命令?..............................................................................443
21、终止进程用什么命令? 带什么参数?............................................. 444
22、怎么查看系统支持的所有信号?...................................................444
23、搜索文件用什么命令? 格式是怎么样的?..................................... 444
24、查看当前谁在使用该主机用什么命令? 查找自己所在的终端信息
用什么命令?..............................................................................................445
25、使用什么命令查看用过的命令列表?.............................................445
26、使用什么命令查看磁盘使用空间? 空闲空间呢?.......................445
27、使用什么命令查看网络是否连通?.................................................446
28、使用什么命令查看 ip 地址及接口信息?................................... 446
29、查看各类环境变量用什么命令?.....................................................446
30、通过什么命令指定命令提示符?.....................................................446
31、查找命令的可执行文件是去哪查找的? 怎么对其进行设置及添加?
....................................................................................................................447
32、通过什么命令查找执行命令?.........................................................448
33、怎么对命令进行取别名?...............................................................448
34、du 和 df 的定义,以及区别?....................................................448
35、awk 详解。..................................................................................... 449
36、当你需要给命令绑定一个宏或者按键的时候,应该怎么做呢?449
37、如果一个 linux 新手想要知道当前系统支持的所有命令的列表,他
需要怎么做?............................................................................................450
38、如果你的助手想要打印出当前的目录栈,你会建议他怎么做?451
39、你的系统目前有许多正在运行的任务,在不重启机器的条件下,
有什么方法可以把所有正在运行的进程移除呢?................................451
40、bash shell 中的 hash 命令有什么作用?..................................452
41、哪一个 bash 内置命令能够进行数学运算。................................452

第 30 页 共 485 页
42、怎样一页一页地查看一个大文件的内容呢?...............................453
43、数据字典属于哪一个用户的?.......................................................453
44、怎样查看一个 linux 命令的概要与用法?假设你在/bin 目录中偶
然看到一个你从没见过的的命令,怎样才能知道它的作用和用法呢?453
45、使用哪一个命令可以查看自己文件系统的磁盘空间配额呢?...454
Spring Boot 面试题...............................................................................................454
1、什么是 Spring Boot?..................................................................... 454
2、Spring Boot 有哪些优点?............................................................. 455
3、什么是 JavaConfig?....................................................................... 455
4、如何重新加载 Spring Boot 上的更改,而无需重新启动服务器?456
5、Spring Boot 中的监视器是什么?................................................. 457
6、如何在 Spring Boot 中禁用 Actuator 端点安全性?..................457
7、如何在自定义端口上运行 Spring Boot 应用程序?.................... 457
8、什么是 YAML?................................................................................. 458
9、如何实现 Spring Boot 应用程序的安全性?................................ 458
10、如何集成 Spring Boot 和 ActiveMQ?...................................... 458
11、如何使用 Spring Boot 实现分页和排序?.................................. 458
12、什么是 Swagger?你用 Spring Boot 实现了它吗?................ 459
13、什么是 Spring Profiles?.............................................................. 459
14、什么是 Spring Batch?................................................................. 459
15、什么是 FreeMarker 模板?........................................................... 459
16、如何使用 Spring Boot 实现异常处理?...................................... 460
17、您使用了哪些 starter maven 依赖项?...................................... 460
18、什么是 CSRF 攻击?....................................................................... 460
19、什么是 WebSockets?.................................................................. 460
20、什么是 AOP?................................................................................. 461
21、什么是 Apache Kafka?................................................................462
22、我们如何监视所有 Spring Boot 微服务?.................................. 462
Spring Cloud 面试题.............................................................................................462
1、什么是 Spring Cloud?................................................................... 462
2、使用 Spring Cloud 有什么优势?.................................................. 463

第 31 页 共 485 页
3、服务注册和发现是什么意思?Spring Cloud 如何实现?........... 463
4、负载平衡的意义什么?..................................................................... 463
5、什么是 Hystrix?它如何实现容错?............................................... 464
6、什么是 Hystrix 断路器?我们需要它吗?...................................... 465
7、什么是 Netflix Feign?它的优点是什么?.................................... 466
8、什么是 Spring Cloud Bus?我们需要它吗?............................... 468
RabbitMQ 面试题..................................................................................................470
1、什么是 rabbitmq.............................................................................. 470
2、为什么要使用 rabbitmq.................................................................. 471
3、使用 rabbitmq 的场景..................................................................... 471
4、如何确保消息正确地发送至 RabbitMQ? 如何确保消息接收方消
费了消息?................................................................................................471
5.如何避免消息重复投递或重复消费?................................................ 472
6、消息基于什么传输?......................................................................... 473
7、消息如何分发?................................................................................. 473
8、消息怎么路由?................................................................................. 473
9、如何确保消息不丢失?..................................................................... 474
10、使用 RabbitMQ 有什么好处?..................................................... 474
11、RabbitMQ 的集群.......................................................................... 474
12、mq 的缺点........................................................................................475
无标题..........................................................................................错误!未定义书签。
kafka 面试题........................................................................................................... 475
1、如何获取 topic 主题的列表..............................................................475
2、生产者和消费者的命令行是什么?................................................. 476
3、consumer 是推还是拉?................................................................. 476
4、讲讲 kafka 维护消费状态跟踪的方法............................................. 477
5、讲一下主从同步**..............................................................................478
6、为什么需要消息系统,mysql 不能满足需求吗?.........................478
7、Zookeeper 对于 Kafka 的作用是什么?....................................... 479
8、数据传输的事务定义有哪三种?..................................................... 480
9、Kafka 判断一个节点是否还活着有那两个条件?..........................480

第 32 页 共 485 页
10、Kafka 与传统 MQ 消息系统之间有三个关键区别..................... 481
11、讲一讲 kafka 的 ack 的三种机制.................................................. 481
13、消费者故障,出现活锁问题如何解决?.......................................482
14、如何控制消费的位置.......................................................................483
15、kafka 分布式(不是单机)的情况下,如何保证消息的顺序消费?483
16、kafka 的高可用机制是什么?........................................................483
17、kafka 如何减少数据丢失................................................................484
18、kafka 如何不消费重复数据?比如扣款,我们不能重复的扣。484

MyBatis 面试题

1、什么是 Mybatis?

1、Mybatis 是 一 个 半 ORM( 对 象 关 系 映 射 )框 架 ,它 内 部 封 装 了 JDBC,开 发 时

只 需 要 关 注 SQL 语 句 本 身 , 不 需 要 花 费 精 力 去 处 理 加 载 驱 动 、 创 建 连 接 、 创 建

statement 等 繁 杂 的 过 程 。程 序 员 直 接 编 写 原 生 态 sql,可 以 严 格 控 制 sql 执 行 性

能,灵活度高。

2、 MyBatis 可 以 使 用 XML 或 注 解 来 配 置 和 映 射 原 生 信 息 , 将 POJO 映 射 成 数

据 库 中 的 记 录 , 避 免 了 几 乎 所 有 的 JDBC 代 码 和 手 动 设 置 参 数 以 及 获 取 结 果 集 。

3、 通 过 xml 文 件 或 注 解 的 方 式 将 要 执 行 的 各 种 statement 配 置 起 来 , 并 通 过

java 对 象 和 statement 中 sql 的 动 态 参 数 进 行 映 射 生 成 最 终 执 行 的 sql 语 句 ,最

后 由 mybatis 框 架 执 行 sql 并 将 结 果 映 射 为 java 对 象 并 返 回 。 ( 从 执 行 sql 到 返

回 result 的 过 程 ) 。

2、Mybaits 的优点:

第 33 页 共 485 页
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、MyBatis 框架适用场合:

1、 MyBatis 专 注 于 SQL 本 身 , 是 一 个 足 够 灵 活 的 DAO 层 解 决 方 案 。

2、对 性 能 的 要 求 很 高 ,或 者 需 求 变 化 较 多 的 项 目 ,如 互 联 网 项 目 ,MyBatis 将 是

不错的选择。

第 34 页 共 485 页
5、MyBatis 与 Hibernate 有哪些不同?

1、Mybatis 和 hibernate 不 同 ,它 不 完 全 是 一 个 ORM 框 架 ,因 为 MyBatis 需 要

程 序 员 自 己 编 写 Sql 语 句 。

2、 Mybatis 直 接 编 写 原 生 态 sql, 可 以 严 格 控 制 sql 执 行 性 能 , 灵 活 度 高 , 非 常

适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需

求 变 化 要 求 迅 速 输 出 成 果 。 但 是 灵 活 的 前 提 是 mybatis 无 法 做 到 数 据 库 无 关 性 ,

如 果 需 要 实 现 支 持 多 种 数 据 库 的 软 件 ,则 需 要 自 定 义 多 套 sql 映 射 文 件 ,工 作 量 大 。

3、 Hibernate 对 象 /关 系 映 射 能 力 强 , 数 据 库 无 关 性 好 , 对 于 关 系 模 型 要 求 高 的

软 件 , 如 果 用 hibernate 开 发 可 以 节 省 很 多 代 码 , 提 高 效 率 。

6、#{}和${}的区别是什么?

#{} 是 预 编 译 处 理 , ${}是 字 符 串 替 换 。

Mybatis 在 处 理 #{}时 ,会 将 sql 中 的 #{}替 换 为 ?号 ,调 用 PreparedStatement 的

set 方 法 来 赋 值 ;

Mybatis 在 处 理 ${}时 , 就 是 把 ${}替 换 成 变 量 的 值 。

使 用 #{}可 以 有 效 的 防 止 SQL 注 入 , 提 高 系 统 安 全 性 。

7、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

第 1 种 : 通 过 在 查 询 的 sql 语 句 中 定 义 字 段 名 的 别 名 , 让 字 段 名 的 别 名 和 实 体 类

的属性名一致。

第 35 页 共 485 页
<select id=”selectorder” parametertype=”int” resultetype=”
me.gacl.domain.order”>
select order_id id, order_no orderno ,order_price price form
orders where order_id=#{id};
</select>

第 2 种 : 通 过 <resultMap> 来 映 射 字 段 名 和 实 体 类 属 性 名 的 一 一 对 应 的 关 系 。

<select id="getOrder" parameterType="int"


resultMap="orderresultmap">
select * from orders where order_id=#{id}
</select>

<resultMap type=”me.gacl.domain.order” id=”orderresultmap”>


<!–用 id 属性来映射主键字段–>
<id property=”id” column=”order_id”>

<!–用 result 属性来映射非主键字段,property 为实体类属性名,column


为数据表中的属性–>
<result property = “orderno” column =”order_no”/>
<result property=”price” column=”order_price” />
</reslutMap>

8、 模糊查询 like 语句该怎么写?

第 1 种 : 在 Java 代 码 中 添 加 sql 通 配 符 。

string wildcardname = “%smi%”;


list<name> names = mapper.selectlike(wildcardname);

第 36 页 共 485 页
<select id=”selectlike”>
select * from foo where bar like #{value}
</select>

第 2 种 : 在 sql 语 句 中 拼 接 通 配 符 , 会 引 起 sql 注 入

string wildcardname = “smi”;


list<name> names = mapper.selectlike(wildcardname);

<select id=”selectlike”>
select * from foo where bar like "%"#{value}"%"
</select>

9、通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,

请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,

参数不同时,方法能重载吗?

Dao 接 口 即 Mapper 接 口 。接 口 的 全 限 名 ,就 是 映 射 文 件 中 的 namespace 的 值 ;

接 口 的 方 法 名 , 就 是 映 射 文 件 中 Mapper 的 Statement 的 id 值 ; 接 口 方 法 内 的

参 数 , 就 是 传 递 给 sql 的 参 数 。

Mapper 接 口 是 没 有 实 现 类 的 ,当 调 用 接 口 方 法 时 ,接 口 全 限 名 +方 法 名 拼 接 字 符

串 作 为 key 值 , 可 唯 一 定 位 一 个 MapperStatement。 在 Mybatis 中 , 每 一 个

<select>、<insert>、<update>、<delete> 标 签 , 都 会 被 解 析 为 一 个
MapperStatement 对 象 。

第 37 页 共 485 页
举 例 : com.mybatis3.mappers.StudentDao.findStudentById , 可 以 唯

一 找 到 namespace 为 com.mybatis3.mappers.StudentDao 下 面 id 为
findStudentById 的 MapperStatement。

Mapper 接 口 里 的 方 法 ,是 不 能 重 载 的 ,因 为 是 使 用 全 限 名 +方 法 名 的 保 存 和 寻

找 策 略 。Mapper 接 口 的 工 作 原 理 是 JDK 动 态 代 理 ,Mybatis 运 行 时 会 使 用 JDK

动 态 代 理 为 Mapper 接 口 生 成 代 理 对 象 proxy, 代 理 对 象 会 拦 截 接 口 方 法 , 转 而

执 行 MapperStatement 所 代 表 的 sql, 然 后 将 sql 执 行 结 果 返 回 。

10、Mybatis 是如何进行分页的?分页插件的原理是什么?

Mybatis 使 用 RowBounds 对 象 进 行 分 页 , 它 是 针 对 ResultSet 结 果 集 执 行 的 内

存 分 页 ,而 非 物 理 分 页 。可 以 在 sql 内 直 接 书 写 带 有 物 理 分 页 的 参 数 来 完 成 物 理 分

页功能,也可以使用分页插件来完成物理分页。

分 页 插 件 的 基 本 原 理 是 使 用 Mybatis 提 供 的 插 件 接 口 , 实 现 自 定 义 插 件 , 在 插 件

的 拦 截 方 法 内 拦 截 待 执 行 的 sql,然 后 重 写 sql,根 据 dialect 方 言 ,添 加 对 应 的 物

理分页语句和物理分页参数。

11、Mybatis 是如何将 sql 执行结果封装为目标对象并返回的?

都有哪些映射形式?

第 一 种 是 使 用 <resultMap> 标 签 , 逐 一 定 义 数 据 库 列 名 和 对 象 属 性 名 之 间 的 映
射关系。

第 二 种 是 使 用 sql 列 的 别 名 功 能 , 将 列 的 别 名 书 写 为 对 象 属 性 名 。

第 38 页 共 485 页
有 了 列 名 与 属 性 名 的 映 射 关 系 后 , Mybatis 通 过 反 射 创 建 对 象 , 同 时 使 用 反 射 给

对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

12、如何执行批量插入?

首 先 ,创 建 一 个 简 单 的 insert 语 句 :

<insert id=”insertname”>
insert into names (name) values (#{value})
</insert>

然 后 在 java 代 码 中 像 下 面 这 样 执 行 批 处 理 插 入 :

list < string > names = new arraylist();


names.add(“fred”);
names.add(“barney”);
names.add(“betty”);
names.add(“wilma”);
// 注意这里 executortype.batch
sqlsession sqlsession =
sqlsessionfactory.opensession(executortype.batch);
try {
namemapper mapper = sqlsession.getmapper(namemapper.class);
for (string name: names) {
mapper.insertname(name);
}
sqlsession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();

第 39 页 共 485 页
throw e;
}
finally {
sqlsession.close();
}

13、如何获取自动生成的(主)键值?

insert 方 法 总 是 返 回 一 个 int 值 , 这 个 值 代 表 的 是 插 入 的 行 数 。

如 果 采 用 自 增 长 策 略 ,自 动 生 成 的 键 值 在 insert 方 法 执 行 完 后 可 以 被 设 置 到 传 入

的参数对象中。

示例:

<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”


id”>
insert into names (name) values (#{name})
</insert>
name name = new name();
name.setname(“fred”);

int rows = mapper.insertname(name);


// 完成后,id 已经被设置到对象中
system.out.println(“rows inserted = ” + rows);
system.out.println(“generated key value = ” + name.getid());

14、在 mapper 中如何传递多个参数?

第 40 页 共 485 页
1、 第 一 种 :

DAO 层 的 函 数

public UserselectUser(String name,String area);


对应的 xml,#{0}代表接收的是 dao 层中的第一个参数,#{1}代表 dao 层中第二
参数,更多参数一致往后加即可。

<select id="selectUser"resultMap="BaseResultMap">
select * fromuser_user_t whereuser_name = #{0}
anduser_area=#{1}
</select>

2、 第 二 种 : 使 用 @param 注 解 :

public interface usermapper {


user selectuser(@param(“username”) string
username,@param(“hashedpassword”) string hashedpassword);
}

然 后 ,就 可 以 在 xml 像 下 面 这 样 使 用 (推 荐 封 装 为 一 个 map,作 为 单 个 参 数 传 递 给

mapper):

<select id=”selectuser” resulttype=”user”>


select id, username, hashedpassword
from some_table
where username = #{username}
and hashedpassword = #{hashedpassword}
</select>

3、 第 三 种 : 多 个 参 数 封 装 成 map

第 41 页 共 485 页
try {
//映射文件的命名空间.SQL 片段的 ID,就可以调用对应的映射文件中的
SQL
//由于我们的参数超过了两个,而方法中只有一个 Object 参数收集,因此
我们使用 Map 集合来装载我们的参数
Map < String, Object > map = new HashMap();
map.put("start", start);
map.put("end", end);
return sqlSession.selectList("StudentID.pagination", map);
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
throw e;
} finally {
MybatisUtil.closeSqlSession();
}

15、Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?

Mybatis 动 态 sql 可 以 在 Xml 映 射 文 件 内 ,以 标 签 的 形 式 编 写 动 态 sql,执 行 原 理

是 根 据 表 达 式 的 值 完 成 逻 辑 判 断 并 动 态 拼 接 sql 的 功 能 。

Mybatis 提 供 了 9 种 动 态 sql 标 签 :trim | where | set | foreach | if | choose

| when | otherwise | bind 。

16、Xml 映射文件中,
除了常见的 select|insert|updae|delete

标签之外,还有哪些标签?

第 42 页 共 485 页
答 : <resultMap>、<parameterMap>、<sql>、<include>、

<selectKey> , 加 上 动 态 sql 的 9 个 标 签 , 其 中 <sql> 为 sql 片 段 标 签 , 通 过


<include> 标 签 引 入 sql 片 段 , <selectKey> 为 不 支 持 自 增 的 主 键 生 成 策 略 标
签。

17、 Mybatis 的 Xml 映 射 文 件 中 , 不 同 的 Xml 映 射 文 件 , id 是 否 可 以 重 复 ?

不 同 的 Xml 映 射 文 件 , 如 果 配 置 了 namespace, 那 么 id 可 以 重 复 ; 如 果 没 有 配

置 namespace, 那 么 id 不 能 重 复 ;

原 因 就 是 namespace+id 是 作 为 Map <String, MapperStatement> 的 key


使 用 的 , 如 果 没 有 namespace, 就 剩 下 id, 那 么 , id 重 复 会 导 致 数 据 互 相 覆 盖 。

有 了 namespace,自 然 id 就 可 以 重 复 ,namespace 不 同 ,namespace+id 自 然

也就不同。

18、为什么说 Mybatis 是半自动 ORM 映射工具?它与全自动

的区别在哪里?

Hibernate 属 于 全 自 动 ORM 映 射 工 具 , 使 用 Hibernate 查 询 关 联 对 象 或 者 关 联

集 合 对 象 时 , 可 以 根 据 对 象 关 系 模 型 直 接 获 取 , 所 以 它 是 全 自 动 的 。 而 Mybatis

在 查 询 关 联 对 象 或 关 联 集 合 对 象 时 ,需 要 手 动 编 写 sql 来 完 成 ,所 以 ,称 之 为 半 自

动 ORM 映 射 工 具 。

19、 一对一、一对多的关联查询 ?

<mapper namespace="com.lcb.mapping.userMapper">
<!--association 一对一关联查询 -->

第 43 页 共 485 页
<select id="getClass" parameterType="int"
resultMap="ClassesResultMap">
select * from class c,teacher t where c.teacher_id=t.t_id and
c.c_id=#{id}
</select>

<resultMap type="com.lcb.user.Classes" id="ClassesResultMap">


<!-- 实体类的字段名和数据表的字段名映射 -->
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher"
javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap>

<!--collection 一对多关联查询 -->


<select id="getClass2" parameterType="int"
resultMap="ClassesResultMap2">
select * from class c,teacher t,student s where c.teacher_id=t.t_id
and c.c_id=s.class_id and c.c_id=#{id}
</select>

<resultMap type="com.lcb.user.Classes" id="ClassesResultMap2">


<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher"
javaType="com.lcb.user.Teacher">
<id property="id" column="t_id"/>

第 44 页 共 485 页
<result property="name" column="t_name"/>
</association>

<collection property="student"
ofType="com.lcb.user.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
</mapper>

20、MyBatis 实现一对一有几种方式?具体怎么操作的?

有 联 合 查 询 和 嵌 套 查 询 ,联 合 查 询 是 几 个 表 联 合 查 询 ,只 查 询 一 次 , 通 过 在

resultMap 里 面 配 置 association 节 点 配 置 一 对 一 的 类 就 可 以 完 成 ;

嵌 套 查 询 是 先 查 一 个 表 ,根 据 这 个 表 里 面 的 结 果 的 外 键 id,去 再 另 外 一 个 表 里 面

查 询 数 据 ,也 是 通 过 association 配 置 ,但 另 外 一 个 表 的 查 询 通 过 select 属 性 配 置 。

21、MyBatis 实现一对多有几种方式,怎么操作的?

有 联 合 查 询 和 嵌 套 查 询 。 联 合 查 询 是 几 个 表 联 合 查 询 ,只 查 询 一 次 ,通 过 在

resultMap 里 面 的 collection 节 点 配 置 一 对 多 的 类 就 可 以 完 成 ; 嵌 套 查 询 是 先 查

一 个 表 ,根 据 这 个 表 里 面 的 结 果 的 外 键 id,去 再 另 外 一 个 表 里 面 查 询 数 据 ,也 是 通 过

配 置 collection,但 另 外 一 个 表 的 查 询 通 过 select 节 点 配 置 。

第 45 页 共 485 页
22、Mybatis 是否支持延迟加载?如果支持,它的实现原理是

什么?

答 : Mybatis 仅 支 持 association 关 联 对 象 和 collection 关 联 集 合 对 象 的 延 迟 加

载 , association 指 的 就 是 一 对 一 ,collection 指 的 就 是 一 对 多 查 询 。 在 Mybatis

配 置 文 件 中 , 可 以 配 置 是 否 启 用 延 迟 加 载 lazyLoadingEnabled=true|false。

它 的 原 理 是 , 使 用 CGLIB 创 建 目 标 对 象 的 代 理 对 象 , 当 调 用 目 标 方 法 时 , 进 入 拦

截 器 方 法 , 比 如 调 用 a.getB().getName(), 拦 截 器 invoke()方 法 发 现 a.getB()是

null 值 , 那 么 就 会 单 独 发 送 事 先 保 存 好 的 查 询 关 联 B 对 象 的 sql, 把 B 查 询 上 来 ,

然 后 调 用 a.setB(b),于 是 a 的 对 象 b 属 性 就 有 值 了 ,接 着 完 成 a.getB().getName()

方法的调用。这就是延迟加载的基本原理。

当 然 了 , 不 光 是 Mybatis, 几 乎 所 有 的 包 括 Hibernate, 支 持 延 迟 加 载 的 原 理 都

是一样的。

23、Mybatis 的一级、二级缓存:

1) 一 级 缓 存 : 基 于 PerpetualCache 的 HashMap 本 地 缓 存 , 其 存 储 作 用 域 为

Session, 当 Session flush 或 close 之 后 , 该 Session 中 的 所 有 Cache 就

将清空,默认打开一级缓存。

2)二 级 缓 存 与 一 级 缓 存 其 机 制 相 同 ,默 认 也 是 采 用 PerpetualCache,HashMap

存 储 , 不 同 在 于 其 存 储 作 用 域 为 Mapper(Namespace), 并 且 可 自 定 义 存 储 源 ,

如 Ehcache。 默 认 不 打 开 二 级 缓 存 , 要 开 启 二 级 缓 存 , 使 用 二 级 缓 存 属 性 类 需 要

实 现 Serializable 序 列 化 接 口 (可 用 来 保 存 对 象 的 状 态 ),可 在 它 的 映 射 文 件 中 配 置

<cache/> ;

第 46 页 共 485 页
3) 对 于 缓 存 数 据 更 新 机 制 , 当 某 一 个 作 用 域 (一 级 缓 存 Session/二 级 缓 存

Namespaces)的 进 行 了 C/U/D 操 作 后 ,默 认 该 作 用 域 下 所 有 select 中 的 缓 存 将

被 clear。

24、什么是 MyBatis 的接口绑定?有哪些实现方式?

接 口 绑 定 ,就 是 在 MyBatis 中 任 意 定 义 接 口 ,然 后 把 接 口 里 面 的 方 法 和 SQL 语 句 绑

定 , 我 们 直 接 调 用 接 口 方 法 就 可 以 ,这 样 比 起 原 来 了 SqlSession 提 供 的 方 法 我 们 可

以有更加灵活的选择和设置。

接 口 绑 定 有 两 种 实 现 方 式 ,一 种 是 通 过 注 解 绑 定 , 就 是 在 接 口 的 方 法 上 面 加 上

@Select、 @Update 等 注 解 , 里 面 包 含 Sql 语 句 来 绑 定 ; 另 外 一 种 就 是 通 过 xml

里 面 写 SQL 来 绑 定 , 在 这 种 情 况 下 ,要 指 定 xml 映 射 文 件 里 面 的 namespace 必 须

为 接 口 的 全 路 径 名 。 当 Sql 语 句 比 较 简 单 时 候 ,用 注 解 绑 定 , 当 SQL 语 句 比 较 复 杂

时 候 ,用 xml 绑 定 ,一 般 用 xml 绑 定 的 比 较 多 。

25、使用 MyBatis 的 mapper 接口调用时有哪些要求?

1、 Mapper 接 口 方 法 名 和 mapper.xml 中 定 义 的 每 个 sql 的 id 相 同 ;

2、 Mapper 接 口 方 法 的 输 入 参 数 类 型 和 mapper.xml 中 定 义 的 每 个 sql 的

parameterType 的 类 型 相 同 ;

3、 Mapper 接 口 方 法 的 输 出 参 数 类 型 和 mapper.xml 中 定 义 的 每 个 sql 的

resultType 的 类 型 相 同 ;

4、 Mapper.xml 文 件 中 的 namespace 即 是 mapper 接 口 的 类 路 径 。

26、Mapper 编写有哪几种方式?

第 47 页 共 485 页
第 一 种 : 接 口 实 现 类 继 承 SqlSessionDaoSupport: 使 用 此 种 方 法 需 要 编 写

mapper 接 口 , mapper 接 口 实 现 类 、 mapper.xml 文 件 。

1、 在 sqlMapConfig.xml 中 配 置 mapper.xml 的 位 置

<mappers>
<mapper resource="mapper.xml 文件的地址" />
<mapper resource="mapper.xml 文件的地址" />
</mappers>

1、 定 义 mapper 接 口

3、 实 现 类 集 成 SqlSessionDaoSupport

mapper 方 法 中 可 以 this.getSqlSession()进 行 数 据 增 删 改 查 。

4、 spring 配 置

<bean id=" " class="mapper 接口的实现">


<property name="sqlSessionFactory"
ref="sqlSessionFactory"></property>
</bean>

第 二 种 : 使 用 org.mybatis.spring.mapper.MapperFactoryBean :

1、 在 sqlMapConfig.xml 中 配 置 mapper.xml 的 位 置 , 如 果 mapper.xml 和

mappre 接 口 的 名 称 相 同 且 在 同 一 个 目 录 , 这 里 可 以 不 用 配 置

<mappers>
<mapper resource="mapper.xml 文件的地址" />
<mapper resource="mapper.xml 文件的地址" />
</mappers>

2、 定 义 mapper 接 口 :

第 48 页 共 485 页
1、 mapper.xml 中 的 namespace 为 mapper 接 口 的 地 址

2、 mapper 接 口 中 的 方 法 名 和 mapper.xml 中 的 定 义 的 statement 的 id 保 持 一

3、 Spring 中 定 义

<bean id="" class="org.mybatis.spring.mapper.MapperFactoryBean">


<property name="mapperInterface" value="mapper 接口地址" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

第 三 种 : 使 用 mapper 扫 描 器 :

1、 mapper.xml 文 件 编 写 :

mapper.xml 中 的 namespace 为 mapper 接 口 的 地 址 ;

mapper 接 口 中 的 方 法 名 和 mapper.xml 中 的 定 义 的 statement 的 id 保 持 一 致 ;

如 果 将 mapper.xml 和 mapper 接 口 的 名 称 保 持 一 致 则 不 用 在 sqlMapConfig.xml

中进行配置。

2、 定 义 mapper 接 口 :

注 意 mapper.xml 的 文 件 名 和 mapper 的 接 口 名 称 保 持 一 致 , 且 放 在 同 一 个 目 录

3、 配 置 mapper 扫 描 器 :

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="mapper 接口包地址
"></property>
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory"/>
</bean>

第 49 页 共 485 页
4、 使 用 扫 描 器 后 从 spring 容 器 中 获 取 mapper 的 实 现 对 象 。

27、简述 Mybatis 的插件运行原理,以及如何编写一个插件。

答 : Mybatis 仅 可 以 编 写 针 对 ParameterHandler、 ResultSetHandler、

StatementHandler、 Executor 这 4 种 接 口 的 插 件 , Mybatis 使 用 JDK 的 动 态 代

理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这 4 种

接 口 对 象 的 方 法 时 ,就 会 进 入 拦 截 方 法 ,具 体 就 是 InvocationHandler 的 invoke()

方法,当然,只会拦截那些你指定需要拦截的方法。

编 写 插 件 : 实 现 Mybatis 的 Interceptor 接 口 并 复 写 intercept()方 法 , 然 后 在 给

插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文

件中配置你编写的插件。

ZooKeeper 面试题

1. ZooKeeper 面试题?

ZooKeeper 是 一 个 开 放 源 码 的 分 布 式 协 调 服 务 , 它 是 集 群 的 管 理 者 , 监 视 着 集 群

中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用

的接口和性能高效、功能稳定的系统提供给用户。

分 布 式 应 用 程 序 可 以 基 于 Zookeeper 实 现 诸 如 数 据 发 布 /订 阅 、 负 载 均 衡 、 命 名

服 务 、分 布 式 协 调 /通 知 、集 群 管 理 、Master 选 举 、分 布 式 锁 和 分 布 式 队 列 等 功 能 。

Zookeeper 保 证 了 如 下 分 布 式 一 致 性 特 性 :

第 50 页 共 485 页
1、 顺 序 一 致 性

2、 原 子 性

3、 单 一 视 图

4、 可 靠 性

5、 实 时 性 ( 最 终 一 致 性 )

客户端的读请求可以被集群中的任意一台机器处理,如果读请求在节点上注册了

监 听 器 ,这 个 监 听 器 也 是 由 所 连 接 的 zookeeper 机 器 来 处 理 。对 于 写 请 求 ,这 些

请 求 会 同 时 发 给 其 他 zookeeper 机 器 并 且 达 成 一 致 后 ,请 求 才 会 返 回 成 功 。因 此 ,

随 着 zookeeper 的 集 群 机 器 增 多 ,读 请 求 的 吞 吐 会 提 高 但 是 写 请 求 的 吞 吐 会 下 降 。

有 序 性 是 zookeeper 中 非 常 重 要 的 一 个 特 性 , 所 有 的 更 新 都 是 全 局 有 序 的 , 每 个

更 新 都 有 一 个 唯 一 的 时 间 戳 ,这 个 时 间 戳 称 为 zxid( Zookeeper Transaction Id)。

而读请求只会相对于更新有序,也就是读请求的返回结果中会带有这个

zookeeper 最 新 的 zxid。

2. ZooKeeper 提供了什么?

1、 文 件 系 统

2、 通 知 机 制

3. Zookeeper 文件系统

Zookeeper 提 供 一 个 多 层 级 的 节 点 命 名 空 间 ( 节 点 称 为 znode) 。 与 文 件 系 统 不

同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放

数据而目录节点不行。

Zookeeper 为 了 保 证 高 吞 吐 和 低 延 迟 , 在 内 存 中 维 护 了 这 个 树 状 的 目 录 结 构 , 这

种 特 性 使 得 Zookeeper 不 能 用 于 存 放 大 量 的 数 据 , 每 个 节 点 的 存 放 数 据 上 限 为

1M。

第 51 页 共 485 页
4. ZAB 协议?

ZAB 协 议 是 为 分 布 式 协 调 服 务 Zookeeper 专 门 设 计 的 一 种 支 持 崩 溃 恢 复 的 原 子 广

播协议。

ZAB 协 议 包 括 两 种 基 本 的 模 式 : 崩 溃 恢 复 和 消 息 广 播 。

当 整 个 zookeeper 集 群 刚 刚 启 动 或 者 Leader 服 务 器 宕 机 、 重 启 或 者 网 络 故 障 导

致 不 存 在 过 半 的 服 务 器 与 Leader 服 务 器 保 持 正 常 通 信 时 ,所 有 进 程( 服 务 器 )进

入 崩 溃 恢 复 模 式 , 首 先 选 举 产 生 新 的 Leader 服 务 器 , 然 后 集 群 中 Follower 服 务

器 开 始 与 新 的 Leader 服 务 器 进 行 数 据 同 步 , 当 集 群 中 超 过 半 数 机 器 与 该 Leader

服 务 器 完 成 数 据 同 步 之 后 , 退 出 恢 复 模 式 进 入 消 息 广 播 模 式 , Leader 服 务 器 开 始

接收客户端的事务请求生成事物提案来进行事务请求处理。

5. 四种类型的数据节点 Znode

1、 PERSISTENT-持 久 节 点

除 非 手 动 删 除 , 否 则 节 点 一 直 存 在 于 Zookeeper 上

2、 EPHEMERAL-临 时 节 点

临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与

zookeeper 连 接 断 开 不 一 定 会 话 失 效 ) , 那 么 这 个 客 户 端 创 建 的 所 有 临 时 节 点 都

会被移除。

3、 PERSISTENT_SEQUENTIAL-持 久 顺 序 节 点

基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维

护的自增整型数字。

第 52 页 共 485 页
4、 EPHEMERAL_SEQUENTIAL-临 时 顺 序 节 点

基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的

自增整型数字。

6. Zookeeper Watcher 机制 -- 数据变更通知

Zookeeper 允 许 客 户 端 向 服 务 端 的 某 个 Znode 注 册 一 个 Watcher 监 听 , 当 服 务

端 的 一 些 指 定 事 件 触 发 了 这 个 Watcher, 服 务 端 会 向 指 定 客 户 端 发 送 一 个 事 件 通

知 来 实 现 分 布 式 的 通 知 功 能 , 然 后 客 户 端 根 据 Watcher 通 知 状 态 和 事 件 类 型 做 出

业务上的改变。

工作机制:

1、 客 户 端 注 册 watcher

2、 服 务 端 处 理 watcher

3、 客 户 端 回 调 watcher

Watcher 特 性 总 结 :

1、 一 次 性

无 论 是 服 务 端 还 是 客 户 端 ,一 旦 一 个 Watcher 被 触 发 ,Zookeeper 都 会 将 其 从 相

应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频

繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的

压力都非常大。

2、 客 户 端 串 行 执 行

客 户 端 Watcher 回 调 的 过 程 是 一 个 串 行 同 步 的 过 程 。

3、 轻 量

第 53 页 共 485 页
3.1、Watcher 通 知 非 常 简 单 ,只 会 告 诉 客 户 端 发 生 了 事 件 ,而 不 会 说 明 事 件 的 具

体内容。

3.2、 客 户 端 向 服 务 端 注 册 Watcher 的 时 候 , 并 不 会 把 客 户 端 真 实 的 Watcher 对

象 实 体 传 递 到 服 务 端 ,仅 仅 是 在 客 户 端 请 求 中 使 用 boolean 类 型 属 性 进 行 了 标 记 。

4、 watcher event 异 步 发 送 watcher 的 通 知 事 件 从 server 发 送 到 client 是 异 步

的 ,这 就 存 在 一 个 问 题 ,不 同 的 客 户 端 和 服 务 器 之 间 通 过 socket 进 行 通 信 ,由 于

网 络 延 迟 或 其 他 因 素 导 致 客 户 端 在 不 通 的 时 刻 监 听 到 事 件 ,由 于 Zookeeper 本 身

提 供 了 ordering guarantee, 即 客 户 端 监 听 事 件 后 , 才 会 感 知 它 所 监 视 znode

发 生 了 变 化 。 所 以 我 们 使 用 Zookeeper 不 能 期 望 能 够 监 控 到 节 点 每 次 的 变 化 。

Zookeeper 只 能 保 证 最 终 的 一 致 性 , 而 无 法 保 证 强 一 致 性 。

5、 注 册 watcher getData、 exists、 getChildren

6、 触 发 watcher create、 delete、 setData

7、当 一 个 客 户 端 连 接 到 一 个 新 的 服 务 器 上 时 ,watch 将 会 被 以 任 意 会 话 事 件 触 发 。

当 与 一 个 服 务 器 失 去 连 接 的 时 候 , 是 无 法 接 收 到 watch 的 。 而 当 client 重 新 连 接

时 , 如 果 需 要 的 话 , 所 有 先 前 注 册 过 的 watch, 都 会 被 重 新 注 册 。 通 常 这 是 完 全

透 明 的 。 只 有 在 一 个 特 殊 情 况 下 , watch 可 能 会 丢 失 : 对 于 一 个 未 创 建 的 znode

的 exist watch, 如 果 在 客 户 端 断 开 连 接 期 间 被 创 建 了 , 并 且 随 后 在 客 户 端 连 接 上

之 前 又 删 除 了 , 这 种 情 况 下 , 这 个 watch 事 件 可 能 会 被 丢 失 。

7. 客户端注册 Watcher 实现

1、 调 用 getData()/getChildren()/exist()三 个 API, 传 入 Watcher 对 象

2、 标 记 请 求 request, 封 装 Watcher 到 WatchRegistration

3、 封 装 成 Packet 对 象 , 发 服 务 端 发 送 request

4、 收 到 服 务 端 响 应 后 , 将 Watcher 注 册 到 ZKWatcherManager 中 进 行 管 理

5、 请 求 返 回 , 完 成 注 册 。

第 54 页 共 485 页
8. 服务端处理 Watcher 实现

1、 服 务 端 接 收 Watcher 并 存 储

接 收 到 客 户 端 请 求 , 处 理 请 求 判 断 是 否 需 要 注 册 Watcher, 需 要 的 话 将 数 据 节 点

的 节 点 路 径 和 ServerCnxn( ServerCnxn 代 表 一 个 客 户 端 和 服 务 端 的 连 接 , 实 现

了 Watcher 的 process 接 口 , 此 时 可 以 看 成 一 个 Watcher 对 象 ) 存 储 在

WatcherManager 的 WatchTable 和 watch2Paths 中 去 。

2、 Watcher 触 发

以 服 务 端 接 收 到 setData() 事 务 请 求 触 发 NodeDataChanged 事 件 为 例 :

2.1 封 装 WatchedEvent

将 通 知 状 态 ( SyncConnected) 、 事 件 类 型 ( NodeDataChanged) 以 及 节 点 路

径 封 装 成 一 个 WatchedEvent 对 象

2.2 查 询 Watcher

从 WatchTable 中 根 据 节 点 路 径 查 找 Watcher

2.3 没 找 到 ; 说 明 没 有 客 户 端 在 该 数 据 节 点 上 注 册 过 Watcher

2.4 找 到 ;提 取 并 从 WatchTable 和 Watch2Paths 中 删 除 对 应 Watcher( 从 这 里

可 以 看 出 Watcher 在 服 务 端 是 一 次 性 的 , 触 发 一 次 就 失 效 了 )

3、 调 用 process 方 法 来 触 发 Watcher

这 里 process 主 要 就 是 通 过 ServerCnxn 对 应 的 TCP 连 接 发 送 Watcher 事 件 通 知 。

9. 客户端回调 Watcher

第 55 页 共 485 页
客 户 端 SendThread 线 程 接 收 事 件 通 知 , 交 由 EventThread 线 程 回 调 Watcher。

客 户 端 的 Watcher 机 制 同 样 是 一 次 性 的 , 一 旦 被 触 发 后 , 该 Watcher 就 失 效 了 。

10. ACL 权限控制机制

UGO( User/Group/Others)

目 前 在 Linux/Unix 文 件 系 统 中 使 用 ,也 是 使 用 最 广 泛 的 权 限 控 制 方 式 。是 一 种 粗

粒度的文件系统权限控制模式。

ACL( Access Control List) 访 问 控 制 列 表

包括三个方面:

权 限 模 式 ( Scheme)

1、 IP: 从 IP 地 址 粒 度 进 行 权 限 控 制

2、 Digest: 最 常 用 , 用 类 似 于 username:password 的 权 限 标 识 来 进 行 权 限 配

置,便于区分不同应用来进行权限控制

3、World:最 开 放 的 权 限 控 制 方 式 ,是 一 种 特 殊 的 digest 模 式 ,只 有 一 个 权 限 标

识 “ world:anyone”

4、 Super: 超 级 用 户

授权对象

授 权 对 象 指 的 是 权 限 赋 予 的 用 户 或 一 个 指 定 实 体 , 例 如 IP 地 址 或 是 机 器 灯 。

权 限 Permission

1、 CREATE: 数 据 节 点 创 建 权 限 , 允 许 授 权 对 象 在 该 Znode 下 创 建 子 节 点

第 56 页 共 485 页
2、 DELETE: 子 节 点 删 除 权 限 , 允 许 授 权 对 象 删 除 该 数 据 节 点 的 子 节 点

3、 READ: 数 据 节 点 的 读 取 权 限 , 允 许 授 权 对 象 访 问 该 数 据 节 点 并 读 取 其 数 据 内

容或子节点列表等

4、 WRITE: 数 据 节 点 更 新 权 限 , 允 许 授 权 对 象 对 该 数 据 节 点 进 行 更 新 操 作

5、ADMIN:数 据 节 点 管 理 权 限 ,允 许 授 权 对 象 对 该 数 据 节 点 进 行 ACL 相 关 设 置

操作

11. Chroot 特性

3.2.0 版 本 后 ,添 加 了 Chroot 特 性 ,该 特 性 允 许 每 个 客 户 端 为 自 己 设 置 一 个 命 名

空 间 。如 果 一 个 客 户 端 设 置 了 Chroot,那 么 该 客 户 端 对 服 务 器 的 任 何 操 作 , 都 将

会被限制在其自己的命名空间下。

通 过 设 置 Chroot, 能 够 将 一 个 客 户 端 应 用 于 Zookeeper 服 务 端 的 一 颗 子 树 相 对

应 ,在 那 些 多 个 应 用 公 用 一 个 Zookeeper 进 群 的 场 景 下 ,对 实 现 不 同 应 用 间 的 相

互隔离非常有帮助。

12. 会话管理

分 桶 策 略 :将 类 似 的 会 话 放 在 同 一 区 块 中 进 行 管 理 ,以 便 于 Zookeeper 对 会 话 进

行不同区块的隔离处理以及同一区块的统一处理。

分 配 原 则 : 每 个 会 话 的 “ 下 次 超 时 时 间 点 ” ( ExpirationTime)

计算公式:

ExpirationTime_ = currentTime + sessionTimeout

第 57 页 共 485 页
ExpirationTime = (ExpirationTime_ / ExpirationInrerval + 1) *
ExpirationInterval , ExpirationInterval 是指 Zookeeper 会话超时检查时间
间隔,默认 tickTime

13. 服务器角色

Leader

1、 事 务 请 求 的 唯 一 调 度 和 处 理 者 , 保 证 集 群 事 务 处 理 的 顺 序 性

2、 集 群 内 部 各 服 务 的 调 度 者

Follower

1、 处 理 客 户 端 的 非 事 务 请 求 , 转 发 事 务 请 求 给 Leader 服 务 器

2、 参 与 事 务 请 求 Proposal 的 投 票

3、 参 与 Leader 选 举 投 票

Observer

1、 3.0 版 本 以 后 引 入 的 一 个 服 务 器 角 色 , 在 不 影 响 集 群 事 务 处 理 能 力 的 基 础 上 提

升集群的非事务处理能力

2、 处 理 客 户 端 的 非 事 务 请 求 , 转 发 事 务 请 求 给 Leader 服 务 器

3、 不 参 与 任 何 形 式 的 投 票

14. Zookeeper 下 Server 工作状态

服 务 器 具 有 四 种 状 态 ,分 别 是 LOOKING、FOLLOWING、LEADING、OBSERVING。

第 58 页 共 485 页
1、LOOKING:寻 找 Leader 状 态 。当 服 务 器 处 于 该 状 态 时 ,它 会 认 为 当 前 集 群 中

没 有 Leader, 因 此 需 要 进 入 Leader 选 举 状 态 。

2、 FOLLOWING: 跟 随 者 状 态 。 表 明 当 前 服 务 器 角 色 是 Follower。

3、 LEADING: 领 导 者 状 态 。 表 明 当 前 服 务 器 角 色 是 Leader。

4、 OBSERVING: 观 察 者 状 态 。 表 明 当 前 服 务 器 角 色 是 Observer。

15. 数据同步

整 个 集 群 完 成 Leader 选 举 之 后 , Learner( Follower 和 Observer 的 统 称 ) 回 向

Leader 服 务 器 进 行 注 册 。 当 Learner 服 务 器 想 Leader 服 务 器 完 成 注 册 后 , 进 入

数据同步环节。

数据同步流程:(均以消息传递的方式进行)

Learner 向 Learder 注 册

数据同步

同步确认

Zookeeper 的 数 据 同 步 通 常 分 为 四 类 :

1、 直 接 差 异 化 同 步 ( DIFF 同 步 )

2、 先 回 滚 再 差 异 化 同 步 ( TRUNC+DIFF 同 步 )

3、 仅 回 滚 同 步 ( TRUNC 同 步 )

4、 全 量 同 步 ( SNAP 同 步 )

在 进 行 数 据 同 步 前 , Leader 服 务 器 会 完 成 数 据 同 步 初 始 化 :

peerLastZxid:

第 59 页 共 485 页
 从 learner 服务器注册时发送的 ACKEPOCH 消息中提取 lastZxid(该
Learner 服务器最后处理的 ZXID)

minCommittedLog:

 Leader 服务器 Proposal 缓存队列 committedLog 中最小 ZXID

maxCommittedLog:

 Leader 服务器 Proposal 缓存队列 committedLog 中最大 ZXID

直 接 差 异 化 同 步 ( DIFF 同 步 )

 场景:peerLastZxid 介于 minCommittedLog 和 maxCommittedLog


之间

先 回 滚 再 差 异 化 同 步 ( TRUNC+DIFF 同 步 )

 场景:当新的 Leader 服务器发现某个 Learner 服务器包含了一条自己没


有的事务记录,那么就需要让该 Learner 服务器进行事务回滚--回滚到 Leader
服务器上存在的,同时也是最接近于 peerLastZxid 的 ZXID

仅 回 滚 同 步 ( TRUNC 同 步 )

第 60 页 共 485 页
 场景:peerLastZxid 大于 maxCommittedLog

全 量 同 步 ( SNAP 同 步 )

 场景一:peerLastZxid 小于 minCommittedLog
 场景二:Leader 服务器上没有 Proposal 缓存队列且 peerLastZxid 不等
于 lastProcessZxid

16. zookeeper 是如何保证事务的顺序一致性的?

zookeeper 采 用 了 全 局 递 增 的 事 务 Id 来 标 识 , 所 有 的 proposal( 提 议 ) 都 在 被

提 出 的 时 候 加 上 了 zxid,zxid 实 际 上 是 一 个 64 位 的 数 字 ,高 32 位 是 epoch( 时

期 ; 纪 元 ; 世 ; 新 时 代 )用 来 标 识 leader 周 期 ,如 果 有 新 的 leader 产 生 出 来 ,epoch

会 自 增 , 低 32 位 用 来 递 增 计 数 。 当 新 产 生 proposal 的 时 候 , 会 依 据 数 据 库 的 两

阶 段 过 程 , 首 先 会 向 其 他 的 server 发 出 事 务 执 行 请 求 , 如 果 超 过 半 数 的 机 器 都 能

执行并且能够成功,那么就会开始执行。

17. 分布式集群中为什么会有 Master?

在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机

器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行

leader 选 举 。

18. zk 节点宕机如何处理?

第 61 页 共 485 页
Zookeeper 本 身 也 是 集 群 ,推 荐 配 置 不 少 于 3 个 服 务 器 。Zookeeper 自 身 也 要 保

证当一个节点宕机时,其他节点会继续提供服务。

如 果 是 一 个 Follower 宕 机 , 还 有 2 台 服 务 器 提 供 访 问 , 因 为 Zookeeper 上 的 数

据是有多个副本的,数据并不会丢失;

如 果 是 一 个 Leader 宕 机 , Zookeeper 会 选 举 出 新 的 Leader。

ZK 集 群 的 机 制 是 只 要 超 过 半 数 的 节 点 正 常 , 集 群 就 能 正 常 提 供 服 务 。 只 有 在 ZK

节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。

所以

3 个 节 点 的 cluster 可 以 挂 掉 1 个 节 点 (leader 可 以 得 到 2 票 >1.5)

2 个 节 点 的 cluster 就 不 能 挂 掉 任 何 1 个 节 点 了 (leader 可 以 得 到 1 票 <=1)

19. zookeeper 负载均衡和 nginx 负载均衡区别

zk 的 负 载 均 衡 是 可 以 调 控 , nginx 只 是 能 调 权 重 , 其 他 需 要 可 控 的 都 需 要 自 己 写

插 件 ; 但 是 nginx 的 吞 吐 量 比 zk 大 很 多 , 应 该 说 按 业 务 选 择 用 哪 种 方 式 。

20. Zookeeper 有哪几种几种部署模式?

部署模式:单机模式、伪集群模式、集群模式。

21. 集群最少要几台机器,集群规则是怎样的?

集 群 规 则 为 2N+1 台 , N>0, 即 3 台 。

22. 集群支持动态添加机器吗?

其 实 就 是 水 平 扩 容 了 , Zookeeper 在 这 方 面 不 太 好 。 两 种 方 式 :

第 62 页 共 485 页
全 部 重 启 :关 闭 所 有 Zookeeper 服 务 ,修 改 配 置 之 后 启 动 。不 影 响 之 前 客 户 端 的

会话。

逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供

服务。这是比较常用的方式。

3.5 版 本 开 始 支 持 动 态 扩 容 。

23. Zookeeper 对节点的 watch 监听通知是永久的吗?为什么

不是永久的?

不 是 。 官 方 声 明 : 一 个 Watch 事 件 是 一 个 一 次 性 的 触 发 器 , 当 被 设 置 了 Watch

的 数 据 发 生 了 改 变 的 时 候 , 则 服 务 器 将 这 个 改 变 发 送 给 设 置 了 Watch 的 客 户 端 ,

以便通知它们。

为什么不是永久的,举个例子,如果服务端变动频繁,而监听的客户端很多情况

下,每次变动都要通知到所有的客户端,给网络和服务器造成很大压力。

一 般 是 客 户 端 执 行 getData(“ /节 点 A” ,true), 如 果 节 点 A 发 生 了 变 更 或 删 除 ,

客 户 端 会 得 到 它 的 watch 事 件 , 但 是 在 之 后 节 点 A 又 发 生 了 变 更 , 而 客 户 端 又 没

有 设 置 watch 事 件 , 就 不 再 给 客 户 端 发 送 。

在实际应用中,很多情况下,我们的客户端不需要知道服务端的每一次变动,我

只要最新的数据即可。

24. Zookeeper 的 java 客户端都有哪些?

java 客 户 端 : zk 自 带 的 zkclient 及 Apache 开 源 的 Curator。

第 63 页 共 485 页
25. chubby 是什么,和 zookeeper 比你怎么看?

chubby 是 google 的 , 完 全 实 现 paxos 算 法 , 不 开 源 。 zookeeper 是 chubby

的 开 源 实 现 , 使 用 zab 协 议 , paxos 算 法 的 变 种 。

26. 说几个 zookeeper 常用的命令。

常 用 命 令 : ls get set create delete 等 。

27. ZAB 和 Paxos 算法的联系与区别?

相同点:

1、两 者 都 存 在 一 个 类 似 于 Leader 进 程 的 角 色 ,由 其 负 责 协 调 多 个 Follower 进 程

的运行

2、Leader 进 程 都 会 等 待 超 过 半 数 的 Follower 做 出 正 确 的 反 馈 后 ,才 会 将 一 个 提

案进行提交

3、 ZAB 协 议 中 , 每 个 Proposal 中 都 包 含 一 个 epoch 值 来 代 表 当 前 的 Leader

周 期 , Paxos 中 名 字 为 Ballot

不同点:

ZAB 用 来 构 建 高 可 用 的 分 布 式 数 据 主 备 系 统 ( Zookeeper) , Paxos 是 用 来 构 建

分布式一致性状态机系统。

28. Zookeeper 的典型应用场景

第 64 页 共 485 页
Zookeeper 是 一 个 典 型 的 发 布 /订 阅 模 式 的 分 布 式 数 据 管 理 与 协 调 框 架 ,开 发 人 员

可以使用它来进行分布式数据的发布和订阅。

通 过 对 Zookeeper 中 丰 富 的 数 据 节 点 进 行 交 叉 使 用 , 配 合 Watcher 事 件 通 知 机

制,可以非常方便的构建一系列分布式应用中年都会涉及的核心功能,如:

1、 数 据 发 布 /订 阅

2、 负 载 均 衡

3、 命 名 服 务

4、 分 布 式 协 调 /通 知

5、 集 群 管 理

6、 Master 选 举

7、 分 布 式 锁

8、 分 布 式 队 列

1. 数 据 发 布 /订 阅

介绍

数 据 发 布 /订 阅 系 统 , 即 所 谓 的 配 置 中 心 , 顾 名 思 义 就 是 发 布 者 发 布 数 据 供 订 阅 者

进行数据订阅。

目的

动态获取数据(配置信息)

实现数据(配置信息)的集中式管理和数据的动态更新

设计模式

Push 模 式

Pull 模 式

第 65 页 共 485 页
数据(配置信息)特性

1、 数 据 量 通 常 比 较 小

2、 数 据 内 容 在 运 行 时 会 发 生 动 态 更 新

3、 集 群 中 各 机 器 共 享 , 配 置 一 致

如:机器列表信息、运行时开关配置、数据库配置信息等

基 于 Zookeeper 的 实 现 方 式

 数据存储:将数据(配置信息)存储到 Zookeeper 上的一个数据节点


 数据获取:应用在启动初始化节点从 Zookeeper 数据节点读取数据,并
在该节点上注册一个数据变更 Watcher
 数据变更:当变更数据时,更新 Zookeeper 对应节点数据,Zookeeper
会将数据变更通知发到各客户端,客户端接到通知后重新读取变更后的数据即
可。

2. 负 载 均 衡

zk 的 命 名 服 务

命 名 服 务 是 指 通 过 指 定 的 名 字 来 获 取 资 源 或 者 服 务 的 地 址 ,利 用 zk 创 建 一 个 全 局

的路径,这个路径就可以作为一个名字,指向集群中的集群,提供的服务的地址,

或者一个远程的对象等等。

分布式通知和协调

对于系统调度来说:操作人员发送通知实际是通过控制台改变某个节点的状态,

然 后 zk 将 这 些 变 化 发 送 给 注 册 了 这 个 节 点 的 watcher 的 所 有 客 户 端 。

第 66 页 共 485 页
对于执行情况汇报:每个工作进程都在某个目录下创建一个临时节点。并携带工

作的进度数据,这样汇总的进程可以监控目录子节点的变化获得工作进度的实时

的全局情况。

zk 的 命 名 服 务 ( 文 件 系 统 )

命 名 服 务 是 指 通 过 指 定 的 名 字 来 获 取 资 源 或 者 服 务 的 地 址 ,利 用 zk 创 建 一 个 全 局

的路径,即是唯一的路径,这个路径就可以作为一个名字,指向集群中的集群,

提供的服务的地址,或者一个远程的对象等等。

zk 的 配 置 管 理 ( 文 件 系 统 、 通 知 机 制 )

程 序 分 布 式 的 部 署 在 不 同 的 机 器 上 ,将 程 序 的 配 置 信 息 放 在 zk 的 znode 下 ,当 有

配 置 发 生 改 变 时 ,也 就 是 znode 发 生 变 化 时 ,可 以 通 过 改 变 zk 中 某 个 目 录 节 点 的

内 容 , 利 用 watcher 通 知 给 各 个 客 户 端 , 从 而 更 改 配 置 。

Zookeeper 集 群 管 理 ( 文 件 系 统 、 通 知 机 制 )

所 谓 集 群 管 理 无 在 乎 两 点 : 是 否 有 机 器 退 出 和 加 入 、 选 举 master。

对于第一点,所有机器约定在父目录下创建临时目录节点,然后监听父目录节点

的 子 节 点 变 化 消 息 。一 旦 有 机 器 挂 掉 ,该 机 器 与 zookeeper 的 连 接 断 开 ,其 所 创

建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于

是,所有人都知道:它上船了。

新 机 器 加 入 也 是 类 似 ,所 有 机 器 收 到 通 知 :新 兄 弟 目 录 加 入 ,highcount 又 有 了 ,

对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选

取 编 号 最 小 的 机 器 作 为 master 就 好 。

Zookeeper 分 布 式 锁 ( 文 件 系 统 、 通 知 机 制 )

有 了 zookeeper 的 一 致 性 文 件 系 统 , 锁 的 问 题 变 得 容 易 。 锁 服 务 可 以 分 为 两 类 ,

一个是保持独占,另一个是控制时序。

第 67 页 共 485 页
对 于 第 一 类 ,我 们 将 zookeeper 上 的 一 个 znode 看 作 是 一 把 锁 ,通 过 createznode

的 方 式 来 实 现 。所 有 客 户 端 都 去 创 建 /distribute_lock 节 点 ,最 终 成 功 创 建 的 那

个 客 户 端 也 即 拥 有 了 这 把 锁 。用 完 删 除 掉 自 己 创 建 的 distribute_lock 节 点 就 释 放

出锁。

对 于 第 二 类 , /distribute_lock 已 经 预 先 存 在 ,所 有 客 户 端 在 它 下 面 创 建 临 时 顺

序 编 号 目 录 节 点 , 和 选 master 一 样 , 编 号 最 小 的 获 得 锁 , 用 完 删 除 , 依 次 方 便 。

Zookeeper 队 列 管 理 ( 文 件 系 统 、 通 知 机 制 )

两种类型的队列:

1、同 步 队 列 ,当 一 个 队 列 的 成 员 都 聚 齐 时 ,这 个 队 列 才 可 用 ,否 则 一 直 等 待 所 有

成员到达。

2、 队 列 按 照 FIFO 方 式 进 行 入 队 和 出 队 操 作 。

第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。

第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按

编 号 。 在 特 定 的 目 录 下 创 建 PERSISTENT_SEQUENTIAL 节 点 , 创 建 成 功 时

Watcher 通 知 等 待 的 队 列 , 队 列 删 除 序 列 号 最 小 的 节 点 用 以 消 费 。 此 场 景 下

Zookeeper 的 znode 用 于 消 息 存 储 ,znode 存 储 的 数 据 就 是 消 息 队 列 中 的 消 息 内

容 , SEQUENTIAL 序 列 号 就 是 消 息 的 编 号 , 按 序 取 出 即 可 。 由 于 创 建 的 节 点 是 持

久化的,所以不必担心队列消息的丢失问题。

Dubbo 面试题

1、为什么要用 Dubbo?

第 68 页 共 485 页
随着服务化的进一步发展,服务越来越多,服务之间的调用和依赖关系也越来越

复 杂 , 诞 生 了 面 向 服 务 的 架 构 体 系 (SOA),

也因此衍生出了一系列相应的技术,如对服务提供、服务调用、连接处理、通信

协议、序列化方式、服务发现、服务路由、日志输出等行为进行封装的服务框架。

就 这 样 为 分 布 式 系 统 的 服 务 治 理 框 架 就 出 现 了 , Dubbo 也 就 这 样 产 生 了 。

2、Dubbo 的整体架构设计有哪些分层?

接 口 服 务 层( Service):该 层 与 业 务 逻 辑 相 关 ,根 据 provider 和 consumer 的

业务设计对应的接口和实现

配 置 层( Config):对 外 配 置 接 口 ,以 ServiceConfig 和 ReferenceConfig 为

中心

服 务 代 理 层( Proxy):服 务 接 口 透 明 代 理 ,生 成 服 务 的 客 户 端 Stub 和 服 务 端

的 Skeleton, 以 ServiceProxy 为 中 心 , 扩 展 接 口 为 ProxyFactory

服 务 注 册 层 ( Registry) : 封 装 服 务 地 址 的 注 册 和 发 现 , 以 服 务 URL 为 中 心 ,

扩 展 接 口 为 RegistryFactory、 Registry、 RegistryService

路 由 层 ( Cluster) : 封 装 多 个 提 供 者 的 路 由 和 负 载 均 衡 , 并 桥 接 注 册 中 心 , 以

Invoker 为 中 心 , 扩 展 接 口 为 Cluster、 Directory、 Router 和 LoadBlancce

监 控 层( Monitor): RPC 调 用 次 数 和 调 用 时 间 监 控 ,以 Statistics 为 中 心 , 扩

展 接 口 为 MonitorFactory、 Monitor 和 MonitorService

远 程 调 用 层( Protocal):封 装 RPC 调 用 ,以 Invocation 和 Result 为 中 心 ,

扩 展 接 口 为 Protocal、 Invoker 和 Exporter

第 69 页 共 485 页
信 息 交 换 层 ( Exchange) : 封 装 请 求 响 应 模 式 , 同 步 转 异 步 。 以 Request 和

Response 为 中 心 , 扩 展 接 口 为 Exchanger、 ExchangeChannel、

ExchangeClient 和 ExchangeServer

网 络 传 输 层( Transport):抽 象 mina 和 netty 为 统 一 接 口 ,以 Message 为

中 心 , 扩 展 接 口 为 Channel、 Transporter、 Client、 Server 和 Codec

数 据 序 列 化 层 ( Serialize) : 可 复 用 的 一 些 工 具 , 扩 展 接 口 为 Serialization、

ObjectInput、 ObjectOutput 和 ThreadPool

3、默认使用的是什么通信框架,还有别的选择吗?

默 认 也 推 荐 使 用 netty 框 架 , 还 有 mina。

4、服务调用是阻塞的吗?

默认是阻塞的,可以异步调用,没有返回值的可以这么做。

Dubbo 是 基 于 NIO 的 非 阻 塞 实 现 并 行 调 用 ,客 户 端 不 需 要 启 动 多 线 程 即 可 完 成

并 行 调 用 多 个 远 程 服 务 , 相 对 多 线 程 开 销 较 小 , 异 步 调 用 会 返 回 一 个 Future 对

象。

5、一般使用什么注册中心?还有别的选择吗?

推 荐 使 用 Zookeeper 作 为 注 册 中 心 , 还 有 Redis、 Multicast、 Simple 注 册 中

心,但不推荐。

第 70 页 共 485 页
6、默认使用什么序列化框架,你知道的还有哪些?

推 荐 使 用 Hessian 序 列 化 , 还 有 Duddo、 FastJson、 Java 自 带 序 列 化 。

7、服务提供者能实现失效踢出是什么原理?

服 务 失 效 踢 出 基 于 zookeeper 的 临 时 节 点 原 理 。

8、服务上线怎么不影响旧版本?

采用多版本开发,不影响旧版本。

9、如何解决服务调用链过长的问题?

可 以 结 合 zipkin 实 现 分 布 式 服 务 追 踪 。

10、说说核心的配置有哪些?

配置 配置说明

dubbo:service 服务配置

dubbo:reference 引用配置

dubbo:protocol 协议配置

dubbo:applicatio 应用配置

第 71 页 共 485 页
dubbo:module 模块配置

dubbo:registry 注册中心配置

dubbo:monitor 监控中心配置

dubbo:provider 提供方配置

dubbo:consumer 消费方配置

dubbo:method 方法配置

dubbo:argument 参数配置

11、Dubbo 推荐用什么协议?

 dubbo://(推荐)
 rmi://
 hessian://
 http://
 webservice://
 thrift://
 memcached://
 redis://
 rest://

12、同一个服务多个注册的情况下可以直连某一个服务吗?

可 以 点 对 点 直 连 , 修 改 配 置 即 可 , 也 可 以 通 过 telnet 直 接 某 个 服 务 。

第 72 页 共 485 页
13、画一画服务注册与发现的流程图?

14、Dubbo 集群容错有几种方案?

集群容错方案 说明

Failover Cluster 失败自动切换,自动重试其它服务器(默认)

Failfast Cluster 快速失败,立即报错,只发起一次调用

Failsafe Cluster 失败安全,出现异常时,直接忽略

Failback Cluster 失败自动恢复,记录失败请求,定时重发

Forking Cluster 并行调用多个服务器,只要一个成功即返回

第 73 页 共 485 页
Broadcast Cluster 广播逐个调用所有提供者,任意一个报错则报错

15、Dubbo 服务降级,失败重试怎么做?

可 以 通 过 dubbo:reference 中 设 置 mock="return null"。 mock 的 值 也 可 以 修

改 为 true,然 后 再 跟 接 口 同 一 个 路 径 下 实 现 一 个 Mock 类 ,命 名 规 则 是 “ 接 口

名 称 +Mock” 后 缀 。 然 后 在 Mock 类 里 实 现 自 己 的 降 级 逻 辑

16、Dubbo 使用过程中都遇到了些什么问题?

在 注 册 中 心 找 不 到 对 应 的 服 务 ,检 查 service 实 现 类 是 否 添 加 了 @service 注 解

无 法 连 接 到 注 册 中 心 ,检 查 配 置 文 件 中 的 对 应 的 测 试 ip 是 否 正 确

17、Dubbo Monitor 实现原理?

Consumer 端 在 发 起 调 用 之 前 会 先 走 filter 链 ; provider 端 在 接 收 到 请 求 时 也 是

先 走 filter 链 , 然 后 才 进 行 真 正 的 业 务 逻 辑 处 理 。

默 认 情 况 下 , 在 consumer 和 provider 的 filter 链 中 都 会 有 Monitorfilter。

1、 MonitorFilter 向 DubboMonitor 发 送 数 据

2、 DubboMonitor 将 数 据 进 行 聚 合 后 ( 默 认 聚 合 1min 中 的 统 计 数 据 ) 暂 存 到

ConcurrentMap<Statistics, AtomicReference> statisticsMap, 然 后 使 用 一 个

含 有 3 个 线 程( 线 程 名 字 :DubboMonitorSendTimer)的 线 程 池 每 隔 1min 钟 ,

调 用 SimpleMonitorService 遍 历 发 送 statisticsMap 中 的 统 计 数 据 ,每 发 送 完 毕

一 个 , 就 重 置 当 前 的 Statistics 的 AtomicReference

3、 SimpleMonitorService 将 这 些 聚 合 数 据 塞 入 BlockingQueue queue 中 ( 队

列 大 写 为 100000)

第 74 页 共 485 页
4、 SimpleMonitorService 使 用 一 个 后 台 线 程 ( 线 程 名 为 :

DubboMonitorAsyncWriteLogThread)将 queue 中 的 数 据 写 入 文 件( 该 线 程 以

死循环的形式来写)

5、 SimpleMonitorService 还 会 使 用 一 个 含 有 1 个 线 程 ( 线 程 名 字 :

DubboMonitorTimer) 的 线 程 池 每 隔 5min 钟 , 将 文 件 中 的 统 计 数 据 画 成 图 表

18、Dubbo 用到哪些设计模式?

Dubbo 框 架 在 初 始 化 和 通 信 过 程 中 使 用 了 多 种 设 计 模 式 , 可 灵 活 控 制 类 加 载 、 权

限控制等功能。

工厂模式

Provider 在 export 服 务 时 ,会 调 用 ServiceConfig 的 export 方 法 。ServiceConfig

中有个字段:

private static final Protocol protocol =


ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtensi
on();

Dubbo 里 有 很 多 这 种 代 码 。 这 也 是 一 种 工 厂 模 式 , 只 是 实 现 类 的 获 取 采 用 了 JDK

SPI 的 机 制 。 这 么 实 现 的 优 点 是 可 扩 展 性 强 , 想 要 扩 展 实 现 , 只 需 要 在 classpath

下 增 加 个 文 件 就 可 以 了 ,代 码 零 侵 入 。另 外 ,像 上 面 的 Adaptive 实 现 ,可 以 做 到

调用时动态决定调用哪个实现,但是由于这种实现采用了动态代理,会造成代码

调试比较麻烦,需要分析出实际调用的实现类。

装饰器模式

Dubbo 在 启 动 和 调 用 阶 段 都 大 量 使 用 了 装 饰 器 模 式 。以 Provider 提 供 的 调 用 链 为

例 , 具 体 的 调 用 链 代 码 是 在 ProtocolFilterWrapper 的 buildInvokerChain 完 成

的 , 具 体 是 将 注 解 中 含 有 group=provider 的 Filter 实 现 , 按 照 order 排 序 , 最

后的调用顺序是:

第 75 页 共 485 页
EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter ->
ExecuteLimitFilter -> TraceFilter -> TimeoutFilter -> MonitorFilter ->
ExceptionFilter

更 确 切 地 说 ,这 里 是 装 饰 器 和 责 任 链 模 式 的 混 合 使 用 。例 如 ,EchoFilter 的 作 用 是

判断是否是回声测试请求,是的话直接返回内容,这是一种责任链的体现。而像

ClassLoaderFilter 则 只 是 在 主 功 能 上 添 加 了 功 能 ,更 改 当 前 线 程 的 ClassLoader,

这是典型的装饰器模式。

观察者模式

Dubbo 的 Provider 启 动 时 ,需 要 与 注 册 中 心 交 互 ,先 注 册 自 己 的 服 务 ,再 订 阅 自

己 的 服 务 ,订 阅 时 ,采 用 了 观 察 者 模 式 ,开 启 一 个 listener。注 册 中 心 会 每 5 秒 定

时 检 查 是 否 有 服 务 更 新 , 如 果 有 更 新 , 向 该 服 务 的 提 供 者 发 送 一 个 notify 消 息 ,

provider 接 受 到 notify 消 息 后 , 即 运 行 NotifyListener 的 notify 方 法 , 执 行 监

听器方法。

动态代理模式

Dubbo 扩 展 JDK SPI 的 类 ExtensionLoader 的 Adaptive 实 现 是 典 型 的 动 态 代 理

实 现 。 Dubbo 需 要 灵 活 地 控 制 实 现 类 , 即 在 调 用 阶 段 动 态 地 根 据 参 数 决 定 调 用 哪

个实现类,所以采用先生成代理类的方法,能够做到灵活的调用。生成代理类的

代 码 是 ExtensionLoader 的 createAdaptiveExtensionClassCode 方 法 。代 理 类

的 主 要 逻 辑 是 , 获 取 URL 参 数 中 指 定 参 数 的 值 作 为 获 取 实 现 类 的 key。

19、Dubbo 配置文件是如何加载到 Spring 中的?

Spring 容 器 在 启 动 的 时 候 ,会 读 取 到 Spring 默 认 的 一 些 schema 以 及 Dubbo 自

定 义 的 schema, 每 个 schema 都 会 对 应 一 个 自 己 的 NamespaceHandler,

NamespaceHandler 里 面 通 过 BeanDefinitionParser 来 解 析 配 置 信 息 并 转 化 为

需 要 加 载 的 bean 对 象 !

第 76 页 共 485 页
20、Dubbo SPI 和 Java SPI 区别?

JDK SPI

JDK 标 准 的 SPI 会 一 次 性 加 载 所 有 的 扩 展 实 现 ,如 果 有 的 扩 展 吃 实 话 很 耗 时 ,但

也没用上,很浪费资源。

所以只希望加载某个的实现,就不现实了

DUBBO SPI

1, 对 Dubbo 进 行 扩 展 , 不 需 要 改 动 Dubbo 的 源 码

2, 延 迟 加 载 , 可 以 一 次 只 加 载 自 己 想 要 加 载 的 扩 展 实 现 。

3, 增 加 了 对 扩 展 点 IOC 和 AOP 的 支 持 , 一 个 扩 展 点 可 以 直 接 setter 注 入 其

它扩展点。

3, Dubbo 的 扩 展 机 制 能 很 好 的 支 持 第 三 方 IoC 容 器 , 默 认 支 持 Spring Bean。

21、Dubbo 支持分布式事务吗?

目 前 暂 时 不 支 持 , 可 与 通 过 tcc-transaction 框 架 实 现

介 绍 : tcc-transaction 是 开 源 的 TCC 补 偿 性 分 布 式 事 务 框 架

Git 地 址 : https://github.com/changmingxie/tcc-transaction

TCC-Transaction 通 过 Dubbo 隐 式 传 参 的 功 能 , 避 免 自 己 对 业 务 代 码 的 入 侵 。

22、Dubbo 可以对结果进行缓存吗?

第 77 页 共 485 页
为 了 提 高 数 据 访 问 的 速 度 。 Dubbo 提 供 了 声 明 式 缓 存 , 以 减 少 用 户 加 缓 存 的 工 作

<dubbo:reference cache="true" />

其 实 比 普 通 的 配 置 文 件 就 多 了 一 个 标 签 cache="true"

23、服务上线怎么兼容旧版本?

可 以 用 版 本 号 ( version) 过 渡 , 多 个 不 同 版 本 的 服 务 注 册 到 注 册 中 心 , 版 本 号 不

同的服务相互间不引用。这个和服务分组的概念有一点类似。

24、Dubbo 必须依赖的包有哪些?

Dubbo 必 须 依 赖 JDK, 其 他 为 可 选 。

25、Dubbo telnet 命令能做什么?

dubbo 服 务 发 布 之 后 , 我 们 可 以 利 用 telnet 命 令 进 行 调 试 、 管 理 。

Dubbo2.0.5 以 上 版 本 服 务 提 供 端 口 支 持 telnet 命 令

连接服务

telnet localhost 20880 //键 入 回 车 进 入 Dubbo 命 令 模 式 。

查看服务列表

dubbo>ls
com.test.TestService

第 78 页 共 485 页
dubbo>ls com.test.TestService
create
delete
query

 ls (list services and methods)


 ls : 显示服务列表。
 ls -l : 显示服务详细信息列表。
 ls XxxService:显示服务的方法列表。
 ls -l XxxService:显示服务的方法详细信息列表。

26、Dubbo 支持服务降级吗?

以 通 过 dubbo:reference 中 设 置 mock="return null"。 mock 的 值 也 可 以 修 改

为 true,然 后 再 跟 接 口 同 一 个 路 径 下 实 现 一 个 Mock 类 ,命 名 规 则 是 “ 接 口 名

称 +Mock” 后 缀 。 然 后 在 Mock 类 里 实 现 自 己 的 降 级 逻 辑

27、Dubbo 如何优雅停机?

Dubbo 是 通 过 JDK 的 ShutdownHook 来 完 成 优 雅 停 机 的 , 所 以 如 果 使 用

kill -9 PID 等 强 制 关 闭 指 令 ,是 不 会 执 行 优 雅 停 机 的 ,只 有 通 过 kill PID 时 , 才

会执行。

28、Dubbo 和 Dubbox 之间的区别?

Dubbox 是 继 Dubbo 停 止 维 护 后 ,当 当 网 基 于 Dubbo 做 的 一 个 扩 展 项 目 ,如

加 了 服 务 可 Restful 调 用 , 更 新 了 开 源 组 件 等 。

第 79 页 共 485 页
29、Dubbo 和 Spring Cloud 的区别?

根 据 微 服 务 架 构 在 各 方 面 的 要 素 , 看 看 Spring Cloud 和 Dubbo 都 提 供 了 哪 些 支

持。

Dubbo Spring Cloud

服务注册中心 Zookeep Spring Cloud Netflix Eureka

er

服务调用方式 RPC REST API

服务网关 无 Spring Cloud Netflix Zuul

断路器 不完善 Spring Cloud Netflix Hystrix

分布式配置 无 Spring Cloud Config

服务跟踪 无 Spring Cloud Sleuth

消息总线 无 Spring Cloud Bus

数据流 无 Spring Cloud Stream

批量任务 无 Spring Cloud Task

…… …… ……

使 用 Dubbo 构 建 的 微 服 务 架 构 就 像 组 装 电 脑 ,各 环 节 我 们 的 选 择 自 由 度 很 高 ,但

是最终结果很有可能因为一条内存质量不行就点不亮了,总是让人不怎么放心,

但 是 如 果 你 是 一 名 高 手 , 那 这 些 都 不 是 问 题 ; 而 Spring Cloud 就 像 品 牌 机 , 在

Spring Source 的 整 合 下 ,做 了 大 量 的 兼 容 性 测 试 ,保 证 了 机 器 拥 有 更 高 的 稳 定 性 ,

但是如果要在使用非原装组件外的东西,就需要对其基础有足够的了解。

第 80 页 共 485 页
30、你还了解别的分布式框架吗?

别 的 还 有 spring 的 spring cloud, facebook 的 thrift, twitter 的 finagle 等

Elasticsearch 面试题

1、elasticsearch 了解多少,说说你们公司 es 的集群架构,索

引数据大小,分片有多少,以及一些调优手段 。

面试官 : 想 了 解 应 聘 者 之 前 公 司 接 触 的 ES 使 用 场 景 、 规 模 , 有 没 有 做 过 比 较 大
规模的索引设计、规划、调优。

解答 :
如实结合自己的实践场景回答即可。

比 如 : ES 集 群 架 构 13 个 节 点 , 索 引 根 据 通 道 不 同 共 20+索 引 , 根 据 日 期 , 每 日

递 增 20+, 索 引 : 10 分 片 , 每 日 递 增 1 亿 +数 据 ,

每 个 通 道 每 天 索 引 大 小 控 制 : 150GB 之 内 。

仅索引层面调优手段:

1.1、 设 计 阶 段 调 优

1、 根 据 业 务 增 量 需 求 , 采 取 基 于 日 期 模 板 创 建 索 引 , 通 过 roll over API 滚 动 索

引;

2、 使 用 别 名 进 行 索 引 管 理 ;

3、 每 天 凌 晨 定 时 对 索 引 做 force_merge 操 作 , 以 释 放 空 间 ;

第 81 页 共 485 页
4、采 取 冷 热 分 离 机 制 ,热 数 据 存 储 到 SSD,提 高 检 索 效 率 ;冷 数 据 定 期 进 行 shrink

操作,以缩减存储;

5、 采 取 curator 进 行 索 引 的 生 命 周 期 管 理 ;

6、 仅 针 对 需 要 分 词 的 字 段 , 合 理 的 设 置 分 词 器 ;

7、Mapping 阶 段 充 分 结 合 各 个 字 段 的 属 性 ,是 否 需 要 检 索 、是 否 需 要 存 储 等 。… … ..

1.2、 写 入 调 优

1、 写 入 前 副 本 数 设 置 为 0;

2、 写 入 前 关 闭 refresh_interval 设 置 为 -1, 禁 用 刷 新 机 制 ;

3、 写 入 过 程 中 : 采 取 bulk 批 量 写 入 ;

4、 写 入 后 恢 复 副 本 数 和 刷 新 间 隔 ;

5、 尽 量 使 用 自 动 生 成 的 id。

1.3、 查 询 调 优

1、 禁 用 wildcard;

2、 禁 用 批 量 terms( 成 百 上 千 的 场 景 ) ;

3、 充 分 利 用 倒 排 索 引 机 制 , 能 keyword 类 型 尽 量 keyword;

4、 数 据 量 大 时 候 , 可 以 先 基 于 时 间 敲 定 索 引 再 检 索 ;

第 82 页 共 485 页
5、 设 置 合 理 的 路 由 机 制 。

1.4、 其 他 调 优

部署调优,业务调优等。

上面的提及一部分,面试者就基本对你之前的实践或者运维经验有所评估了。

2、elasticsearch 的倒排索引是什么

面试官 : 想 了 解 你 对 基 础 概 念 的 认 知 。
解答 : 通 俗 解 释 一 下 就 可 以 。

传统的我们的检索是通过文章,逐个遍历找到对应关键词的位置。

而 倒 排 索 引 ,是 通 过 分 词 策 略 ,形 成 了 词 和 文 章 的 映 射 关 系 表 ,这 种 词 典 +映 射 表

即为倒排索引。

有 了 倒 排 索 引 , 就 能 实 现 o(1)时间复杂度 的 效 率 检 索 文 章 了 , 极 大 的 提 高 了
检索效率。

第 83 页 共 485 页
学术的解答方式:

倒排索引,相反于一篇文章包含了哪些词,它从词出发,记载了这个词在哪些文

档中出现过,由两部分组成——词典和倒排表。

加分项 : 倒 排 索 引 的 底 层 实 现 是 基 于 : FST( Finite State Transducer) 数 据 结


构。

lucene 从 4+版 本 后 开 始 大 量 使 用 的 数 据 结 构 是 FST。 FST 有 两 个 优 点 :

1、 空 间 占 用 小 。 通 过 对 词 典 中 单 词 前 缀 和 后 缀 的 重 复 利 用 , 压 缩 了 存 储 空 间 ;

2、 查 询 速 度 快 。 O(len(str))的 查 询 时 间 复 杂 度 。

3、elasticsearch 索引数据多了怎么办,如何调优,部署

面试官 : 想 了 解 大 数 据 量 的 运 维 能 力 。
解答 : 索 引 数 据 的 规 划 , 应 在 前 期 做 好 规 划 , 正 所 谓 “ 设 计 先 行 , 编 码 在 后 ” ,
这样才能有效的避免突如其来的数据激增导致集群处理能力不足引发的线上客户

检索或者其他业务受到影响。

如何调优,正如问题 1 所说,这里细化一下:

3.1 动 态 索 引 层 面

基 于 模板+时间+rollover api 滚动 创 建 索 引 , 举 例 : 设 计 阶 段 定 义 : blog 索


引 的 模 板 格 式 为 : blog_index_时 间 戳 的 形 式 , 每 天 递 增 数 据 。

这样做的好处:不至于数据量激增导致单个索引数据量非常大,接近于上线 2 的

32 次 幂 -1, 索 引 存 储 达 到 了 TB+甚 至 更 大 。

一 旦 单 个 索 引 很 大 , 存 储 等 各 种 风 险 也 随 之 而 来 , 所 以 要 提 前 考 虑 +及 早 避 免 。

第 84 页 共 485 页
3.2 存 储 层 面

冷热数据分离存储 ,热 数 据( 比 如 最 近 3 天 或 者 一 周 的 数 据 ),其 余 为 冷 数 据 。
对 于 冷 数 据 不 会 再 写 入 新 数 据 , 可 以 考 虑 定 期 force_merge 加 shrink 压 缩 操 作 ,

节省存储空间和检索效率。

3.3 部 署 层 面

一旦之前没有规划,这里就属于应急策略。

结 合 ES 自 身 的 支 持 动 态 扩 展 的 特 点 ,动 态 新 增 机 器 的 方 式 可 以 缓 解 集 群 压 力 ,注

意 : 如 果 之 前 主 节 点 等 规划合理 , 不 需 要 重 启 集 群 也 能 完 成 动 态 新 增 的 。

4、elasticsearch 是如何实现 master 选举的

面试官 : 想 了 解 ES 集 群 的 底 层 原 理 , 不 再 只 关 注 业 务 层 面 了 。
解答 :
前置前提:

1、 只 有 候 选 主 节 点 ( master: true) 的 节 点 才 能 成 为 主 节 点 。

2、 最 小 主 节 点 数 ( min_master_nodes) 的 目 的 是 防 止 脑 裂 。

这个我看了各种网上分析的版本和源码分析的书籍,云里雾里。

核 对 了 一 下 代 码 ,核 心 入 口 为 findMaster,选 择 主 节 点 成 功 返 回 对 应 Master,否

则 返 回 null。 选 举 流 程 大 致 描 述 如 下 :

第 一 步 : 确 认 候 选 主 节 点 数 达 标 , elasticsearch.yml 设 置 的 值

discovery.zen.minimum_master_nodes;

第 85 页 共 485 页
第 二 步 : 比 较 : 先 判 定 是 否 具 备 master 资 格 , 具 备 候 选 主 节 点 资 格 的 优 先 返 回 ;

若 两 节 点 都 为 候 选 主 节 点 ,则 id 小 的 值 会 主 节 点 。注 意 这 里 的 id 为 string 类 型 。

题 外 话 : 获 取 节 点 id 的 方 法 。

1GET /_cat/nodes?v&h=ip,port,heapPercent,heapMax,id,name
2ip port heapPercent heapMax id name

5、详细描述一下 Elasticsearch 索引文档的过程

面试官 : 想 了 解 ES 的 底 层 原 理 , 不 再 只 关 注 业 务 层 面 了 。
解答 :
这 里 的 索 引 文 档 应 该 理 解 为 文 档 写 入 ES, 创 建 索 引 的 过 程 。

文 档 写 入 包 含 :单 文 档 写 入 和 批 量 bulk 写 入 ,这 里 只 解 释 一 下 :单 文 档 写 入 流 程 。

记住官方文档中的这个图。

第 一 步 :客 户 写 集 群 某 节 点 写 入 数 据 ,发 送 请 求 。( 如 果 没 有 指 定 路 由 /协 调 节 点 ,

请 求 的 节 点 扮 演 路由节点 的 角 色 。 )

第 86 页 共 485 页
第 二 步 : 节 点 1 接 受 到 请 求 后 , 使 用 文 档 _id 来 确 定 文 档 属 于 分 片 0。 请 求 会 被 转

到 另 外 的 节 点 , 假 定 节 点 3。 因 此 分 片 0 的 主 分 片 分 配 到 节 点 3 上 。

第三步:节点 3 在主分片上执行写操作,如果成功,则将请求并行转发到节点 1

和节点 2 的副本分片上,等待结果返回。所有的副本分片都报告成功,节点 3 将

向 协 调 节 点 ( 节 点 1) 报 告 成 功 , 节 点 1 向 请 求 客 户 端 报 告 写 入 成 功 。

如果面试官再问:第二步中的文档获取分片的过程?

回 答 :借 助 路 由 算 法 获 取 ,路 由 算 法 就 是 根 据 路 由 和 文 档 id 计 算 目 标 的 分 片 id 的

过程。

1shard = hash(_routing) % (num_of_primary_shards)

6、详细描述一下 Elasticsearch 搜索的过程?

面试官 : 想 了 解 ES 搜 索 的 底 层 原 理 , 不 再 只 关 注 业 务 层 面 了 。
解答 :
搜 索 拆 解 为 “ query then fetch” 两 个 阶 段 。

query 阶 段 的 目 的 : 定 位 到 位 置 , 但 不 取 。

步骤拆解如下:

1、 假 设 一 个 索 引 数 据 有 5 主 +1 副 本 共 10 分 片 , 一 次 请 求 会 命 中 ( 主 或 者 副 本

分片中)的一个。

2、 每 个 分 片 在 本 地 进 行 查 询 , 结 果 返 回 到 本 地 有 序 的 优 先 队 列 中 。

3、 第 2) 步 骤 的 结 果 发 送 到 协 调 节 点 , 协 调 节 点 产 生 一 个 全 局 的 排 序 列 表 。

fetch 阶 段 的 目 的 : 取 数 据 。

路由节点获取所有文档,返回给客户端。

第 87 页 共 485 页
7、Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法

面试官 : 想 了 解 对 ES 集 群 的 运 维 能 力 。
解答 :

1、 关 闭 缓 存 swap;

2、 堆 内 存 设 置 为 : Min( 节 点 内 存 /2, 32GB) ;

3、 设 置 最 大 文 件 句 柄 数 ;

4、 线 程 池 +队 列 大 小 根 据 业 务 需 要 做 调 整 ;

5、磁 盘 存 储 raid 方 式 — — 存 储 有 条 件 使 用 RAID10,增 加 单 节 点 性 能 以 及 避 免 单

节点存储故障。

8、lucence 内部结构是什么?

面试官 : 想 了 解 你 的 知 识 面 的 广 度 和 深 度 。
解答 :

第 88 页 共 485 页
Lucene 是 有 索 引 和 搜 索 的 两 个 过 程 ,包 含 索 引 创 建 ,索 引 ,搜 索 三 个 要 点 。可 以

基于这个脉络展开一些。

最 近 面 试 一 些 公 司 ,被 问 到 的 关 于 Elasticsearch 和 搜 索 引 擎 相 关 的 问 题 ,以 及 自

己总结的回答。

9、Elasticsearch 是如何实现 Master 选举的?

1、 Elasticsearch 的 选 主 是 ZenDiscovery 模 块 负 责 的 , 主 要 包 含 Ping( 节 点 之

间 通 过 这 个 RPC 来 发 现 彼 此 ) 和 Unicast( 单 播 模 块 包 含 一 个 主 机 列 表 以 控 制 哪

些 节 点 需 要 ping 通 ) 这 两 部 分 ;

第 89 页 共 485 页
2、 对 所 有 可 以 成 为 master 的 节 点 ( node.master: true) 根 据 nodeId 字 典 排

序 ,每 次 选 举 每 个 节 点 都 把 自 己 所 知 道 节 点 排 一 次 序 ,然 后 选 出 第 一 个( 第 0 位 )

节 点 , 暂 且 认 为 它 是 master 节 点 。

3、 如 果 对 某 个 节 点 的 投 票 数 达 到 一 定 的 值 ( 可 以 成 为 master 节 点 数 n/2+1) 并

且 该 节 点 自 己 也 选 举 自 己 , 那 这 个 节 点 就 是 master。 否 则 重 新 选 举 一 直 到 满 足 上

述条件。

4、 补 充 : master 节 点 的 职 责 主 要 包 括 集 群 、 节 点 和 索 引 的 管 理 , 不 负 责 文 档 级

别 的 管 理 ; data 节 点 可 以 关 闭 http 功 能 *。

10、Elasticsearch 中的节点(比如共 20 个),其中的 10 个

选了一个 master,另外 10 个选了另一个 master,怎么办?

1、 当 集 群 master 候 选 数 量 不 小 于 3 个 时 , 可 以 通 过 设 置 最 少 投 票 通 过 数 量

( discovery.zen.minimum_master_nodes) 超 过 所 有 候 选 节 点 一 半 以 上 来 解

决脑裂问题;

2、 当 候 选 数 量 为 两 个 时 , 只 能 修 改 为 唯 一 的 一 个 master 候 选 , 其 他 作 为 data

节点,避免脑裂问题。

11、客户端在和集群连接时,如何选择特定的节点执行请求的?

1、TransportClient 利 用 transport 模 块 远 程 连 接 一 个 elasticsearch 集 群 。它 并

不 加 入 到 集 群 中 ,只 是 简 单 的 获 得 一 个 或 者 多 个 初 始 化 的 transport 地 址 ,并 以 轮

询 的方式与这些地址进行通信。

12、详细描述一下 Elasticsearch 索引文档的过程。

第 90 页 共 485 页
协 调 节 点 默 认 使 用 文 档 ID 参 与 计 算 ( 也 支 持 通 过 routing) , 以 便 为 路 由 提 供 合

适的分片。

shard = hash(document_id) % (num_of_primary_shards)

1、 当 分 片 所 在 的 节 点 接 收 到 来 自 协 调 节 点 的 请 求 后 , 会 将 请 求 写 入 到 Memory

Buffer,然 后 定 时( 默 认 是 每 隔 1 秒 )写 入 到 Filesystem Cache,这 个 从 Momery

Buffer 到 Filesystem Cache 的 过 程 就 叫 做 refresh;

2、当 然 在 某 些 情 况 下 ,存 在 Momery Buffer 和 Filesystem Cache 的 数 据 可 能 会

丢 失 ,ES 是 通 过 translog 的 机 制 来 保 证 数 据 的 可 靠 性 的 。其 实 现 机 制 是 接 收 到 请

求 后 ,同 时 也 会 写 入 到 translog 中 ,当 Filesystem cache 中 的 数 据 写 入 到 磁 盘 中

时 , 才 会 清 除 掉 , 这 个 过 程 叫 做 flush;

3、 在 flush 过 程 中 , 内 存 中 的 缓 冲 将 被 清 除 , 内 容 被 写 入 一 个 新 段 , 段 的 fsync

将 创 建 一 个 新 的 提 交 点 , 并 将 内 容 刷 新 到 磁 盘 , 旧 的 translog 将 被 删 除 并 开 始 一

个 新 的 translog。

4、 flush 触 发 的 时 机 是 定 时 触 发 ( 默 认 30 分 钟 ) 或 者 translog 变 得 太 大 ( 默 认

为 512M) 时 ;

第 91 页 共 485 页
补 充 : 关 于 Lucene 的 Segement:

1、 Lucene 索 引 是 由 多 个 段 组 成 , 段 本 身 是 一 个 功 能 齐 全 的 倒 排 索 引 。

2、 段 是 不 可 变 的 , 允 许 Lucene 将 新 的 文 档 增 量 地 添 加 到 索 引 中 , 而 不 用 从 头 重

建索引。

3、 对 于 每 一 个 搜 索 请 求 而 言 , 索 引 中 的 所 有 段 都 会 被 搜 索 , 并 且 每 个 段 会 消 耗

CPU 的 时 钟 周 、 文 件 句 柄 和 内 存 。 这 意 味 着 段 的 数 量 越 多 , 搜 索 性 能 会 越 低 。

4、为 了 解 决 这 个 问 题 ,Elasticsearch 会 合 并 小 段 到 一 个 较 大 的 段 ,提 交 新 的 合 并

段到磁盘,并删除那些旧的小段。

13、详细描述一下 Elasticsearch 更新和删除文档的过程。

1、 删 除 和 更 新 也 都 是 写 操 作 , 但 是 Elasticsearch 中 的 文 档 是 不 可 变 的 , 因 此 不

能被删除或者改动以展示其变更;

2、磁 盘 上 的 每 个 段 都 有 一 个 相 应 的 .del 文 件 。当 删 除 请 求 发 送 后 ,文 档 并 没 有 真

的 被 删 除 , 而 是 在 .del 文 件 中 被 标 记 为 删 除 。 该 文 档 依 然 能 匹 配 查 询 , 但 是 会 在

结 果 中 被 过 滤 掉 。 当 段 合 并 时 , 在 .del 文 件 中 被 标 记 为 删 除 的 文 档 将 不 会 被 写 入

新段。

3、在 新 的 文 档 被 创 建 时 ,Elasticsearch 会 为 该 文 档 指 定 一 个 版 本 号 ,当 执 行 更 新

时 , 旧 版 本 的 文 档 在 .del 文 件 中 被 标 记 为 删 除 , 新 版 本 的 文 档 被 索 引 到 一 个 新 段 。

旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。

14、详细描述一下 Elasticsearch 搜索的过程。

第 92 页 共 485 页
1、 搜 索 被 执 行 成 一 个 两 阶 段 过 程 , 我 们 称 之 为 Query Then Fetch;

2、在 初 始 查 询 阶 段 时 ,查 询 会 广 播 到 索 引 中 每 一 个 分 片 拷 贝( 主 分 片 或 者 副 本 分

片 ) 。 每 个 分 片 在 本 地 执 行 搜 索 并 构 建 一 个 匹 配 文 档 的 大 小 为 from + size 的

优先队列。

PS:在 搜 索 的 时 候 是 会 查 询 Filesystem Cache 的 ,但 是 有 部 分 数 据 还 在 Memory

Buffer, 所 以 搜 索 是 近 实 时 的 。

3、每 个 分 片 返 回 各 自 优 先 队 列 中 所 有 文 档 的 ID 和 排 序 值 给 协 调 节 点 ,它 合 并

这些值到自己的优先队列中来产生一个全局排序后的结果列表。

4、 接 下 来 就 是 取 回 阶 段 , 协 调 节 点 辨 别 出 哪 些 文 档 需 要 被 取 回 并 向 相 关 的 分 片

提 交 多 个 GET 请 求 。 每 个 分 片 加 载 并 丰 富 文 档 , 如 果 有 需 要 的 话 , 接 着 返 回

文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。

5、 补 充 : Query Then Fetch 的 搜 索 类 型 在 文 档 相 关 性 打 分 的 时 候 参 考 的 是 本 分

片 的 数 据 ,这 样 在 文 档 数 量 较 少 的 时 候 可 能 不 够 准 确 ,DFS Query Then Fetch 增

加 了 一 个 预 查 询 的 处 理 ,询 问 Term 和 Document frequency,这 个 评 分 更 准 确 ,

但是性能会变差。*

第 93 页 共 485 页
15、在 Elasticsearch 中,是怎么根据一个词找到对应的倒排索

引的?

SEE:

 Lucene 的索引文件格式(1)
 Lucene 的索引文件格式(2)

16、Elasticsearch 在部署时,对 Linux 的设置有哪些优化方

法?

1、64 GB 内 存 的 机 器 是 非 常 理 想 的 , 但 是 32 GB 和 16 GB 机 器 也 是 很 常 见 的 。

少 于 8 GB 会 适 得 其 反 。

2、 如 果 你 要 在 更 快 的 CPUs 和 更 多 的 核 心 之 间 选 择 , 选 择 更 多 的 核 心 更 好 。 多

个内核提供的额外并发远胜过稍微快一点点的时钟频率。

3、 如 果 你 负 担 得 起 SSD, 它 将 远 远 超 出 任 何 旋 转 介 质 。 基 于 SSD 的 节 点 , 查

询 和 索 引 性 能 都 有 提 升 。 如 果 你 负 担 得 起 , SSD 是 一 个 好 的 选 择 。

4、即 使 数 据 中 心 们 近 在 咫 尺 ,也 要 避 免 集 群 跨 越 多 个 数 据 中 心 。绝 对 要 避 免 集 群

跨越大的地理距离。

5、 请 确 保 运 行 你 应 用 程 序 的 JVM 和 服 务 器 的 JVM 是 完 全 一 样 的 。 在

Elasticsearch 的 几 个 地 方 , 使 用 Java 的 本 地 序 列 化 。

第 94 页 共 485 页
6、 通 过 设 置 gateway.recover_after_nodes、 gateway.expected_nodes、

gateway.recover_after_time 可 以 在 集 群 重 启 的 时 候 避 免 过 多 的 分 片 交 换 , 这 可

能会让数据恢复从数个小时缩短为几秒钟。

7、 Elasticsearch 默 认 被 配 置 为 使 用 单 播 发 现 , 以 防 止 节 点 无 意 中 加 入 集 群 。 只

有在同一台机器上运行的节点才会自动组成集群。最好使用单播代替组播。

8、 不 要 随 意 修 改 垃 圾 回 收 器 ( CMS) 和 各 个 线 程 池 的 大 小 。

9、 把 你 的 内 存 的 ( 少 于 ) 一 半 给 Lucene( 但 不 要 超 过 32 GB! ) , 通 过

ES_HEAP_SIZE 环 境 变 量 设 置 。

10、 内 存 交 换 到 磁 盘 对 服 务 器 性 能 来 说 是 致 命 的 。 如 果 内 存 交 换 到 磁 盘 上 , 一 个

100 微 秒 的 操 作 可 能 变 成 10 毫 秒 。 再 想 想 那 么 多 10 微 秒 的 操 作 时 延 累 加 起

来 。 不 难 看 出 swapping 对 于 性 能 是 多 么 可 怕 。

11、 Lucene 使 用 了 大 量 的 文 件 。 同 时 , Elasticsearch 在 节 点 和 HTTP 客 户 端

之 间 进 行 通 信 也 使 用 了 大 量 的 套 接 字 。 所 有 这 一 切 都 需 要 足 够 的 文 件 描 述 符 。你

应 该 增 加 你 的 文 件 描 述 符 , 设 置 一 个 很 大 的 值 , 如 64,000。

补充:索引阶段性能提升方法

1、 使 用 批 量 请 求 并 调 整 其 大 小 : 每 次 批 量 数 据 5– 15 MB 大 是 个 不 错 的 起 始 点 。

2、 存 储 : 使 用 SSD

3、段 和 合 并 :Elasticsearch 默 认 值 是 20 MB/s,对 机 械 磁 盘 应 该 是 个 不 错 的 设

置 。如 果 你 用 的 是 SSD,可 以 考 虑 提 高 到 100– 200 MB/s。如 果 你 在 做 批 量 导 入 ,

完全不在意搜索,你可以彻底关掉合并限流。另外还可以增加

第 95 页 共 485 页
index.translog.flush_threshold_size 设 置 , 从 默 认 的 512 MB 到 更 大 一 些 的

值 , 比 如 1 GB, 这 可 以 在 一 次 清 空 触 发 的 时 候 在 事 务 日 志 里 积 累 出 更 大 的 段 。

4、 如 果 你 的 搜 索 结 果 不 需 要 近 实 时 的 准 确 度 , 考 虑 把 每 个 索 引 的

index.refresh_interval 改 到 30s。

5、如 果 你 在 做 大 批 量 导 入 ,考 虑 通 过 设 置 index.number_of_replicas: 0 关 闭 副

本。

17、对于 GC 方面,在使用 Elasticsearch 时要注意什么?

1、 SEE: https://elasticsearch.cn/article/32

2、 倒 排 词 典 的 索 引 需 要 常 驻 内 存 , 无 法 GC, 需 要 监 控 data node 上 segment

memory 增 长 趋 势 。

3、 各 类 缓 存 , field cache, filter cache, indexing cache, bulk queue 等 等 , 要

设 置 合 理 的 大 小 ,并 且 要 应 该 根 据 最 坏 的 情 况 来 看 heap 是 否 够 用 ,也 就 是 各 类 缓

存 全 部 占 满 的 时 候 ,还 有 heap 空 间 可 以 分 配 给 其 他 任 务 吗 ? 避 免 采 用 clear cache

等“自欺欺人”的方式来释放内存。

4、避 免 返 回 大 量 结 果 集 的 搜 索 与 聚 合 。确 实 需 要 大 量 拉 取 数 据 的 场 景 ,可 以 采 用

scan & scroll api 来 实 现 。

5、cluster stats 驻 留 内 存 并 无 法 水 平 扩 展 ,超 大 规 模 集 群 可 以 考 虑 分 拆 成 多 个 集

群 通 过 tribe node 连 接 。

6、 想 知 道 heap 够 不 够 , 必 须 结 合 实 际 应 用 场 景 , 并 对 集 群 的 heap 使 用 情 况 做

持续的监控。

第 96 页 共 485 页
18、Elasticsearch 对于大数据量(上亿量级)的聚合如何实现?

Elasticsearch 提 供 的 首 个 近 似 聚 合 是 cardinality 度 量 。它 提 供 一 个 字 段 的 基 数 ,

即 该 字 段 的 distinct 或 者 unique 值 的 数 目 。 它 是 基 于 HLL 算 法 的 。 HLL 会 先 对

我 们 的 输 入 作 哈 希 运 算 ,然 后 根 据 哈 希 运 算 的 结 果 中 的 bits 做 概 率 估 算 从 而 得 到

基 数 。其 特 点 是 :可 配 置 的 精 度 ,用 来 控 制 内 存 的 使 用( 更 精 确 = 更 多 内 存 );

小的数据集精度是非常高的;我们可以通过配置参数,来设置去重需要的固定内

存使用量。无论数千还是数十亿的唯一值,内存使用量只与你配置的精确度相关。

19、在并发情况下,Elasticsearch 如果保证读写一致?

1、可 以 通 过 版 本 号 使 用 乐 观 并 发 控 制 ,以 确 保 新 版 本 不 会 被 旧 版 本 覆 盖 ,由 应 用

层来处理具体的冲突;

2、 另 外 对 于 写 操 作 , 一 致 性 级 别 支 持 quorum/one/all, 默 认 为 quorum, 即 只

有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络

等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点

上重建。

3、 对 于 读 操 作 , 可 以 设 置 replication 为 sync(默 认 ), 这 使 得 操 作 在 主 分 片 和 副

本 分 片 都 完 成 后 才 会 返 回 ;如 果 设 置 replication 为 async 时 ,也 可 以 通 过 设 置 搜

索 请 求 参 数 _preference 为 primary 来 查 询 主 分 片 , 确 保 文 档 是 最 新 版 本 。

20、如何监控 Elasticsearch 集群状态?

Marvel 让 你 可 以 很 简 单 的 通 过 Kibana 监 控 Elasticsearch。你 可 以 实 时 查 看 你

的集群健康状态和性能,也可以分析过去的集群、索引和节点指标。

第 97 页 共 485 页
21、介绍下你们电商搜索的整体技术架构。

22、介绍一下你们的个性化搜索方案?

SEE 基 于 word2vec 和 Elasticsearch 实 现 个 性 化 搜 索

23、是否了解字典树?

常用字典数据结构如下所示:

第 98 页 共 485 页
Trie 的 核 心 思 想 是 空 间 换 时 间 , 利 用 字 符 串 的 公 共 前 缀 来 降 低 查 询 时 间 的 开 销 以

达到提高效率的目的。它有 3 个基本性质:

1、 根 节 点 不 包 含 字 符 , 除 根 节 点 外 每 一 个 节 点 都 只 包 含 一 个 字 符 。

2、 从 根 节 点 到 某 一 节 点 , 路 径 上 经 过 的 字 符 连 接 起 来 , 为 该 节 点 对 应 的 字 符 串 。

3、 每 个 节 点 的 所 有 子 节 点 包 含 的 字 符 都 不 相 同 。

第 99 页 共 485 页
1、 可 以 看 到 , trie 树 每 一 层 的 节 点 数 是 26^i 级 别 的 。 所 以 为 了 节 省 空 间 , 我 们

还 可 以 用 动 态 链 表 ,或 者 用 数 组 来 模 拟 动 态 。而 空 间 的 花 费 ,不 会 超 过 单 词 数 × 单

词长度。

2、实 现 :对 每 个 结 点 开 一 个 字 母 集 大 小 的 数 组 ,每 个 结 点 挂 一 个 链 表 ,使 用 左 儿

子右兄弟表示法记录这棵树;

3、对 于 中 文 的 字 典 树 ,每 个 节 点 的 子 节 点 用 一 个 哈 希 表 存 储 ,这 样 就 不 用 浪 费 太

大 的 空 间 , 而 且 查 询 速 度 上 可 以 保 留 哈 希 的 复 杂 度 O(1)。

24、拼写纠错是如何实现的?

1、拼 写 纠 错 是 基 于 编 辑 距 离 来 实 现 ;编 辑 距 离 是 一 种 标 准 的 方 法 ,它 用 来 表 示 经

过插入、删除和替换操作从一个字符串转换到另外一个字符串的最小操作步数;

2、 编 辑 距 离 的 计 算 过 程 : 比 如 要 计 算 batyu 和 beauty 的 编 辑 距 离 , 先 创 建 一 个

7× 8 的 表( batyu 长 度 为 5,coffee 长 度 为 6,各 加 2),接 着 ,在 如 下 位 置 填 入

黑色数字。其他格的计算过程是取以下三个值的最小值:

如果最上方的字符等于最左方的字符,则为左上方的数字。否则为左上方的数字

+1。 ( 对 于 3,3 来 说 为 0)

左 方 数 字 +1( 对 于 3,3 格 来 说 为 2)

上 方 数 字 +1( 对 于 3,3 格 来 说 为 2)

最 终 取 右 下 角 的 值 即 为 编 辑 距 离 的 值 3。

第 100 页 共 485 页
对 于 拼 写 纠 错 , 我 们 考 虑 构 造 一 个 度 量 空 间 ( Metric Space) , 该 空 间 内 任 何 关

系满足以下三条基本条件:

d(x,y) = 0 -- 假 如 x 与 y 的 距 离 为 0, 则 x=y

d(x,y) = d(y,x) -- x 到 y 的 距 离 等 同 于 y 到 x 的 距 离

d(x,y) + d(y,z) >= d(x,z) -- 三 角 不 等 式

1、根 据 三 角 不 等 式 ,则 满 足 与 query 距 离 在 n 范 围 内 的 另 一 个 字 符 转 B,其 与 A

的 距 离 最 大 为 d+n, 最 小 为 d-n。

2、 BK 树 的 构 造 就 过 程 如 下 : 每 个 节 点 有 任 意 个 子 节 点 , 每 条 边 有 个 值 表 示 编 辑

距 离 。所 有 子 节 点 到 父 节 点 的 边 上 标 注 n 表 示 编 辑 距 离 恰 好 为 n。比 如 ,我 们 有 棵

第 101 页 共 485 页
树 父 节 点 是 ”book”和 两 个 子 节 点 ”cake”和 ”books”,”book”到 ”books”

的 边 标 号 1, ” book” 到 ” cake” 的 边 上 标 号 4。 从 字 典 里 构 造 好 树 后 , 无 论 何

时你想插入新单词时,计算该单词与根节点的编辑距离,并且查找数值为

d(neweord, root)的 边 。 递 归 得 与 各 子 节 点 进 行 比 较 , 直 到 没 有 子 节 点 , 你 就 可

以 创 建 新 的 子 节 点 并 将 新 单 词 保 存 在 那 。比 如 ,插 入 ”boo”到 刚 才 上 述 例 子 的 树

中 ,我 们 先 检 查 根 节 点 ,查 找 d(“ book”, “ boo”) = 1 的 边 ,然 后 检 查 标 号 为

1 的 边 的 子 节 点 ,得 到 单 词 ”books”。我 们 再 计 算 距 离 d(“ books”, “ boo”)=2,

则 将 新 单 词 插 在 ” books” 之 后 , 边 标 号 为 2。

3、 查 询 相 似 词 如 下 : 计 算 单 词 与 根 节 点 的 编 辑 距 离 d, 然 后 递 归 查 找 每 个 子 节 点

标 号 为 d-n 到 d+n( 包 含 )的 边 。假 如 被 检 查 的 节 点 与 搜 索 单 词 的 距 离 d 小 于 n,

则 返 回 该 节 点 并 继 续 查 询 。比 如 输 入 cape 且 最 大 容 忍 距 离 为 1,则 先 计 算 和 根 的

编 辑 距 离 d(“ book” , “ cape” )=4, 然 后 接 着 找 和 根 节 点 之 间 编 辑 距 离 为 3 到

5 的 , 这 个 就 找 到 了 cake 这 个 节 点 , 计 算 d(“ cake” , “ cape” )=1, 满 足 条 件

所 以 返 回 cake, 然 后 再 找 和 cake 节 点 编 辑 距 离 是 0 到 2 的 , 分 别 找 到 cape 和

cart 节 点 , 这 样 就 得 到 cape 这 个 满 足 条 件 的 结 果 。

Memcached 面试题

1、Memcached 是什么,有什么作用?

第 102 页 共 485 页
Memcached 是 一 个 开 源 的 ,高 性 能 的 内 存 绶 存 软 件 ,从 名 称 上 看 Mem 就 是 内 存

的 意 思 , 而 Cache 就 是 缓 存 的 意 思 。 Memcached 的 作 用 : 通 过 在 事 先 规 划 好 的

内存空间中临时绶存数据库中的各类数据,以达到减少业务对数据库的直接高并

发访问,从而达到提升数据库的访问性能,加速网站集群动态应用服务的能力。

memcached 服 务 在 企 业 集 群 架 构 中 有 哪 些 应 用 场 景 ?

一、作为数据库的前端缓存应用

a、 完 整 缓 存 ( 易 ) , 静 态 缓 存

例如:商品分类(京东),以及商品信息,可事先放在内存里,然后再对外提供

数据访问,这种先放到内存,我们称之为预热,(先把数据存缓存中),用户访

问 时 可 以 只 读 取 memcached 缓 存 , 不 读 取 数 据 库 了 。

b、 执 点 缓 存 ( 难 )

需 要 前 端 web 程 序 配 合 , 只 缓 存 热 点 的 数 据 , 即 缓 存 经 常 被 访 问 的 数 据 。

先预热数据库里的基础数据,然后在动态更新,选读取缓存,如果缓存里没有对

应的数据,程序再去读取数据库,然后程序把读取的新数据放入缓存存储。

特殊说明 :

 如果碰到电商秒杀等高并发的业务,一定要事先预热,或者其它思想实现,
例如:称杀只是获取资格,而不是瞬间秒杀到手商品。

那么什么是获取资格?_
 就是在数据库中,把 0 标成 1.就有资格啦。再慢慢的去领取商品订单。
因为秒杀过程太长会占用服务器资源。
 如果数据更新,同时触发缓存更新,防止给用户过期数据。
 对于持久化缓存存储系统,例如:redis,可以替代一部分数据库的存储,
一些简单的数据业务,投票,统计,好友关注,商品分类等。nosql= not only
sql

第 103 页 共 485 页
二 、 作 业 集 群 的 session 会 话 共 享 存 储 。

 Memcached 服务在不同企业业务应用场景中的工作流程
 当 web 程序需要访问后端数据库获取数据时会优先访问 Memcached 内
存缓存,如果缓存中有数据就直接获取返回前端服务及用户,如果没有数据(没
有命中),在由程序请求后端的数据库服务器,获取到对应的数据后,除了返回
给前端服务及用户数据外,还会把数据放到 Memcached 内存中进行缓存,等
待下次请求被访问,Memcache 内存始终是数据库的挡箭牌,从而大大的减轻
数据库的访问压力,提高整个网站架构的响应速度,提升了用户体验。
 当程序更新,修改或删除数据库中已有的数据时,会同时发送请求通知
Memcached 已经缓存的同一个 ID 内容的旧数据失效,从而保证 Memcache
中数据和数据库中的数据一致。
 如果在高并发场合,除了通知 Memcached 过程的缓存失效外,还会通
过相关机制,使得在用户访问新数据前,通过程序预先把更新过的数据推送到
memcache 中缓存起来,这样可以减少数据库的访问压力,提升 Memcached
中缓存命中率。
 数据库插件可以再写入更新数据库后,自动抛给 MC 缓存起来,自身不
Cache.

2、Memcached 服务分布式集群如何实现?

特 殊 说 明 :Memcached 集 群 和 web 服 务 集 群 是 不 一 样 的 ,所 有 Memcached 的

数 据 总 和 才 是 数 据 库 的 数 据 。 每 台 Memcached 都 是 部 分 数 据 。

( 一 台 memcached 的 数 据 , 就 是 一 部 分 mysql 数 据 库 的 数 据 )

a、 程 序 端 实 现

程 序 加 载 所 有 mc 的 ip 列 表 , 通 过 对 key 做 hash (一 致 性 哈 希 算 法 )

第 104 页 共 485 页
例 如 :web1 (key)===>对 应 A,B,C,D,E,F,G… ..若 干 台 服 务 器 。( 通 过 哈 希 算 法 实

现)

b、 负 载 均 衡 器

通 过 对 key 做 hash (一 致 性 哈 希 算 法 )

一致哈希算法的目的是不但保证每个对象只请求一个对应的服务器,而且当节点

宕机,缓存服务器的更新重新分配比例降到最低。

3、Memcached 服务特点及工作原理是什么?

a、 完 全 基 于 内 存 缓 存 的

b、 节 点 之 间 相 互 独 立

c、 C/S 模 式 架 构 , C 语 言 编 写 , 总 共 2000 行 代 码 。

d、 异 步 I /O 模 型 , 使 用 libevent 作 为 事 件 通 知 机 制 。

e、 被 缓 存 的 数 据 以 key/value 键 值 对 形 式 存 在 的 。

f、 全 部 数 据 存 放 于 内 存 中 , 无 持 久 性 存 储 的 设 计 , 重 启 服 务 器 , 内 存 里 的 数 据 会

丢失。

g、 当 内 存 中 缓 存 的 数 据 容 量 达 到 启 动 时 设 定 的 内 存 值 时 , 就 自 动 使 用 LRU 算 法

删除过期的缓存数据。

h、可 以 对 存 储 的 数 据 设 置 过 期 时 间 ,这 样 过 期 后 的 数 据 自 动 被 清 除 ,服 务 本 身 不

会 监 控 过 期 , 而 是 在 访 问 的 时 候 查 看 key 的 时 间 戳 ,判 断 是 否 过 期 。

j、 memcache 会 对 设 定 的 内 存 进 行 分 块 , 再 把 块 分 组 , 然 后 再 提 供 服 务 。

4、简述 Memcached 内存管理机制原理?

早 期 的 Memcached 内 存 管 理 方 式 是 通 过 malloc 的 分 配 的 内 存 , 使 用 完 后 通 过

free 来 回 收 内 存 , 这 种 方 式 容 易 产 生 内 存 碎 片 , 并 降 低 操 作 系 统 对 内 存 的 管 理 效

率。加重操作系统内存管理器的负担,最坏的情况下,会导致操作系统比

第 105 页 共 485 页
memcached 进 程 本 身 还 慢 , 为 了 解 决 这 个 问 题 , Slab Allocation 内 存 分 配 机 制

就延生了。

现 在 Memcached 利 用 Slab Allocation 机 制 来 分 配 和 管 理 内 存 。

Slab

Allocation 机 制 原 理 是 按 照 预 先 规 定 的 大 小 , 将 分 配 给 memcached 的 内 存 分 割

成 特 定 长 度 的 内 存 块 ( chunk), 再 把 尺 寸 相 同 的 内 存 块 , 分 成 组

( chunks slab class),这 些 内 存 块 不 会 释 放 , 可 以 重 复 利 用 。

而 且 , slab allocator 还 有 重 复 使 用 已 分 配 的 内 存 的 目 的 。 也 就 是 说 , 分 配 到 的

内存不会释放,而是重复利用。

Slab Allocation 的 主 要 术 语

Page

分 配 给 Slab 的 内 存 空 间 ,默 认 是 1MB。分 配 给 Slab 之 后 根 据 slab 的 大 小 切 分 成

chunk。

Chunk

用于缓存记录的内存空间。

SlabClass

特 定 大 小 的 chunk 的 组 。

集群架构方面的问题

第 106 页 共 485 页
5、memcached 是怎么工作的?

Memcached 的 神 奇 来 自 两 阶 段 哈 希 ( two-stage hash) 。 Memcached 就 像 一

个 巨 大 的 、存 储 了 很 多 <key,value>对 的 哈 希 表 。通 过 key,可 以 存 储 或 查 询 任 意

的数据。

客 户 端 可 以 把 数 据 存 储 在 多 台 memcached 上 。 当 查 询 数 据 时 , 客 户 端 首 先 参 考

节 点 列 表 计 算 出 key 的 哈 希 值 ( 阶 段 一 哈 希 ) , 进 而 选 中 一 个 节 点 ; 客 户 端 将 请

求 发 送 给 选 中 的 节 点 , 然 后 memcached 节 点 通 过 一 个 内 部 的 哈 希 算 法 ( 阶 段 二

哈 希 ) , 查 找 真 正 的 数 据 ( item) 。

6、memcached 最大的优势是什么?

Memcached 最 大 的 好 处 就 是 它 带 来 了 极 佳 的 水 平 可 扩 展 性 ,特 别 是 在 一 个 巨 大 的

系 统 中 。 由 于 客 户 端 自 己 做 了 一 次 哈 希 , 那 么 我 们 很 容 易 增 加 大 量 memcached

到 集 群 中 。memcached 之 间 没 有 相 互 通 信 ,因 此 不 会 增 加 memcached 的 负 载 ;

没 有 多 播 协 议 , 不 会 网 络 通 信 量 爆 炸 ( implode) 。 memcached 的 集 群 很 好 用 。

内 存 不 够 了 ? 增 加 几 台 memcached 吧 ; CPU 不 够 用 了 ? 再 增 加 几 台 吧 ; 有 多 余

的内存?在增加几台吧,不要浪费了。

基 于 memcached 的 基 本 原 则 , 可 以 相 当 轻 松 地 构 建 出 不 同 类 型 的 缓 存 架 构 。 除

了 这 篇 FAQ, 在 其 他 地 方 很 容 易 找 到 详 细 资 料 的 。

7、memcached 和 MySQL 的 query

cache 相 比 , 有 什 么 优 缺 点 ?

第 107 页 共 485 页
把 memcached 引 入 应 用 中 , 还 是 需 要 不 少 工 作 量 的 。 MySQL 有 个 使 用 方 便 的

query cache, 可 以 自 动 地 缓 存 SQL 查 询 的 结 果 , 被 缓 存 的 SQL 查 询 可 以 被 反 复

地 快 速 执 行 。Memcached 与 之 相 比 ,怎 么 样 呢 ? MySQL 的 query cache 是 集 中

式 的 , 连 接 到 该 query cache 的 MySQL 服 务 器 都 会 受 益 。

 当您修改表时,MySQL 的 query cache 会立刻被刷新(flush)。存储


一个 memcached item 只需要很少的时间,但是当写操作很频繁时,MySQL
的 query cache 会经常让所有缓存数据都失效。
 在多核 CPU 上,MySQL 的 query cache 会遇到扩展问题(scalability
issues)。在多核 CPU 上,query cache 会增加一个全局锁(global lock), 由
于需要刷新更多的缓存数据,速度会变得更慢。
 在 MySQL 的 query cache 中,我们是不能存储任意的数据的(只能是
SQL 查询结果)。而利用 memcached,我们可以搭建出各种高效的缓存。比
如,可以执行多个独立的查询,构建出一个用户对象(user object),然后将
用户对象缓存到 memcached 中。而 query cache 是 SQL 语句级别的,不可能
做到这一点。在小的网站中,query cache 会有所帮助,但随着网站规模的增加,
query cache 的弊将大于利。
 query cache 能够利用的内存容量受到 MySQL 服务器空闲内存空间的限
制。给数据库服务器增加更多的内存来缓存数据,固然是很好的。但是,有了
memcached,只要您有空闲的内存,都可以用来增加 memcached 集群的规
模,然后您就可以缓存更多的数据。

8、memcached 和服务器的 local cache(比如 PHP 的 APC、

mmap 文件等)相比,有什么优缺点?

首 先 , local cache 有 许 多 与 上 面 (query cache)相 同 的 问 题 。 local cache 能 够 利

用 的 内 存 容 量 受 到 ( 单 台 ) 服 务 器 空 闲 内 存 空 间 的 限 制 。 不 过 , local

第 108 页 共 485 页
cache 有 一 点 比 memcached 和 query cache 都 要 好 , 那 就 是 它 不 但 可 以 存 储 任

意的数据,而且没有网络存取的延迟。

 local cache 的数据查询更快。考虑把 highly common 的数据放在 local


cache 中吧。如果每个页面都需要加载一些数量较少的数据,考虑把它们放在
local

cached 吧。
 local cache 缺少集体失效(group

invalidation)的特性。在 memcached 集群中,删除或更新一个 key 会让所有


的观察者觉察到。但是在 local cache 中, 我们只能通知所有的服务器刷新 cache
(很慢,不具扩展性),或者仅仅依赖缓存超时失效机制。
 local cache 面临着严重的内存限制,这一点上面已经提到。

9、memcached 的 cache 机制是怎样的?

Memcached 主 要 的 cache 机 制 是 LRU( 最 近 最 少 用 ) 算 法 +超 时 失 效 。 当 您 存

数 据 到 memcached 中 , 可 以 指 定 该 数 据 在 缓 存 中 可 以 呆 多 久 Which is forever,

or some time in the future。 如 果 memcached 的 内 存 不 够 用 了 , 过 期 的 slabs

会 优 先 被 替 换 , 接 着 就 轮 到 最 老 的 未 被 使 用 的 slabs。

10、memcached 如何实现冗余机制?

不 实 现 !我 们 对 这 个 问 题 感 到 很 惊 讶 。Memcached 应 该 是 应 用 的 缓 存 层 。它 的 设

计 本 身 就 不 带 有 任 何 冗 余 机 制 。 如 果 一 个 memcached 节 点 失 去 了 所 有 数 据 , 您

应该可以从数据源(比如数据库)再次获取到数据。您应该特别注意,您的应用

应 该 可 以 容 忍 节 点 的 失 效 。 不 要 写 一 些 糟 糕 的 查 询 代 码 , 寄 希 望 于 memcached

第 109 页 共 485 页
来保证一切!如果您担心节点失效会大大加重数据库的负担,那么您可以采取一

些办法。比如您可以增加更多的节点(来减少丢失一个节点的影响),热备节点

( 在 其 他 节 点 down 了 的 时 候 接 管 IP) , 等 等 。

11、memcached 如何处理容错的?

不 处 理 ! 在 memcached 节 点 失 效 的 情 况 下 ,集 群 没 有 必 要 做 任 何 容 错 处 理 。如

果发生了节点失效,应对的措施完全取决于用户。节点失效时,下面列出几种方

案供您选择:

 忽略它! 在失效节点被恢复或替换之前,还有很多其他节点可以应对节
点失效带来的影响。
 把失效的节点从节点列表中移除。做这个操作千万要小心!在默认情况下
(余数式哈希算法),客户端添加或移除节点,会导致所有的缓存数据不可用!
因为哈希参照的节点列表变化了,大部分 key 会因为哈希值的改变而被映射到
(与原来)不同的节点上。
 启动热备节点,接管失效节点所占用的 IP。这样可以防止哈希紊乱
(hashing chaos)。
 如果希望添加和移除节点,而不影响原先的哈希结果,可以使用一致性哈
希算法(consistent hashing)。您可以百度一下一致性哈希算法。支持一致性
哈希的客户端已经很成熟,而且被广泛使用。去尝试一下吧!
 两次哈希(reshing)。当客户端存取数据时,如果发现一个节点 down
了,就再做一次哈希(哈希算法与前一次不同),重新选择另一个节点(需要注
意的时,客户端并没有把 down 的节点从节点列表中移除,下次还是有可能先
哈希到它)。如果某个节点时好时坏,两次哈希的方法就有风险了,好的节点和
坏的节点上都可能存在脏数据(stale data)。

12、如何将 memcached 中 item 批量导入导出?

第 110 页 共 485 页
您 不 应 该 这 样 做 ! Memcached 是 一 个 非 阻 塞 的 服 务 器 。 任 何 可 能 导 致

memcached 暂 停 或 瞬 时 拒 绝 服 务 的 操 作 都 应 该 值 得 深 思 熟 虑 。 向 memcached

中批量导入数据往往不是您真正想要的!想象看,如果缓存数据在导出导入之间

发生了变化,您就需要处理脏数据了;

13、如果缓存数据在导出导入之间过期了,您又怎么处理这些

数据呢?

因此,批量导出导入数据并不像您想象中的那么有用。不过在一个场景倒是很有

用 。如 果 您 有 大 量 的 从 不 变 化 的 数 据 ,并 且 希 望 缓 存 很 快 热 ( warm)起 来 ,批 量

导入缓存数据是很有帮助的。虽然这个场景并不典型,但却经常发生,因此我们

会考虑在将来实现批量导出导入的功能。

如 果 一 个 memcached 节 点 down 了 让 您 很 痛 苦 ,那 么 您 还 会 陷 入 其 他 很 多 麻 烦 。

您的系统太脆弱了。您需要做一些优化工作。比如处理”惊群”问题(比如

memcached 节 点 都 失 效 了 ,反 复 的 查 询 让 您 的 数 据 库 不 堪 重 负 … 这 个 问 题 在 FAQ

的 其 他 提 到 过 ) , 或 者 优 化 不 好 的 查 询 。 记 住 , Memcached 并 不 是 您 逃 避 优 化

查询的借口。

14、memcached 是如何做身份验证的?

没 有 身 份 认 证 机 制 !memcached 是 运 行 在 应 用 下 层 的 软 件( 身 份 验 证 应 该 是 应 用

上 层 的 职 责 )。memcached 的 客 户 端 和 服 务 器 端 之 所 以 是 轻 量 级 的 ,部 分 原 因 就

是 完 全 没 有 实 现 身 份 验 证 机 制 。这 样 ,memcached 可 以 很 快 地 创 建 新 连 接 ,服 务

器端也无需任何配置。

第 111 页 共 485 页
如 果 您 希 望 限 制 访 问 ,您 可 以 使 用 防 火 墙 ,或 者 让 memcached 监 听 unix domain

socket。

15、memcached 的多线程是什么?如何使用它们?

线 程 就 是 定 律 ( threads rule) ! 在 Steven Grimm 和 Facebook 的 努 力 下 ,

memcached 1.2 及 更 高 版 本 拥 有 了 多 线 程 模 式 。多 线 程 模 式 允 许 memcached 能

够 充 分 利 用 多 个 CPU,并 在 CPU 之 间 共 享 所 有 的 缓 存 数 据 。memcached 使 用 一

种简单的锁机制来保证数据更新操作的互斥。相比在同一个物理机器上运行多个

memcached 实 例 , 这 种 方 式 能 够 更 有 效 地 处 理 multi gets。

如果您的系统负载并不重,也许您不需要启用多线程工作模式。如果您在运行一

个拥有大规模硬件的、庞大的网站,您将会看到多线程的好处。

简 单 地 总 结 一 下 :命 令 解 析( memcached 在 这 里 花 了 大 部 分 时 间 )可 以 运 行 在 多

线 程 模 式 下 。memcached 内 部 对 数 据 的 操 作 是 基 于 很 多 全 局 锁 的( 因 此 这 部 分 工

作不是多线程的)。未来对多线程模式的改进,将移除大量的全局锁,提高

memcached 在 负 载 极 高 的 场 景 下 的 性 能 。

16、memcached 能接受的 key 的最大长度是多少?

key 的 最 大 长 度 是 250 个 字 符 。 需 要 注 意 的 是 , 250 是 memcached 服 务 器 端 内

部 的 限 制 ,如 果 您 使 用 的 客 户 端 支 持 ”key 的 前 缀 ”或 类 似 特 性 ,那 么 key( 前 缀

+原 始 key)的 最 大 长 度 是 可 以 超 过 250 个 字 符 的 。我 们 推 荐 使 用 使 用 较 短 的 key,

因为可以节省内存和带宽。

memcached 对 item 的 过 期 时 间 有 什 么 限 制 ?

第 112 页 共 485 页
过 期 时 间 最 大 可 以 达 到 30 天 。memcached 把 传 入 的 过 期 时 间( 时 间 段 )解 释 成

时 间 点 后 ,一 旦 到 了 这 个 时 间 点 ,memcached 就 把 item 置 为 失 效 状 态 。这 是 一

个 简 单 但 obscure 的 机 制 。

17、memcached 最大能存储多大的单个 item?

1MB。 如 果 你 的 数 据 大 于 1MB, 可 以 考 虑 在 客 户 端 压 缩 或 拆 分 到 多 个 key 中 。

为 什 么 单 个 item 的 大 小 被 限 制 在 1M byte 之 内 ?

啊…这是一个大家经常问的问题!

简单的回答:因为内存分配器的算法就是这样的。

详 细 的 回 答 :Memcached 的 内 存 存 储 引 擎( 引 擎 将 来 可 插 拔 … ),使 用 slabs 来

管 理 内 存 。内 存 被 分 成 大 小 不 等 的 slabs chunks( 先 分 成 大 小 相 等 的 slabs,然 后

每 个 slab 被 分 成 大 小 相 等 chunks,不 同 slab 的 chunk 大 小 是 不 相 等 的 )。chunk

的大小依次从一个最小数开始,按某个因子增长,直到达到最大的可能值。

18、memcached 能够更有效地使用内存吗?

Memcache 客 户 端 仅 根 据 哈 希 算 法 来 决 定 将 某 个 key 存 储 在 哪 个 节 点 上 , 而 不 考

虑节点的内存大小。因此,您可以在不同的节点上使用大小不等的缓存。但是一

般 都 是 这 样 做 的 : 拥 有 较 多 内 存 的 节 点 上 可 以 运 行 多 个 memcached 实 例 , 每 个

实例使用的内存跟其他节点上的实例相同。

19、什么是二进制协议,我该关注吗?

第 113 页 共 485 页
关于二进制最好的信息当然是二进制协议规范:

二 进 制 协 议 尝 试 为 端 提 供 一 个 更 有 效 的 、 可 靠 的 协 议 , 减 少 客 户 端 /服 务 器 端 因 处

理 协 议 而 产 生 的 CPU 时 间 。

根 据 Facebook 的 测 试 ,解 析 ASCII 协 议 是 memcached 中 消 耗 CPU 时 间 最 多 的

环 节 。 所 以 , 我 们 为 什 么 不 改 进 ASCII 协 议 呢 ?

20、memcached 的内存分配器是如何工作的?为什么不适用

malloc/free!?为何要使用 slabs?

实 际 上 , 这 是 一 个 编 译 时 选 项 。 默 认 会 使 用 内 部 的 slab 分 配 器 。 您 确 实 确 实 应 该

使 用 内 建 的 slab 分 配 器 。最 早 的 时 候 ,memcached 只 使 用 malloc/free 来 管 理

内 存 。然 而 ,这 种 方 式 不 能 与 OS 的 内 存 管 理 以 前 很 好 地 工 作 。反 复 地 malloc/free

造 成 了 内 存 碎 片 , OS 最 终 花 费 大 量 的 时 间 去 查 找 连 续 的 内 存 块 来 满 足 malloc 的

请 求 ,而 不 是 运 行 memcached 进 程 。如 果 您 不 同 意 ,当 然 可 以 使 用 malloc!只

是不要在邮件列表中抱怨啊

slab 分 配 器 就 是 为 了 解 决 这 个 问 题 而 生 的 。内 存 被 分 配 并 划 分 成 chunks,一 直 被

重 复 使 用 。因 为 内 存 被 划 分 成 大 小 不 等 的 slabs,如 果 item 的 大 小 与 被 选 择 存 放

它 的 slab 不 是 很 合 适 的 话 , 就 会 浪 费 一 些 内 存 。 Steven Grimm 正 在 这 方 面 已 经

做出了有效的改进。

21、memcached 是原子的吗?

第 114 页 共 485 页
所 有 的 被 发 送 到 memcached 的 单 个 命 令 是 完 全 原 子 的 。 如 果 您 针 对 同 一 份 数 据

同 时 发 送 了 一 个 set 命 令 和 一 个 get 命 令 , 它 们 不 会 影 响 对 方 。 它 们 将 被 串 行 化 、

先 后 执 行 。 即 使 在 多 线 程 模 式 , 所 有 的 命 令 都 是 原 子 的 , 除 非 程 序 有 bug:)

命 令 序 列 不 是 原 子 的 。 如 果 您 通 过 get 命 令 获 取 了 一 个 item, 修 改 了 它 , 然 后 想

把 它 set 回 memcached, 我 们 不 保 证 这 个 item 没 有 被 其 他 进 程 ( process, 未

必是操作系统中的进程)操作过。在并发的情况下,您也可能覆写了一个被其他

进 程 set 的 item。

memcached 1.2.5 以 及 更 高 版 本 , 提 供 了 gets 和 cas 命 令 , 它 们 可 以 解 决 上 面

的 问 题 。 如 果 您 使 用 gets 命 令 查 询 某 个 key 的 item, memcached 会 给 您 返 回

该 item 当 前 值 的 唯 一 标 识 。如 果 您 覆 写 了 这 个 item 并 想 把 它 写 回 到 memcached

中 ,您 可 以 通 过 cas 命 令 把 那 个 唯 一 标 识 一 起 发 送 给 memcached。如 果 该 item

存 放 在 memcached 中 的 唯 一 标 识 与 您 提 供 的 一 致 , 您 的 写 操 作 将 会 成 功 。 如 果

另 一 个 进 程 在 这 期 间 也 修 改 了 这 个 item,那 么 该 item 存 放 在 memcached 中 的

唯一标识将会改变,您的写操作就会失败

22、如何实现集群中的 session 共享存储?

Session 是 运 行 在 一 台 服 务 器 上 的 ,所 有 的 访 问 都 会 到 达 我 们 的 唯 一 服 务 器 上 ,这

样 我 们 可 以 根 据 客 户 端 传 来 的 sessionID,来 获 取 session,或 在 对 应 Session 不

存 在 的 情 况 下( session 生 命 周 期 到 了 /用 户 第 一 次 登 录 ),创 建 一 个 新 的 Session;

但 是 , 如 果 我 们 在 集 群 环 境 下 , 假 设 我 们 有 两 台 服 务 器 A, B, 用 户 的 请 求 会 由

Nginx 服 务 器 进 行 转 发 ( 别 的 方 案 也 是 同 理 ) , 用 户 登 录 时 , Nginx 将 请 求 转 发

至 服 务 器 A 上 ,A 创 建 了 新 的 session,并 将 SessionID 返 回 给 客 户 端 ,用 户 在 浏

览 其 他 页 面 时 , 客 户 端 验 证 登 录 状 态 , Nginx 将 请 求 转 发 至 服 务 器 B, 由 于 B 上

并 没 有 对 应 客 户 端 发 来 sessionId 的 session,所 以 会 重 新 创 建 一 个 新 的 session,

并 且 再 将 这 个 新 的 sessionID 返 回 给 客 户 端 , 这 样 , 我 们 可 以 想 象 一 下 , 用 户 每

一 次 操 作 都 有 1/2 的 概 率 进 行 再 次 的 登 录 , 这 样 不 仅 对 用 户 体 验 特 别 差 , 还 会 让

服 务 器 上 的 session 激 增 , 加 大 服 务 器 的 运 行 压 力 。

第 115 页 共 485 页
为 了 解 决 集 群 环 境 下 的 seesion 共 享 问 题 , 共 有 4 种 解 决 方 案 :

1.粘 性 session

粘 性 session 是 指 Ngnix 每 次 都 将 同 一 用 户 的 所 有 请 求 转 发 至 同 一 台 服 务 器 上 ,

即将用户与服务器绑定。

2.服 务 器 session 复 制

即 每 次 session 发 生 变 化 时 , 创 建 或 者 修 改 , 就 广 播 给 所 有 集 群 中 的 服 务 器 , 使

所 有 的 服 务 器 上 的 session 相 同 。

3.session 共 享

缓 存 session, 使 用 redis, memcached。

4.session 持 久 化

将 session 存 储 至 数 据 库 中 , 像 操 作 数 据 一 样 才 做 session。

23、memcached 与 redis 的区别?

1、Redis 不 仅 仅 支 持 简 单 的 k/v 类 型 的 数 据 ,同 时 还 提 供 list,set,zset,hash

等 数 据 结 构 的 存 储 。而 memcache 只 支 持 简 单 数 据 类 型 ,需 要 客 户 端 自 己 处 理 复

杂对象

2、 Redis 支 持 数 据 的 持 久 化 , 可 以 将 内 存 中 的 数 据 保 持 在 磁 盘 中 , 重 启 的 时 候 可

以 再 次 加 载 进 行 使 用 ( PS: 持 久 化 在 rdb、 aof) 。

第 116 页 共 485 页
3、 由 于 Memcache 没 有 持 久 化 机 制 , 因 此 宕 机 所 有 缓 存 数 据 失 效 。 Redis 配 置

为持久化,宕机重启后,将自动加载宕机时刻的数据到缓存系统中。具有更好的

灾备机制。

4、Memcache 可 以 使 用 Magent 在 客 户 端 进 行 一 致 性 hash 做 分 布 式 。Redis 支

持 在 服 务 器 端 做 分 布 式( PS:Twemproxy/Codis/Redis-cluster 多 种 分 布 式 实 现 方

式)

5、Memcached 的 简 单 限 制 就 是 键 ( key)和 Value 的 限 制 。最 大 键 长 为 250 个

字 符 。 可 以 接 受 的 储 存 数 据 不 能 超 过 1MB( 可 修 改 配 置 文 件 变 大 ) , 因 为 这 是 典

型 slab 的 最 大 值 , 不 适 合 虚 拟 机 使 用 。 而 Redis 的 Key 长 度 支 持 到 512k。

6、 Redis 使 用 的 是 单 线 程 模 型 , 保 证 了 数 据 按 顺 序 提 交 。 Memcache 需 要 使 用

cas 保 证 数 据 一 致 性 。 CAS( Check and Set) 是 一 个 确 保 并 发 一 致 性 的 机 制 , 属

于“乐观锁”范畴;原理很简单:拿版本号,操作,对比版本号,如果一致就操

作,不一致就放弃任何操作

cpu 利 用 。 由 于 Redis 只 使 用 单 核 , 而 Memcached 可 以 使 用 多 核 , 所 以 平 均 每

一 个 核 上 Redis 在 存 储 小 数 据 时 比 Memcached 性 能 更 高 。而 在 100k 以 上 的 数

据 中 , Memcached 性 能 要 高 于 Redis 。

7、 memcache 内 存 管 理 : 使 用 Slab Allocation。 原 理 相 当 简 单 , 预 先 分 配 一 系

列 大 小 固 定 的 组 ,然 后 根 据 数 据 大 小 选 择 最 合 适 的 块 存 储 。避 免 了 内 存 碎 片 。( 缺

点 : 不 能 变 长 , 浪 费 了 一 定 空 间 ) memcached 默 认 情 况 下 下 一 个 slab 的 最 大 值

为 前 一 个 的 1.25 倍 。

8、redis 内 存 管 理 : Redis 通 过 定 义 一 个 数 组 来 记 录 所 有 的 内 存 分 配 情 况 , Redis

采 用 的 是 包 装 的 malloc/free, 相 较 于 Memcached 的 内 存 管 理 方 法 来 说 , 要 简

单 很 多 。由 于 malloc 首 先 以 链 表 的 方 式 搜 索 已 管 理 的 内 存 中 可 用 的 空 间 分 配 ,导

致内存碎片比较多

第 117 页 共 485 页
Redis 面试题

1、什么是 Redis?

Redis 是 完 全 开 源 免 费 的 , 遵 守 BSD 协 议 , 是 一 个 高 性 能 的 key-value 数 据 库 。

Redis 与 其 他 key - value 缓 存 产 品 有 以 下 三 个 特 点 :

Redis 支 持 数 据 的 持 久 化 ,可 以 将 内 存 中 的 数 据 保 存 在 磁 盘 中 ,重 启 的 时 候 可 以 再

次加载进行使用。

Redis 不 仅 仅 支 持 简 单 的 key-value 类 型 的 数 据 , 同 时 还 提 供 list, set, zset,

hash 等 数 据 结 构 的 存 储 。

Redis 支 持 数 据 的 备 份 , 即 master-slave 模 式 的 数 据 备 份 。

Redis 优 势

性 能 极 高 – Redis 能 读 的 速 度 是 110000 次 /s,写 的 速 度 是 81000 次 /s 。

丰 富 的 数 据 类 型 – Redis 支 持 二 进 制 案 例 的 Strings, Lists, Hashes, Sets 及

Ordered Sets 数 据 类 型 操 作 。

原 子 – Redis 的 所 有 操 作 都 是 原 子 性 的 ,意 思 就 是 要 么 成 功 执 行 要 么 失 败 完 全 不

执 行 。单 个 操 作 是 原 子 性 的 。多 个 操 作 也 支 持 事 务 ,即 原 子 性 ,通 过 MULTI 和 EXEC

指令包起来。

丰 富 的 特 性 – Redis 还 支 持 publish/subscribe, 通 知 , key 过 期 等 等 特 性 。

Redis 与 其 他 key-value 存 储 有 什 么 不 同 ?

Redis 有 着 更 为 复 杂 的 数 据 结 构 并 且 提 供 对 他 们 的 原 子 性 操 作 ,这 是 一 个 不 同 于 其

他 数 据 库 的 进 化 路 径 。Redis 的 数 据 类 型 都 是 基 于 基 本 数 据 结 构 的 同 时 对 程 序 员 透

明,无需进行额外的抽象。

第 118 页 共 485 页
Redis 运 行 在 内 存 中 但 是 可 以 持 久 化 到 磁 盘 ,所 以 在 对 不 同 数 据 集 进 行 高 速 读 写 时

需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点

是 ,相 比 在 磁 盘 上 相 同 的 复 杂 的 数 据 结 构 ,在 内 存 中 操 作 起 来 非 常 简 单 ,这 样 Redis

可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加

的方式产生的,因为他们并不需要进行随机访问。

2、Redis 的数据类型?

答 :Redis 支 持 五 种 数 据 类 型 :string( 字 符 串 ),hash( 哈 希 ),list( 列 表 ),

set( 集 合 ) 及 zsetsorted set: 有 序 集 合 )。

我 们 实 际 项 目 中 比 较 常 用 的 是 string,hash 如 果 你 是 Redis 中 高 级 用 户 ,还 需 要

加 上 下 面 几 种 数 据 结 构 HyperLogLog、 Geo、 Pub/Sub。

如 果 你 说 还 玩 过 Redis Module,像 BloomFilter,RedisSearch,Redis-ML,面

试官得眼睛就开始发亮了。

3、使用 Redis 有哪些好处?

1、 速 度 快 , 因 为 数 据 存 在 内 存 中 , 类 似 于 HashMap, HashMap 的 优 势 就 是 查

找 和 操 作 的 时 间 复 杂 度 都 是 O1)

2、 支 持 丰 富 数 据 类 型 , 支 持 string, list, set, Zset, hash 等

3、 支 持 事 务 , 操 作 都 是 原 子 性 , 所 谓 的 原 子 性 就 是 对 数 据 的 更 改 要 么 全 部 执 行 ,

要么全部不执行

4、丰 富 的 特 性 :可 用 于 缓 存 ,消 息 ,按 key 设 置 过 期 时 间 ,过 期 后 将 会 自 动 删 除

第 119 页 共 485 页
4、Redis 相比 Memcached 有哪些优势?

1、 Memcached 所 有 的 值 均 是 简 单 的 字 符 串 , redis 作 为 其 替 代 者 , 支 持 更 为 丰

富的数据类

2、 Redis 的 速 度 比 Memcached 快 很

3、 Redis 可 以 持 久 化 其 数 据

5、Memcache 与 Redis 的区别都有哪些?

1、 存 储 方 式 Memecache 把 数 据 全 部 存 在 内 存 之 中 , 断 电 后 会 挂 掉 , 数 据 不 能

超 过 内 存 大 小 。 Redis 有 部 份 存 在 硬 盘 上 , 这 样 能 保 证 数 据 的 持 久 性 。

2、数 据 支 持 类 型 Memcache 对 数 据 类 型 支 持 相 对 简 单 。 Redis 有 复 杂 的 数 据 类

型。

3、使 用 底 层 模 型 不 同 它 们 之 间 底 层 实 现 方 式 以 及 与 客 户 端 之 间 通 信 的 应 用 协 议

不 一 样 。 Redis 直 接 自 己 构 建 了 VM 机 制 ,因 为 一 般 的 系 统 调 用 系 统 函 数 的 话 ,

会浪费一定的时间去移动和请求。

6、Redis 是单进程单线程的?

答 : Redis 是 单 进 程 单 线 程 的 , redis 利 用 队 列 技 术 将 并 发 访 问 变 为 串 行 访 问 , 消

除了传统数据库串行控制的开销。

7、一个字符串类型的值能存储最大容量是多少?

第 120 页 共 485 页
答 : 512M

8、Redis 的持久化机制是什么?各自的优缺点?

Redis 提 供 两 种 持 久 化 机 制 RDB 和 AOF 机 制 :

1、 RDBRedis DataBase)持 久 化 方 式 : 是 指 用 数 据 集 快 照 的 方 式 半 持 久 化 模 式 )

记 录 redis 数 据 库 的 所 有 键 值 对 ,在 某 个 时 间 点 将 数 据 写 入 一 个 临 时 文 件 , 持 久 化

结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。

优点:

1、 只 有 一 个 文 件 dump.rdb, 方 便 持 久 化 。

2、 容 灾 性 好 , 一 个 文 件 可 以 保 存 到 安 全 的 磁 盘 。

3、 性 能 最 大 化 , fork 子 进 程 来 完 成 写 操 作 , 让 主 进 程 继 续 处 理 命 令 , 所 以 是 IO

最 大 化 。使 用 单 独 子 进 程 来 进 行 持 久 化 ,主 进 程 不 会 进 行 任 何 IO 操 作 ,保 证 了 redis

的 高 性 能 ) 4.相 对 于 数 据 集 大 时 , 比 AOF 的 启 动 效 率 更 高 。

缺点:

1、数 据 安 全 性 低 。RDB 是 间 隔 一 段 时 间 进 行 持 久 化 ,如 果 持 久 化 之 间 redis 发 生

故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候)

2、 AOFAppend-only file)持 久 化 方 式 : 是 指 所 有 的 命 令 行 记 录 以 redis 命 令 请

求 协 议 的 格 式 完 全 持 久 化 存 储 )保 存 为 aof 文 件 。

优点:

第 121 页 共 485 页
1、 数 据 安 全 , aof 持 久 化 可 以 配 置 appendfsync 属 性 , 有 always, 每 进 行 一 次

命 令 操 作 就 记 录 到 aof 文 件 中 一 次 。

2、 通 过 append 模 式 写 文 件 , 即 使 中 途 服 务 器 宕 机 , 可 以 通 过 redis-check-aof

工具解决数据一致性问题。

3、AOF 机 制 的 rewrite 模 式 。AOF 文 件 没 被 rewrite 之 前( 文 件 过 大 时 会 对 命 令

进 行 合 并 重 写 ) , 可 以 删 除 其 中 的 某 些 命 令 ( 比 如 误 操 作 的 flushall) )

缺点:

1、 AOF 文 件 比 RDB 文 件 大 , 且 恢 复 速 度 慢 。

2、 数 据 集 大 的 时 候 , 比 rdb 启 动 效 率 低 。

9、Redis 常见性能问题和解决方案:

1、Master 最 好 不 要 写 内 存 快 照 ,如 果 Master 写 内 存 快 照 ,save 命 令 调 度 rdbSave

函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性

暂停服务

2、 如 果 数 据 比 较 重 要 , 某 个 Slave 开 启 AOF 备 份 数 据 , 策 略 设 置 为 每 秒 同 步 一

3、 为 了 主 从 复 制 的 速 度 和 连 接 的 稳 定 性 , Master 和 Slave 最 好 在 同 一 个 局 域 网

4、 尽 量 避 免 在 压 力 很 大 的 主 库 上 增 加 从

5、主 从 复 制 不 要 用 图 状 结 构 ,用 单 向 链 表 结 构 更 为 稳 定 ,即 :Master <- Slave1

<- Slave2 <- Slave3… 这 样 的 结 构 方 便 解 决 单 点 故 障 问 题 ,实 现 Slave 对 Master

的 替 换 。 如 果 Master 挂 了 , 可 以 立 刻 启 用 Slave1 做 Master, 其 他 不 变 。

10、redis 过期键的删除策略?

第 122 页 共 485 页
1、定 时 删 除 :在 设 置 键 的 过 期 时 间 的 同 时 ,创 建 一 个 定 时 器 timer). 让 定 时 器 在 键

的过期时间来临时,立即执行对键的删除操作。

2、惰 性 删 除 :放 任 键 过 期 不 管 ,但 是 每 次 从 键 空 间 中 获 取 键 时 ,都 检 查 取 得 的 键 是

否 过 期 , 如 果 过 期 的 话 , 就 删 除 该 键 ;如 果 没 有 过 期 , 就 返 回 该 键 。

3、定 期 删 除 :每 隔 一 段 时 间 程 序 就 对 数 据 库 进 行 一 次 检 查 ,删 除 里 面 的 过 期 键 。至

于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

11、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( 驱 逐 ) : 禁 止 驱 逐 数 据

注 意 这 里 的 6 种 机 制 ,volatile 和 allkeys 规 定 了 是 对 已 设 置 过 期 时 间 的 数 据 集 淘

汰 数 据 还 是 从 全 部 数 据 集 淘 汰 数 据 , 后 面 的 lru、 ttl 以 及 random 是 三 种 不 同 的

淘 汰 策 略 , 再 加 上 一 种 no-enviction 永 不 回 收 的 策 略 。

第 123 页 共 485 页
使用策略规则:

1、如 果 数 据 呈 现 幂 律 分 布 ,也 就 是 一 部 分 数 据 访 问 频 率 高 ,一 部 分 数 据 访 问 频 率

低 , 则 使 用 allkeys-lru

2、 如 果 数 据 呈 现 平 等 分 布 , 也 就 是 所 有 的 数 据 访 问 频 率 都 相 同 , 则 使 用

allkeys-random

12、为什么 edis 需要把所有数据放到内存中?

答 :Redis 为 了 达 到 最 快 的 读 写 速 度 将 数 据 都 读 到 内 存 中 ,并 通 过 异 步 的 方 式 将 数

据 写 入 磁 盘 。所 以 redis 具 有 快 速 和 数 据 持 久 化 的 特 征 。如 果 不 将 数 据 放 在 内 存 中 ,

磁 盘 I/O 速 度 为 严 重 影 响 redis 的 性 能 。 在 内 存 越 来 越 便 宜 的 今 天 , redis 将 会 越

来越受欢迎。如果设置了最大使用的内存,则数据已有记录数达到内存限值后不

能继续插入新值。

13、Redis 的同步机制了解么?

答 :Redis 可 以 使 用 主 从 同 步 ,从 从 同 步 。第 一 次 同 步 时 ,主 节 点 做 一 次 bgsave,

并 同 时 将 后 续 修 改 操 作 记 录 到 内 存 buffer, 待 完 成 后 将 rdb 文 件 全 量 同 步 到 复 制

节 点 , 复 制 节 点 接 受 完 成 后 将 rdb 镜 像 加 载 到 内 存 。 加 载 完 成 后 , 再 通 知 主 节 点

将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

14、Pipeline 有什么好处,为什么要用 pipeline?

答 :可 以 将 多 次 IO 往 返 的 时 间 缩 减 为 一 次 ,前 提 是 pipeline 执 行 的 指 令 之 间 没 有

因 果 相 关 性 。使 用 redis-benchmark 进 行 压 测 的 时 候 可 以 发 现 影 响 redis 的 QPS

峰 值 的 一 个 重 要 因 素 是 pipeline 批 次 指 令 的 数 目 。

第 124 页 共 485 页
15、是否使用过 Redis 集群,集群的原理是什么?

1)、 Redis Sentinal 着 眼 于 高 可 用 , 在 master 宕 机 时 会 自 动 将 slave 提 升 为

master, 继 续 提 供 服 务 。

2)、 Redis Cluster 着 眼 于 扩 展 性 , 在 单 个 redis 内 存 不 足 时 , 使 用 Cluster 进 行

分片存储。

16、Redis 集群方案什么情况下会导致整个集群不可用?

答 : 有 A, B, C 三 个 节 点 的 集 群 ,在 没 有 复 制 模 型 的 情 况 下 ,如 果 节 点 B 失 败 了 ,

那 么 整 个 集 群 就 会 以 为 缺 少 5501-11000 这 个 范 围 的 槽 而 不 可 用 。

17、Redis 支持的 Java 客户端都有哪些?官方推荐用哪个?

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

18、Jedis 与 Redisson 对比有什么优缺点?

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

的 支 持 ;Redisson 实 现 了 分 布 式 和 可 扩 展 的 Java 数 据 结 构 ,和 Jedis 相 比 ,功 能

较 为 简 单 , 不 支 持 字 符 串 操 作 , 不 支 持 排 序 、 事 务 、 管 道 、 分 区 等 Redis 特 性 。

Redisson 的 宗 旨 是 促 进 使 用 者 对 Redis 的 关 注 分 离 ,从 而 让 使 用 者 能 够 将 精 力 更

集中地放在处理业务逻辑上。

第 125 页 共 485 页
19、Redis 如何设置密码及验证密码?

设 置 密 码 : config set requirepass 123456

授 权 密 码 : auth 123456

20、说说 Redis 哈希槽的概念?

答 : Redis 集 群 没 有 使 用 一 致 性 hash,而 是 引 入 了 哈 希 槽 的 概 念 , Redis 集 群 有

16384 个 哈 希 槽 ,每 个 key 通 过 CRC16 校 验 后 对 16384 取 模 来 决 定 放 置 哪 个 槽 ,

集 群 的 每 个 节 点 负 责 一 部 分 hash 槽 。

21、Redis 集群的主从复制模型是怎样的?

答:为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所

以 集 群 使 用 了 主 从 复 制 模 型 ,每 个 节 点 都 会 有 N-1 个 复 制 品 .

22、Redis 集群会有写操作丢失吗?为什么?

答 :Redis 并 不 能 保 证 数 据 的 强 一 致 性 ,这 意 味 这 在 实 际 中 集 群 在 特 定 的 条 件 下 可

能会丢失写操作。

23、Redis 集群之间是如何复制的?

答:异步复制

第 126 页 共 485 页
24、Redis 集群最大节点个数是多少?

答 : 16384 个 。

25、Redis 集群如何选择数据库?

答 : Redis 集 群 目 前 无 法 做 数 据 库 选 择 , 默 认 在 0 数 据 库 。

26、怎么测试 Redis 的连通性?

答 : 使 用 ping 命 令 。

27、怎么理解 Redis 事务?

答:

1) 事 务 是 一 个 单 独 的 隔 离 操 作 : 事 务 中 的 所 有 命 令 都 会 序 列 化 、 按 顺 序 地 执 行 。

事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。

2) 事 务 是 一 个 原 子 操 作 : 事 务 中 的 命 令 要 么 全 部 被 执 行 , 要 么 全 部 都 不 执 行 。

28、Redis 事务相关的命令有哪几个?

答 : MULTI、 EXEC、 DISCARD、 WATCH

第 127 页 共 485 页
29、Redis key 的过期时间和永久有效分别怎么设置?

答 : EXPIRE 和 PERSIST 命 令 。

30、Redis 如何做内存优化?

答 : 尽 可 能 使 用 散 列 表 ( hashes) , 散 列 表 ( 是 说 散 列 表 里 面 存 储 的 数 少 ) 使 用

的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比

如 你 的 web 系 统 中 有 一 个 用 户 对 象 , 不 要 为 这 个 用 户 的 名 称 , 姓 氏 , 邮 箱 , 密 码

设 置 单 独 的 key,而 是 应 该 把 这 个 用 户 的 所 有 信 息 存 储 到 一 张 散 列 表 里 面 .

31、Redis 回收进程如何工作的?

答 : 一 个 客 户 端 运 行 了 新 的 命 令 , 添 加 了 新 的 数 据 。 Redi 检 查 内 存 使 用 情 况 , 如

果 大 于 maxmemory 的 限 制 , 则 根 据 设 定 好 的 策 略 进 行 回 收 。 一 个 新 的 命 令 被 执

行,等等。所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地

回收回到边界以下。如果一个命令的结果导致大量内存被使用(例如很大的集合

的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。

32、都有哪些办法可以降低 Redis 的内存使用情况呢?

答 :如 果 你 使 用 的 是 32 位 的 Redis 实 例 ,可 以 好 好 利 用 Hash,list,sorted set,set

等 集 合 类 型 数 据 , 因 为 通 常 情 况 下 很 多 小 的 Key-Value 可 以 用 更 紧 凑 的 方 式 存 放

到一起。

33、Redis 的内存用完了会发生什么?

第 128 页 共 485 页
答 :如 果 达 到 设 置 的 上 限 ,Redis 的 写 命 令 会 返 回 错 误 信 息( 但 是 读 命 令 还 可 以 正

常 返 回 。 ) 或 者 你 可 以 将 Redis 当 缓 存 来 使 用 配 置 淘 汰 机 制 , 当 Redis 达 到 内 存

上限时会冲刷掉旧的内容。

34、一个 Redis 实例最多能存放多少的 keys?List、Set、

Sorted Set 他们最多能存放多少元素?

答 :理 论 上 Redis 可 以 处 理 多 达 232 的 keys,并 且 在 实 际 中 进 行 了 测 试 ,每 个 实

例 至 少 存 放 了 2 亿 5 千 万 的 keys。 我 们 正 在 测 试 一 些 较 大 的 值 。 任 何 list、 set、

和 sorted set 都 可 以 放 232 个 元 素 。 换 句 话 说 , Redis 的 存 储 极 限 是 系 统 中 的 可

用内存值。

35、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如

何保证 redis 中的数据都是热点数据?

答 : Redis 内 存 数 据 集 大 小 上 升 到 一 定 大 小 的 时 候 , 就 会 施 行 数 据 淘 汰 策 略 。

相 关 知 识 : Redis 提 供 6 种 数 据 淘 汰 策 略 :

volatile-lru:从 已 设 置 过 期 时 间 的 数 据 集( server.db[i].expires)中 挑 选 最 近 最

少使用的数据淘汰

volatile-ttl: 从 已 设 置 过 期 时 间 的 数 据 集 ( server.db[i].expires) 中 挑 选 将 要 过

期的数据淘汰

第 129 页 共 485 页
volatile-random: 从 已 设 置 过 期 时 间 的 数 据 集 ( server.db[i].expires) 中 任 意

选择数据淘汰

allkeys-lru: 从 数 据 集 ( server.db[i].dict) 中 挑 选 最 近 最 少 使 用 的 数 据 淘 汰

allkeys-random: 从 数 据 集 ( server.db[i].dict) 中 任 意 选 择 数 据 淘 汰

no-enviction( 驱 逐 ) : 禁 止 驱 逐 数 据

36、Redis 最适合的场景?

1、 会 话 缓 存 ( Session Cache)

最 常 用 的 一 种 使 用 Redis 的 情 景 是 会 话 缓 存( session cache)。用 Redis 缓 存 会

话 比 其 他 存 储( 如 Memcached)的 优 势 在 于 :Redis 提 供 持 久 化 。当 维 护 一 个 不

是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不

高 兴 的 , 现 在 , 他 们 还 会 这 样 吗 ? 幸 运 的 是 , 随 着 Redis 这 些 年 的 改 进 , 很 容

易 找 到 怎 么 恰 当 的 使 用 Redis 来 缓 存 会 话 的 文 档 。 甚 至 广 为 人 知 的 商 业 平 台

Magento 也 提 供 Redis 的 插 件 。

2、 全 页 缓 存 ( FPC)

除 基 本 的 会 话 token 之 外 , Redis 还 提 供 很 简 便 的 FPC 平 台 。 回 到 一 致 性 问 题 ,

即 使 重 启 了 Redis 实 例 , 因 为 有 磁 盘 的 持 久 化 , 用 户 也 不 会 看 到 页 面 加 载 速 度 的

下 降 ,这 是 一 个 极 大 改 进 ,类 似 PHP 本 地 FPC。 再 次 以 Magento 为 例 ,Magento

提 供 一 个 插 件 来 使 用 Redis 作 为 全 页 缓 存 后 端 。 此 外 , 对 WordPress 的 用 户 来

说 , Pantheon 有 一 个 非 常 好 的 插 件 wp-redis, 这 个 插 件 能 帮 助 你 以 最 快 速 度 加

载你曾浏览过的页面。

3、 队 列

第 130 页 共 485 页
Reids 在 内 存 存 储 引 擎 领 域 的 一 大 优 点 是 提 供 list 和 set 操 作 , 这 使 得 Redis

能 作 为 一 个 很 好 的 消 息 队 列 平 台 来 使 用 。Redis 作 为 队 列 使 用 的 操 作 ,就 类 似 于 本

地 程 序 语 言( 如 Python)对 list 的 push/pop 操 作 。 如 果 你 快 速 的 在 Google

中 搜 索 “ Redis queues” , 你 马 上 就 能 找 到 大 量 的 开 源 项 目 , 这 些 项 目 的 目 的 就

是 利 用 Redis 创 建 非 常 好 的 后 端 工 具 , 以 满 足 各 种 队 列 需 求 。 例 如 , Celery 有 一

个 后 台 就 是 使 用 Redis 作 为 broker, 你 可 以 从 这 里 去 查 看 。

4, 排 行 榜 /计 数 器

Redis 在 内 存 中 对 数 字 进 行 递 增 或 递 减 的 操 作 实 现 的 非 常 好 。 集 合 ( Set) 和 有 序

集 合( Sorted Set)也 使 得 我 们 在 执 行 这 些 操 作 的 时 候 变 的 非 常 简 单 ,Redis 只 是

正 好 提 供 了 这 两 种 数 据 结 构 。 所 以 , 我 们 要 从 排 序 集 合 中 获 取 到 排 名 最 靠 前 的 10

个 用 户 – 我 们 称 之 为 “ user_scores” , 我 们 只 需 要 像 下 面 一 样 执 行 即 可 : 当 然 ,

这 是 假 定 你 是 根 据 你 用 户 的 分 数 做 递 增 的 排 序 。如 果 你 想 返 回 用 户 及 用 户 的 分 数 ,

你 需 要 这 样 执 行 : ZRANGE user_scores 0 10 WITHSCORES Agora Games 就

是 一 个 很 好 的 例 子 , 用 Ruby 实 现 的 , 它 的 排 行 榜 就 是 使 用 Redis 来 存 储 数 据 的 ,

你可以在这里看到。

5、 发 布 /订 阅

最 后( 但 肯 定 不 是 最 不 重 要 的 )是 Redis 的 发 布 /订 阅 功 能 。发 布 /订 阅 的 使 用 场 景

确 实 非 常 多 。 我 已 看 见 人 们 在 社 交 网 络 连 接 中 使 用 , 还 可 作 为 基 于 发 布 /订 阅 的 脚

本 触 发 器 , 甚 至 用 Redis 的 发 布 /订 阅 功 能 来 建 立 聊 天 系 统 !

37、假如 Redis 里面有 1 亿个 key,其中有 10w 个 key 是以

某个固定的已知的前缀开头的,如果将它们全部找出来?

答 : 使 用 keys 指 令 可 以 扫 出 指 定 模 式 的 key 列 表 。

第 131 页 共 485 页
对 方 接 着 追 问 : 如 果 这 个 redis 正 在 给 线 上 的 业 务 提 供 服 务 , 那 使 用 keys 指 令 会

有什么问题?

这 个 时 候 你 要 回 答 redis 关 键 的 一 个 特 性 :redis 的 单 线 程 的 。keys 指 令 会 导 致 线

程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时

候 可 以 使 用 scan 指 令 , scan 指 令 可 以 无 阻 塞 的 提 取 出 指 定 模 式 的 key 列 表 , 但

是会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间

会 比 直 接 用 keys 指 令 长 。

38、如果有大量的 key 需要设置同一时间过期,一般需要注意

什么?

答 :如 果 大 量 的 key 过 期 时 间 设 置 的 过 于 集 中 ,到 过 期 的 那 个 时 间 点 ,redis 可 能

会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一

些。

39、使用过 Redis 做异步队列么,你是怎么用的?

答 :一 般 使 用 list 结 构 作 为 队 列 ,rpush 生 产 消 息 ,lpop 消 费 消 息 。当 lpop 没 有

消 息 的 时 候 , 要 适 当 sleep 一 会 再 重 试 。

如 果 对 方 追 问 可 不 可 以 不 用 sleep 呢 ?

list 还 有 个 指 令 叫 blpop,在 没 有 消 息 的 时 候 ,它 会 阻 塞 住 直 到 消 息 到 来 。如 果 对

方 追 问 能 不 能 生 产 一 次 消 费 多 次 呢 ? 使 用 pub/sub 主 题 订 阅 者 模 式 , 可 以 实 现

1:N 的 消 息 队 列 。

第 132 页 共 485 页
如 果 对 方 追 问 pub/sub 有 什 么 缺 点 ?

在 消 费 者 下 线 的 情 况 下 ,生 产 的 消 息 会 丢 失 ,得 使 用 专 业 的 消 息 队 列 如 RabbitMQ

等。

如 果 对 方 追 问 redis 如 何 实 现 延 时 队 列 ?

我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这

么 详 细 。但 是 你 很 克 制 ,然 后 神 态 自 若 的 回 答 道 :使 用 sortedset,拿 时 间 戳 作 为

score,消 息 内 容 作 为 key 调 用 zadd 来 生 产 消 息 ,消 费 者 用 zrangebyscore 指 令

获取 N 秒之前的数据轮询进行处理。到这里,面试官暗地里已经对你竖起了大拇

指。但是他不知道的是此刻你却竖起了中指,在椅子背后。

40、使用过 Redis 分布式锁么,它是什么回事?

先 拿 setnx 来 争 抢 锁 , 抢 到 之 后 , 再 用 expire 给 锁 加 一 个 过 期 时 间 防 止 锁 忘 记 了

释放。

这 时 候 对 方 会 告 诉 你 说 你 回 答 得 不 错 , 然 后 接 着 问 如 果 在 setnx 之 后 执 行 expire

之 前 进 程 意 外 crash 或 者 要 重 启 维 护 了 , 那 会 怎 么 样 ?

这时候你要给予惊讶的反馈:唉,是喔,这个锁就永远得不到释放了。紧接着你

需要抓一抓自己得脑袋,故作思考片刻,好像接下来的结果是你主动思考出来的,

然 后 回 答 : 我 记 得 set 指 令 有 非 常 复 杂 的 参 数 , 这 个 应 该 是 可 以 同 时 把 setnx 和

expire 合 成 一 条 指 令 来 用 的 ! 对 方 这 时 会 显 露 笑 容 , 心 里 开 始 默 念 : 摁 , 这 小 子

还不错。

MySQL 面试题

第 133 页 共 485 页
1、MySQL 中有哪几种锁?

1、 表 级 锁 : 开 销 小 , 加 锁 快 ; 不 会 出 现 死 锁 ;锁 定 粒 度 大 , 发 生 锁 冲 突 的 概 率 最

高,并发度最低。

2、 行 级 锁 : 开 销 大 , 加 锁 慢 ; 会 出 现 死 锁 ; 锁 定 粒 度 最 小 , 发 生 锁 冲 突 的 概 率 最

低,并发度也最高。

3、页 面 锁 :开 销 和 加 锁 时 间 界 于 表 锁 和 行 锁 之 间 ;会 出 现 死 锁 ;锁 定 粒 度 界 于 表

锁和行锁之间,并发度一般。

2、MySQL 中有哪些不同的表格?

共有 5 种类型的表格:

1、 MyISAM

2、 Heap

3、 Merge

4、 INNODB

5、 ISAM

3、简述在 MySQL 数据库中 MyISAM 和 InnoDB 的区别

MyISAM:

第 134 页 共 485 页
不支持事务,但是每次查询都是原子的;

支持表级锁,即每次操作是对整个表加锁;

存储表的总行数;

一 个 MYISAM 表 有 三 个 文 件 : 索 引 文 件 、 表 结 构 文 件 、 数 据 文 件 ;

采用菲聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引

基本一致,但是辅索引不用保证唯一性。

InnoDb:

支 持 ACID 的 事 务 , 支 持 事 务 的 四 种 隔 离 级 别 ;

支持行级锁及外键约束:因此可以支持写并发;

不存储总行数:

一 个 InnoDb 引 擎 存 储 在 一 个 文 件 空 间 ( 共 享 表 空 间 , 表 大 小 不 受 操 作 系 统 控 制 ,

一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受

操 作 系 统 文 件 大 小 限 制 , 一 般 为 2G) , 受 操 作 系 统 文 件 大 小 的 限 制 ;

主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存

储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅

索 引 ; 最 好 使 用 自 增 主 键 , 防 止 插 入 数 据 时 , 为 维 持 B+树 结 构 , 文 件 的 大 调 整 。

第 135 页 共 485 页
4、MySQL 中 InnoDB 支持的四种事务隔离级别名称,以及逐

级之间的区别?

SQL 标 准 定 义 的 四 个 隔 离 级 别 为 :

1、 read uncommited : 读 到 未 提 交 数 据

2、 read committed: 脏 读 , 不 可 重 复 读

3、 repeatable read: 可 重 读

4、 serializable : 串 行 事 物

5、CHAR 和 VARCHAR 的区别?

1、 CHAR 和 VARCHAR 类 型 在 存 储 和 检 索 方 面 有 所 不 同

2、 CHAR 列 长 度 固 定 为 创 建 表 时 声 明 的 长 度 , 长 度 值 范 围 是 1 到 255 当 CHAR

值 被 存 储 时 , 它 们 被 用 空 格 填 充 到 特 定 长 度 , 检 索 CHAR 值 时 需 删 除 尾 随 空 格 。

6、主键和候选键有什么区别?

表 格 的 每 一 行 都 由 主 键 唯 一 标 识 ,一 个 表 只 有 一 个 主 键 。

主键也是候选键。按照惯例,候选键可以被指定为主键,并且可以用于任何外键

引用。

第 136 页 共 485 页
7、myisamchk 是用来做什么的?

它 用 来 压 缩 MyISAM 表 , 这 减 少 了 磁 盘 或 内 存 使 用 。

MyISAM Static 和 MyISAM Dynamic 有 什 么 区 别 ?

在 MyISAM Static 上 的 所 有 字 段 有 固 定 宽 度 。 动 态 MyISAM 表 将 具 有 像 TEXT,

BLOB 等 字 段 , 以 适 应 不 同 长 度 的 数 据 类 型 。

MyISAM Static 在 受 损 情 况 下 更 容 易 恢 复 。

8、如果一个表有一列定义为 TIMESTAMP,将发生什么?

每当行被更改时,时间戳字段将获取当前时间戳。

列 设 置 为 AUTO INCREMENT 时 , 如 果 在 表 中 达 到 最 大 值 , 会 发 生 什 么 情 况 ?

它会停止递增,任何进一步的插入都将产生错误,因为密钥已被使用。

怎样才能找出最后一次插入时分配了哪个自动增量?

LAST_INSERT_ID 将 返 回 由 Auto_increment 分 配 的 最 后 一 个 值 , 并 且 不 需 要 指

定表名称。

9、你怎么看到为表格定义的所有索引?

索引是通过以下方式为表格定义的:

第 137 页 共 485 页
SHOW INDEX FROM <tablename>;

10、LIKE 声明中的%和_是什么意思?

% 对 应 于 0 个 或 更 多 字 符 , _只 是 LIKE 语 句 中 的 一 个 字 符 。

如 何 在 Unix 和 MySQL 时 间 戳 之 间 进 行 转 换 ?

UNIX_TIMESTAMP 是 从 MySQL 时 间 戳 转 换 为 Unix 时 间 戳 的 命 令

FROM_UNIXTIME 是 从 Unix 时 间 戳 转 换 为 MySQL 时 间 戳 的 命 令

11、列对比运算符是什么?

在 SELECT 语 句 的 列 比 较 中 使 用 =,<>,<=,<,> =,>,<<,>>,<=>,AND,

OR 或 LIKE 运 算 符 。

12、BLOB 和 TEXT 有什么区别?

BLOB 是 一 个 二 进 制 对 象 , 可 以 容 纳 可 变 数 量 的 数 据 。 TEXT 是 一 个 不 区 分 大 小 写

的 BLOB。

BLOB 和 TEXT 类 型 之 间 的 唯 一 区 别 在 于 对 BLOB 值 进 行 排 序 和 比 较 时 区 分 大 小

写 , 对 TEXT 值 不 区 分 大 小 写 。

13、MySQL_fetch_array 和 MySQL_fetch_object 的区别是

什么?

第 138 页 共 485 页
以 下 是 MySQL_fetch_array 和 MySQL_fetch_object 的 区 别 :

MySQL_fetch_array( ) – 将 结 果 行 作 为 关 联 数 组 或 来 自 数 据 库 的 常 规 数 组 返 回 。

MySQL_fetch_object – 从 数 据 库 返 回 结 果 行 作 为 对 象 。

14、MyISAM 表格将在哪里存储,并且还提供其存储格式?

每 个 MyISAM 表 格 以 三 种 格 式 存 储 在 磁 盘 上 :

· “ .frm” 文 件 存 储 表 定 义

· 数 据 文 件 具 有 “ .MYD” ( MYData) 扩 展 名

索 引 文 件 具 有 “ .MYI” ( MYIndex) 扩 展 名

15、MySQL 如何优化 DISTINCT?

DISTINCT 在 所 有 列 上 转 换 为 GROUP BY, 并 与 ORDER BY 子 句 结 合 使 用 。

SELECT DISTINCT t1.a FROM t1,t2 where t1.a=t2.a;

16、如何显示前 50 行?

在 MySQL 中 , 使 用 以 下 代 码 查 询 显 示 前 50 行 :

SELECT*FROM

第 139 页 共 485 页
LIMIT 0,50;

17、可以使用多少列创建索引?

任 何 标 准 表 最 多 可 以 创 建 16 个 索 引 列 。

18、NOW()和 CURRENT_DATE()有什么区别?

NOW( ) 命 令 用 于 显 示 当 前 年 份 , 月 份 , 日 期 , 小 时 , 分 钟 和 秒 。

CURRENT_DATE( ) 仅 显 示 当 前 年 份 , 月 份 和 日 期 。

19、什么是非标准字符串类型?

1、 TINYTEXT

2、 TEXT

3、 MEDIUMTEXT

4、 LONGTEXT

20、什么是通用 SQL 函数?

1、CONCAT(A, B) – 连 接 两 个 字 符 串 值 以 创 建 单 个 字 符 串 输 出 。通 常 用 于 将 两 个

或多个字段合并为一个字段。

第 140 页 共 485 页
2、 FORMAT(X, D)- 格 式 化 数 字 X 到 D 有 效 数 字 。

3、 CURRDATE(), CURRTIME()- 返 回 当 前 日 期 或 时 间 。

4、 NOW( ) – 将 当 前 日 期 和 时 间 作 为 一 个 值 返 回 。

5、MONTH( ),DAY( ),YEAR( ),WEEK( ),WEEKDAY( ) – 从 日 期

值中提取给定数据。

6、 HOUR( ) , MINUTE( ) , SECOND( ) – 从 时 间 值 中 提 取 给 定 数 据 。

7、 DATEDIFF( A, B) – 确 定 两 个 日 期 之 间 的 差 异 , 通 常 用 于 计 算 年 龄

8、 SUBTIMES( A, B) – 确 定 两 次 之 间 的 差 异 。

9、 FROMDAYS( INT) – 将 整 数 天 数 转 换 为 日 期 值 。

21、MySQL 支持事务吗?

在 缺 省 模 式 下 ,MySQL 是 autocommit 模 式 的 ,所 有 的 数 据 库 更 新 操 作 都 会 即 时

提 交 , 所 以 在 缺 省 情 况 下 , MySQL 是 不 支 持 事 务 的 。

但 是 如 果 你 的 MySQL 表 类 型 是 使 用 InnoDB Tables 或 BDB tables 的 话 , 你 的

MySQL 就 可 以 使 用 事 务 处 理 ,使 用 SET

AUTOCOMMIT=0 就 可 以 使 MySQL 允 许 在 非 autocommit 模 式 , 在 非

autocommit 模 式 下 ,你 必 须 使 用 COMMIT 来 提 交 你 的 更 改 ,或 者 用 ROLLBACK

来回滚你的更改。

第 141 页 共 485 页
22、MySQL 里记录货币用什么字段类型好

NUMERIC 和 DECIMAL 类 型 被 MySQL 实 现 为 同 样 的 类 型 , 这 在 SQL92 标 准 允

许。他们被用于保存值,该值的准确精度是极其重要的值,例如与金钱有关的数

据 。 当 声 明 一 个 类 是 这 些 类 型 之 一 时 , 精 度 和 规 模 的 能 被 (并 且 通 常 是 )指 定 。

例如:

salary DECIMAL(9,2)

在 这 个 例 子 中 ,9(precision)代 表 将 被 用 于 存 储 值 的 总 的 小 数 位 数 ,而 2(scale)代

表将被用于存储小数点后的位数。

因 此 , 在 这 种 情 况 下 , 能 被 存 储 在 salary 列 中 的 值 的 范 围 是 从 -9999999.99 到

9999999.99。

23、MySQL 有关权限的表都有哪几个?

MySQL 服 务 器 通 过 权 限 表 来 控 制 用 户 对 数 据 库 的 访 问 , 权 限 表 存 放 在 MySQL 数

据 库 里 ,由 MySQL_install_db 脚 本 初 始 化 。这 些 权 限 表 分 别 user,db,table_priv,

columns_priv 和 host。

24、列的字符串类型可以是什么?

字符串类型是:

1、 SET

第 142 页 共 485 页
2、 BLOB

3、 ENUM

4、 CHAR

5、 TEXT

25、MySQL 数据库作发布系统的存储,一天五万条以上的增量,

预计运维三年,怎么优化?

1、 设 计 良 好 的 数 据 库 结 构 , 允 许 部 分 数 据 冗 余 , 尽 量 避 免 join 查 询 , 提 高 效 率 。

2、 选 择 合 适 的 表 字 段 数 据 类 型 和 存 储 引 擎 , 适 当 的 添 加 索 引 。

3、 MySQL 库 主 从 读 写 分 离 。

4、 找 规 律 分 表 , 减 少 单 表 中 的 数 据 量 提 高 查 询 速 度 。

5、 添 加 缓 存 机 制 , 比 如 memcached, apc 等 。

6、 不 经 常 改 动 的 页 面 , 生 成 静 态 页 面 。

7、 书 写 高 效 率 的 SQL。 比 如 SELECT * FROM TABEL 改 为 SELECT field_1,

field_2, field_3 FROM TABLE.

26、锁的优化策略

第 143 页 共 485 页
1、 读 写 分 离

2、 分 段 加 锁

3、 减 少 锁 持 有 的 时 间

4.多 个 线 程 尽 量 以 相 同 的 顺 序 去 获 取 资 源

不能将锁的粒度过于细化,不然可能会出现线程的加锁和释放次数过多,反而效

率不如一次加一把大锁。

27、索引的底层实现原理和优化

B+树 , 经 过 优 化 的 B+树

主 要 是 在 所 有 的 叶 子 结 点 中 增 加 了 指 向 下 一 个 叶 子 节 点 的 指 针 , 因 此 InnoDB 建

议为大部分表使用默认自增的主键作为主索引。

28、什么情况下设置了索引但无法使用

1、 以 “ %” 开 头 的 LIKE 语 句 , 模 糊 匹 配

2、 OR 语 句 前 后 没 有 同 时 使 用 索 引

3、数 据 类 型 出 现 隐 式 转 化( 如 varchar 不 加 单 引 号 的 话 可 能 会 自 动 转 换 为 int 型 )

29、实践中如何优化 MySQL

第 144 页 共 485 页
最好是按照以下顺序优化:

1、 SQL 语 句 及 索 引 的 优 化

2、 数 据 库 表 结 构 的 优 化

3、 系 统 配 置 的 优 化

4、 硬 件 的 优 化

详 细 可 以 查 看 阿 里 P8 架 构 师 谈 :MySQL 慢 查 询 优 化 、索 引 优 化 、以 及 表 等 优 化

总结

30、优化数据库的方法

1、选 取 最 适 用 的 字 段 属 性 ,尽 可 能 减 少 定 义 字 段 宽 度 ,尽 量 把 字 段 设 置 NOTNULL,

例 如 ’ 省 份 ’ 、 ’ 性 别 ’ 最 好 适 用 ENUM

2、 使 用 连 接 (JOIN)来 代 替 子 查 询

3、 适 用 联 合 (UNION)来 代 替 手 动 创 建 的 临 时 表

4、 事 务 处 理

5、 锁 定 表 、 优 化 事 务 处 理

6、 适 用 外 键 , 优 化 锁 定 表

7、 建 立 索 引

8、 优 化 查 询 语 句

第 145 页 共 485 页
31、简单描述 MySQL 中,索引,主键,唯一索引,联合索引

的区别,对数据库的性能有什么影响(从读写两方面)

索 引 是 一 种 特 殊 的 文 件 (InnoDB 数 据 表 上 的 索 引 是 表 空 间 的 一 个 组 成 部 分 ), 它 们

包含着对数据表里所有记录的引用指针。

普 通 索 引 (由 关 键 字 KEY 或 INDEX 定 义 的 索 引 )的 唯 一 任 务 是 加 快 对 数 据 的 访 问 速

度。

普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼

此 各 不 相 同 的 值 , 在 为 这 个 数 据 列 创 建 索 引 的 时 候 就 应 该 用 关 键 字 UNIQUE 把 它

定义为一个唯一索引。也就是说,唯一索引可以保证数据记录的唯一性。

主键,是一种特殊的唯一索引,在一张表中只能定义一个主键索引,主键用于唯

一 标 识 一 条 记 录 , 使 用 关 键 字 PRIMARY KEY 来 创 建 。

索 引 可 以 覆 盖 多 个 数 据 列 ,如 像 INDEX(columnA, columnB)索 引 ,这 就 是 联 合 索

引。

索引可以极大的提高数据的查询速度,但是会降低插入、删除、更新表的速度,

因为在执行这些写操作时,还要操作索引文件。

32、数据库中的事务是什么?

事 务 ( transaction) 是 作 为 一 个 单 元 的 一 组 有 序 的 数 据 库 操 作 。 如 果 组 中 的 所 有

操作都成功,则认为事务成功,即使只有一个操作失败,事务也不成功。如果所

第 146 页 共 485 页
有操作完成,事务则提交,其修改将作用于所有其他数据库进程。如果一个操作

失败,则事务将回滚,该事务所有操作的影响都将取消。

事务特性:

1、 原 子 性 : 即 不 可 分 割 性 , 事 务 要 么 全 部 被 执 行 , 要 么 就 全 部 不 被 执 行 。

2、一 致 性 或 可 串 性 。事 务 的 执 行 使 得 数 据 库 从 一 种 正 确 状 态 转 换 成 另 一 种 正 确 状

3、隔 离 性 。在 事 务 正 确 提 交 之 前 ,不 允 许 把 该 事 务 对 数 据 的 任 何 改 变 提 供 给 任 何

其他事务,

4、持 久 性 。事 务 正 确 提 交 后 ,其 结 果 将 永 久 保 存 在 数 据 库 中 ,即 使 在 事 务 提 交 后

有了其他故障,事务的处理结果也会得到保存。

或者这样理解:

事 务 就 是 被 绑 定 在 一 起 作 为 一 个 逻 辑 工 作 单 元 的 SQL 语 句 分 组 , 如 果 任 何 一 个 语

句操作失败那么整个操作就被失败,以后操作就会回滚到操作前状态,或者是上

有个节点。为了确保要么执行,要么不执行,就可以使用事务。要将有组语句作

为 事 务 考 虑 , 就 需 要 通 过 ACID 测 试 , 即 原 子 性 , 一 致 性 , 隔 离 性 和 持 久 性 。

33、SQL 注入漏洞产生的原因?如何防止?

SQL 注 入 产 生 的 原 因 : 程 序 开 发 过 程 中 不 注 意 规 范 书 写 sql 语 句 和 对 特 殊 字 符 进

行 过 滤 ,导 致 客 户 端 可 以 通 过 全 局 变 量 POST 和 GET 提 交 一 些 sql 语 句 正 常 执 行 。

防 止 SQL 注 入 的 方 式 :

开 启 配 置 文 件 中 的 magic_quotes_gpc 和 magic_quotes_runtime 设 置

第 147 页 共 485 页
执 行 sql 语 句 时 使 用 addslashes 进 行 sql 语 句 转 换

Sql 语 句 书 写 尽 量 不 要 省 略 双 引 号 和 单 引 号 。

过 滤 掉 sql 语 句 中 的 一 些 关 键 词 : update、 insert、 delete、 select、 * 。

提高数据库表和字段的命名技巧,对一些重要的字段根据程序的特点命名,取不

易被猜到的。

34、为表中得字段选择合适得数据类型

字 段 类 型 优 先 级 : 整 形 >date,time>enum,char>varchar>blob,text

优先考虑数字类型,其次是日期或者二进制类型,最后是字符串类型,同级别得

数据类型,应该优先选择占用空间小的数据类型

35、存储时期

Datatime:以 YYYY-MM-DD HH:MM:SS 格 式 存 储 时 期 时 间 , 精 确 到 秒 ,


占 用 8 个 字 节 得 存 储 空 间 , datatime 类 型 与 时 区 无 关

Timestamp:以 时 间 戳 格 式 存 储 ,占 用 4 个 字 节 ,范 围 小 1970-1-1 到 2038-1-19,

显示依赖于所指定得时区,默认在第一个列行的数据修改时可以自动得修改

timestamp 列 得 值

Date:( 生 日 ) 占 用 得 字 节 数 比 使 用 字 符 串 .datatime.int 储 存 要 少 , 使 用 date 只

需要 3 个字节,存储日期月份,还可以利用日期时间函数进行日期间得计算

Time:存 储 时 间 部 分 得 数 据

注 意 :不 要 使 用 字 符 串 类 型 来 存 储 日 期 时 间 数 据( 通 常 比 字 符 串 占 用 得 储 存 空 间 小 ,

在进行查找过滤可以利用日期得函数)

使 用 int 存 储 日 期 时 间 不 如 使 用 timestamp 类 型

第 148 页 共 485 页
36、对于关系型数据库而言,索引是相当重要的概念,请回答

有关索引的几个问题:

1、 索 引 的 目 的 是 什 么 ?

快速访问数据表中的特定信息,提高检索速度

创建唯一性索引,保证数据库表中每一行数据的唯一性。

加速表和表之间的连接

使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间

2、 索 引 对 数 据 库 系 统 的 负 面 影 响 是 什 么 ?

负面影响:

创建索引和维护索引需要耗费时间,这个时间随着数据量的增加而增加;索引需

要占用物理空间,不光是表需要占用数据空间,每个索引也需要占用物理空间;

当对表进行增、删、改、的时候索引也要动态维护,这样就降低了数据的维护速

度。

3、 为 数 据 表 建 立 索 引 的 原 则 有 哪 些 ?

在最频繁使用的、用以缩小查询范围的字段上建立索引。

在频繁使用的、需要排序的字段上建立索引

4、 什 么 情 况 下 不 宜 建 立 索 引 ?

第 149 页 共 485 页
对于查询中很少涉及的列或者重复值比较多的列,不宜建立索引。

对 于 一 些 特 殊 的 数 据 类 型 , 不 宜 建 立 索 引 , 比 如 文 本 字 段 ( text) 等

37、解释 MySQL 外连接、内连接与自连接的区别

先 说 什 么 是 交 叉 连 接 : 交 叉 连 接 又 叫 笛 卡 尔 积 ,它 是 指 不 使 用 任 何 条 件 ,直 接 将 一

个表的所有记录和另一个表中的所有记录一一匹配。

内 连 接 则 是 只 有 条 件 的 交 叉 连 接 ,根 据 某 个 条 件 筛 选 出 符 合 条 件 的 记 录 ,不 符 合

条件的记录不会出现在结果集中,即内连接只连接匹配的行。

外 连 接 其 结 果 集 中 不 仅 包 含 符 合 连 接 条 件 的 行 ,而 且 还 会 包 括 左 表 、右 表 或 两 个

表中

的所有数据行,这三种情况依次称之为左外连接,右外连接,和全外连接。

左外连接,也称左连接,左表为主表,左表中的所有记录都会出现在结果集中,

对于那些在右表中并没有匹配的记录,仍然要显示,右边对应的那些字段值以

NULL 来 填 充 。右 外 连 接 ,也 称 右 连 接 ,右 表 为 主 表 ,右 表 中 的 所 有 记 录 都 会 出 现

在 结 果 集 中 。 左 连 接 和 右 连 接 可 以 互 换 , MySQL 目 前 还 不 支 持 全 外 连 接 。

38、Myql 中的事务回滚机制概述

事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个

不可分割的工作单位,事务回滚是指将该事务已经完成的对数据库的更新操作撤

销。

要同时修改数据库中两个不同表时,如果它们不是一个事务的话,当第一个表修

改完,可能第二个表修改过程中出现了异常而没能修改,此时就只有第二个表依

旧是未修改之前的状态,而第一个表已经被修改完毕。而当你把它们设定为一个

第 150 页 共 485 页
事务的时候,当第一个表修改完,第二表修改出现异常而没能修改,第一个表和

第二个表都要回到未修改的状态,这就是所谓的事务回滚

39、SQL 语言包括哪几部分?每部分都有哪些操作关键字?

SQL 语 言 包 括 数 据 定 义 (DDL)、数 据 操 纵 (DML),数 据 控 制 (DCL)和 数 据 查 询( DQL)

四个部分。

数 据 定 义 : Create Table,Alter Table,Drop Table, Craete/Drop Index 等

数 据 操 纵 : Select ,insert,update,delete,

数 据 控 制 : grant,revoke

数 据 查 询 : select

40、完整性约束包括哪些?

数 据 完 整 性 (Data Integrity)是 指 数 据 的 精 确 (Accuracy)和 可 靠 性 (Reliability)。

分为以下四类:

1、 实 体 完 整 性 : 规 定 表 的 每 一 行 在 表 中 是 惟 一 的 实 体 。

2、域 完 整 性 :是 指 表 中 的 列 必 须 满 足 某 种 特 定 的 数 据 类 型 约 束 ,其 中 约 束 又 包 括

取值范围、精度等规定。

3、参 照 完 整 性 :是 指 两 个 表 的 主 关 键 字 和 外 关 键 字 的 数 据 应 一 致 ,保 证 了 表 之 间

的数据的一致性,防止了数据丢失或无意义的数据在数据库中扩散。

第 151 页 共 485 页
4、用 户 定 义 的 完 整 性 :不 同 的 关 系 数 据 库 系 统 根 据 其 应 用 环 境 的 不 同 ,往 往 还 需

要一些特殊的约束条件。用户定义的完整性即是针对某个特定关系数据库的约束

条件,它反映某一具体应用必须满足的语义要求。

与 表 有 关 的 约 束 :包 括 列 约 束 (NOT NULL( 非 空 约 束 ))和 表 约 束 (PRIMARY KEY、

foreign key、 check、 UNIQUE) 。

41、什么是锁?

答:数据库是一个多用户使用的共享资源。当多个用户并发地存取数据时,在数

据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可

能会读取和存储不正确的数据,破坏数据库的一致性。

加锁是实现数据库并发控制的一个非常重要的技术。当事务在对某个数据对象进

行操作前,先向系统发出请求,对其加锁。加锁后事务就对该数据对象有了一定

的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更新操作。

基本锁类型:锁包括行级锁和表级锁

42、什么叫视图?游标是什么?

答:视图是一种虚拟的表,具有和物理表相同的功能。可以对视图进行增,改,

查,操作,视图通常是有一个表或者多个表的行或列的子集。对视图的修改不影

响基本表。它使得我们获取数据更容易,相比多表查询。

游标:是对查询出来的结果集作为一个单元来有效的处理。游标可以定在该单元

中的特定行,从结果集的当前行检索一行或多行。可以对结果集当前行做修改。

一般不使用游标,但是需要逐条处理数据的时候,游标显得十分重要。

第 152 页 共 485 页
43、什么是存储过程?用什么来调用?

答 : 存 储 过 程 是 一 个 预 编 译 的 SQL 语 句 , 优 点 是 允 许 模 块 化 的 设 计 , 就 是 说 只 需

创 建 一 次 , 以 后 在 该 程 序 中 就 可 以 调 用 多 次 。 如 果 某 次 操 作 需 要 执 行 多 次 SQL,

使 用 存 储 过 程 比 单 纯 SQL 语 句 执 行 要 快 。 可 以 用 一 个 命 令 对 象 来 调 用 存 储 过 程 。

44、如何通俗地理解三个范式?

答 :第 一 范 式 :1NF 是 对 属 性 的 原 子 性 约 束 ,要 求 属 性 具 有 原 子 性 ,不 可 再 分 解 ;

第 二 范 式 :2NF 是 对 记 录 的 惟 一 性 约 束 ,要 求 记 录 有 惟 一 标 识 ,即 实 体 的 惟 一 性 ;

第 三 范 式 : 3NF 是 对 字 段 冗 余 性 的 约 束 , 即 任 何 字 段 不 能 由 其 他 字 段 派 生 出 来 ,

它要求字段没有冗余。。

范式化设计优缺点:

优点:

可以尽量得减少数据冗余,使得更新快,体积小

缺 点 :对 于 查 询 需 要 多 个 表 进 行 关 联 , 减 少 写 得 效 率 增 加 读 得 效 率 , 更 难 进 行 索 引

优化

反范式化:

优 点 :可 以 减 少 表 得 关 联 , 可 以 更 好 得 进 行 索 引 优 化

第 153 页 共 485 页
缺 点 :数 据 冗 余 以 及 数 据 异 常 , 数 据 得 修 改 需 要 更 多 的 成 本

45、什么是基本表?什么是视图?

答 :基 本 表 是 本 身 独 立 存 在 的 表 ,在 SQL 中 一 个 关 系 就 对 应 一 个 表 。 视 图 是 从

一个或几个基本表导出的表。视图本身不独立存储在数据库中,是一个虚表

46、试述视图的优点?

答 : (1) 视 图 能 够 简 化 用 户 的 操 作 (2) 视 图 使 用 户 能 以 多 种 角 度 看 待 同 一 数 据 ;

(3) 视 图 为 数 据 库 提 供 了 一 定 程 度 的 逻 辑 独 立 性 ; (4) 视 图 能 够 对 机 密 数 据 提 供

安全保护。

47、 NULL 是什么意思

答 : NULL 这 个 值 表 示 UNKNOWN(未 知 ):它 不 表 示 “ ” (空 字 符 串 )。 对 NULL 这

个 值 的 任 何 比 较 都 会 生 产 一 个 NULL 值 。 您 不 能 把 任 何 值 与 一 个 NULL 值 进 行 比

较,并在逻辑上希望获得一个答案。

使 用 IS NULL 来 进 行 NULL 判 断

48、主键、外键和索引的区别?

主键、外键和索引的区别

定义:

第 154 页 共 485 页
主键–唯一标识一条记录,不能有重复的,不允许为空

外键–表的外键是另一表的主键, 外键可以有重复的, 可以是空值

索引–该字段没有重复值,但可以有一个空值

作用:

主键–用来保证数据完整性

外键–用来和其他表建立联系用的

索引–是提高查询排序的速度

个数:

主键–主键只能有一个

外键–一个表可以有多个外键

索引–一个表可以有多个唯一索引

49、你可以用什么来确保表格里的字段只接受特定范围里的值?

答 : Check 限 制 , 它 在 数 据 库 表 格 里 被 定 义 , 用 来 限 制 输 入 该 列 的 值 。

触发器也可以被用来限制数据库表格里的字段能够接受的值,但是这种办法要求

触发器在表格里被定义,这可能会在某些情况下影响到性能。

第 155 页 共 485 页
50、说说对 SQL 语句优化有哪些方法?(选择几条)

1、Where 子 句 中 :where 表 之 间 的 连 接 必 须 写 在 其 他 Where 条 件 之 前 ,那 些 可

以 过 滤 掉 最 大 数 量 记 录 的 条 件 必 须 写 在 Where 子 句 的 末 尾 .HAVING 最 后 。

2、 用 EXISTS 替 代 IN、 用 NOT EXISTS 替 代 NOT IN。

3、 避 免 在 索 引 列 上 使 用 计 算

4、 避 免 在 索 引 列 上 使 用 IS NULL 和 IS NOT NULL

5、对 查 询 进 行 优 化 ,应 尽 量 避 免 全 表 扫 描 ,首 先 应 考 虑 在 where 及 order by 涉

及的列上建立索引。

6、 应 尽 量 避 免 在 where 子 句 中 对 字 段 进 行 null 值 判 断 , 否 则 将 导 致 引 擎 放 弃

使用索引而进行全表扫描

7、应 尽 量 避 免 在 where 子 句 中 对 字 段 进 行 表 达 式 操 作 ,这 将 导 致 引 擎 放 弃 使 用

索引而进行全表扫描

Java 并发编程(一)

1、在 java 中守护线程和本地线程区别?

java 中 的 线 程 分 为 两 种 : 守 护 线 程 ( Daemon) 和 用 户 线 程 ( User) 。

第 156 页 共 485 页
任 何 线 程 都 可 以 设 置 为 守 护 线 程 和 用 户 线 程 ,通 过 方 法 Thread.setDaemon(bool

on);true 则 把 该 线 程 设 置 为 守 护 线 程 ,反 之 则 为 用 户 线 程 。Thread.setDaemon()

必 须 在 Thread.start()之 前 调 用 , 否 则 运 行 时 会 抛 出 异 常 。

两者的区别:

唯 一 的 区 别 是 判 断 虚 拟 机 (JVM)何 时 离 开 ,Daemon 是 为 其 他 线 程 提 供 服 务 ,如 果

全 部 的 User Thread 已 经 撤 离 , Daemon 没 有 可 服 务 的 线 程 , JVM 撤 离 。 也 可

以 理 解 为 守 护 线 程 是 JVM 自 动 创 建 的 线 程 ( 但 不 一 定 ) , 用 户 线 程 是 程 序 创 建 的

线 程 ; 比 如 JVM 的 垃 圾 回 收 线 程 是 一 个 守 护 线 程 , 当 所 有 线 程 已 经 撤 离 , 不 再 产

生 垃 圾 ,守 护 线 程 自 然 就 没 事 可 干 了 ,当 垃 圾 回 收 线 程 是 Java 虚 拟 机 上 仅 剩 的 线

程 时 , Java 虚 拟 机 会 自 动 离 开 。

扩 展 : Thread Dump 打 印 出 来 的 线 程 信 息 , 含 有 daemon 字 样 的 线 程 即 为 守 护

进 程 ,可 能 会 有 :服 务 守 护 进 程 、编 译 守 护 进 程 、windows 下 的 监 听 Ctrl+break

的 守 护 进 程 、 Finalizer 守 护 进 程 、 引 用 处 理 守 护 进 程 、 GC 守 护 进 程 。

2、线程与进程的区别?

进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。

一 个 程 序 至 少 有 一 个 进 程 ,一 个 进 程 至 少 有 一 个 线 程 。

3、什么是多线程中的上下文切换?

多 线 程 会 共 同 使 用 一 组 计 算 机 上 的 CPU,而 线 程 数 大 于 给 程 序 分 配 的 CPU 数 量 时 ,

为 了 让 各 个 线 程 都 有 执 行 的 机 会 ,就 需 要 轮 转 使 用 CPU。不 同 的 线 程 切 换 使 用 CPU

发生的切换数据等就是上下文切换。

第 157 页 共 485 页
4、死锁与活锁的区别,死锁与饥饿的区别?

死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成

的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

产生死锁的必要条件:

1、 互 斥 条 件 : 所 谓 互 斥 就 是 进 程 在 某 一 时 间 内 独 占 资 源 。

2、 请 求 与 保 持 条 件 : 一 个 进 程 因 请 求 资 源 而 阻 塞 时 , 对 已 获 得 的 资 源 保 持 不 放 。

3、 不 剥 夺 条 件 :进 程 已 获 得 资 源 , 在 末 使 用 完 之 前 , 不 能 强 行 剥 夺 。

4、 循 环 等 待 条 件 :若 干 进 程 之 间 形 成 一 种 头 尾 相 接 的 循 环 等 待 资 源 关 系 。

活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,

失败,尝试,失败。

活 锁 和 死 锁 的 区 别 在 于 ,处 于 活 锁 的 实 体 是 在 不 断 的 改 变 状 态 ,所 谓 的“ 活 ”, 而

处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。

饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执

行的状态。

Java 中 导 致 饥 饿 的 原 因 :

1、 高 优 先 级 线 程 吞 噬 所 有 的 低 优 先 级 线 程 的 CPU 时 间 。

2、线 程 被 永 久 堵 塞 在 一 个 等 待 进 入 同 步 块 的 状 态 ,因 为 其 他 线 程 总 是 能 在 它 之 前

持续地对该同步块进行访问。

3、 线 程 在 等 待 一 个 本 身 也 处 于 永 久 等 待 完 成 的 对 象 (比 如 调 用 这 个 对 象 的 wait 方

法 ), 因 为 其 他 线 程 总 是 被 持 续 地 获 得 唤 醒 。

5、Java 中用到的线程调度算法是什么?

采用时间片轮转的方式。可以设置线程的优先级,会映射到下层的系统上面的优

先级上,如非特别需要,尽量不要用,防止线程饥饿。

第 158 页 共 485 页
6、什么是线程组,为什么在 Java 中不推荐使用?

ThreadGroup 类 ,可 以 把 线 程 归 属 到 某 一 个 线 程 组 中 ,线 程 组 中 可 以 有 线 程 对 象 ,

也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式。

为什么不推荐使用?因为使用有很多的安全隐患吧,没有具体追究,如果需要使

用,推荐使用线程池。

7、为什么使用 Executor 框架?

每 次 执 行 任 务 创 建 线 程 new Thread()比 较 消 耗 性 能 , 创 建 一 个 线 程 是 比 较 耗 时 、

耗资源的。

调 用 new Thread()创 建 的 线 程 缺 乏 管 理 ,被 称 为 野 线 程 ,而 且 可 以 无 限 制 的 创 建 ,

线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的

频繁交替也会消耗很多系统资源。

接 使 用 new Thread() 启 动 的 线 程 不 利 于 扩 展 , 比 如 定 时 执 行 、 定 期 执 行 、 定 时

定期执行、线程中断等都不便实现。

8、在 Java 中 Executor 和 Executors 的区别?

Executors 工 具 类 的 不 同 方 法 按 照 我 们 的 需 求 创 建 了 不 同 的 线 程 池 , 来 满 足 业 务

的需求。

Executor 接 口 对 象 能 执 行 我 们 的 线 程 任 务 。

ExecutorService 接 口 继 承 了 Executor 接 口 并 进 行 了 扩 展 , 提 供 了 更 多 的 方 法 我

们能获得任务执行的状态并且可以获取任务的返回值。

使 用 ThreadPoolExecutor 可 以 创 建 自 定 义 线 程 池 。

第 159 页 共 485 页
Future 表 示 异 步 计 算 的 结 果 ,他 提 供 了 检 查 计 算 是 否 完 成 的 方 法 ,以 等 待 计 算 的

完 成 , 并 可 以 使 用 get()方 法 获 取 计 算 的 结 果 。

9、如何在 Windows 和 Linux 上查找哪个线程使用的 CPU 时

间最长?

参考:

http://daiguahub.com/2016/07/31/使 用 jstack 找 出 消 耗 CPU 最 多 的 线 程 代 码

10、什么是原子操作?在 Java Concurrency API 中有哪些原

子类(atomic classes)?

原 子 操 作 ( atomic operation) 意 为 ” 不 可 被 中 断 的 一 个 或 一 系 列 操 作 ” 。

处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作。

在 Java 中 可 以 通 过 锁 和 循 环 CAS 的 方 式 来 实 现 原 子 操 作 。 CAS 操 作 — —

Compare & Set,或 是 Compare & Swap,现 在 几 乎 所 有 的 CPU 指 令 都 支 持 CAS

的原子操作。

原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境

下避免数据不一致必须的手段。

int++并 不 是 一 个 原 子 操 作 ,所 以 当 一 个 线 程 读 取 它 的 值 并 加 1 时 ,另 外 一 个 线 程

有可能会读到之前的值,这就会引发错误。

为 了 解 决 这 个 问 题 ,必 须 保 证 增 加 操 作 是 原 子 的 ,在 JDK1.5 之 前 我 们 可 以 使 用 同

步 技 术 来 做 到 这 一 点 。 到 JDK1.5, java.util.concurrent.atomic 包 提 供 了 int 和

long 类 型 的 原 子 包 装 类 , 它 们 可 以 自 动 的 保 证 对 于 他 们 的 操 作 是 原 子 的 并 且 不 需

要使用同步。

第 160 页 共 485 页
java.util.concurrent 这 个 包 里 面 提 供 了 一 组 原 子 类 。 其 基 本 的 特 性 就 是 在 多 线 程

环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当

某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像

自 旋 锁 一 样 , 一 直 等 到 该 方 法 执 行 完 成 , 才 由 JVM 从 等 待 队 列 中 选 择 一 个 另 一 个

线程进入,这只是一种逻辑上的理解。

原 子 类 : AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference

原 子 数 组 : AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray

原 子 属 性 更 新 器 : AtomicLongFieldUpdater, AtomicIntegerFieldUpdater,

AtomicReferenceFieldUpdater

解 决 ABA 问 题 的 原 子 类 : AtomicMarkableReference( 通 过 引 入 一 个 boolean

来 反 映 中 间 有 没 有 变 过 ) , AtomicStampedReference( 通 过 引 入 一 个 int 来 累

加来反映中间有没有变过)

11、Java Concurrency API 中的 Lock 接口(Lock interface)

是什么?对比同步它有什么优势?

Lock 接 口 比 同 步 方 法 和 同 步 块 提 供 了 更 具 扩 展 性 的 锁 操 作 。

他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的

条件对象。

它的优势有:

可以使锁更公平

可以使线程在等待锁的时候响应中断

可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间

可以在不同的范围,以不同的顺序获取和释放锁

第 161 页 共 485 页
整 体 上 来 说 Lock 是 synchronized 的 扩 展 版 , Lock 提 供 了 无 条 件 的 、 可 轮 询 的

(tryLock 方 法 )、 定 时 的 (tryLock 带 参 方 法 )、 可 中 断 的 (lockInterruptibly)、 可 多

条 件 队 列 的 (newCondition 方 法 )锁 操 作 。 另 外 Lock 的 实 现 类 基 本 都 支 持 非 公 平

锁 (默 认 )和 公 平 锁 , synchronized 只 支 持 非 公 平 锁 , 当 然 , 在 大 部 分 情 况 下 , 非

公平锁是高效的选择。

12、什么是 Executors 框架?

Executor 框 架 是 一 个 根 据 一 组 执 行 策 略 调 用 , 调 度 , 执 行 和 控 制 的 异 步 任 务 的 框

架。

无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的

解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。利用

Executors 框 架 可 以 非 常 方 便 的 创 建 一 个 线 程 池 。

13、什么是阻塞队列?阻塞队列的实现原理是什么?如何使用

阻塞队列来实现生产者-消费者模型?

阻 塞 队 列 ( BlockingQueue) 是 一 个 支 持 两 个 附 加 操 作 的 队 列 。

这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当

队列满时,存储元素的线程会等待队列可用。

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消

费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者

也只从容器里拿元素。

JDK7 提 供 了 7 个 阻 塞 队 列 。 分 别 是 :

第 162 页 共 485 页
ArrayBlockingQueue : 一 个 由 数 组 结 构 组 成 的 有 界 阻 塞 队 列 。

LinkedBlockingQueue : 一 个 由 链 表 结 构 组 成 的 有 界 阻 塞 队 列 。

PriorityBlockingQueue : 一 个 支 持 优 先 级 排 序 的 无 界 阻 塞 队 列 。

DelayQueue: 一 个 使 用 优 先 级 队 列 实 现 的 无 界 阻 塞 队 列 。

SynchronousQueue: 一 个 不 存 储 元 素 的 阻 塞 队 列 。

LinkedTransferQueue: 一 个 由 链 表 结 构 组 成 的 无 界 阻 塞 队 列 。

LinkedBlockingDeque: 一 个 由 链 表 结 构 组 成 的 双 向 阻 塞 队 列 。

Java 5 之 前 实 现 同 步 存 取 时 , 可 以 使 用 普 通 的 一 个 集 合 , 然 后 在 使 用 线 程 的 协 作

和线程同步可以实现生产者,消费者模式,主要的技术就是用好,

wait ,notify,notifyAll,sychronized 这 些 关 键 字 。 而 在 java 5 之 后 , 可 以 使 用 阻

塞队列来实现,此方式大大简少了代码量,使得多线程编程更加容易,安全方面

也有保障。

BlockingQueue 接 口 是 Queue 的 子 接 口 , 它 的 主 要 用 途 并 不 是 作 为 容 器 , 而 是

作为线程同步的的工具,因此他具有一个很明显的特性,当生产者线程试图向

BlockingQueue 放 入 元 素 时 , 如 果 队 列 已 满 , 则 线 程 被 阻 塞 , 当 消 费 者 线 程 试 图

从中取出一个元素时,如果队列为空,则该线程会被阻塞,正是因为它所具有这

个 特 性 , 所 以 在 程 序 中 多 个 线 程 交 替 向 BlockingQueue 中 放 入 元 素 , 取 出 元 素 ,

它可以很好的控制线程之间的通信。

阻 塞 队 列 使 用 最 经 典 的 场 景 就 是 socket 客 户 端 数 据 的 读 取 和 解 析 ,读 取 数 据 的 线

程不断将数据放入队列,然后解析线程不断从队列取数据解析。

14、什么是 Callable 和 Future?

Callable 接 口 类 似 于 Runnable,从 名 字 就 可 以 看 出 来 了 ,但 是 Runnable 不 会 返

回 结 果 , 并 且 无 法 抛 出 返 回 结 果 的 异 常 , 而 Callable 功 能 更 强 大 一 些 , 被 线 程 执

行 后 ,可 以 返 回 值 ,这 个 返 回 值 可 以 被 Future 拿 到 ,也 就 是 说 ,Future 可 以 拿 到

异步执行任务的返回值。

可 以 认 为 是 带 有 回 调 的 Runnable。

第 163 页 共 485 页
Future 接 口 表 示 异 步 任 务 ,是 还 没 有 完 成 的 任 务 给 出 的 未 来 结 果 。所 以 说 Callable

用 于 产 生 结 果 , Future 用 于 获 取 结 果 。

15、什么是 FutureTask?使用 ExecutorService 启动任务。

在 Java 并 发 程 序 中 FutureTask 表 示 一 个 可 以 取 消 的 异 步 运 算 。 它 有 启 动 和 取 消

运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才

能 取 回 ,如 果 运 算 尚 未 完 成 get 方 法 将 会 阻 塞 。一 个 FutureTask 对 象 可 以 对 调 用

了 Callable 和 Runnable 的 对 象 进 行 包 装 ,由 于 FutureTask 也 是 调 用 了 Runnable

接 口 所 以 它 可 以 提 交 给 Executor 来 执 行 。

16、什么是并发容器的实现?

何 为 同 步 容 器 :可 以 简 单 地 理 解 为 通 过 synchronized 来 实 现 同 步 的 容 器 ,如 果 有

多 个 线 程 调 用 同 步 容 器 的 方 法 , 它 们 将 会 串 行 执 行 。 比 如 Vector, Hashtable,

以 及 Collections.synchronizedSet, synchronizedList 等 方 法 返 回 的 容 器 。

可 以 通 过 查 看 Vector, Hashtable 等 这 些 同 步 容 器 的 实 现 代 码 , 可 以 看 到 这 些 容

器实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上

关 键 字 synchronized。

并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,

例 如 在 ConcurrentHashMap 中 采 用 了 一 种 粒 度 更 细 的 加 锁 机 制 , 可 以 称 为 分 段

锁 , 在 这 种 锁 机 制 下 , 允 许 任 意 数 量 的 读 线 程 并 发 地 访 问 map, 并 且 执 行 读 操 作

的 线 程 和 写 操 作 的 线 程 也 可 以 并 发 的 访 问 map, 同 时 允 许 一 定 数 量 的 写 操 作 线 程

并 发 地 修 改 map, 所 以 它 可 以 在 并 发 环 境 下 实 现 更 高 的 吞 吐 量 。

17、多线程同步和互斥有几种实现方法,都是什么?

第 164 页 共 485 页
线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程

的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。

线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若

干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它

要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成

是一种特殊的线程同步。

线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式

就 是 指 利 用 系 统 内 核 对 象 的 单 一 性 来 进 行 同 步 ,使 用 时 需 要 切 换 内 核 态 与 用 户 态 ,

而用户模式就是不需要切换到内核态,只在用户态完成操作。

用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模

式下的方法有:事件,信号量,互斥量。

18、什么是竞争条件?你怎样发现和解决竞争?

当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的

顺 序 时 , 则 我 们 认 为 这 发 生 了 竞 争 条 件 ( race condition) 。

19、你将如何使用 thread dump?你将如何分析 Thread

dump?

新 建 状 态 ( New)

用 new 语 句 创 建 的 线 程 处 于 新 建 状 态 ,此 时 它 和 其 他 Java 对 象 一 样 ,仅 仅 在 堆 区

中被分配了内存。

就 绪 状 态 ( Runnable)

第 165 页 共 485 页
当 一 个 线 程 对 象 创 建 后 ,其 他 线 程 调 用 它 的 start()方 法 ,该 线 程 就 进 入 就 绪 状 态 ,

Java 虚 拟 机 会 为 它 创 建 方 法 调 用 栈 和 程 序 计 数 器 。 处 于 这 个 状 态 的 线 程 位 于 可 运

行 池 中 , 等 待 获 得 CPU 的 使 用 权 。

运 行 状 态 ( Running)

处 于 这 个 状 态 的 线 程 占 用 CPU, 执 行 程 序 代 码 。 只 有 处 于 就 绪 状 态 的 线 程 才 有 机

会转到运行状态。

阻 塞 状 态 ( Blocked)

阻 塞 状 态 是 指 线 程 因 为 某 些 原 因 放 弃 CPU, 暂 时 停 止 运 行 。 当 线 程 处 于 阻 塞 状 态

时 , Java 虚 拟 机 不 会 给 线 程 分 配 CPU。 直 到 线 程 重 新 进 入 就 绪 状 态 , 它 才 有 机 会

转到运行状态。

阻塞状态可分为以下 3 种:

位 于 对 象 等 待 池 中 的 阻 塞 状 态 ( Blocked in object’ s wait pool) :

当 线 程 处 于 运 行 状 态 时 , 如 果 执 行 了 某 个 对 象 的 wait()方 法 , Java 虚 拟 机 就 会 把

线程放到这个对象的等待池中,这涉及到“线程通信”的内容。

位 于 对 象 锁 池 中 的 阻 塞 状 态 ( Blocked in object’ s lock pool) :

当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已

经 被 其 他 线 程 占 用 , Java 虚 拟 机 就 会 把 这 个 线 程 放 到 这 个 对 象 的 锁 池 中 , 这 涉 及

到“线程同步”的内容。

其 他 阻 塞 状 态 ( Otherwise Blocked) :

当 前 线 程 执 行 了 sleep()方 法 ,或 者 调 用 了 其 他 线 程 的 join()方 法 ,或 者 发 出 了 I/O

请求时,就会进入这个状态。

第 166 页 共 485 页
死 亡 状 态 ( Dead)

当 线 程 退 出 run()方 法 时 , 就 进 入 死 亡 状 态 , 该 线 程 结 束 生 命 周 期 。

我 们 运 行 之 前 的 那 个 死 锁 代 码 SimpleDeadLock.java, 然 后 尝 试 输 出 信 息 (

/* 时间,jvm 信息 */
2017-11-01 17:36:28
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed
mode):

/* 线程名称:DestroyJavaVM
编号:#13
优先级:5
系统优先级:0
jvm 内部线程 id:0x0000000001c88800
对应系统线程 id(NativeThread ID):0x1c18
线程状态: waiting on condition [0x0000000000000000] (等待某个条件)
线程详细状态:java.lang.Thread.State: RUNNABLE 及之后所有*/
"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x0000000001c88800
nid=0x1c18 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Thread-1" #12 prio=5 os_prio=0 tid=0x0000000018d49000


nid=0x17b8 waiting for monitor entry [0x0000000019d7f000]
/* 线程状态:阻塞(在对象同步上)
代码位置:at
com.leo.interview.SimpleDeadLock$B.run(SimpleDeadLock.java:56)
等待锁:0x00000000d629b4d8
已经获得锁:0x00000000d629b4e8*/
java.lang.Thread.State: BLOCKED (on object monitor)
at
com.leo.interview.SimpleDeadLock$B.run(SimpleDeadLock.java:56)

第 167 页 共 485 页
- waiting to lock <0x00000000d629b4d8> (a java.lang.Object)
- locked <0x00000000d629b4e8> (a java.lang.Object)

"Thread-0" #11 prio=5 os_prio=0 tid=0x0000000018d44000 nid=0x1ebc


waiting for monitor entry [0x000000001907f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at
com.leo.interview.SimpleDeadLock$A.run(SimpleDeadLock.java:34)
- waiting to lock <0x00000000d629b4e8> (a java.lang.Object)
- locked <0x00000000d629b4d8> (a java.lang.Object)

"Service Thread" #10 daemon prio=9 os_prio=0


tid=0x0000000018ca5000 nid=0x1264 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #9 daemon prio=9 os_prio=2


tid=0x0000000018c46000 nid=0xb8c waiting on condition
[0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2


tid=0x0000000018be4800 nid=0x1db4 waiting on condition
[0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2


tid=0x0000000018be3800 nid=0x810 waiting on condition
[0x0000000000000000]
java.lang.Thread.State: RUNNABLE

第 168 页 共 485 页
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0
tid=0x0000000018bcc800 nid=0x1c24 runnable [0x00000000193ce000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at
java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x00000000d632b928> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x00000000d632b928> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at
com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:6
4)

"Attach Listener" #5 daemon prio=5 os_prio=2


tid=0x0000000017781800 nid=0x524 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2


tid=0x000000001778f800 nid=0x1b08 waiting on condition
[0x0000000000000000]
java.lang.Thread.State: RUNNABLE

第 169 页 共 485 页
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001776a800
nid=0xdac in Object.wait() [0x0000000018b6f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d6108ec8> (a
java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000d6108ec8> (a
java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=2


tid=0x0000000017723800 nid=0x1670 in Object.wait()
[0x00000000189ef000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d6106b68> (a
java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000d6106b68> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x000000001771b800 nid=0x604 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000001c9d800


nid=0x9f0 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000001c9f000


nid=0x154c runnable

第 170 页 共 485 页
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000001ca0800
nid=0xcd0 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000001ca2000


nid=0x1e58 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000018c5a000


nid=0x1b58 waiting on condition

JNI global references: 33

/* 此处可以看待死锁的相关信息! */
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x0000000017729fc8 (object
0x00000000d629b4d8, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x0000000017727738 (object
0x00000000d629b4e8, a java.lang.Object),
which is held by "Thread-1"

Java stack information for the threads listed above:


==============================================
=====
"Thread-1":
at
com.leo.interview.SimpleDeadLock$B.run(SimpleDeadLock.java:56)

第 171 页 共 485 页
- waiting to lock <0x00000000d629b4d8> (a java.lang.Object)
- locked <0x00000000d629b4e8> (a java.lang.Object)
"Thread-0":
at
com.leo.interview.SimpleDeadLock$A.run(SimpleDeadLock.java:34)
- waiting to lock <0x00000000d629b4e8> (a java.lang.Object)
- locked <0x00000000d629b4d8> (a java.lang.Object)

Found 1 deadlock.

/* 内存使用状况,详情得看 JVM 方面的书 */


Heap
PSYoungGen total 37888K, used 4590K [0x00000000d6100000,
0x00000000d8b00000, 0x0000000100000000)
eden space 32768K, 14% used
[0x00000000d6100000,0x00000000d657b968,0x00000000d8100000)
from space 5120K, 0% used
[0x00000000d8600000,0x00000000d8600000,0x00000000d8b00000)
to space 5120K, 0% used
[0x00000000d8100000,0x00000000d8100000,0x00000000d8600000)
ParOldGen total 86016K, used 0K [0x0000000082200000,
0x0000000087600000, 0x00000000d6100000)
object space 86016K, 0% used
[0x0000000082200000,0x0000000082200000,0x0000000087600000)
Metaspace used 3474K, capacity 4500K, committed 4864K,
reserved 1056768K
class space used 382K, capacity 388K, committed 512K, reserved
1048576K

第 172 页 共 485 页
20、为什么我们调用 start()方法时会执行 run()方法,为什么

我们不能直接调用 run()方法?

当 你 调 用 start()方 法 时 你 将 创 建 新 的 线 程 , 并 且 执 行 在 run()方 法 里 的 代 码 。

但 是 如 果 你 直 接 调 用 run()方 法 ,它 不 会 创 建 新 的 线 程 也 不 会 执 行 调 用 线 程 的 代 码 ,

只 会 把 run 方 法 当 作 普 通 方 法 去 执 行 。

21、Java 中你怎样唤醒一个阻塞的线程?

在 Java 发 展 史 上 曾 经 使 用 suspend()、resume()方 法 对 于 线 程 进 行 阻 塞 唤 醒 ,但

随之出现很多问题,比较典型的还是死锁问题。

解 决 方 案 可 以 使 用 以 对 象 为 目 标 的 阻 塞 , 即 利 用 Object 类 的 wait()和 notify()方

法实现线程阻塞。

首 先 ,wait、notify 方 法 是 针 对 对 象 的 ,调 用 任 意 对 象 的 wait()方 法 都 将 导 致 线 程

阻 塞 ,阻 塞 的 同 时 也 将 释 放 该 对 象 的 锁 ,相 应 地 ,调 用 任 意 对 象 的 notify()方 法 则

将随机解除该对象阻塞的线程,但它需要重新获取改对象的锁,直到获取成功才

能 往 下 执 行 ; 其 次 , wait、 notify 方 法 必 须 在 synchronized 块 或 方 法 中 被 调 用 ,

并 且 要 保 证 同 步 块 或 方 法 的 锁 对 象 与 调 用 wait、 notify 方 法 的 对 象 是 同 一 个 , 如

此 一 来 在 调 用 wait 之 前 当 前 线 程 就 已 经 成 功 获 取 某 对 象 的 锁 ,执 行 wait 阻 塞 后 当

前线程就将之前获取的对象锁释放。

22、在 Java 中 CycliBarriar 和 CountdownLatch 有什么区

别?

CyclicBarrier 可 以 重 复 使 用 , 而 CountdownLatch 不 能 重 复 使 用 。

第 173 页 共 485 页
Java 的 concurrent 包 里 面 的 CountDownLatch 其 实 可 以 把 它 看 作 一 个 计 数 器 ,

只不过这个计数器的操作是原子操作,同时只能有一个线程去操作这个计数器,

也就是同时只能有一个线程去减这个计数器里面的值。

你 可 以 向 CountDownLatch 对 象 设 置 一 个 初 始 的 数 字 作 为 计 数 值 ,任 何 调 用 这 个

对 象 上 的 await()方 法 都 会 阻 塞 ,直 到 这 个 计 数 器 的 计 数 值 被 其 他 的 线 程 减 为 0 为

止。

所 以 在 当 前 计 数 到 达 零 之 前 , await 方 法 会 一 直 受 阻 塞 。 之 后 , 会 释 放 所 有 等 待

的 线 程 ,await 的 所 有 后 续 调 用 都 将 立 即 返 回 。这 种 现 象 只 出 现 一 次 — — 计 数 无 法

被 重 置 。 如 果 需 要 重 置 计 数 , 请 考 虑 使 用 CyclicBarrier。

CountDownLatch 的 一 个 非 常 典 型 的 应 用 场 景 是 : 有 一 个 任 务 想 要 往 下 执 行 , 但

必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续

往 下 执 行 的 任 务 调 用 一 个 CountDownLatch 对 象 的 await()方 法 , 其 他 的 任 务 执

行 完 自 己 的 任 务 后 调 用 同 一 个 CountDownLatch 对 象 上 的 countDown()方 法 ,

这 个 调 用 await()方 法 的 任 务 将 一 直 阻 塞 等 待 , 直 到 这 个 CountDownLatch 对 象

的计数值减到 0 为止。

CyclicBarrier 一 个 同 步 辅 助 类 , 它 允 许 一 组 线 程 互 相 等 待 , 直 到 到 达 某 个 公 共 屏

障 点 (common barrier point)。在 涉 及 一 组 固 定 大 小 的 线 程 的 程 序 中 ,这 些 线 程

必 须 不 时 地 互 相 等 待 , 此 时 CyclicBarrier 很 有 用 。 因 为 该 barrier 在 释 放 等 待

线 程 后 可 以 重 用 , 所 以 称 它 为 循 环 的 barrier。

23、什么是不可变对象,它对写并发应用有什么帮助?

不 可 变 对 象 (Immutable Objects)即 对 象 一 旦 被 创 建 它 的 状 态( 对 象 的 数 据 ,也 即

对 象 属 性 值 ) 就 不 能 改 变 , 反 之 即 为 可 变 对 象 (Mutable Objects)。

不 可 变 对 象 的 类 即 为 不 可 变 类 (Immutable Class)。Java 平 台 类 库 中 包 含 许 多 不 可

变 类 , 如 String、 基 本 类 型 的 包 装 类 、 BigInteger 和 BigDecimal 等 。

不可变对象天生是线程安全的。它们的常量(域)是在构造函数中创建的。既然

它们的状态无法修改,这些常量永远不会变。

第 174 页 共 485 页
不可变对象永远是线程安全的。

只有满足如下状态,一个对象才是不可变的;

它的状态不能在创建后再被修改;

所 有 域 都 是 final 类 型 ; 并 且 ,

它 被 正 确 创 建 ( 创 建 期 间 没 有 发 生 this 引 用 的 逸 出 ) 。

24、什么是多线程中的上下文切换?

在 上 下 文 切 换 过 程 中 , CPU 会 停 止 处 理 当 前 运 行 的 程 序 , 并 保 存 当 前 程 序 运 行 的

具体位置以便之后继续运行。从这个角度来看,上下文切换有点像我们同时阅读

几本书,在来回切换书本的同时我们需要记住每本书当前读到的页码。在程序中,

上 下 文 切 换 过 程 中 的 “ 页 码 ” 信 息 是 保 存 在 进 程 控 制 块 ( PCB) 中 的 。 PCB 还 经

常 被 称 作 “ 切 换 桢 ” ( switchframe) 。 “ 页 码 ” 信 息 会 一 直 保 存 到 CPU 的 内 存

中,直到他们被再次使用。

上 下 文 切 换 是 存 储 和 恢 复 CPU 状 态 的 过 程 , 它 使 得 线 程 执 行 能 够 从 中 断 点 恢 复 执

行。上下文切换是多任务操作系统和多线程环境的基本特征。

25、Java 中用到的线程调度算法是什么?

计 算 机 通 常 只 有 一 个 CPU,在 任 意 时 刻 只 能 执 行 一 条 机 器 指 令 ,每 个 线 程 只 有 获 得

CPU 的 使 用 权 才 能 执 行 指 令 .所 谓 多 线 程 的 并 发 运 行 ,其 实 是 指 从 宏 观 上 看 ,各 个 线

程 轮 流 获 得 CPU 的 使 用 权 ,分 别 执 行 各 自 的 任 务 .在 运 行 池 中 ,会 有 多 个 处 于 就 绪 状

态 的 线 程 在 等 待 CPU,JAVA 虚 拟 机 的 一 项 任 务 就 是 负 责 线 程 的 调 度 ,线 程 调 度 是 指

按 照 特 定 机 制 为 多 个 线 程 分 配 CPU 的 使 用 权 .

有两种调度模型:分时调度模型和抢占式调度模型。

分 时 调 度 模 型 是 指 让 所 有 的 线 程 轮 流 获 得 cpu 的 使 用 权 ,并 且 平 均 分 配 每 个 线 程 占

用 的 CPU 的 时 间 片 这 个 也 比 较 好 理 解 。

第 175 页 共 485 页
java 虚 拟 机 采 用 抢 占 式 调 度 模 型 , 是 指 优 先 让 可 运 行 池 中 优 先 级 高 的 线 程 占 用

CPU, 如 果 可 运 行 池 中 的 线 程 优 先 级 相 同 , 那 么 就 随 机 选 择 一 个 线 程 , 使 其 占 用

CPU。 处 于 运 行 状 态 的 线 程 会 一 直 运 行 , 直 至 它 不 得 不 放 弃 CPU。

26、什么是线程组,为什么在 Java 中不推荐使用?

线程组和线程池是两个不同的概念,他们的作用完全不同,前者是为了方便线程

的管理,后者是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销。

27、为什么使用 Executor 框架比使用应用创建和管理线程好?

为 什 么 要 使 用 Executor 线 程 池 框 架

1、 每 次 执 行 任 务 创 建 线 程 new Thread()比 较 消 耗 性 能 , 创 建 一 个 线 程 是 比 较 耗

时、耗资源的。

2、 调 用 new Thread()创 建 的 线 程 缺 乏 管 理 , 被 称 为 野 线 程 , 而 且 可 以 无 限 制 的

创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程

之间的频繁交替也会消耗很多系统资源。

3、 直 接 使 用 new Thread() 启 动 的 线 程 不 利 于 扩 展 , 比 如 定 时 执 行 、 定 期 执 行 、

定时定期执行、线程中断等都不便实现。

使 用 Executor 线 程 池 框 架 的 优 点

1、能 复 用 已 存 在 并 空 闲 的 线 程 从 而 减 少 线 程 对 象 的 创 建 从 而 减 少 了 消 亡 线 程 的 开

销。

2、 可 有 效 控 制 最 大 并 发 线 程 数 , 提 高 系 统 资 源 使 用 率 , 同 时 避 免 过 多 资 源 竞 争 。

3、 框 架 中 已 经 有 定 时 、 定 期 、 单 线 程 、 并 发 数 控 制 等 功 能 。

综 上 所 述 使 用 线 程 池 框 架 Executor 能 更 好 的 管 理 线 程 、 提 供 系 统 资 源 使 用 率 。

第 176 页 共 485 页
28、java 中有几种方法可以实现一个线程?

继 承 Thread 类

实 现 Runnable 接 口

实 现 Callable 接 口 , 需 要 实 现 的 是 call() 方 法

29、如何停止一个正在运行的线程?

使用共享变量的方式

在这种方式中,之所以引入共享变量,是因为该变量可以被多个执行相同任务的

线程用来作为是否中断的信号,通知中断线程的执行。

使 用 interrupt 方 法 终 止 线 程

如果一个线程由于等待某些事件的发生而被阻塞,又该怎样停止该线程呢?这种

情况经常会发生,比如当一个线程由于需要等候键盘输入而被阻塞,或者调用

Thread.join()方 法 , 或 者 Thread.sleep()方 法 , 在 网 络 中 调 用

ServerSocket.accept()方 法 ,或 者 调 用 了 DatagramSocket.receive()方 法 时 ,都

有可能导致线程阻塞,使线程处于处于不可运行状态时,即使主程序中将该线程

的 共 享 变 量 设 置 为 true, 但 该 线 程 此 时 根 本 无 法 检 查 循 环 标 志 , 当 然 也 就 无 法 立

即 中 断 。这 里 我 们 给 出 的 建 议 是 ,不 要 使 用 stop()方 法 ,而 是 使 用 Thread 提 供 的

interrupt()方 法 ,因 为 该 方 法 虽 然 不 会 中 断 一 个 正 在 运 行 的 线 程 ,但 是 它 可 以 使 一

个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代

码。

30、notify()和 notifyAll()有什么区别?

第 177 页 共 485 页
当 一 个 线 程 进 入 wait 之 后 ,就 必 须 等 其 他 线 程 notify/notifyall,使 用 notifyall,可

以 唤 醒 所 有 处 于 wait 状 态 的 线 程 ,使 其 重 新 进 入 锁 的 争 夺 队 列 中 ,而 notify 只 能

唤醒一个。

如 果 没 把 握 , 建 议 notifyAll, 防 止 notigy 因 为 信 号 丢 失 而 造 成 程 序 异 常 。

31、什么是 Daemon 线程?它有什么意义?

所 谓 后 台 (daemon)线 程 , 是 指 在 程 序 运 行 的 时 候 在 后 台 提 供 一 种 通 用 服 务 的 线

程,并且这个线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程

结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,

只要有任何非后台线程还在运行,程序就不会终止。必须在线程启动之前调用

setDaemon()方 法 , 才 能 把 它 设 置 为 后 台 线 程 。 注 意 : 后 台 进 程 在 不 执 行 finally

子 句 的 情 况 下 就 会 终 止 其 run()方 法 。

比 如 : JVM 的 垃 圾 回 收 线 程 就 是 Daemon 线 程 , Finalizer 也 是 守 护 线 程 。

32、java 如何实现多线程之间的通讯和协作?

中断 和 共享变量

33、什么是可重入锁(ReentrantLock)?

举例来说明锁的可重入性

public class UnReentrant{


Lock lock = new Lock();
public void outer(){

第 178 页 共 485 页
lock.lock();
inner();
lock.unlock();
}
public void inner(){
lock.lock();
//do something
lock.unlock();
}
}

outer 中 调 用 了 inner,outer 先 锁 住 了 lock,这 样 inner 就 不 能 再 获 取 lock。其

实 调 用 outer 的 线 程 已 经 获 取 了 lock 锁 , 但 是 不 能 在 inner 中 重 复 利 用 已 经 获 取

的 锁 资 源 ,这 种 锁 即 称 之 为 不 可 重 入 可 重 入 就 意 味 着 :线 程 可 以 进 入 任 何 一 个 它

已经拥有的锁所同步着的代码块。

synchronized、 ReentrantLock 都 是 可 重 入 的 锁 , 可 重 入 锁 相 对 来 说 简 化 了 并 发

编程的开发。

34、当一个线程进入某个对象的一个 synchronized 的实例方

法后,其它线程是否可进入此对象的其它方法?

如 果 其 他 方 法 没 有 synchronized 的 话 , 其 他 线 程 是 可 以 进 入 的 。

所以要开放一个线程安全的对象时,得保证每个方法都是线程安全的。

35、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

第 179 页 共 485 页
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每

次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传

统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写

锁 等 , 都 是 在 做 操 作 之 前 先 上 锁 。 再 比 如 Java 里 面 的 同 步 原 语 synchronized 关

键字的实现也是悲观锁。

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所

以 不 会 上 锁 ,但 是 在 更 新 的 时 候 会 判 断 一 下 在 此 期 间 别 人 有 没 有 去 更 新 这 个 数 据 ,

可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,

像 数 据 库 提 供 的 类 似 于 write_condition 机 制 , 其 实 都 是 提 供 的 乐 观 锁 。 在 Java

中 java.util.concurrent.atomic 包 下 面 的 原 子 变 量 类 就 是 使 用 了 乐 观 锁 的 一 种 实

现 方 式 CAS 实 现 的 。

乐观锁的实现方式:

1、使 用 版 本 标 识 来 确 定 读 到 的 数 据 与 提 交 时 的 数 据 是 否 一 致 。提 交 后 修 改 版 本 标

识,不一致时可以采取丢弃和再次尝试的策略。

2、 java 中 的 Compare and Swap 即 CAS , 当 多 个 线 程 尝 试 使 用 CAS 同 时 更 新

同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的

线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。 CAS 操 作

中 包 含 三 个 操 作 数 — — 需 要 读 写 的 内 存 位 置 ( V) 、 进 行 比 较 的 预 期 原 值 ( A)

和 拟 写 入 的 新 值 (B)。如 果 内 存 位 置 V 的 值 与 预 期 原 值 A 相 匹 配 ,那 么 处 理 器 会 自

动 将 该 位 置 值 更 新 为 新 值 B。 否 则 处 理 器 不 做 任 何 操 作 。

CAS 缺 点 :

1、 ABA 问 题 :

比 如 说 一 个 线 程 one 从 内 存 位 置 V 中 取 出 A,这 时 候 另 一 个 线 程 two 也 从 内 存 中

取 出 A,并 且 two 进 行 了 一 些 操 作 变 成 了 B,然 后 two 又 将 V 位 置 的 数 据 变 成 A,

这 时 候 线 程 one 进 行 CAS 操 作 发 现 内 存 中 仍 然 是 A,然 后 one 操 作 成 功 。尽 管 线

程 one 的 CAS 操 作 成 功 ,但 可 能 存 在 潜 藏 的 问 题 。从 Java1.5 开 始 JDK 的 atomic

包 里 提 供 了 一 个 类 AtomicStampedReference 来 解 决 ABA 问 题 。

第 180 页 共 485 页
2、 循 环 时 间 长 开 销 大 :

对 于 资 源 竞 争 严 重 ( 线 程 冲 突 严 重 ) 的 情 况 , CAS 自 旋 的 概 率 会 比 较 大 , 从 而 浪

费 更 多 的 CPU 资 源 , 效 率 低 于 synchronized。

3、 只 能 保 证 一 个 共 享 变 量 的 原 子 操 作 :

当 对 一 个 共 享 变 量 执 行 操 作 时 , 我 们 可 以 使 用 循 环 CAS 的 方 式 来 保 证 原 子 操 作 ,

但 是 对 多 个 共 享 变 量 操 作 时 , 循 环 CAS 就 无 法 保 证 操 作 的 原 子 性 , 这 个 时 候 就 可

以用锁。

36、SynchronizedMap 和 ConcurrentHashMap 有什么区

别?

SynchronizedMap 一 次 锁 住 整 张 表 来 保 证 线 程 安 全 ,所 以 每 次 只 能 有 一 个 线 程 来

访 为 map。

ConcurrentHashMap 使 用 分 段 锁 来 保 证 在 多 线 程 下 的 性 能 。

ConcurrentHashMap 中 则 是 一 次 锁 住 一 个 桶 。 ConcurrentHashMap 默 认 将

hash 表 分 为 16 个 桶 , 诸 如 get,put,remove 等 常 用 操 作 只 锁 当 前 需 要 用 到 的 桶 。

这 样 ,原 来 只 能 一 个 线 程 进 入 ,现 在 却 能 同 时 有 16 个 写 线 程 执 行 ,并 发 性 能 的 提

升是显而易见的。

另 外 ConcurrentHashMap 使 用 了 一 种 不 同 的 迭 代 方 式 。 在 这 种 迭 代 方 式 中 , 当

iterator 被 创 建 后 集 合 再 发 生 改 变 就 不 再 是 抛 出

ConcurrentModificationException, 取 而 代 之 的 是 在 改 变 时 new 新 的 数 据 从 而

不 影 响 原 有 的 数 据 ,iterator 完 成 后 再 将 头 指 针 替 换 为 新 的 数 据 ,这 样 iterator

线程可以使用原来老的数据,而写线程也可以并发的完成改变。

37、CopyOnWriteArrayList 可以用于什么应用场景?

第 181 页 共 485 页
CopyOnWriteArrayList(免 锁 容 器 )的 好 处 之 一 是 当 多 个 迭 代 器 同 时 遍 历 和 修 改 这

个 列 表 时 , 不 会 抛 出 ConcurrentModificationException。 在

CopyOnWriteArrayList 中 ,写 入 将 导 致 创 建 整 个 底 层 数 组 的 副 本 ,而 源 数 组 将 保

留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。

1、由 于 写 操 作 的 时 候 ,需 要 拷 贝 数 组 ,会 消 耗 内 存 ,如 果 原 数 组 的 内 容 比 较 多 的

情 况 下 , 可 能 导 致 young gc 或 者 full gc;

2、不 能 用 于 实 时 读 的 场 景 ,像 拷 贝 数 组 、新 增 元 素 都 需 要 时 间 ,所 以 调 用 一 个 set

操 作 后 ,读 取 到 数 据 可 能 还 是 旧 的 ,虽 然 CopyOnWriteArrayList 能 做 到 最 终 一 致

性 ,但 是 还 是 没 法 满 足 实 时 性 要 求 ;

CopyOnWriteArrayList 透 露 的 思 想

1、 读 写 分 离 , 读 和 写 分 开

2、 最 终 一 致 性

3、 使 用 另 外 开 辟 空 间 的 思 路 , 来 解 决 并 发 冲 突

38、什么叫线程安全?servlet 是线程安全吗?

线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够

正确地处理多个线程之间的共享变量,使程序功能正确完成。

Servlet 不 是 线 程 安 全 的 ,servlet 是 单 实 例 多 线 程 的 ,当 多 个 线 程 同 时 访 问 同 一 个

方法,是不能保证共享变量的线程安全性的。

Struts2 的 action 是 多 实 例 多 线 程 的 , 是 线 程 安 全 的 ,每 个 请 求 过 来 都 会 new 一

个 新 的 action 分 配 给 这 个 请 求 , 请 求 完 成 后 销 毁 。

SpringMVC 的 Controller 是 线 程 安 全 的 吗 ? 不 是 的 ,和 Servlet 类 似 的 处 理 流 程 。

Struts2 好 处 是 不 用 考 虑 线 程 安 全 问 题 ; Servlet 和 SpringMVC 需 要 考 虑 线 程 安

全 问 题 ,但 是 性 能 可 以 提 升 不 用 处 理 太 多 的 gc,可 以 使 用 ThreadLocal 来 处 理 多

线程的问题。

第 182 页 共 485 页
39、volatile 有什么用?能否用一句话说明下 volatile 的应用

场景?

volatile 保 证 内 存 可 见 性 和 禁 止 指 令 重 排 。

volatile 用 于 多 线 程 环 境 下 的 单 次 操 作 (单 次 读 或 者 单 次 写 )。

40、为什么代码会重排序?

在执行程序时,为了提供性能,处理器和编译器常常会对指令进行重排序,但是

不能随意重排序,不是你想怎么排序就怎么排序,它需要满足以下两个条件:

在单线程环境下不能改变程序运行的结果;

存在数据依赖关系的不允许重排序

需要注意的是:重排序不会影响单线程环境的执行结果,但是会破坏多线程的执

行语义。

41、在 java 中 wait 和 sleep 方法的不同?

最 大 的 不 同 是 在 等 待 时 wait 会 释 放 锁 ,而 sleep 一 直 持 有 锁 。Wait 通 常 被 用 于 线

程 间 交 互 , sleep 通 常 被 用 于 暂 停 执 行 。

直接了解的深入一点吧:

在 Java 中 线 程 的 状 态 一 共 被 分 成 6 种 :

第 183 页 共 485 页
初 始 态 : NEW

创 建 一 个 Thread 对 象 , 但 还 未 调 用 start()启 动 线 程 时 , 线 程 处 于 初 始 态 。

运 行 态 : RUNNABLE

在 Java 中 , 运 行 态 包 括 就 绪 态 和 运 行 态 。

就 绪 态 该 状 态 下 的 线 程 已 经 获 得 执 行 所 需 的 所 有 资 源 , 只 要 CPU 分 配 执 行 权 就

能运行。所有就绪态的线程存放在就绪队列中。

运 行 态 获 得 CPU 执 行 权 , 正 在 执 行 的 线 程 。 由 于 一 个 CPU 同 一 时 刻 只 能 执 行 一

条 线 程 , 因 此 每 个 CPU 每 个 时 刻 只 有 一 条 运 行 态 的 线 程 。

阻塞态

当 一 条 正 在 执 行 的 线 程 请 求 某 一 资 源 失 败 时 , 就 会 进 入 阻 塞 态 。 而 在 Java 中 , 阻

塞态专指请求锁失败时进入的状态。由一个阻塞队列存放所有阻塞态的线程。处

于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执行。

PS: 锁 、 IO、 Socket 等 都 资 源 。

等待态

当 前 线 程 中 调 用 wait、 join、 park 函 数 时 , 当 前 线 程 就 会 进 入 等 待 态 。 也 有 一 个

等待队列存放所有等待态的线程。线程处于等待态表示它需要等待其他线程的指

示 才 能 继 续 运 行 。 进 入 等 待 态 的 线 程 会 释 放 CPU 执 行 权 , 并 释 放 资 源 ( 如 : 锁 )

超时等待态

当 运 行 中 的 线 程 调 用 sleep(time)、 wait、 join、 parkNanos、 parkUntil 时 , 就

会进入该状态;它和等待态一样,并不是因为请求不到资源,而是主动进入,并

且 进 入 后 需 要 其 他 线 程 唤 醒 ;进 入 该 状 态 后 释 放 CPU 执 行 权 和 占 有 的 资 源 。与

等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。

第 184 页 共 485 页
终止态

线程执行结束后的状态。

注意:

wait()方 法 会 释 放 CPU 执 行 权 和 占 有 的 锁 。

sleep(long)方 法 仅 释 放 CPU 使 用 权 ,锁 仍 然 占 用 ;线 程 被 放 入 超 时 等 待 队 列 ,与

yield 相 比 , 它 会 使 线 程 较 长 时 间 得 不 到 运 行 。

yield()方 法 仅 释 放 CPU 执 行 权 , 锁 仍 然 占 用 , 线 程 会 被 放 入 就 绪 队 列 , 会 在 短 时

间内再次执行。

wait 和 notify 必 须 配 套 使 用 , 即 必 须 使 用 同 一 把 锁 调 用 ;

wait 和 notify 必 须 放 在 一 个 同 步 块 中 调 用 wait 和 notify 的 对 象 必 须 是 他 们 所 处

同步块的锁对象。

42、用 Java 实现阻塞队列

参 考 java 中 的 阻 塞 队 列 的 内 容 吧 , 直 接 实 现 有 点 烦 :

http://www.infoq.com/cn/articles/java-blocking-queue

43、一个线程运行时发生异常会怎样?

如 果 异 常 没 有 被 捕 获 该 线 程 将 会 停 止 执 行 。Thread.UncaughtExceptionHandler

是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异

常 将 造 成 线 程 中 断 的 时 候 JVM 会 使 用 Thread.getUncaughtExceptionHandler()

来 查 询 线 程 的 UncaughtExceptionHandler 并 将 线 程 和 异 常 作 为 参 数 传 递 给

handler 的 uncaughtException()方 法 进 行 处 理 。

第 185 页 共 485 页
44、如何在两个线程间共享数据?

在两个线程间共享变量即可实现共享。

一般来说,共享变量要求变量本身是线程安全的,然后在线程内使用的时候,如

果有对共享变量的复合操作,那么也得保证复合操作的线程安全性。

45、Java 中 notify 和 notifyAll 有什么区别?

notify() 方 法 不 能 唤 醒 某 个 具 体 的 线 程 , 所 以 只 有 一 个 线 程 在 等 待 的 时 候 它 才 有

用 武 之 地 。 而 notifyAll()唤 醒 所 有 线 程 并 允 许 他 们 争 夺 锁 确 保 了 至 少 有 一 个 线 程

能继续运行。

46、为什么 wait, notify 和 notifyAll 这些方法不在 thread

类里面?

一 个 很 明 显 的 原 因 是 JAVA 提 供 的 锁 是 对 象 级 的 而 不 是 线 程 级 的 , 每 个 对 象 都 有

锁 ,通 过 线 程 获 得 。由 于 wait,notify 和 notifyAll 都 是 锁 级 别 的 操 作 ,所 以 把 他

们 定 义 在 Object 类 中 因 为 锁 属 于 对 象 。

47、什么是 ThreadLocal 变量?

ThreadLocal 是 Java 里 一 种 特 殊 的 变 量 。每 个 线 程 都 有 一 个 ThreadLocal 就 是 每

个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。它是为创建代价

高 昂 的 对 象 获 取 线 程 安 全 的 好 方 法 , 比 如 你 可 以 用 ThreadLocal 让

SimpleDateFormat 变 成 线 程 安 全 的 , 因 为 那 个 类 创 建 代 价 高 昂 且 每 次 调 用 都 需

要创建不同的实例所以不值得在局部范围使用它,如果为每个线程提供一个自己

第 186 页 共 485 页
独有的变量拷贝,将大大提高效率。首先,通过复用减少了代价高昂的对象的创

建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。

48、Java 中 interrupted 和 isInterrupted 方法的区别?

interrupt

interrupt 方 法 用 于 中 断 线 程 。调 用 该 方 法 的 线 程 的 状 态 为 将 被 置 为 ”中 断 ”状 态 。

注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监

视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出

interruptedException 的 方 法 ) 就 是 在 监 视 线 程 的 中 断 状 态 , 一 旦 线 程 的 中 断 状

态被置为“中断状态”,就会抛出中断异常。

interrupted

查询当前线程的中断状态,并且清除原状态。如果一个线程被中断了,第一次调

用 interrupted 则 返 回 true, 第 二 次 和 后 面 的 就 返 回 false 了 。

isInterrupted

仅仅是查询当前线程的中断状态

49、为什么 wait 和 notify 方法要在同步块中调用?

Java API 强 制 要 求 这 样 做 , 如 果 你 不 这 么 做 , 你 的 代 码 会 抛 出

IllegalMonitorStateException 异 常 。 还 有 一 个 原 因 是 为 了 避 免 wait 和 notify

之间产生竞态条件。

第 187 页 共 485 页
50、为什么你应该在循环中检查等待条件?

处于等待状态的线程可能会收到错误警报和伪唤醒,如果不在循环中检查等待条

件,程序就会在没有满足结束条件的情况下退出。

51、Java 中的同步集合与并发集合有什么区别?

同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发

集 合 的 可 扩 展 性 更 高 。在 Java1.5 之 前 程 序 员 们 只 有 同 步 集 合 来 用 且 在 多 线 程 并 发

的 时 候 会 导 致 争 用 , 阻 碍 了 系 统 的 扩 展 性 。 Java5 介 绍 了 并 发 集 合 像

ConcurrentHashMap,不 仅 提 供 线 程 安 全 还 用 锁 分 离 和 内 部 分 区 等 现 代 技 术 提 高

了可扩展性。

52、什么是线程池? 为什么要使用它?

创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变

长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候

就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从

JDK1.5 开 始 , Java API 提 供 了 Executor 框 架 让 你 可 以 创 建 不 同 的 线 程 池 。

53、怎么检测一个线程是否拥有锁?

在 java.lang.Thread 中 有 一 个 方 法 叫 holdsLock(), 它 返 回 true 如 果 当 且 仅 当 当

前线程拥有某个具体对象的锁。

54、你如何在 Java 中获取线程堆栈?

第 188 页 共 485 页
kill -3 [java pid]

不 会 在 当 前 终 端 输 出 , 它 会 输 出 到 代 码 执 行 的 或 指 定 的 地 方 去 。 比 如 , kill -3

tomcat pid, 输 出 堆 栈 到 log 目 录 下 。

Jstack [java pid]

这个比较简单,在当前终端显示,也可以重定向到指定文件中。

-JvisualVM: Thread Dump

不 做 说 明 , 打 开 JvisualVM 后 , 都 是 界 面 操 作 , 过 程 还 是 很 简 单 的 。

55、 JVM 中 哪 个 参 数 是 用 来 控 制 线 程 的 栈 堆 栈 小 的 ?

-Xss 每 个 线 程 的 栈 大 小

56、Thread 类中的 yield 方法有什么作用?

使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。

当前线程到了就绪状态,那么接下来哪个线程会从就绪状态变成执行状态呢?可

能是当前线程,也可能是其他线程,看系统的分配了。

57、Java 中 ConcurrentHashMap 的并发度是什么?

ConcurrentHashMap 把 实 际 map 划 分 成 若 干 部 分 来 实 现 它 的 可 扩 展 性 和 线 程 安

全 。 这 种 划 分 是 使 用 并 发 度 获 得 的 , 它 是 ConcurrentHashMap 类 构 造 函 数 的 一

个 可 选 参 数 , 默 认 值 为 16, 这 样 在 多 线 程 情 况 下 就 能 避 免 争 用 。

在 JDK8 后 , 它 摒 弃 了 Segment( 锁 段 ) 的 概 念 , 而 是 启 用 了 一 种 全 新 的 方 式 实

现 ,利 用 CAS 算 法 。同 时 加 入 了 更 多 的 辅 助 变 量 来 提 高 并 发 度 ,具 体 内 容 还 是 查 看

源码吧。

第 189 页 共 485 页
58、Java 中 Semaphore 是什么?

Java 中 的 Semaphore 是 一 种 新 的 同 步 类 , 它 是 一 个 计 数 信 号 。 从 概 念 上 讲 , 从

概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个

acquire(),然 后 再 获 取 该 许 可 。每 个 release()添 加 一 个 许 可 ,从 而 可 能 释 放 一 个

正 在 阻 塞 的 获 取 者 。但 是 ,不 使 用 实 际 的 许 可 对 象 ,Semaphore 只 对 可 用 许 可 的

号码进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据

库连接池。

59、Java 线程池中 submit() 和 execute()方法有什么区别?

两 个 方 法 都 可 以 向 线 程 池 提 交 任 务 ,execute()方 法 的 返 回 类 型 是 void,它 定 义 在

Executor 接 口 中 。

而 submit()方 法 可 以 返 回 持 有 计 算 结 果 的 Future 对 象 , 它 定 义 在

ExecutorService 接 口 中 , 它 扩 展 了 Executor 接 口 , 其 它 线 程 池 类 像

ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 都 有 这 些 方 法 。

60、什么是阻塞式方法?

阻 塞 式 方 法 是 指 程 序 会 一 直 等 待 该 方 法 完 成 期 间 不 做 其 他 事 情 , ServerSocket 的

accept()方 法 就 是 一 直 等 待 客 户 端 连 接 。这 里 的 阻 塞 是 指 调 用 结 果 返 回 之 前 ,当 前

线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在

任务完成前就返回。

61、Java 中的 ReadWriteLock 是什么?

第 190 页 共 485 页
读写锁是用来提升并发程序性能的锁分离技术的成果。

62、volatile 变量和 atomic 变量有什么不同?

Volatile 变 量 可 以 确 保 先 行 关 系 ,即 写 操 作 会 发 生 在 后 续 的 读 操 作 之 前 , 但 它 并 不

能 保 证 原 子 性 。 例 如 用 volatile 修 饰 count 变 量 那 么 count++ 操 作 就 不 是 原 子

性的。

而 AtomicInteger 类 提 供 的 atomic 方 法 可 以 让 这 种 操 作 具 有 原 子 性 如

getAndIncrement()方 法 会 原 子 性 的 进 行 增 量 操 作 把 当 前 值 加 一 , 其 它 数 据 类 型

和引用变量也可以进行相似操作。

63、可以直接调用 Thread 类的 run ()方法么?

当 然 可 以 。 但 是 如 果 我 们 调 用 了 Thread 的 run()方 法 , 它 的 行 为 就 会 和 普 通 的 方

法一样,会在当前线程中执行。为了在新的线程中执行我们的代码,必须使用

Thread.start()方 法 。

64、如何让正在运行的线程暂停一段时间?

我 们 可 以 使 用 Thread 类 的 Sleep()方 法 让 线 程 暂 停 一 段 时 间 。 需 要 注 意 的 是 , 这

并 不 会 让 线 程 终 止 ,一 旦 从 休 眠 中 唤 醒 线 程 ,线 程 的 状 态 将 会 被 改 变 为 Runnable,

并且根据线程调度,它将得到执行。

65、你对线程优先级的理解是什么?

第 191 页 共 485 页
每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,

但 这 依 赖 于 线 程 调 度 的 实 现 , 这 个 实 现 是 和 操 作 系 统 相 关 的 (OS dependent)。 我

们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线

程 前 执 行 。 线 程 优 先 级 是 一 个 int 变 量 (从 1-10), 1 代 表 最 低 优 先 级 , 10 代 表 最

高优先级。

java 的 线 程 优 先 级 调 度 会 委 托 给 操 作 系 统 去 处 理 , 所 以 与 具 体 的 操 作 系 统 优 先 级

有关,如非特别需要,一般无需设置线程优先级。

66、什么是线程调度器(Thread Scheduler)和时间分片(Time

Slicing )?

线 程 调 度 器 是 一 个 操 作 系 统 服 务 ,它 负 责 为 Runnable 状 态 的 线 程 分 配 CPU 时 间 。

一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。

同 上 一 个 问 题 ,线 程 调 度 并 不 受 到 Java 虚 拟 机 控 制 ,所 以 由 应 用 程 序 来 控 制 它 是

更好的选择(也就是说不要让你的程序依赖于线程的优先级)。

时 间 分 片 是 指 将 可 用 的 CPU 时 间 分 配 给 可 用 的 Runnable 线 程 的 过 程 。分 配 CPU

时间可以基于线程优先级或者线程等待的时间。

67、你如何确保 main()方法所在的线程是 Java 程序最后结束

的线程?

我 们 可 以 使 用 Thread 类 的 join()方 法 来 确 保 所 有 程 序 创 建 的 线 程 在 main()方 法 退

出前结束。

第 192 页 共 485 页
68、线程之间是如何通信的?

当 线 程 间 是 可 以 共 享 资 源 时 , 线 程 间 通 信 是 协 调 它 们 的 重 要 的 手 段 。 Object 类 中

wait()\notify()\notifyAll()方 法 可 以 用 于 线 程 间 通 信 关 于 资 源 的 锁 的 状 态 。

69、为什么线程通信的方法 wait(), notify()和 notifyAll()被定

义在 Object 类里?

Java 的 每 个 对 象 中 都 有 一 个 锁 (monitor,也 可 以 成 为 监 视 器 ) 并 且 wait(),notify()

等 方 法 用 于 等 待 对 象 的 锁 或 者 通 知 其 他 线 程 对 象 的 监 视 器 可 用 。在 Java 的 线 程 中

并 没 有 可 供 任 何 对 象 使 用 的 锁 和 同 步 器 。这 就 是 为 什 么 这 些 方 法 是 Object 类 的 一

部 分 , 这 样 Java 的 每 一 个 类 都 有 用 于 线 程 间 通 信 的 基 本 方 法 。

70、为什么 wait(), notify()和 notifyAll ()必须在同步方法或

者同步块中被调用?

当 一 个 线 程 需 要 调 用 对 象 的 wait()方 法 的 时 候 ,这 个 线 程 必 须 拥 有 该 对 象 的 锁 ,接

着 它 就 会 释 放 这 个 对 象 锁 并 进 入 等 待 状 态 直 到 其 他 线 程 调 用 这 个 对 象 上 的 notify()

方 法 。同 样 的 ,当 一 个 线 程 需 要 调 用 对 象 的 notify()方 法 时 ,它 会 释 放 这 个 对 象 的

锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要

线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者

同步块中被调用。

71、为什么 Thread 类的 sleep()和 yield ()方法是静态的?

第 193 页 共 485 页
Thread 类 的 sleep()和 yield()方 法 将 在 当 前 正 在 执 行 的 线 程 上 运 行 。 所 以 在 其 他

处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静

态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在

其他非运行线程调用这些方法。

72、如何确保线程安全?

在 Java 中 可 以 有 很 多 方 法 来 保 证 线 程 安 全 — — 同 步 , 使 用 原 子 类 (atomic

concurrent classes), 实 现 并 发 锁 , 使 用 volatile 关 键 字 , 使 用 不 变 类 和 线 程 安

全类。

73、同步方法和同步块,哪个是更好的选择?

同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对

象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通

常会导致他们停止执行并需要等待获得这个对象上的锁。

同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象,这样

从侧面来说也可以避免死锁。

74、如何创建守护线程?

使 用 Thread 类 的 setDaemon(true)方 法 可 以 将 线 程 设 置 为 守 护 线 程 , 需 要 注 意

的 是 , 需 要 在 调 用 start()方 法 前 调 用 这 个 方 法 , 否 则 会 抛 出

IllegalThreadStateException 异 常 。

第 194 页 共 485 页
75、什么是 Java Timer 类?如何创建一个有特定时间间隔的

任务?

java.util.Timer 是 一 个 工 具 类 , 可 以 用 于 安 排 一 个 线 程 在 未 来 的 某 个 特 定 时 间 执

行 。 Timer 类 可 以 用 安 排 一 次 性 任 务 或 者 周 期 任 务 。

java.util.TimerTask 是 一 个 实 现 了 Runnable 接 口 的 抽 象 类 , 我 们 需 要 去 继 承 这

个 类 来 创 建 我 们 自 己 的 定 时 任 务 并 使 用 Timer 去 安 排 它 的 执 行 。

Java 并发编程(二)

1、并发编程三要素?

1、 原 子 性

原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操

作打断,要么就全部都不执行。

2、 可 见 性

可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他

线程可以立即看到修改的结果。

3、 有 序 性

有序性,即程序的执行顺序按照代码的先后顺序来执行。

2、实现可见性的方法有哪些?

synchronized 或 者 Lock:保 证 同 一 个 时 刻 只 有 一 个 线 程 获 取 锁 执 行 代 码 ,锁 释 放

之前把最新的值刷新到主内存,实现可见性。

第 195 页 共 485 页
3、多线程的价值?

1、 发 挥 多 核 CPU 的 优 势

多 线 程 ,可 以 真 正 发 挥 出 多 核 CPU 的 优 势 来 ,达 到 充 分 利 用 CPU 的 目 的 ,采 用 多

线程的方式去同时完成几件事情而不互相干扰。

2、 防 止 阻 塞

从 程 序 运 行 效 率 的 角 度 来 看 , 单 核 CPU 不 但 不 会 发 挥 出 多 线 程 的 优 势 , 反 而 会 因

为 在 单 核 CPU 上 运 行 多 线 程 导 致 线 程 上 下 文 的 切 换 , 而 降 低 程 序 整 体 的 效 率 。 但

是 单 核 CPU 我 们 还 是 要 应 用 多 线 程 ,就 是 为 了 防 止 阻 塞 。试 想 ,如 果 单 核 CPU 使

用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未

返 回 又 没 有 设 置 超 时 时 间 ,那 么 你 的 整 个 程 序 在 数 据 返 回 回 来 之 前 就 停 止 运 行 了 。

多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数

据阻塞,也不会影响其它任务的执行。

3、 便 于 建 模

这 是 另 外 一 个 没 有 这 么 明 显 的 优 点 了 。假 设 有 一 个 大 的 任 务 A,单 线 程 编 程 ,那 么

就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务 A 分解成

几 个 小 任 务 ,任 务 B、任 务 C、任 务 D,分 别 建 立 程 序 模 型 ,并 通 过 多 线 程 分 别 运

行这几个任务,那就简单很多了。

4、创建线程的有哪些方式?

1、 继 承 Thread 类 创 建 线 程 类

2、 通 过 Runnable 接 口 创 建 线 程 类

3、 通 过 Callable 和 Future 创 建 线 程

4、 通 过 线 程 池 创 建

5、创建线程的三种方式的对比?

第 196 页 共 485 页
1、 采 用 实 现 Runnable、 Callable 接 口 的 方 式 创 建 多 线 程 。

优势是:

线 程 类 只 是 实 现 了 Runnable 接 口 或 Callable 接 口 , 还 可 以 继 承 其 他 类 。

在 这 种 方 式 下 , 多 个 线 程 可 以 共 享 同 一 个 target 对 象 , 所 以 非 常 适 合 多 个 相 同 线

程 来 处 理 同 一 份 资 源 的 情 况 , 从 而 可 以 将 CPU、 代 码 和 数 据 分 开 , 形 成 清 晰 的 模

型,较好地体现了面向对象的思想。

劣势是:

编 程 稍 微 复 杂 ,如 果 要 访 问 当 前 线 程 ,则 必 须 使 用 Thread.currentThread()方 法 。

2、 使 用 继 承 Thread 类 的 方 式 创 建 多 线 程

优势是:

编 写 简 单 , 如 果 需 要 访 问 当 前 线 程 , 则 无 需 使 用 Thread.currentThread()方 法 ,

直 接 使 用 this 即 可 获 得 当 前 线 程 。

劣势是:

线 程 类 已 经 继 承 了 Thread 类 , 所 以 不 能 再 继 承 其 他 父 类 。

3、 Runnable 和 Callable 的 区 别

1、Callable 规 定( 重 写 )的 方 法 是 call(),Runnable 规 定( 重 写 )的 方 法 是 run()。

2、 Callable 的 任 务 执 行 后 可 返 回 值 , 而 Runnable 的 任 务 是 不 能 返 回 值 的 。

3、 Call 方 法 可 以 抛 出 异 常 , run 方 法 不 可 以 。

4、 运 行 Callable 任 务 可 以 拿 到 一 个 Future 对 象 , 表 示 异 步 计 算 的 结 果 。 它 提 供

了 检 查 计 算 是 否 完 成 的 方 法 ,以 等 待 计 算 的 完 成 ,并 检 索 计 算 的 结 果 。通 过 Future

对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

6、线程的状态流转图

线程的生命周期及五种基本状态:

![img_2.png][img_2.png]

7、Java 线程具有五中基本状态

第 197 页 共 485 页
1、新 建 状 态( New):当 线 程 对 象 对 创 建 后 ,即 进 入 了 新 建 状 态 ,如 :Thread t

= new MyThread();

2、 就 绪 状 态 ( Runnable) : 当 调 用 线 程 对 象 的 start()方 法 ( t.start();) , 线 程

即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时

等 待 CPU 调 度 执 行 , 并 不 是 说 执 行 了 t.start()此 线 程 立 即 就 会 执 行 ;

3、 运 行 状 态 ( Running) : 当 CPU 开 始 调 度 处 于 就 绪 状 态 的 线 程 时 , 此 时 线 程

才 得 以 真 正 执 行 ,即 进 入 到 运 行 状 态 。注 :就 绪 状 态 是 进 入 到 运 行 状 态 的 唯 一 入

口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;

4、阻 塞 状 态( Blocked):处 于 运 行 状 态 中 的 线 程 由 于 某 种 原 因 ,暂 时 放 弃 对 CPU

的 使 用 权 ,停 止 执 行 , 此 时 进 入 阻 塞 状 态 ,直 到 其 进 入 到 就 绪 状 态 , 才 有 机 会 再

次 被 CPU 调 用 以 进 入 到 运 行 状 态 。

根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1、等 待 阻 塞 :运 行 状 态 中 的 线 程 执 行 wait()方 法 ,使 本 线 程 进 入 到 等 待 阻 塞 状 态 ;

2、同 步 阻 塞 :线 程 在 获 取 synchronized 同 步 锁 失 败 (因 为 锁 被 其 它 线 程 所 占 用 ),

它会进入同步阻塞状态;

3、其 他 阻 塞 :通 过 调 用 线 程 的 sleep()或 join()或 发 出 了 I/O 请 求 时 ,线 程 会 进 入

到 阻 塞 状 态 。 当 sleep()状 态 超 时 、 join()等 待 线 程 终 止 或 者 超 时 、 或 者 I/O 处 理

完毕时,线程重新转入就绪状态。

5、死 亡 状 态( Dead):线 程 执 行 完 了 或 者 因 异 常 退 出 了 run()方 法 ,该 线 程 结 束

生命周期。

8、什么是线程池?有哪几种创建方式?

线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处

理任务,处理完之后线程并不会被销毁,而是等待下一个任务。由于创建和销毁

线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程的时候就可以考

虑使用线程池来提升系统的性能。

java 提 供 了 一 个 java.util.concurrent.Executor 接 口 的 实 现 用 于 创 建 线 程 池 。

第 198 页 共 485 页
9、四种线程池的创建:

1、 newCachedThreadPool 创 建 一 个 可 缓 存 线 程 池

2、 newFixedThreadPool 创 建 一 个 定 长 线 程 池 , 可 控 制 线 程 最 大 并 发 数 。

3、 newScheduledThreadPool 创 建 一 个 定 长 线 程 池 , 支 持 定 时 及 周 期 性 任 务 执

行。

4、 newSingleThreadExecutor 创 建 一 个 单 线 程 化 的 线 程 池 , 它 只 会 用 唯 一 的 工

作线程来执行任务。

10、线程池的优点?

1、 重 用 存 在 的 线 程 , 减 少 对 象 创 建 销 毁 的 开 销 。

2、可 有 效 的 控 制 最 大 并 发 线 程 数 ,提 高 系 统 资 源 的 使 用 率 ,同 时 避 免 过 多 资 源 竞

争,避免堵塞。

3、 提 供 定 时 执 行 、 定 期 执 行 、 单 线 程 、 并 发 数 控 制 等 功 能 。

11、常用的并发工具类有哪些?

1、 CountDownLatch

2、 CyclicBarrier

3、 Semaphore

4、 Exchanger

12、CyclicBarrier 和 CountDownLatch 的区别

1、CountDownLatch 简 单 的 说 就 是 一 个 线 程 等 待 ,直 到 他 所 等 待 的 其 他 线 程 都 执

行 完 成 并 且 调 用 countDown()方 法 发 出 通 知 后 , 当 前 线 程 才 可 以 继 续 执 行 。

第 199 页 共 485 页
2、 cyclicBarrier 是 所 有 线 程 都 进 行 等 待 , 直 到 所 有 线 程 都 准 备 好 进 入 await()方

法之后,所有线程同时开始执行!

3、 CountDownLatch 的 计 数 器 只 能 使 用 一 次 。 而 CyclicBarrier 的 计 数 器 可 以 使

用 reset() 方 法 重 置 。 所 以 CyclicBarrier 能 处 理 更 为 复 杂 的 业 务 场 景 , 比 如 如 果

计算发生错误,可以重置计数器,并让线程们重新执行一次。

4、 CyclicBarrier 还 提 供 其 他 有 用 的 方 法 , 比 如 getNumberWaiting 方 法 可 以 获

得 CyclicBarrier 阻 塞 的 线 程 数 量 。isBroken 方 法 用 来 知 道 阻 塞 的 线 程 是 否 被 中 断 。

如 果 被 中 断 返 回 true, 否 则 返 回 false。

13、synchronized 的作用?

在 Java 中 , synchronized 关 键 字 是 用 来 控 制 线 程 同 步 的 , 就 是 在 多 线 程 的 环 境

下 , 控 制 synchronized 代 码 段 不 被 多 个 线 程 同 时 执 行 。

synchronized 既 可 以 加 在 一 段 代 码 上 , 也 可 以 加 在 方 法 上 。

14、volatile 关键字的作用

对 于 可 见 性 , Java 提 供 了 volatile 关 键 字 来 保 证 可 见 性 。

当 一 个 共 享 变 量 被 volatile 修 饰 时 , 它 会 保 证 修 改 的 值 会 立 即 被 更 新 到 主 存 , 当

有其他线程需要读取时,它会去内存中读取新值。

从 实 践 角 度 而 言 , volatile 的 一 个 重 要 作 用 就 是 和 CAS 结 合 , 保 证 了 原 子 性 , 详

细 的 可 以 参 见 java.util.concurrent.atomic 包 下 的 类 , 比 如 AtomicInteger。

15、什么是 CAS

CAS 是 compare and swap 的 缩 写 , 即 我 们 所 说 的 比 较 交 换 。

cas 是 一 种 基 于 锁 的 操 作 , 而 且 是 乐 观 锁 。 在 java 中 锁 分 为 乐 观 锁 和 悲 观 锁 。 悲

观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访

第 200 页 共 485 页
问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通

过 给 记 录 加 version 来 获 取 数 据 , 性 能 较 悲 观 锁 有 很 大 的 提 高 。

CAS 操 作 包 含 三 个 操 作 数 — — 内 存 位 置 ( V) 、 预 期 原 值 ( A) 和 新 值 (B)。 如

果 内 存 地 址 里 面 的 值 和 A 的 值 是 一 样 的 , 那 么 就 将 内 存 里 面 的 值 更 新 成 B。 CAS

是 通 过 无 限 循 环 来 获 取 数 据 的 ,若 果 在 第 一 轮 循 环 中 ,a 线 程 获 取 地 址 里 面 的 值 被

b 线程修改了,那么 a 线程需要自旋,到下次循环才有可能机会执行。

java.util.concurrent.atomic 包 下 的 类 大 多 是 使 用 CAS 操 作 来 实 现 的

( AtomicInteger,AtomicBoolean,AtomicLong)。

16、CAS 的问题

1、 CAS 容 易 造 成 ABA 问 题

一 个 线 程 a 将 数 值 改 成 了 b, 接 着 又 改 成 了 a, 此 时 CAS 认 为 是 没 有 变 化 , 其 实

是已经变化过了,而这个问题的解决方案可以使用版本号标识,每操作一次

version 加 1。在 java5 中 ,已 经 提 供 了 AtomicStampedReference 来 解 决 问 题 。

2、 不 能 保 证 代 码 块 的 原 子 性

CAS 机 制 所 保 证 的 知 识 一 个 变 量 的 原 子 性 操 作 ,而 不 能 保 证 整 个 代 码 块 的 原 子 性 。

比 如 需 要 保 证 3 个 变 量 共 同 进 行 原 子 性 的 更 新 , 就 不 得 不 使 用 synchronized 了 。

3、 CAS 造 成 CPU 利 用 率 增 加

之 前 说 过 了 CAS 里 面 是 一 个 循 环 判 断 的 过 程 ,如 果 线 程 一 直 没 有 获 取 到 状 态 ,cpu

资源会一直被占用。

17、什么是 Future?

在并发编程中,我们经常用到非阻塞的模型,在之前的多线程的三种实现中,不

管 是 继 承 thread 类 还 是 实 现 runnable 接 口 ,都 无 法 保 证 获 取 到 之 前 的 执 行 结 果 。

通 过 实 现 Callback 接 口 , 并 用 Future 可 以 来 接 收 多 线 程 的 执 行 结 果 。

Future 表 示 一 个 可 能 还 没 有 完 成 的 异 步 任 务 的 结 果 , 针 对 这 个 结 果 可 以 添 加

Callback 以 便 在 任 务 执 行 成 功 或 失 败 后 作 出 相 应 的 操 作 。

第 201 页 共 485 页
18、什么是 AQS

AQS 是 AbustactQueuedSynchronizer 的 简 称 , 它 是 一 个 Java 提 高 的 底 层 同 步

工 具 类 , 用 一 个 int 类 型 的 变 量 表 示 同 步 状 态 , 并 提 供 了 一 系 列 的 CAS 操 作 来 管

理这个同步状态。

AQS 是 一 个 用 来 构 建 锁 和 同 步 器 的 框 架 ,使 用 AQS 能 简 单 且 高 效 地 构 造 出 应 用 广

泛 的 大 量 的 同 步 器 , 比 如 我 们 提 到 的 ReentrantLock, Semaphore, 其 他 的 诸 如

ReentrantReadWriteLock, SynchronousQueue, FutureTask 等 等 皆 是 基 于

AQS 的 。

19、AQS 支持两种同步方式:

1、 独 占 式

2、 共 享 式

这 样 方 便 使 用 者 实 现 不 同 类 型 的 同 步 组 件 , 独 占 式 如 ReentrantLock, 共 享 式 如

Semaphore,CountDownLatch,组 合 式 的 如 ReentrantReadWriteLock。总 之 ,

AQS 为 使 用 提 供 了 底 层 支 撑 , 如 何 组 装 实 现 , 使 用 者 可 以 自 由 发 挥 。

20、ReadWriteLock 是什么

首 先 明 确 一 下 ,不 是 说 ReentrantLock 不 好 ,只 是 ReentrantLock 某 些 时 候 有 局

限 。 如 果 使 用 ReentrantLock, 可 能 本 身 是 为 了 防 止 线 程 A 在 写 数 据 、 线 程 B 在

读数据造成的数据不一致,但这样,如果线程 C 在读数据、线程 D 也在读数据,

读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能 。

因 为 这 个 ,才 诞 生 了 读 写 锁 ReadWriteLock。ReadWriteLock 是 一 个 读 写 锁 接 口 ,

ReentrantReadWriteLock 是 ReadWriteLock 接 口 的 一 个 具 体 实 现 ,实 现 了 读 写

第 202 页 共 485 页
的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、

写和写之间才会互斥,提升了读写的性能。

21、FutureTask 是什么

这 个 其 实 前 面 有 提 到 过 ,FutureTask 表 示 一 个 异 步 运 算 的 任 务 。FutureTask 里 面

可 以 传 入 一 个 Callable 的 具 体 实 现 类 , 可 以 对 这 个 异 步 运 算 的 任 务 的 结 果 进 行 等

待 获 取 、 判 断 是 否 已 经 完 成 、 取 消 任 务 等 操 作 。 当 然 , 由 于 FutureTask 也 是

Runnable 接 口 的 实 现 类 , 所 以 FutureTask 也 可 以 放 入 线 程 池 中 。

22、synchronized 和 ReentrantLock 的区别

synchronized 是 和 if、 else、 for、 while 一 样 的 关 键 字 , ReentrantLock 是 类 ,

这 是 二 者 的 本 质 区 别 。 既 然 ReentrantLock 是 类 , 那 么 它 就 提 供 了 比

synchronized 更 多 更 灵 活 的 特 性 , 可 以 被 继 承 、可 以 有 方 法 、可 以 有 各 种 各 样 的

类 变 量 , ReentrantLock 比 synchronized 的 扩 展 性 体 现 在 几 点 上 :

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

2、 ReentrantLock 可 以 获 取 各 种 锁 的 信 息

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

另 外 ,二 者 的 锁 机 制 其 实 也 是 不 一 样 的 。ReentrantLock 底 层 调 用 的 是 Unsafe 的

park 方 法 加 锁 , synchronized 操 作 的 应 该 是 对 象 头 中 mark word, 这 点 我 不 能

确定。

23、什么是乐观锁和悲观锁

1、乐 观 锁 :就 像 它 的 名 字 一 样 ,对 于 并 发 间 操 作 产 生 的 线 程 安 全 问 题 持 乐 观 状 态 ,

乐 观 锁 认 为 竞 争 不 总 是 会 发 生 , 因 此 它 不 需 要 持 有 锁 , 将 比 较 -替 换 这 两 个 动 作 作

第 203 页 共 485 页
为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应

该有相应的重试逻辑。

2、悲 观 锁 :还 是 像 它 的 名 字 一 样 ,对 于 并 发 间 操 作 产 生 的 线 程 安 全 问 题 持 悲 观 状

态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个

独 占 的 锁 , 就 像 synchronized, 不 管 三 七 二 十 一 , 直 接 上 了 锁 就 操 作 资 源 了 。

24、线程 B 怎么知道线程 A 修改了变量

1、 volatile 修 饰 变 量

2、 synchronized 修 饰 修 改 变 量 的 方 法

3、 wait/notify

4、 while 轮 询

25、synchronized、volatile、CAS 比较

1、 synchronized 是 悲 观 锁 , 属 于 抢 占 式 , 会 引 起 其 他 线 程 阻 塞 。

2、 volatile 提 供 多 线 程 共 享 变 量 可 见 性 和 禁 止 指 令 重 排 序 优 化 。

3、 CAS 是 基 于 冲 突 检 测 的 乐 观 锁 ( 非 阻 塞 )

26、sleep 方法和 wait 方法有什么区别?

这 个 问 题 常 问 ,sleep 方 法 和 wait 方 法 都 可 以 用 来 放 弃 CPU 一 定 的 时 间 ,不 同 点

在 于 如 果 线 程 持 有 某 个 对 象 的 监 视 器 , sleep 方 法 不 会 放 弃 这 个 对 象 的 监 视 器 ,

wait 方 法 会 放 弃 这 个 对 象 的 监 视 器

27、ThreadLocal 是什么?有什么用?

第 204 页 共 485 页
ThreadLocal 是 一 个 本 地 线 程 副 本 变 量 工 具 类 。 主 要 用 于 将 私 有 线 程 和 该 线 程 存

放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可

以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景。

简 单 说 ThreadLocal 就 是 一 种 以 空 间 换 时 间 的 做 法 , 在 每 个 Thread 里 面 维 护 了

一 个 以 开 地 址 法 实 现 的 ThreadLocal.ThreadLocalMap, 把 数 据 进 行 隔 离 , 数 据

不共享,自然就没有线程安全方面的问题了。

28、为什么 wait()方法和 notify()/notifyAll()方法要在同步块

中被调用

这 是 JDK 强 制 的 ,wait()方 法 和 notify()/notifyAll()方 法 在 调 用 前 都 必 须 先 获 得 对

象的锁

29、多线程同步有哪几种方法?

Synchronized 关 键 字 , Lock 锁 实 现 , 分 布 式 锁 等 。

30、线程的调度策略

线程调度器选择优先级最高的线程运行,但是,如果发生以下情况,就会终止线

程的运行:

1、 线 程 体 中 调 用 了 yield 方 法 让 出 了 对 cpu 的 占 用 权 利

2、 线 程 体 中 调 用 了 sleep 方 法 使 线 程 进 入 睡 眠 状 态

3、 线 程 由 于 IO 操 作 受 到 阻 塞

4、 另 外 一 个 更 高 优 先 级 线 程 出 现

5) 在 支 持 时 间 片 的 系 统 中 , 该 线 程 的 时 间 片 用 完

第 205 页 共 485 页
31、ConcurrentHashMap 的并发度是什么

ConcurrentHashMap 的 并 发 度 就 是 segment 的 大 小 , 默 认 为 16, 这 意 味 着 最

多 同 时 可 以 有 16 条 线 程 操 作 ConcurrentHashMap, 这 也 是

ConcurrentHashMap 对 Hashtable 的 最 大 优 势 , 任 何 情 况 下 , Hashtable 能 同

时 有 两 条 线 程 获 取 Hashtable 中 的 数 据 吗 ?

32、Linux 环境下如何查找哪个线程使用 CPU 最长

1、 获 取 项 目 的 pid, jps 或 者 ps -ef | grep java, 这 个 前 面 有 讲 过

2、 top -H -p pid, 顺 序 不 能 改 变

33、Java 死锁以及如何避免?

Java 中 的 死 锁 是 一 种 编 程 情 况 ,其 中 两 个 或 多 个 线 程 被 永 久 阻 塞 ,Java 死 锁 情 况

出现至少两个线程和两个或更多资源。

Java 发 生 死 锁 的 根 本 原 因 是 : 在 申 请 锁 时 发 生 了 交 叉 闭 环 申 请 。

34、死锁的原因

1、是 多 个 线 程 涉 及 到 多 个 锁 ,这 些 锁 存 在 着 交 叉 ,所 以 可 能 会 导 致 了 一 个 锁 依 赖

的闭环。

例 如 : 线 程 在 获 得 了 锁 A 并 且 没 有 释 放 的 情 况 下 去 申 请 锁 B, 这 时 , 另 一 个 线 程

已 经 获 得 了 锁 B,在 释 放 锁 B 之 前 又 要 先 获 得 锁 A,因 此 闭 环 发 生 ,陷 入 死 锁 循 环 。

2、 默 认 的 锁 申 请 操 作 是 阻 塞 的 。

第 206 页 共 485 页
所以要避免死锁,就要在一遇到多个对象锁交叉的情况,就要仔细审查这几个对

象的类中的所有方法,是否存在着导致锁依赖的环路的可能性。总之是尽量避免

在一个同步方法中调用其它对象的延时方法和同步方法。

35、怎么唤醒一个阻塞的线程

如 果 线 程 是 因 为 调 用 了 wait()、sleep()或 者 join()方 法 而 导 致 的 阻 塞 ,可 以 中 断 线

程 , 并 且 通 过 抛 出 InterruptedException 来 唤 醒 它 ; 如 果 线 程 遇 到 了 IO 阻 塞 ,

无 能 为 力 ,因 为 IO 是 操 作 系 统 实 现 的 ,Java 代 码 并 没 有 办 法 直 接 接 触 到 操 作 系 统 。

36、不可变对象对多线程有什么帮助

前面有提到过的一个问题,不可变对象保证了对象的内存可见性,对不可变对象

的读取不需要进行额外的同步手段,提升了代码执行效率。

37、什么是多线程的上下文切换

多 线 程 的 上 下 文 切 换 是 指 CPU 控 制 权 由 一 个 已 经 正 在 运 行 的 线 程 切 换 到 另 外 一 个

就 绪 并 等 待 获 取 CPU 执 行 权 的 线 程 的 过 程 。

38、如果你提交任务时,线程池队列已满,这时会发生什么

这里区分一下:

1、 如 果 使 用 的 是 无 界 队 列 LinkedBlockingQueue, 也 就 是 无 界 队 列 的 话 , 没 关

系 ,继 续 添 加 任 务 到 阻 塞 队 列 中 等 待 执 行 ,因 为 LinkedBlockingQueue 可 以 近 乎

认为是一个无穷大的队列,可以无限存放任务

第 207 页 共 485 页
2、 如 果 使 用 的 是 有 界 队 列 比 如 ArrayBlockingQueue, 任 务 首 先 会 被 添 加 到

ArrayBlockingQueue 中 , ArrayBlockingQueue 满 了 , 会 根 据

maximumPoolSize 的 值 增 加 线 程 数 量 , 如 果 增 加 了 线 程 数 量 还 是 处 理 不 过 来 ,

ArrayBlockingQueue 继 续 满 , 那 么 则 会 使 用 拒 绝 策 略

RejectedExecutionHandler 处 理 满 了 的 任 务 , 默 认 是 AbortPolicy

39、Java 中用到的线程调度算法是什么

抢 占 式 。 一 个 线 程 用 完 CPU 之 后 , 操 作 系 统 会 根 据 线 程 优 先 级 、 线 程 饥 饿 情 况 等

数据算出一个总的优先级并分配下一个时间片给某个线程执行。

40、什么是线程调度器(Thread Scheduler)和时间分片(Time

Slicing)?

线 程 调 度 器 是 一 个 操 作 系 统 服 务 ,它 负 责 为 Runnable 状 态 的 线 程 分 配 CPU 时 间 。

一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分

片 是 指 将 可 用 的 CPU 时 间 分 配 给 可 用 的 Runnable 线 程 的 过 程 。分 配 CPU 时 间 可

以 基 于 线 程 优 先 级 或 者 线 程 等 待 的 时 间 。线 程 调 度 并 不 受 到 Java 虚 拟 机 控 制 ,所

以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优

先级)。

41、什么是自旋

很 多 synchronized 里 面 的 代 码 只 是 一 些 很 简 单 的 代 码 ,执 行 时 间 非 常 快 ,此 时 等

待的线程都加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核

态 切 换 的 问 题 。既 然 synchronized 里 面 的 代 码 执 行 得 非 常 快 ,不 妨 让 等 待 锁 的 线

第 208 页 共 485 页
程 不 要 被 阻 塞 ,而 是 在 synchronized 的 边 界 做 忙 循 环 ,这 就 是 自 旋 。如 果 做 了 多

次忙循环发现还没有获得锁,再阻塞,这样可能是一种更好的策略。

42、Java Concurrency API 中的 Lock 接口(Lock interface)

是什么?对比同步它有什么优势?

Lock 接 口 比 同 步 方 法 和 同 步 块 提 供 了 更 具 扩 展 性 的 锁 操 作 。 他 们 允 许 更 灵 活 的 结

构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。

它的优势有:

1、 可 以 使 锁 更 公 平

2、 可 以 使 线 程 在 等 待 锁 的 时 候 响 应 中 断

3、 可 以 让 线 程 尝 试 获 取 锁 , 并 在 无 法 获 取 锁 的 时 候 立 即 返 回 或 者 等 待 一 段 时 间

4、 可 以 在 不 同 的 范 围 , 以 不 同 的 顺 序 获 取 和 释 放 锁

43、单例模式的线程安全性

老生常谈的问题了,首先要说的是单例模式的线程安全意味着:某个类的实例在

多线程环境下只会被创建一次出来。单例模式有很多种的写法,我总结一下:

1、 饿 汉 式 单 例 模 式 的 写 法 : 线 程 安 全

2、 懒 汉 式 单 例 模 式 的 写 法 : 非 线 程 安 全

3、 双 检 锁 单 例 模 式 的 写 法 : 线 程 安 全

44、Semaphore 有什么作用

Semaphore 就 是 一 个 信 号 量 ,它 的 作 用 是 限 制 某 段 代 码 块 的 并 发 数 。Semaphore

有 一 个 构 造 函 数 ,可 以 传 入 一 个 int 型 整 数 n,表 示 某 段 代 码 最 多 只 有 n 个 线 程 可

以 访 问 ,如 果 超 出 了 n,那 么 请 等 待 ,等 到 某 个 线 程 执 行 完 毕 这 段 代 码 块 ,下 一 个

第 209 页 共 485 页
线 程 再 进 入 。 由 此 可 以 看 出 如 果 Semaphore 构 造 函 数 中 传 入 的 int 型 整 数 n=1,

相 当 于 变 成 了 一 个 synchronized 了 。

45、Executors 类是什么?

Executors 为 Executor, ExecutorService, ScheduledExecutorService,

ThreadFactory 和 Callable 类 提 供 了 一 些 工 具 方 法 。

Executors 可 以 用 于 方 便 的 创 建 线 程 池

46、线程类的构造方法、静态块是被哪个线程调用的

这 是 一 个 非 常 刁 钻 和 狡 猾 的 问 题 。 请 记 住 : 线 程 类 的 构 造 方 法 、 静 态 块 是 被 new

这 个 线 程 类 所 在 的 线 程 所 调 用 的 , 而 run 方 法 里 面 的 代 码 才 是 被 线 程 自 身 所 调 用

的。

如 果 说 上 面 的 说 法 让 你 感 到 困 惑 , 那 么 我 举 个 例 子 , 假 设 Thread2 中 new 了

Thread1, main 函 数 中 new 了 Thread2, 那 么 :

1、 Thread2 的 构 造 方 法 、 静 态 块 是 main 线 程 调 用 的 , Thread2 的 run()方 法 是

Thread2 自 己 调 用 的

2、 Thread1 的 构 造 方 法 、 静 态 块 是 Thread2 调 用 的 , Thread1 的 run()方 法 是

Thread1 自 己 调 用 的

47、同步方法和同步块,哪个是更好的选择?

同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代

码的效率。请知道一条原则:同步的范围越小越好。

48、Java 线程数过多会造成什么异常?

第 210 页 共 485 页
1、 线 程 的 生 命 周 期 开 销 非 常 高

2、 消 耗 过 多 的 CPU 资 源

如果可运行的线程数量多于可用处理器的数量,那么有线程将会被闲置。大量空

闲 的 线 程 会 占 用 许 多 内 存 , 给 垃 圾 回 收 器 带 来 压 力 , 而 且 大 量 的 线 程 在 竞 争 CPU

资源时还将产生其他性能的开销。

3、 降 低 稳 定 性

JVM 在 可 创 建 线 程 的 数 量 上 存 在 一 个 限 制 ,这 个 限 制 值 将 随 着 平 台 的 不 同 而 不 同 ,

并 且 承 受 着 多 个 因 素 制 约 , 包 括 JVM 的 启 动 参 数 、 Thread 构 造 函 数 中 请 求 栈 的

大小,以及底层操作系统对线程的限制等。如果破坏了这些限制,那么可能抛出

OutOfMemoryError 异 常 。

Java 面试题(一)

1、面向对象的特征有哪些方面?

答:

面向对象的特征主要有以下几个方面:

 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽
象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的
细节是什么。
 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类
被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让
变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要
手段(如果不能理解请阅读阎宏博士的《Java 与模式》或《设计模式精解》中
关于桥梁模式的部分)。

第 211 页 共 485 页
 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问
只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自
治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写
一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,
只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别,
明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是
封装得足够好的,因为几个按键就搞定了所有的事情)。
 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。
简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分
为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的
服务,那么运行时的多态性可以解释为:当 A 系统访问 B 系统提供的服务时,B
系统有多种提供服务的方式,但一切对 A 系统来说都是透明的(就像电动剃须
刀是 A 系统,它的供电系统是 B 系统,B 系统可以使用电池供电或者用交流电,
甚至还有可能是太阳能,A 系统只会通过 B 类对象调用供电的方法,但并不知道
供电系统的底层实现是什么,究竟通过何种方式获得了动力)。方法重载
(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)
实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的
东西,要实现多态需要做两件事:1). 方法重写(子类继承父类并重写父类中已
有的或抽象的方法);2). 对象造型(用父类型引用引用子类型对象,这样同样
的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

2、访问修饰符 public,private,protected,以及不写(默认)

时的区别?

答:

修饰符 当前类 同 包 子 类 其他包

public √ √ √ √

第 212 页 共 485 页
protecte √ √ √ ×

default √ √ × ×

private √ × × ×

类 的 成 员 不 写 访 问 修 饰 时 默 认 为 default。默 认 对 于 同 一 个 包 中 的 其 他 类 相 当 于 公

开 ( public) , 对 于 不 是 同 一 个 包 中 的 其 他 类 相 当 于 私 有 ( private) 。 受 保 护

( protected)对 子 类 相 当 于 公 开 ,对 不 是 同 一 包 中 的 没 有 父 子 关 系 的 类 相 当 于 私

有 。Java 中 ,外 部 类 的 修 饰 符 只 能 是 public 或 默 认 ,类 的 成 员( 包 括 内 部 类 )的

修饰符可以是以上四种。

3、String 是最基本的数据类型吗?

答:

不 是 。Java 中 的 基 本 数 据 类 型 只 有 8 个 :byte、short、int、long、float、double、

char、boolean;除 了 基 本 类 型( primitive type),剩 下 的 都 是 引 用 类 型( reference

type) , Java 5 以 后 引 入 的 枚 举 类 型 也 算 是 一 种 比 较 特 殊 的 引 用 类 型 。

4、float f=3.4;是否正确?

答 :不 正 确 。 3.4 是 双 精 度 数 , 将 双 精 度 型 ( double) 赋 值 给 浮 点 型 ( float) 属 于

下 转 型 ( down-casting, 也 称 为 窄 化 ) 会 造 成 精 度 损 失 , 因 此 需 要 强 制 类 型 转 换

float f =(float)3.4; 或 者 写 成 float f =3.4F;。

第 213 页 共 485 页
5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;

有错吗?

答:

对 于 short s1 = 1; s1 = s1 + 1;由 于 1 是 int 类 型 , 因 此 s1+1 运 算 结 果 也 是 int

型 , 需 要 强 制 转 换 类 型 才 能 赋 值 给 short 型 。 而 short s1 = 1; s1 += 1;可 以 正 确

编 译 , 因 为 s1+= 1;相 当 于 s1 = (short)(s1 + 1);其 中 有 隐 含 的 强 制 类 型 转 换 。

6、Java 有没有 goto?

答:

goto 是 Java 中 的 保 留 字 ,在 目 前 版 本 的 Java 中 没 有 使 用 。


( 根 据 James Gosling

( Java 之 父 ) 编 写 的 《 The Java Programming Language》 一 书 的 附 录 中 给 出

了 一 个 Java 关 键 字 列 表 , 其 中 有 goto 和 const, 但 是 这 两 个 是 目 前 无 法 使 用 的

关键字,因此有些地方将其称之为保留字,其实保留字这个词应该有更广泛的意

义,因为熟悉 C 语言的程序员都知道,在系统类库中使用过的有特殊意义的单词

或单词的组合都被视为保留字)

7、int 和 Integer 有什么区别?

答:

Java 是 一 个 近 乎 纯 洁 的 面 向 对 象 编 程 语 言 , 但 是 为 了 编 程 的 方 便 还 是 引 入 了 基 本

数 据 类 型 , 但 是 为 了 能 够 将 这 些 基 本 数 据 类 型 当 成 对 象 操 作 , Java 为 每 一 个 基 本

第 214 页 共 485 页
数 据 类 型 都 引 入 了 对 应 的 包 装 类 型( wrapper class),int 的 包 装 类 就 是 Integer,

从 Java 5 开 始 引 入 了 自 动 装 箱 /拆 箱 机 制 , 使 得 二 者 可 以 相 互 转 换 。

Java 为 每 个 原 始 类 型 提 供 了 包 装 类 型 :

 原始类型: boolean,char,byte,short,int,long,float,double
 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,
Double

class AutoUnboxingTest {

public static void main(String[] args) {


Integer a = new Integer(3);
Integer b = 3; // 将 3 自动装箱成 Integer 类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对

System.out.println(a == c); // true a 自动拆箱成 int 类型再和 c
比较
}
}

最近还遇到一个面试题,也是和自动装箱和拆箱有点关系的,代码如下所示:

public class Test03 {

public static void main(String[] args) {


Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;

System.out.println(f1 == f2);
System.out.println(f3 == f4);

第 215 页 共 485 页
}
}

如 果 不 明 就 里 很 容 易 认 为 两 个 输 出 要 么 都 是 true 要 么 都 是 false。首 先 需 要 注 意 的

是 f1、f2、f3、f4 四 个 变 量 都 是 Integer 对 象 引 用 ,所 以 下 面 的 ==运 算 比 较 的 不

是 值 而 是 引 用 。装 箱 的 本 质 是 什 么 呢 ? 当 我 们 给 一 个 Integer 对 象 赋 一 个 int 值 的

时 候 , 会 调 用 Integer 类 的 静 态 方 法 valueOf, 如 果 看 看 valueOf 的 源 代 码 就 知

道发生了什么。

public static Integer valueOf(int i) {


if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

IntegerCache 是 Integer 的 内 部 类 , 其 代 码 如 下 所 示 :

/**
* Cache to support the object identity semantics of autoboxing for
values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>}
option.
* During VM initialization, java.lang.Integer.IntegerCache.high
property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/

第 216 页 共 485 页
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =

sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int,
ignore it.
}
}
high = h;

cache = new Integer[(high - low) + 1];


int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)


assert IntegerCache.high >= 127;

第 217 页 共 485 页
}

private IntegerCache() {}
}

简 单 的 说 ,如 果 整 型 字 面 量 的 值 在 -128 到 127 之 间 ,那 么 不 会 new 新 的 Integer

对 象 , 而 是 直 接 引 用 常 量 池 中 的 Integer 对 象 , 所 以 上 面 的 面 试 题 中 f1f4 的 结 果

是 false。

提醒:越是貌似简单的面试题其中的玄机就越多,需要面试者有相当深厚的功力。

8、&和&&的区别?

答:

&运 算 符 有 两 种 用 法 : (1)按 位 与 ; (2)逻 辑 与 。 &&运 算 符 是 短 路 与 运 算 。 逻 辑 与

跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是

true 整 个 表 达 式 的 值 才 是 true。&&之 所 以 称 为 短 路 运 算 是 因 为 ,如 果 &&左 边 的

表 达 式 的 值 是 false, 右 边 的 表 达 式 会 被 直 接 短 路 掉 , 不 会 进 行 运 算 。 很 多 时 候 我

们 可 能 都 需 要 用 &&而 不 是 &,例 如 在 验 证 用 户 登 录 时 判 定 用 户 名 不 是 null 而 且 不

是 空 字 符 串 , 应 当 写 为 : username != null &&!username.equals(“ ” ), 二 者

的 顺 序 不 能 交 换 ,更 不 能 用 &运 算 符 ,因 为 第 一 个 条 件 如 果 不 成 立 ,根 本 不 能 进 行

字 符 串 的 equals 比 较 , 否 则 会 产 生 NullPointerException 异 常 。 注 意 : 逻 辑 或

运 算 符 ( |) 和 短 路 或 运 算 符 ( ||) 的 差 别 也 是 如 此 。

补 充 : 如 果 你 熟 悉 JavaScript, 那 你 可 能 更 能 感 受 到 短 路 运 算 的 强 大 , 想 成 为

JavaScript 的 高 手 就 先 从 玩 转 短 路 运 算 开 始 吧 。

第 218 页 共 485 页
9、解释内存中的栈(stack)、堆(heap)和方法区(method area)

的用法。

答:

通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的

现 场 保 存 都 使 用 JVM 中 的 栈 空 间 ;而 通 过 new 关 键 字 和 构 造 器 创 建 的 对 象 则 放 在

堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收

集 算 法 , 所 以 堆 空 间 还 可 以 细 分 为 新 生 代 和 老 生 代 , 再 具 体 一 点 可 以 分 为 Eden、

Survivor( 又 可 分 为 From Survivor 和 To Survivor) 、 Tenured; 方 法 区 和 堆 都

是 各 个 线 程 共 享 的 内 存 区 域 , 用 于 存 储 已 经 被 JVM 加 载 的 类 信 息 、 常 量 、 静 态 变

量 、JIT 编 译 器 编 译 后 的 代 码 等 数 据 ;程 序 中 的 字 面 量( literal)如 直 接 书 写 的 100、”

hello” 和 常 量 都 是 放 在 常 量 池 中 , 常 量 池 是 方 法 区 的 一 部 分 , 。 栈 空 间 操 作 起 来

最 快 但 是 栈 很 小 ,通 常 大 量 的 对 象 都 是 放 在 堆 空 间 ,栈 和 堆 的 大 小 都 可 以 通 过 JVM

的 启 动 参 数 来 进 行 调 整 , 栈 空 间 用 光 了 会 引 发 StackOverflowError, 而 堆 和 常 量

池 空 间 不 足 则 会 引 发 OutOfMemoryError。

String str = new String("hello");

上 面 的 语 句 中 变 量 str 放 在 栈 上 , 用 new 创 建 出 来 的 字 符 串 对 象 放 在 堆 上 , 而 ”

hello” 这 个 字 面 量 是 放 在 方 法 区 的 。

补 充 1: 较 新 版 本 的 Java( 从 Java 6 的 某 个 更 新 开 始 ) 中 , 由 于 JIT 编 译 器 的 发

展和”逃逸分析”技术的逐渐成熟,栈上分配、标量替换等优化技术使得对象一

定分配在堆上这件事情已经变得不那么绝对了。

补 充 2: 运 行 时 常 量 池 相 当 于 Class 文 件 常 量 池 具 有 动 态 性 , Java 语 言 并 不 要 求

常 量 一 定 只 有 编 译 期 间 才 能 产 生 , 运 行 期 间 也 可 以 将 新 的 常 量 放 入 池 中 , String

类 的 intern()方 法 就 是 这 样 的 。

第 219 页 共 485 页
看 看 下 面 代 码 的 执 行 结 果 是 什 么 并 且 比 较 一 下 Java 7 以 前 和 以 后 的 运 行 结 果 是 否

一致。

String s1 = new StringBuilder("go")


.append("od").toString();
System.out.println(s1.intern() == s1);
String s2 = new StringBuilder("ja")
.append("va").toString();
System.out.println(s2.intern() == s2);

10、Math.round(11.5) 等于多少?Math.round(-11.5)等于

多少?

答:

Math.round(11.5)的 返 回 值 是 12, Math.round(-11.5)的 返 回 值 是 -11。 四 舍 五

入 的 原 理 是 在 参 数 上 加 0.5 然 后 进 行 下 取 整 。

11、switch 是否能作用在 byte 上,是否能作用在 long 上,

是否能作用在 String 上?

答:

在 Java 5 以 前 ,switch(expr)中 ,expr 只 能 是 byte、short、char、int。从 Java

5 开 始 , Java 中 引 入 了 枚 举 类 型 , expr 也 可 以 是 enum 类 型 , 从 Java 7 开 始 ,

expr 还 可 以 是 字 符 串 ( String) , 但 是 长 整 型 ( long) 在 目 前 所 有 的 版 本 中 都 是

不可以的。

第 220 页 共 485 页
12、用最有效率的方法计算 2 乘以 8?

答:

2 << 3( 左 移 3 位 相 当 于 乘 以 2 的 3 次 方 , 右 移 3 位 相 当 于 除 以 2 的 3 次 方 ) 。

补 充 : 我 们 为 编 写 的 类 重 写 hashCode 方 法 时 , 可 能 会 看 到 如 下 所 示 的 代 码 , 其

实我们不太理解为什么要使用这样的乘法运算来产生哈希码(散列码),而且为

什 么 这 个 数 是 个 素 数 ,为 什 么 通 常 选 择 31 这 个 数 ? 前 两 个 问 题 的 答 案 你 可 以 自 己

百 度 一 下 ,选 择 31 是 因 为 可 以 用 移 位 和 减 法 运 算 来 代 替 乘 法 ,从 而 得 到 更 好 的 性

能 。 说 到 这 里 你 可 能 已 经 想 到 了 : 31 * num 等 价 于 (num << 5) - num, 左 移 5

位 相 当 于 乘 以 2 的 5 次 方 再 减 去 自 身 就 相 当 于 乘 以 31,现 在 的 VM 都 能 自 动 完 成

这个优化。

public class PhoneNumber {


private int areaCode;
private String prefix;
private String lineNumber;

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + areaCode;
result = prime * result
+ ((lineNumber == null) ? 0 : lineNumber.hashCode());
result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
return result;
}

第 221 页 共 485 页
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PhoneNumber other = (PhoneNumber) obj;
if (areaCode != other.areaCode)
return false;
if (lineNumber == null) {
if (other.lineNumber != null)
return false;
} else if (!lineNumber.equals(other.lineNumber))
return false;
if (prefix == null) {
if (other.prefix != null)
return false;
} else if (!prefix.equals(other.prefix))
return false;
return true;
}

13、数组有没有 length()方法?String 有没有 length()方法?

答:

第 222 页 共 485 页
数 组 没 有 length()方 法 ,有 length 的 属 性 。String 有 length()方 法 。JavaScript

中 , 获 得 字 符 串 的 长 度 是 通 过 length 属 性 得 到 的 , 这 一 点 容 易 和 Java 混 淆 。

14、在 Java 中,如何跳出当前的多重嵌套循环?

答:

在 最 外 层 循 环 前 加 一 个 标 记 如 A, 然 后 用 break A;可 以 跳 出 多 重 循 环 。 ( Java 中

支 持 带 标 签 的 break 和 continue 语 句 , 作 用 有 点 类 似 于 C 和 C++中 的 goto 语

句 ,但 是 就 像 要 避 免 使 用 goto 一 样 ,应 该 避 免 使 用 带 标 签 的 break 和 continue,

因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法

其实不知道更好)

15、构造器(constructor)是否可被重写(override)?

答:

构造器不能被继承,因此不能被重写,但可以被重载。

16、两个对象值相同(x.equals(y) == true),但却可有不同的

hash code,这句话对不对?

答:

不 对 ,如 果 两 个 对 象 x 和 y 满 足 x.equals(y) == true,它 们 的 哈 希 码( hash code)

应 当 相 同 。 Java 对 于 eqauls 方 法 和 hashCode 方 法 是 这 样 规 定 的 : (1)如 果 两 个

第 223 页 共 485 页
对 象 相 同 ( equals 方 法 返 回 true) , 那 么 它 们 的 hashCode 值 一 定 要 相 同 ; (2)

如 果 两 个 对 象 的 hashCode 相 同 , 它 们 并 不 一 定 相 同 。 当 然 , 你 未 必 要 按 照 要 求

去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现

在 Set 集 合 中 , 同 时 增 加 新 元 素 的 效 率 会 大 大 下 降 ( 对 于 使 用 哈 希 存 储 的 系 统 ,

如果哈希码频繁的冲突将会造成存取性能急剧下降)。

补 充 : 关 于 equals 和 hashCode 方 法 , 很 多 Java 程 序 都 知 道 , 但 很 多 人 也 就 是

仅 仅 知 道 而 已 , 在 Joshua Bloch 的 大 作 《 Effective Java》 ( 很 多 软 件 公 司 ,

《 Effective Java》、《 Java 编 程 思 想 》以 及《 重 构 :改 善 既 有 代 码 质 量 》是 Java

程序员必看书籍,如果你还没看过,那就赶紧去亚马逊买一本吧)中是这样介绍

equals 方 法 的 :首 先 equals 方 法 必 须 满 足 自 反 性( x.equals(x)必 须 返 回 true)、

对 称 性 ( x.equals(y)返 回 true 时 , y.equals(x)也 必 须 返 回 true) 、 传 递 性

( x.equals(y)和 y.equals(z)都 返 回 true 时 , x.equals(z)也 必 须 返 回 true) 和 一

致 性 ( 当 x 和 y 引 用 的 对 象 信 息 没 有 被 修 改 时 , 多 次 调 用 x.equals(y)应 该 得 到 同

样 的 返 回 值 ) , 而 且 对 于 任 何 非 null 值 的 引 用 x, x.equals(null)必 须 返 回 false。

实 现 高 质 量 的 equals 方 法 的 诀 窍 包 括 : 1. 使 用 ==操 作 符 检 查 ” 参 数 是 否 为 这 个

对 象 的 引 用 ”;2. 使 用 instanceof 操 作 符 检 查 ”参 数 是 否 为 正 确 的 类 型 ”;3. 对

于 类 中 的 关 键 属 性 ,检 查 参 数 传 入 对 象 的 属 性 是 否 与 之 相 匹 配 ;4. 编 写 完 equals

方 法 后 , 问 自 己 它 是 否 满 足 对 称 性 、 传 递 性 、 一 致 性 ; 5. 重 写 equals 时 总 是 要

重 写 hashCode;6. 不 要 将 equals 方 法 参 数 中 的 Object 对 象 替 换 为 其 他 的 类 型 ,

在 重 写 时 不 要 忘 掉 @Override 注 解 。

17、是否可以继承 String 类?

答:

String 类 是 final 类 , 不 可 以 被 继 承 。

补 充 :继 承 String 本 身 就 是 一 个 错 误 的 行 为 ,对 String 类 型 最 好 的 重 用 方 式 是 关

联 关 系 ( Has-A) 和 依 赖 关 系 ( Use-A) 而 不 是 继 承 关 系 ( Is-A) 。

第 224 页 共 485 页
18、当一个对象被当作参数传递到一个方法后,此方法可改变

这个对象的属性,并可返回变化后的结果,那么这里到底是值传

递还是引用传递?

答:

是 值 传 递 。 Java 语 言 的 方 法 调 用 只 支 持 参 数 的 值 传 递 。 当 一 个 对 象 实 例 作 为 一 个

参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调

用 过 程 中 被 改 变 ,但 对 对 象 引 用 的 改 变 是 不 会 影 响 到 调 用 者 的 。C++和 C#中 可 以

通 过 传 引 用 或 传 输 出 参 数 来 改 变 传 入 的 参 数 的 值 。在 C#中 可 以 编 写 如 下 所 示 的 代

码 , 但 是 在 Java 中 却 做 不 到 。

using System;

namespace CS01 {

class Program {
public static void swap(ref int x, ref int y) {
int temp = x;
x = y;
y = temp;
}

public static void Main (string[] args) {


int a = 5, b = 10;
swap (ref a, ref b);
// a = 10, b = 5;

第 225 页 共 485 页
Console.WriteLine ("a = {0}, b = {1}", a, b);
}
}
}

说 明 :Java 中 没 有 传 引 用 实 在 是 非 常 的 不 方 便 ,这 一 点 在 Java 8 中 仍 然 没 有 得 到

改 进 , 正 是 如 此 在 Java 编 写 的 代 码 中 才 会 出 现 大 量 的 Wrapper 类 ( 将 需 要 通 过

方 法 调 用 修 改 的 引 用 置 于 一 个 Wrapper 类 中 , 再 将 Wrapper 对 象 传 入 方 法 ) ,

这 样 的 做 法 只 会 让 代 码 变 得 臃 肿 , 尤 其 是 让 从 C 和 C++转 型 为 Java 程 序 员 的 开

发者无法容忍。

19、String 和 StringBuilder、StringBuffer 的区别?

答:

Java 平 台 提 供 了 两 种 类 型 的 字 符 串 : String 和 StringBuffer/StringBuilder, 它

们 可 以 储 存 和 操 作 字 符 串 。其 中 String 是 只 读 字 符 串 ,也 就 意 味 着 String 引 用 的

字 符 串 内 容 是 不 能 被 改 变 的 。而 StringBuffer/StringBuilder 类 表 示 的 字 符 串 对 象

可 以 直 接 进 行 修 改 。 StringBuilder 是 Java 5 中 引 入 的 , 它 和 StringBuffer 的 方

法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被

synchronized 修 饰 , 因 此 它 的 效 率 也 比 StringBuffer 要 高 。

面 试 题 1 - 什 么 情 况 下 用 +运 算 符 进 行 字 符 串 连 接 比 调 用

StringBuffer/StringBuilder 对 象 的 append 方 法 连 接 字 符 串 性 能 更 好 ?

面试题 2 - 请说出下面程序的输出。

class StringEqualTest {

public static void main(String[] args) {


String s1 = "Programming";

第 226 页 共 485 页
String s2 = new String("Programming");
String s3 = "Program";
String s4 = "ming";
String s5 = "Program" + "ming";
String s6 = s3 + s4;
System.out.println(s1 == s2);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s1 == s6.intern());
System.out.println(s2 == s2.intern());
}
}

补 充 :解 答 上 面 的 面 试 题 需 要 清 除 两 点 :1. String 对 象 的 intern 方 法 会 得 到 字 符

串 对 象 在 常 量 池 中 对 应 的 版 本 的 引 用 ( 如 果 常 量 池 中 有 一 个 字 符 串 与 String 对 象

的 equals 结 果 是 true),如 果 常 量 池 中 没 有 对 应 的 字 符 串 ,则 该 字 符 串 将 被 添 加

到 常 量 池 中 , 然 后 返 回 常 量 池 中 字 符 串 的 引 用 ; 2. 字 符 串 的 +操 作 其 本 质 是 创 建

了 StringBuilder 对 象 进 行 append 操 作 ,然 后 将 拼 接 后 的 StringBuilder 对 象 用

toString 方 法 处 理 成 String 对 象 ,这 一 点 可 以 用 javap -c StringEqualTest.class

命 令 获 得 class 文 件 对 应 的 JVM 字 节 码 指 令 就 可 以 看 出 来 。

20、重载(Overload)和重写(Override)的区别。重载的

方法能否根据返回类型进行区分?

答:

方 法 的 重 载 和 重 写 都 是 实 现 多 态 的 方 式 ,区 别 在 于 前 者 实 现 的 是 编 译 时 的 多 态 性 ,

而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同

的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写

第 227 页 共 485 页
发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返

回 类 型 ,比 父 类 被 重 写 方 法 更 好 访 问 ,不 能 比 父 类 被 重 写 方 法 声 明 更 多 的 异 常( 里

氏代换原则)。重载对返回类型没有特殊的要求。

面 试 题 :华 为 的 面 试 题 中 曾 经 问 过 这 样 一 个 问 题 - “ 为 什 么 不 能 根 据 返 回 类 型 来

区分重载”,快说出你的答案吧!

21、描述一下 JVM 加载 class 文件的原理机制?

答:

JVM 中 类 的 装 载 是 由 类 加 载 器 ( ClassLoader) 和 它 的 子 类 来 实 现 的 , Java 中 的

类 加 载 器 是 一 个 重 要 的 Java 运 行 时 系 统 组 件 ,它 负 责 在 运 行 时 查 找 和 装 入 类 文 件

中的类。

由 于 Java 的 跨 平 台 性 , 经 过 编 译 的 Java 源 程 序 并 不 是 一 个 可 执 行 程 序 , 而 是 一

个 或 多 个 类 文 件 。当 Java 程 序 需 要 使 用 某 个 类 时 ,JVM 会 确 保 这 个 类 已 经 被 加 载 、

连 接( 验 证 、准 备 和 解 析 )和 初 始 化 。类 的 加 载 是 指 把 类 的 .class 文 件 中 的 数 据 读

入 到 内 存 中 ,通 常 是 创 建 一 个 字 节 数 组 读 入 .class 文 件 ,然 后 产 生 与 所 加 载 类 对 应

的 Class 对 象 。加 载 完 成 后 ,Class 对 象 还 不 完 整 ,所 以 此 时 的 类 还 不 可 用 。当 类

被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设

置 默 认 的 初 始 值 ) 和 解 析 ( 将 符 号 引 用 替 换 为 直 接 引 用 ) 三 个 步 骤 。 最 后 JVM 对

类 进 行 初 始 化 ,包 括 :1)如 果 类 存 在 直 接 的 父 类 并 且 这 个 类 还 没 有 被 初 始 化 ,那 么

就 先 初 始 化 父 类 ; 2)如 果 类 中 存 在 初 始 化 语 句 , 就 依 次 执 行 这 些 初 始 化 语 句 。

类 的 加 载 是 由 类 加 载 器 完 成 的 ,类 加 载 器 包 括 :根 加 载 器( BootStrap)、扩 展 加

载 器 ( Extension) 、 系 统 加 载 器 ( System) 和 用 户 自 定 义 类 加 载 器

( java.lang.ClassLoader 的 子 类 ) 。 从 Java 2( JDK 1.2) 开 始 , 类 加 载 过 程 采

取 了 父 亲 委 托 机 制 ( PDM) 。 PDM 更 好 的 保 证 了 Java 平 台 的 安 全 性 , 在 该 机 制

中 , JVM 自 带 的 Bootstrap 是 根 加 载 器 , 其 他 的 加 载 器 都 有 且 仅 有 一 个 父 类 加 载

器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载

第 228 页 共 485 页
器 自 行 加 载 。JVM 不 会 向 Java 程 序 提 供 对 Bootstrap 的 引 用 。下 面 是 关 于 几 个 类

加载器的说明:

 Bootstrap:一般用本地代码实现,负责加载 JVM 基础核心类库(rt.jar);


 Extension:从 java.ext.dirs 系统属性所指定的目录中加载类库,它的父
加载器是 Bootstrap;
 System:又叫应用类加载器,其父类是 Extension。它是应用最广泛的
类加载器。它从环境变量 classpath 或者系统属性 java.class.path 所指定的目
录中记载类,是用户自定义加载器的默认父加载器。

22、char 型变量中能不能存贮一个中文汉字,为什么?

答:

char 类 型 可 以 存 储 一 个 中 文 汉 字 , 因 为 Java 中 使 用 的 编 码 是 Unicode( 不 选 择

任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一

个 char 类 型 占 2 个 字 节 ( 16 比 特 ) , 所 以 放 一 个 中 文 是 没 问 题 的 。

补 充 : 使 用 Unicode 意 味 着 字 符 在 JVM 内 部 和 外 部 有 不 同 的 表 现 形 式 , 在 JVM

内 部 都 是 Unicode,当 这 个 字 符 被 从 JVM 内 部 转 移 到 外 部 时( 例 如 存 入 文 件 系 统

中 ) , 需 要 进 行 编 码 转 换 。 所 以 Java 中 有 字 节 流 和 字 符 流 , 以 及 在 字 符 流 和 字 节

流 之 间 进 行 转 换 的 转 换 流 , 如 InputStreamReader 和 OutputStreamReader,

这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;对于 C 程

序 员 来 说 , 要 完 成 这 样 的 编 码 转 换 恐 怕 要 依 赖 于 union( 联 合 体 /共 用 体 ) 共 享 内

存的特征来实现了。

第 229 页 共 485 页
23、抽象类(abstract class)和接口(interface)有什么异

同?

答:

抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如

果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实

现,否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,因为抽象类中

可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其

中 的 方 法 全 部 都 是 抽 象 方 法 。抽 象 类 中 的 成 员 可 以 是 private、默 认 、protected、

public 的 ,而 接 口 中 的 成 员 全 都 是 public 的 。抽 象 类 中 可 以 定 义 成 员 变 量 ,而 接

口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而

抽象类未必要有抽象方法。

24、静态嵌套类(Static Nested Class)和内部类(Inner Class)

的不同?

答:

Static Nested Class 是 被 声 明 为 静 态( static)的 内 部 类 ,它 可 以 不 依 赖 于 外 部 类

实例被实例化。而通常的内部类需要在外部类实例化后才能实例化,其语法看起

来挺诡异的,如下所示。

/**
* 扑克类(一副扑克)
* @author 骆昊
*

第 230 页 共 485 页
*/
public class Poker {
private static String[] suites = {"黑桃", "红桃", "草花", "方块"};
private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};

private Card[] cards;

/**
* 构造器
*
*/
public Poker() {
cards = new Card[52];
for(int i = 0; i < suites.length; i++) {
for(int j = 0; j < faces.length; j++) {
cards[i * 13 + j] = new Card(suites[i], faces[j]);
}
}
}

/**
* 洗牌 (随机乱序)
*
*/
public void shuffle() {
for(int i = 0, len = cards.length; i < len; i++) {
int index = (int) (Math.random() * len);
Card temp = cards[index];
cards[index] = cards[i];
cards[i] = temp;
}

第 231 页 共 485 页
}

/**
* 发牌
* @param index 发牌的位置
*
*/
public Card deal(int index) {
return cards[index];
}

/**
* 卡片类(一张扑克)
* [内部类]
* @author 骆昊
*
*/
public class Card {
private String suite; // 花色
private int face; // 点数

public Card(String suite, int face) {


this.suite = suite;
this.face = face;
}

@Override
public String toString() {
String faceStr = "";
switch(face) {
case 1: faceStr = "A"; break;

第 232 页 共 485 页
case 11: faceStr = "J"; break;
case 12: faceStr = "Q"; break;
case 13: faceStr = "K"; break;
default: faceStr = String.valueOf(face);
}
return suite + faceStr;
}
}
}

测试代码:

class PokerTest {

public static void main(String[] args) {


Poker poker = new Poker();
poker.shuffle(); // 洗牌
Poker.Card c1 = poker.deal(0); // 发第一张牌
// 对于非静态内部类 Card
// 只有通过其外部类 Poker 对象才能创建 Card 对象
Poker.Card c2 = poker.new Card("红心", 1); // 自己创建一张牌

System.out.println(c1); // 洗牌后的第一张
System.out.println(c2); // 打印: 红心 A
}
}

面试题 - 下面的代码哪些地方会产生编译错误?

class Outer {

第 233 页 共 485 页
class Inner {}

public static void foo() { new Inner(); }

public void bar() { new Inner(); }

public static void main(String[] args) {


new Inner();
}
}

注 意 :Java 中 非 静 态 内 部 类 对 象 的 创 建 要 依 赖 其 外 部 类 对 象 ,上 面 的 面 试 题 中 foo

和 main 方 法 都 是 静 态 方 法 ,静 态 方 法 中 没 有 this,也 就 是 说 没 有 所 谓 的 外 部 类 对

象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样

做:

new Outer().new Inner();

25、Java 中会存在内存泄漏吗,请简单描述。

答:

理 论 上 Java 因 为 有 垃 圾 回 收 机 制 ( GC) 不 会 存 在 内 存 泄 露 问 题 ( 这 也 是 Java 被

广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无

用 但 可 达 的 对 象 ,这 些 对 象 不 能 被 GC 回 收 ,因 此 也 会 导 致 内 存 泄 露 的 发 生 。例 如

Hibernate 的 Session( 一 级 缓 存 )中 的 对 象 属 于 持 久 态 ,垃 圾 回 收 器 是 不 会 回 收

这 些 对 象 的 ,然 而 这 些 对 象 中 可 能 存 在 无 用 的 垃 圾 对 象 ,如 果 不 及 时 关 闭( close)

或 清 空 ( flush) 一 级 缓 存 就 可 能 导 致 内 存 泄 露 。 下 面 例 子 中 的 代 码 也 会 导 致 内 存

泄露。

第 234 页 共 485 页
import java.util.Arrays;
import java.util.EmptyStackException;

public class MyStack<T> {


private T[] elements;
private int size = 0;

private static final int INIT_CAPACITY = 16;

public MyStack() {
elements = (T[]) new Object[INIT_CAPACITY];
}

public void push(T elem) {


ensureCapacity();
elements[size++] = elem;
}

public T pop() {
if(size == 0)
throw new EmptyStackException();
return elements[--size];
}

private void ensureCapacity() {


if(elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}

第 235 页 共 485 页
上 面 的 代 码 实 现 了 一 个 栈 ( 先 进 后 出 ( FILO) ) 结 构 , 乍 看 之 下 似 乎 没 有 什 么 明

显 的 问 题 , 它 甚 至 可 以 通 过 你 编 写 的 各 种 单 元 测 试 。 然 而 其 中 的 pop 方 法 却 存 在

内 存 泄 露 的 问 题 , 当 我 们 用 pop 方 法 弹 出 栈 中 的 对 象 时 , 该 对 象 不 会 被 当 作 垃 圾

回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过

期 引 用( obsolete reference)。在 支 持 垃 圾 回 收 的 语 言 中 ,内 存 泄 露 是 很 隐 蔽 的 ,

这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起

来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,

即 使 这 样 的 对 象 只 有 少 数 几 个 ,也 可 能 会 导 致 很 多 的 对 象 被 排 除 在 垃 圾 回 收 之 外 ,

从 而 对 性 能 造 成 重 大 影 响 ,极 端 情 况 下 会 引 发 Disk Paging( 物 理 内 存 与 硬 盘 的 虚

拟 内 存 交 换 数 据 ) , 甚 至 造 成 OutOfMemoryError。

26、抽象的(abstract)方法是否可同时是静态的(static),

是否可同时是本地方法(native),是否可同时被 synchronized

修饰?

答:

都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛

盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现

的 ,也 是 矛 盾 的 。synchronized 和 方 法 的 实 现 细 节 有 关 ,抽 象 方 法 不 涉 及 实 现 细

节,因此也是相互矛盾的。

27、阐述静态变量和实例变量的区别。

答:

第 236 页 共 485 页
静 态 变 量 是 被 static 修 饰 符 修 饰 的 变 量 , 也 称 为 类 变 量 , 它 属 于 类 , 不 属 于 类 的

任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷

贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。

静态变量可以实现让多个对象共享内存。

补 充 : 在 Java 开 发 中 , 上 下 文 类 和 工 具 类 中 通 常 会 有 大 量 的 静 态 成 员 。

28、是否可以从一个静态(static)方法内部发出对非静态

(non-static)方法的调用?

答:

不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在

调用静态方法时可能对象并没有被初始化。

29、如何实现对象克隆?

答:

有两种方式:

1). 实 现 Cloneable 接 口 并 重 写 Object 类 中 的 clone()方 法 ;

2). 实 现 Serializable 接 口 ,通 过 对 象 的 序 列 化 和 反 序 列 化 实 现 克 隆 ,可 以 实 现 真

正的深度克隆,代码如下。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

第 237 页 共 485 页
import java.io.Serializable;

public class MyUtil {

private MyUtil() {
throw new AssertionError();
}

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) throws
Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);

ByteArrayInputStream bin = new


ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();

// 说明:调用 ByteArrayInputStream 或 ByteArrayOutputStream


对象的 close 方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这
一点不同于对外部资源(如文件流)的释放
}
}

下面是测试代码:

import java.io.Serializable;

第 238 页 共 485 页
/**
* 人类
* @author 骆昊
*
*/
class Person implements Serializable {
private static final long serialVersionUID = -9102017020286042305L;

private String name; // 姓名


private int age; // 年龄
private Car car; // 座驾

public Person(String name, int age, Car car) {


this.name = name;
this.age = age;
this.car = car;
}

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}

public int getAge() {


return age;
}

public void setAge(int age) {

第 239 页 共 485 页
this.age = age;
}

public Car getCar() {


return car;
}

public void setCar(Car car) {


this.car = car;
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", car=" +
car + "]";
}

/**
* 小汽车类
* @author 骆昊
*
*/
class Car implements Serializable {
private static final long serialVersionUID = -5713945027627603702L;

private String brand; // 品牌


private int maxSpeed; // 最高时速

public Car(String brand, int maxSpeed) {

第 240 页 共 485 页
this.brand = brand;
this.maxSpeed = maxSpeed;
}

public String getBrand() {


return brand;
}

public void setBrand(String brand) {


this.brand = brand;
}

public int getMaxSpeed() {


return maxSpeed;
}

public void setMaxSpeed(int maxSpeed) {


this.maxSpeed = maxSpeed;
}

@Override
public String toString() {
return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed +
"]";
}

class CloneTest {

public static void main(String[] args) {

第 241 页 共 485 页
try {
Person p1 = new Person("Hao LUO", 33, new Car("Benz",
300));
Person p2 = MyUtil.clone(p1); // 深度克隆
p2.getCar().setBrand("BYD");
// 修改克隆的 Person 对象 p2 关联的汽车对象的品牌属性
// 原来的 Person 对象 p1 关联的汽车不会受到任何影响
// 因为在克隆 Person 对象时其关联的汽车对象也被克隆了
System.out.println(p1);
} catch (Exception e) {
e.printStackTrace();
}
}
}

注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛

型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,

不 是 在 运 行 时 抛 出 异 常 ,这 种 是 方 案 明 显 优 于 使 用 Object 类 的 clone 方 法 克 隆 对

象。让问题在编译的时候暴露出来总是好过把问题留到运行时。

30、GC 是什么?为什么要有 GC?

答:

GC 是 垃 圾 收 集 的 意 思 ,内 存 处 理 是 编 程 人 员 容 易 出 现 问 题 的 地 方 ,忘 记 或 者 错 误

的 内 存 回 收 会 导 致 程 序 或 系 统 的 不 稳 定 甚 至 崩 溃 , Java 提 供 的 GC 功 能 可 以 自 动

监 测 对 象 是 否 超 过 作 用 域 从 而 达 到 自 动 回 收 内 存 的 目 的 , Java 语 言 没 有 提 供 释 放

已 分 配 内 存 的 显 示 操 作 方 法 。 Java 程 序 员 不 用 担 心 内 存 管 理 , 因 为 垃 圾 收 集 器 会

自 动 进 行 管 理 。 要 请 求 垃 圾 收 集 , 可 以 调 用 下 面 的 方 法 之 一 : System.gc() 或

Runtime.getRuntime().gc() , 但 JVM 可 以 屏 蔽 掉 显 示 的 垃 圾 回 收 调 用 。

第 242 页 共 485 页
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通

常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死

亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回

收 器 对 某 个 对 象 或 所 有 对 象 进 行 垃 圾 回 收 。 在 Java 诞 生 初 期 , 垃 圾 回 收 是 Java

最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过

境 迁 ,如 今 Java 的 垃 圾 回 收 机 制 已 经 成 为 被 诟 病 的 东 西 。移 动 智 能 终 端 用 户 通 常

觉 得 iOS 的 系 统 比 Android 系 统 有 更 好 的 用 户 体 验 , 其 中 一 个 深 层 次 的 原 因 就 在

于 Android 系 统 中 垃 圾 回 收 的 不 可 预 知 性 。

补充:垃圾回收机制有很多种,包括:分代复制垃圾回收、标记垃圾回收、增量

垃 圾 回 收 等 方 式 。 标 准 的 Java 进 程 既 有 栈 又 有 堆 。栈 保 存 了 原 始 型 局 部 变 量 , 堆

保 存 了 要 创 建 的 对 象 。 Java 平 台 对 堆 内 存 回 收 和 再 利 用 的 基 本 算 法 被 称 为 标 记 和

清 除 ,但 是 Java 对 其 进 行 了 改 进 ,采 用“ 分 代 式 垃 圾 收 集 ”。这 种 方 法 会 跟 Java

对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象

移动到不同区域:

 伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,
这里是它们唯一存在过的区域。
 幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
 终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集
(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身
颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,
以便为大对象腾出足够的空间。

与 垃 圾 回 收 相 关 的 JVM 参 数 :

 -Xms / -Xmx — 堆的初始大小 / 堆的最大大小


 -Xmn — 堆中年轻代的大小
 -XX:-DisableExplicitGC — 让 System.gc()不产生任何作用
 -XX:+PrintGCDetails — 打印 GC 的细节

第 243 页 共 485 页
 -XX:+PrintGCDateStamps — 打印 GC 操作的时间戳
 -XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小
 -XX:NewRatio — 可以设置老生代和新生代的比例
 -XX:PrintTenuringDistribution — 设置每次新生代 GC 后输出幸存者
乐园中对象年龄的分布
 -XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老
年代阀值的初始值和最大值
 -XX:TargetSurvivorRatio:设置幸存区的目标使用率

31、String s = new String(“xyz”);创建了几个字符串对象?

答:

两 个 对 象 , 一 个 是 静 态 区 的 ” xyz” , 一 个 是 用 new 创 建 在 堆 上 的 对 象 。

32、接口是否可继承(extends)接口?抽象类是否可实现

(implements)接口?抽象类是否可继承具体类(concrete

class)?

答:

接 口 可 以 继 承 接 口 , 而 且 支 持 多 重 继 承 。 抽 象 类 可 以 实 现 (implements)接 口 , 抽

象类可继承具体类也可以继承抽象类。

第 244 页 共 485 页
33、一个”.java”源文件中是否可以包含多个类(不是内部类)

有什么限制?

答:

可 以 , 但 一 个 源 文 件 中 最 多 只 能 有 一 个 公 开 类 ( public class) 而 且 文 件 名 必 须 和

公开类的类名完全保持一致。

34、Anonymous Inner Class(匿名内部类)是否可以继承其它

类?是否可以实现接口?

答:

可 以 继 承 其 他 类 或 实 现 其 他 接 口 ,在 Swing 编 程 和 Android 开 发 中 常 用 此 方 式 来

实现事件监听和回调。

35、内部类可以引用它的包含类(外部类)的成员吗?有没有

什么限制?

答:

一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。

36、Java 中的 final 关键字有哪些用法?

第 245 页 共 485 页
答:

(1)修 饰 类 :表 示 该 类 不 能 被 继 承 ;(2)修 饰 方 法 :表 示 方 法 不 能 被 重 写 ;(3)修 饰 变

量:表示变量只能一次赋值以后值不能被修改(常量)。

37、指出下面程序的运行结果

class A {

static {
System.out.print("1");
}

public A() {
System.out.print("2");
}
}

class B extends A{

static {
System.out.print("a");
}

public B() {
System.out.print("b");
}
}

第 246 页 共 485 页
public class Hello {

public static void main(String[] args) {


A ab = new B();
ab = new B();
}

答:

执 行 结 果 : 1a2b2b。 创 建 对 象 时 构 造 器 的 调 用 顺 序 是 : 先 初 始 化 静 态 成 员 , 然 后

调用父类构造器,再初始化非静态成员,最后调用自身构造器。

提 示 : 如 果 不 能 给 出 此 题 的 正 确 答 案 , 说 明 之 前 第 21 题 Java 类 加 载 机 制 还 没 有

完全理解,赶紧再看看吧。

38、数据类型之间的转换:

 如何将字符串转换为基本数据类型?
 如何将基本数据类型转换为字符串?

答:

 调用基本数据类型对应的包装类中的方法 parseXXX(String)或
valueOf(String)即可返回相应基本类型;

第 247 页 共 485 页
 一种方法是将基本数据类型与空字符串(”“)连接(+)即可获得其所
对应的字符串;另一种方法是调用 String 类中的 valueOf()方法返回相应字符

39、如何实现字符串的反转及替换?

答:

方 法 很 多 , 可 以 自 己 写 实 现 也 可 以 使 用 String 或 StringBuffer/StringBuilder 中

的方法。有一道很常见的面试题是用递归实现字符串反转,代码如下所示:

public static String reverse(String originStr) {


if(originStr == null || originStr.length() <= 1)
return originStr;
return reverse(originStr.substring(1)) + originStr.charAt(0);
}

40、怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的

字符串?

答:

代码如下所示:

String s1 = "你好";
String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");

第 248 页 共 485 页
41、日期和时间:

 如何取得年月日、小时分钟秒?
 如何取得从 1970 年 1 月 1 日 0 时 0 分 0 秒到现在的毫秒数?
 如何取得某月的最后一天?
 如何格式化日期?

答:

问 题 1: 创 建 java.util.Calendar 实 例 , 调 用 其 get()方 法 传 入 不 同 的 参 数 即 可 获

得 参 数 所 对 应 的 值 。 Java 8 中 可 以 使 用 java.time.LocalDateTimel 来 获 取 , 代 码

如下所示。

public class DateTimeTest {


public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH)); // 0 - 11
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));

// Java 8
LocalDateTime dt = LocalDateTime.now();
System.out.println(dt.getYear());
System.out.println(dt.getMonthValue()); // 1 - 12
System.out.println(dt.getDayOfMonth());
System.out.println(dt.getHour());

第 249 页 共 485 页
System.out.println(dt.getMinute());
System.out.println(dt.getSecond());
}
}

问 题 2: 以 下 方 法 均 可 获 得 该 毫 秒 数 。

Calendar.getInstance().getTimeInMillis();
System.currentTimeMillis();
Clock.systemDefaultZone().millis(); // Java 8

问 题 3: 代 码 如 下 所 示 。

Calendar time = Calendar.getInstance();


time.getActualMaximum(Calendar.DAY_OF_MONTH);

问 题 4: 利 用 java.text.DataFormat 的 子 类 ( 如 SimpleDateFormat 类 ) 中 的

format(Date)方 法 可 将 日 期 格 式 化 。 Java 8 中 可 以 用

java.time.format.DateTimeFormatter 来 格 式 化 时 间 日 期 , 代 码 如 下 所 示 。

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Date;

class DateFormatTest {

public static void main(String[] args) {


SimpleDateFormat oldFormatter = new
SimpleDateFormat("yyyy/MM/dd");
Date date1 = new Date();

第 250 页 共 485 页
System.out.println(oldFormatter.format(date1));

// Java 8
DateTimeFormatter newFormatter =
DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date2 = LocalDate.now();
System.out.println(date2.format(newFormatter));
}
}

补 充 :Java 的 时 间 日 期 API 一 直 以 来 都 是 被 诟 病 的 东 西 ,为 了 解 决 这 一 问 题 ,Java

8 中 引 入 了 新 的 时 间 日 期 API,其 中 包 括 LocalDate、LocalTime、LocalDateTime、

Clock、 Instant 等 类 , 这 些 的 类 的 设 计 都 使 用 了 不 变 模 式 , 因 此 是 线 程 安 全 的 设

计 。 如 果 不 理 解 这 些 内 容 , 可 以 参 考 我 的 另 一 篇 文 章 《 关 于 Java 并 发 编 程 的 总 结

和思考》。

42、打印昨天的当前时刻。

答:

import java.util.Calendar;

class YesterdayCurrent {
public static void main(String[] args){
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1);
System.out.println(cal.getTime());
}
}

第 251 页 共 485 页
在 Java 8 中 , 可 以 用 下 面 的 代 码 实 现 相 同 的 功 能 。

import java.time.LocalDateTime;

class YesterdayCurrent {

public static void main(String[] args) {


LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = today.minusDays(1);

System.out.println(yesterday);
}
}

43、比较一下 Java 和 JavaSciprt。

答:

JavaScript 与 Java 是 两 个 公 司 开 发 的 不 同 的 两 个 产 品 。 Java 是 原 Sun

Microsystems 公 司 推 出 的 面 向 对 象 的 程 序 设 计 语 言 ,特 别 适 合 于 互 联 网 应 用 程 序

开 发 ; 而 JavaScript 是 Netscape 公 司 的 产 品 , 为 了 扩 展 Netscape 浏 览 器 的 功

能 而 开 发 的 一 种 可 以 嵌 入 Web 页 面 中 运 行 的 基 于 对 象 和 事 件 驱 动 的 解 释 性 语 言 。

JavaScript 的 前 身 是 LiveScript; 而 Java 的 前 身 是 Oak 语 言 。

下面对两种语言间的异同作如下比较:

 基于对象和面向对象:Java 是一种真正的面向对象的语言,即使是开发
简单的程序,必须设计对象;JavaScript 是种脚本语言,它可以用来制作与网络
无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和

第 252 页 共 485 页
事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对
象供设计人员使用。
 解释和编译:Java 的源代码在执行之前,必须经过编译。JavaScript 是
一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏
览器几乎都使用了 JIT(即时编译)技术来提升 JavaScript 的运行效率)
 强类型变量和类型弱变量:Java 采用强类型变量检查,即所有变量在编
译之前必须作声明;JavaScript 中变量是弱类型的,甚至在使用变量前可以不作
声明,JavaScript 的解释器在运行时检查推断其数据类型。
 代码格式不一样。

补 充 : 上 面 列 出 的 四 点 是 网 上 流 传 的 所 谓 的 标 准 答 案 。 其 实 Java 和 JavaScript

最重要的区别是一个是静态语言,一个是动态语言。目前的编程语言的发展趋势

是 函 数 式 语 言 和 动 态 语 言 。 在 Java 中 类 ( class) 是 一 等 公 民 , 而 JavaScript 中

函 数( function)是 一 等 公 民 ,因 此 JavaScript 支 持 函 数 式 编 程 ,可 以 使 用 Lambda

函 数 和 闭 包( closure),当 然 Java 8 也 开 始 支 持 函 数 式 编 程 ,提 供 了 对 Lambda

表达式以及函数式接口的支持。对于这类问题,在面试的时候最好还是用自己的

语言回答会更加靠谱,不要背网上所谓的标准答案。

44、什么时候用断言(assert)?

答:

断言在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一

般来说,断言用于保证程序最基本、关键的正确性。断言检查通常在开发和测试

时开启。为了保证程序的执行效率,在软件发布后断言检查通常是关闭的。断言

是 一 个 包 含 布 尔 表 达 式 的 语 句 , 在 执 行 这 个 语 句 时 假 定 该 表 达 式 为 true; 如 果 表

达 式 的 值 为 false,那 么 系 统 会 报 告 一 个 AssertionError。断 言 的 使 用 如 下 面 的 代

码所示:

第 253 页 共 485 页
assert(a > 0); // throws an AssertionError if a <= 0

断言可以有两种形式:

assert Expression1;

assert Expression1 : Expression2 ;

Expression1 应 该 总 是 产 生 一 个 布 尔 值 。

Expression2 可 以 是 得 出 一 个 值 的 任 意 表 达 式 ; 这 个 值 用 于 生 成 显 示 更 多 调 试 信

息的字符串消息。

要 在 运 行 时 启 用 断 言 , 可 以 在 启 动 JVM 时 使 用 -enableassertions 或 者 -ea 标 记 。

要 在 运 行 时 选 择 禁 用 断 言 , 可 以 在 启 动 JVM 时 使 用 -da 或 者 -disableassertions

标 记 。要 在 系 统 类 中 启 用 或 禁 用 断 言 ,可 使 用 -esa 或 -dsa 标 记 。还 可 以 在 包 的 基

础上启用或者禁用断言。

注意:断言不应该以任何方式改变程序的状态。简单的说,如果希望在不满足某

些条件时阻止代码的执行,就可以考虑用断言来阻止它。

45、Error 和 Exception 有什么区别?

答:

Error 表 示 系 统 级 的 错 误 和 程 序 不 必 处 理 的 异 常 ,是 恢 复 不 是 不 可 能 但 很 困 难 的 情

况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;

Exception 表 示 需 要 捕 捉 或 者 需 要 程 序 进 行 处 理 的 异 常 , 是 一 种 设 计 或 实 现 问 题 ;

也就是说,它表示如果程序运行正常,从不会发生的情况。

面 试 题 :2005 年 摩 托 罗 拉 的 面 试 中 曾 经 问 过 这 么 一 个 问 题“ If a process reports

a stack overflow run-time error, what’ s the most possible cause?” , 给 了

四 个 选 项 a. lack of memory; b. write on an invalid memory space; c.

recursive function calling; d. array index out of boundary. Java 程 序 在 运 行

第 254 页 共 485 页
时 也 可 能 会 遭 遇 StackOverflowError, 这 是 一 个 无 法 恢 复 的 错 误 , 只 能 重 新 修 改

代 码 了 ,这 个 面 试 题 的 答 案 是 c。如 果 写 了 不 能 迅 速 收 敛 的 递 归 ,则 很 有 可 能 引 发

栈溢出的错误,如下所示:

class StackOverflowErrorTest {

public static void main(String[] args) {


main(null);
}
}

提 示 :用 递 归 编 写 程 序 时 一 定 要 牢 记 两 点 :1. 递 归 公 式 ;2. 收 敛 条 件( 什 么 时 候

就不再继续递归)。

46、try{}里有一个 return 语句,那么紧跟在这个 try 后的

finally{}里的代码会不会被执行,什么时候被执行,在 return

前还是后?

答:

会执行,在方法返回调用者前执行。

注 意 :在 finally 中 改 变 返 回 值 的 做 法 是 不 好 的 ,因 为 如 果 存 在 finally 代 码 块 ,try

中 的 return 语 句 不 会 立 马 返 回 调 用 者 ,而 是 记 录 下 返 回 值 待 finally 代 码 块 执 行 完

毕 之 后 再 向 调 用 者 返 回 其 值 , 然 后 如 果 在 finally 中 修 改 了 返 回 值 , 就 会 返 回 修 改

后 的 值 。显 然 ,在 finally 中 返 回 或 者 修 改 返 回 值 会 对 程 序 造 成 很 大 的 困 扰 ,C#中

直 接 用 编 译 错 误 的 方 式 来 阻 止 程 序 员 干 这 种 龌 龊 的 事 情 , Java 中 也 可 以 通 过 提 升

第 255 页 共 485 页
编 译 器 的 语 法 检 查 级 别 来 产 生 警 告 或 错 误 ,Eclipse 中 可 以 在 如 图 所 示 的 地 方 进 行

设置,强烈建议将此项设置为编译错误。

47、Java 语言如何进行异常处理,关键字:throws、throw、

try、catch、finally 分别如何使用?

答:

Java 通 过 面 向 对 象 的 方 法 进 行 异 常 处 理 , 把 各 种 不 同 的 异 常 进 行 分 类 , 并 提 供 了

良 好 的 接 口 。 在 Java 中 , 每 个 异 常 都 是 一 个 对 象 , 它 是 Throwable 类 或 其 子 类

的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,

第 256 页 共 485 页
调 用 这 个 对 象 的 方 法 可 以 捕 获 到 这 个 异 常 并 可 以 对 其 进 行 处 理 。 Java 的 异 常 处 理

是 通 过 5 个 关 键 词 来 实 现 的 : try、 catch、 throw、 throws 和 finally。 一 般 情 况

下 是 用 try 来 执 行 一 段 程 序 ,如 果 系 统 会 抛 出( throw)一 个 异 常 对 象 ,可 以 通 过

它 的 类 型 来 捕 获 ( catch) 它 , 或 通 过 总 是 执 行 代 码 块 ( finally) 来 处 理 ; try 用

来 指 定 一 块 预 防 所 有 异 常 的 程 序 ;catch 子 句 紧 跟 在 try 块 后 面 ,用 来 指 定 你 想 要

捕 获 的 异 常 的 类 型 ; throw 语 句 用 来 明 确 地 抛 出 一 个 异 常 ; throws 用 来 声 明 一 个

方 法 可 能 抛 出 的 各 种 异 常 ( 当 然 声 明 异 常 时 允 许 无 病 呻 吟 ) ; finally 为 确 保 一 段

代 码 不 管 发 生 什 么 异 常 状 况 都 要 被 执 行 ; try 语 句 可 以 嵌 套 , 每 当 遇 到 一 个 try 语

句 ,异 常 的 结 构 就 会 被 放 入 异 常 栈 中 ,直 到 所 有 的 try 语 句 都 完 成 。如 果 下 一 级 的

try 语 句 没 有 对 某 种 异 常 进 行 处 理 ,异 常 栈 就 会 执 行 出 栈 操 作 ,直 到 遇 到 有 处 理 这

种 异 常 的 try 语 句 或 者 最 终 将 异 常 抛 给 JVM。

48、运行时异常与受检异常有何异同?

答:

异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常

操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就

不会发生。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可

能 因 使 用 的 问 题 而 引 发 。Java 编 译 器 要 求 方 法 必 须 声 明 抛 出 可 能 发 生 的 受 检 异 常 ,

但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样,是面向对

象 程 序 设 计 中 经 常 被 滥 用 的 东 西 ,在 Effective Java 中 对 异 常 的 使 用 给 出 了 以 下 指

导原则:

 不要将异常处理用于正常的控制流(设计良好的 API 不应该强迫它的调


用者为了正常的控制流而使用异常)
 对可以恢复的情况使用受检异常,对编程错误使用运行时异常
 避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发
生)

第 257 页 共 485 页
 优先使用标准的异常
 每个方法抛出的异常都要有文档
 保持异常的原子性
 不要在 catch 中忽略掉捕获到的异常

49、列出一些你常见的运行时异常?

答:

 ArithmeticException(算术异常)
 ClassCastException (类转换异常)
 IllegalArgumentException (非法参数异常)
 IndexOutOfBoundsException (下标越界异常)
 NullPointerException (空指针异常)
 SecurityException (安全异常)

50、阐述 final、finally、finalize 的区别。

答:

 final:修饰符(关键字)有三种用法:如果一个类被声明为 final,意味
着它不能再派生出新的子类,即不能被继承,因此它和 abstract 是反义词。将
变量声明为 final,可以保证它们在使用中不被改变,被声明为 final 的变量必须
在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为 final 的方
法也同样只能使用,不能在子类中被重写。

第 258 页 共 485 页
 finally:通常放在 try…catch…的后面构造总是执行代码块,这就意味着
程序无论正常执行还是发生异常,这里的代码只要 JVM 不关闭都能执行,可以
将释放外部资源的代码写在 finally 块中。
 finalize:Object 类中定义的方法,Java 中允许使用 finalize()方法在垃
圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收
集器在销毁对象时调用的,通过重写 finalize()方法可以整理系统资源或者执行
其他清理工作。

51、类 ExampleA 继承 Exception,类 ExampleB 继承

ExampleA。

有如下代码片断:

try {
throw new ExampleB("b")
} catch(ExampleA e){
System.out.println("ExampleA");
} catch(Exception e){
System.out.println("Exception");
}

**请 问 执 行 此 段 代 码 的 输 出 是 什 么 ?

答:

输 出 :ExampleA。( 根 据 里 氏 代 换 原 则 [能 使 用 父 类 型 的 地 方 一 定 能 使 用 子 类 型 ],

抓 取 ExampleA 类 型 异 常 的 catch 块 能 够 抓 住 try 块 中 抛 出 的 ExampleB 类 型 的

异常)

第 259 页 共 485 页
面 试 题 - 说 出 下 面 代 码 的 运 行 结 果 。 ( 此 题 的 出 处 是 《 Java 编 程 思 想 》 一 书 )

class Annoyance extends Exception {}


class Sneeze extends Annoyance {}

class Human {

public static void main(String[] args)


throws Exception {
try {
try {
throw new Sneeze();
}
catch ( Annoyance a ) {
System.out.println("Caught Annoyance");
throw a;
}
}
catch ( Sneeze s ) {
System.out.println("Caught Sneeze");
return ;
}
finally {
System.out.println("Hello World!");
}
}
}

52、List、Set、Map 是否继承自 Collection 接口?

第 260 页 共 485 页
答:

List、Set 是 ,Map 不 是 。Map 是 键 值 对 映 射 容 器 ,与 List 和 Set 有 明 显 的 区 别 ,

而 Set 存 储 的 零 散 的 元 素 且 不 允 许 有 重 复 元 素 ( 数 学 中 的 集 合 也 是 如 此 ) , List

是线性结构的容器,适用于按数值索引访问元素的情形。

53、阐述 ArrayList、Vector、LinkedList 的存储性能和特性。

答:

ArrayList 和 Vector 都 是 使 用 数 组 方 式 存 储 数 据 , 此 数 组 元 素 数 大 于 实 际 存 储 的

数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉

及 数 组 元 素 移 动 等 内 存 操 作 , 所 以 索 引 数 据 快 而 插 入 数 据 慢 , Vector 中 的 方 法 由

于 添 加 了 synchronized 修 饰 , 因 此 Vector 是 线 程 安 全 的 容 器 , 但 性 能 上 较

ArrayList 差 , 因 此 已 经 是 Java 中 的 遗 留 容 器 。 LinkedList 使 用 双 向 链 表 实 现 存

储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索

引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更

高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本

项 的 前 后 项 即 可 ,所 以 插 入 速 度 较 快 。Vector 属 于 遗 留 容 器( Java 早 期 的 版 本 中

提 供 的 容 器 , 除 此 之 外 , Hashtable、 Dictionary、 BitSet、 Stack、 Properties

都 是 遗 留 容 器 ) , 已 经 不 推 荐 使 用 , 但 是 由 于 ArrayList 和 LinkedListed 都 是 非

线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类

Collections 中 的 synchronizedList 方 法 将 其 转 换 成 线 程 安 全 的 容 器 后 再 使 用( 这

是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强

实现)。

补 充 :遗 留 容 器 中 的 Properties 类 和 Stack 类 在 设 计 上 有 严 重 的 问 题 ,Properties

是一个键和值都是字符串的特殊的键值对映射,在设计上应该是关联一个

Hashtable 并 将 其 两 个 泛 型 参 数 设 置 为 String 类 型 , 但 是 Java API 中 的

Properties 直 接 继 承 了 Hashtable, 这 很 明 显 是 对 继 承 的 滥 用 。 这 里 复 用 代 码 的

第 261 页 共 485 页
方 式 应 该 是 Has-A 关 系 而 不 是 Is-A 关 系 ,另 一 方 面 容 器 都 属 于 工 具 类 ,继 承 工 具

类 本 身 就 是 一 个 错 误 的 做 法 , 使 用 工 具 类 最 好 的 方 式 是 Has-A 关 系 ( 关 联 ) 或

Use-A 关 系 ( 依 赖 )。 同 理 , Stack 类 继 承 Vector 也 是 不 正 确 的 。Sun 公 司 的 工

程师们也会犯这种低级错误,让人唏嘘不已。

54、Collection 和 Collections 的区别?

答:

Collection 是 一 个 接 口 , 它 是 Set、 List 等 容 器 的 父 接 口 ; Collections 是 个 一 个

工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、

排序、线程安全化等等。

55、List、Map、Set 三个接口存取元素时,各有什么特点?

答:

List 以 特 定 索 引 来 存 取 元 素 ,可 以 有 重 复 元 素 。Set 不 能 存 放 重 复 元 素( 用 对 象 的

equals()方 法 来 区 分 元 素 是 否 重 复 ) 。 Map 保 存 键 值 对 ( key-value pair) 映 射 ,

映 射 关 系 可 以 是 一 对 一 或 多 对 一 。 Set 和 Map 容 器 都 有 基 于 哈 希 存 储 和 排 序 树 的

两 种 实 现 版 本 , 基 于 哈 希 存 储 的 版 本 理 论 存 取 时 间 复 杂 度 为 O(1), 而 基 于 排 序 树

版 本 的 实 现 在 插 入 或 删 除 元 素 时 会 按 照 元 素 或 元 素 的 键( key)构 成 排 序 树 从 而 达

到排序和去重的效果。

56、TreeMap 和 TreeSet 在排序时如何比较元素?

Collections 工具类中的 sort()方法如何比较元素?

第 262 页 共 485 页
答:

TreeSet 要 求 存 放 的 对 象 所 属 的 类 必 须 实 现 Comparable 接 口 , 该 接 口 提 供 了 比

较 元 素 的 compareTo()方 法 , 当 插 入 元 素 时 会 回 调 该 方 法 比 较 元 素 的 大 小 。

TreeMap 要 求 存 放 的 键 值 对 映 射 的 键 必 须 实 现 Comparable 接 口 从 而 根 据 键 对 元

素 进 行 排 序 。Collections 工 具 类 的 sort 方 法 有 两 种 重 载 的 形 式 ,第 一 种 要 求 传 入

的 待 排 序 容 器 中 存 放 的 对 象 比 较 实 现 Comparable 接 口 以 实 现 元 素 的 比 较 ; 第 二

种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是

Comparator 接 口 的 子 类 型( 需 要 重 写 compare 方 法 实 现 元 素 的 比 较 ),相 当 于

一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对

回 调 模 式 的 应 用 ( Java 中 对 函 数 式 编 程 的 支 持 ) 。

例 子 1:

public class Student implements Comparable<Student> {


private String name; // 姓名
private int age; // 年龄

public Student(String name, int age) {


this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}

@Override
public int compareTo(Student o) {
return this.age - o.age; // 比较年龄(年龄的升序)
}

第 263 页 共 485 页
}

import java.util.Set;
import java.util.TreeSet;

class Test01 {

public static void main(String[] args) {


Set<Student> set = new TreeSet<>(); // Java 7 的钻石语法
(构造器后面的尖括号中不需要写类型)
set.add(new Student("Hao LUO", 33));
set.add(new Student("XJ WANG", 32));
set.add(new Student("Bruce LEE", 60));
set.add(new Student("Bob YANG", 22));

for(Student stu : set) {


System.out.println(stu);
}
// 输出结果:
// Student [name=Bob YANG, age=22]
// Student [name=XJ WANG, age=32]
// Student [name=Hao LUO, age=33]
// Student [name=Bruce LEE, age=60]
}
}

例 子 2:

public class Student {


private String name; // 姓名

第 264 页 共 485 页
private int age; // 年龄

public Student(String name, int age) {


this.name = name;
this.age = age;
}

/**
* 获取学生姓名
*/
public String getName() {
return name;
}

/**
* 获取学生年龄
*/
public int getAge() {
return age;
}

@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

第 265 页 共 485 页
import java.util.List;

class Test02 {

public static void main(String[] args) {


List<Student> list = new ArrayList<>(); // Java 7 的钻石语法
(构造器后面的尖括号中不需要写类型)
list.add(new Student("Hao LUO", 33));
list.add(new Student("XJ WANG", 32));
list.add(new Student("Bruce LEE", 60));
list.add(new Student("Bob YANG", 22));

// 通过 sort 方法的第二个参数传入一个 Comparator 接口对象


// 相当于是传入一个比较对象大小的算法到 sort 方法中
// 由于 Java 中没有函数指针、仿函数、委托这样的概念
// 因此要将一个算法传入一个方法中唯一的选择就是通过接口回调
Collections.sort(list, new Comparator<Student> () {

@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName()); //
比较学生姓名
}
});

for(Student stu : list) {


System.out.println(stu);
}
// 输出结果:
// Student [name=Bob YANG, age=22]
// Student [name=Bruce LEE, age=60]

第 266 页 共 485 页
// Student [name=Hao LUO, age=33]
// Student [name=XJ WANG, age=32]
}
}

57、Thread 类的 sleep()方法和对象的 wait()方法都可以让线

程暂停执行,它们有什么区别?

答:

sleep()方 法 ( 休 眠 ) 是 线 程 类 ( Thread) 的 静 态 方 法 , 调 用 此 方 法 会 让 当 前 线 程

暂 停 执 行 指 定 的 时 间 , 将 执 行 机 会 ( CPU) 让 给 其 他 线 程 , 但 是 对 象 的 锁 依 然 保

持 ,因 此 休 眠 时 间 结 束 后 会 自 动 恢 复( 线 程 回 到 就 绪 状 态 ,请 参 考 第 66 题 中 的 线

程 状 态 转 换 图 ) 。 wait()是 Object 类 的 方 法 , 调 用 对 象 的 wait()方 法 导 致 当 前 线

程 放 弃 对 象 的 锁 ( 线 程 暂 停 执 行 ) , 进 入 对 象 的 等 待 池 ( wait pool) , 只 有 调 用

对 象 的 notify()方 法( 或 notifyAll()方 法 )时 才 能 唤 醒 等 待 池 中 的 线 程 进 入 等 锁 池

( lock pool) , 如 果 线 程 重 新 获 得 对 象 的 锁 就 可 以 进 入 就 绪 状 态 。

补充:可能不少人对什么是进程,什么是线程还比较模糊,对于为什么需要多线

程编程也不是特别理解。简单的说:进程是具有一定独立功能的程序关于某个数

据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位;线

程 是 进 程 的 一 个 实 体 , 是 CPU 调 度 和 分 派 的 基 本 单 位 , 是 比 进 程 更 小 的 能 独 立 运

行的基本单位。线程的划分尺度小于进程,这使得多线程程序的并发性高;进程

在执行时通常拥有独立的内存单元,而线程之间可以共享内存。使用多线程的编

程通常能够带来更好的性能和用户体验,但是多线程的程序对于其他程序是不友

好 的 , 因 为 它 可 能 占 用 了 更 多 的 CPU 资 源 。 当 然 , 也 不 是 线 程 越 多 , 程 序 的 性 能

就 越 好 , 因 为 线 程 之 间 的 调 度 和 切 换 也 会 浪 费 CPU 时 间 。 时 下 很 时 髦 的 Node.js

就 采 用 了 单 线 程 异 步 I/O 的 工 作 模 式 。

第 267 页 共 485 页
58、线程的 sleep()方法和 yield()方法有什么区别?

答:

① sleep()方 法 给 其 他 线 程 运 行 机 会 时 不 考 虑 线 程 的 优 先 级 ,因 此 会 给 低 优 先 级 的

线 程 以 运 行 的 机 会 ; yield()方 法 只 会 给 相 同 优 先 级 或 更 高 优 先 级 的 线 程 以 运 行 的

机会;

② 线 程 执 行 sleep()方 法 后 转 入 阻 塞 ( blocked) 状 态 , 而 执 行 yield()方 法 后 转

入 就 绪 ( ready) 状 态 ;

③ sleep()方 法 声 明 抛 出 InterruptedException, 而 yield()方 法 没 有 声 明 任 何 异

常;

④ sleep()方 法 比 yield()方 法( 跟 操 作 系 统 CPU 调 度 相 关 )具 有 更 好 的 可 移 植 性 。

59、当一个线程进入一个对象的 synchronized 方法 A 之后,

其它线程是否可进入此对象的 synchronized 方法 B?

答:

不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静

态 方 法 上 的 synchronized 修 饰 符 要 求 执 行 方 法 时 要 获 得 对 象 的 锁 ,如 果 已 经 进 入

A 方法说明对象锁已经被取走,那么试图进入 B 方法的线程就只能在等锁池(注

意不是等待池哦)中等待对象的锁。

60、请说出与线程同步以及线程调度相关的方法。

答:

第 268 页 共 485 页
 wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
 sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用
此方法要处理 InterruptedException 异常;
 notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并
不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且
与优先级无关;
 notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给
所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

提 示 :关 于 Java 多 线 程 和 并 发 编 程 的 问 题 ,建 议 大 家 看 我 的 另 一 篇 文 章《 关 于 Java

并发编程的总结和思考》。

补 充 : Java 5 通 过 Lock 接 口 提 供 了 显 式 的 锁 机 制 ( explicit lock) , 增 强 了 灵 活

性 以 及 对 线 程 的 协 调 。Lock 接 口 中 定 义 了 加 锁( lock())和 解 锁( unlock())的 方

法 ,同 时 还 提 供 了 newCondition()方 法 来 产 生 用 于 线 程 之 间 通 信 的 Condition 对

象 ; 此 外 , Java 5 还 提 供 了 信 号 量 机 制 ( semaphore) , 信 号 量 可 以 用 来 限 制 对

某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信

号 量 的 许 可( 调 用 Semaphore 对 象 的 acquire()方 法 );在 完 成 对 资 源 的 访 问 后 ,

线 程 必 须 向 信 号 量 归 还 许 可 ( 调 用 Semaphore 对 象 的 release()方 法 ) 。

下 面 的 例 子 演 示 了 100 个 线 程 同 时 向 一 个 银 行 账 户 中 存 入 1 元 钱 , 在 没 有 使 用 同

步机制和使用同步机制情况下的执行情况。

 银行账户类:

/**
* 银行账户
* @author 骆昊
*
*/

第 269 页 共 485 页
public class Account {
private double balance; // 账户余额

/**
* 存款
* @param money 存入金额
*/
public void deposit(double money) {
double newBalance = balance + money;
try {
Thread.sleep(10); // 模拟此业务需要一段处理时间
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
balance = newBalance;
}

/**
* 获得账户余额
*/
public double getBalance() {
return balance;
}
}

 存钱线程类:

/**
* 存钱线程

第 270 页 共 485 页
* @author 骆昊
*
*/
public class AddMoneyThread implements Runnable {
private Account account; // 存入账户
private double money; // 存入金额

public AddMoneyThread(Account account, double money) {


this.account = account;
this.money = money;
}

@Override
public void run() {
account.deposit(money);
}

 测试类:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test01 {

public static void main(String[] args) {


Account account = new Account();
ExecutorService service = Executors.newFixedThreadPool(100);

第 271 页 共 485 页
for(int i = 1; i <= 100; i++) {
service.execute(new AddMoneyThread(account, 1));
}

service.shutdown();

while(!service.isTerminated()) {}

System.out.println("账户余额: " + account.getBalance());


}
}

在 没 有 同 步 的 情 况 下 ,执 行 结 果 通 常 是 显 示 账 户 余 额 在 10 元 以 下 ,出 现 这 种 状 况

的原因是,当一个线程 A 试图存入 1 元的时候,另外一个线程 B 也能够进入存款

的方法中,线程 B 读取到的账户余额仍然是线程 A 存入 1 元钱之前的账户余额,

因 此 也 是 在 原 来 的 余 额 0 上 面 做 了 加 1 元 的 操 作 ,同 理 线 程 C 也 会 做 类 似 的 事 情 ,

所 以 最 后 100 个 线 程 执 行 结 束 时 , 本 来 期 望 账 户 余 额 为 100 元 , 但 实 际 得 到 的 通

常 在 10 元 以 下( 很 可 能 是 1 元 哦 )。解 决 这 个 问 题 的 办 法 就 是 同 步 ,当 一 个 线 程

对银行账户存钱时,需要将此账户锁定,待其操作完成后才允许其他的线程进行

操作,代码有如下几种调整方案:

 在银行账户的存款(deposit)方法上同步(synchronized)关键字

/**
* 银行账户
* @author 骆昊
*
*/
public class Account {
private double balance; // 账户余额

第 272 页 共 485 页
/**
* 存款
* @param money 存入金额
*/
public synchronized void deposit(double money) {
double newBalance = balance + money;
try {
Thread.sleep(10); // 模拟此业务需要一段处理时间
}
catch(InterruptedException ex) {
ex.printStackTrace();
}
balance = newBalance;
}

/**
* 获得账户余额
*/
public double getBalance() {
return balance;
}
}

 在线程调用存款方法时对银行账户进行同步

/**
* 存钱线程
* @author 骆昊
*

第 273 页 共 485 页
*/
public class AddMoneyThread implements Runnable {
private Account account; // 存入账户
private double money; // 存入金额

public AddMoneyThread(Account account, double money) {


this.account = account;
this.money = money;
}

@Override
public void run() {
synchronized (account) {
account.deposit(money);
}
}

 通过 Java 5 显示的锁机制,为每个银行账户创建一个锁对象,在存款操
作进行加锁和解锁的操作

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 银行账户
*
* @author 骆昊
*

第 274 页 共 485 页
*/
public class Account {
private Lock accountLock = new ReentrantLock();
private double balance; // 账户余额

/**
* 存款
*
* @param money
* 存入金额
*/
public void deposit(double money) {
accountLock.lock();
try {
double newBalance = balance + money;
try {
Thread.sleep(10); // 模拟此业务需要一段处理时间
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
balance = newBalance;
}
finally {
accountLock.unlock();
}
}

/**
* 获得账户余额
*/

第 275 页 共 485 页
public double getBalance() {
return balance;
}
}

按 照 上 述 三 种 方 式 对 代 码 进 行 修 改 后 , 重 写 执 行 测 试 代 码 Test01, 将 看 到 最 终 的

账 户 余 额 为 100 元 。当 然 也 可 以 使 用 Semaphore 或 CountdownLatch 来 实 现 同

步。

61、编写多线程程序有几种实现方式?

答:

Java 5 以 前 实 现 多 线 程 有 两 种 实 现 方 法 : 一 种 是 继 承 Thread 类 ; 另 一 种 是 实 现

Runnable 接 口 。 两 种 方 式 都 要 通 过 重 写 run()方 法 来 定 义 线 程 的 行 为 , 推 荐 使 用

后 者 , 因 为 Java 中 的 继 承 是 单 继 承 , 一 个 类 有 一 个 父 类 , 如 果 继 承 了 Thread 类

就 无 法 再 继 承 其 他 类 了 , 显 然 使 用 Runnable 接 口 更 为 灵 活 。

补 充 :Java 5 以 后 创 建 线 程 还 有 第 三 种 方 式 :实 现 Callable 接 口 ,该 接 口 中 的 call

方法可以在线程执行结束时产生一个返回值,代码如下所示:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class MyTask implements Callable<Integer> {

第 276 页 共 485 页
private int upperBounds;

public MyTask(int upperBounds) {


this.upperBounds = upperBounds;
}

@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 1; i <= upperBounds; i++) {
sum += i;
}
return sum;
}

class Test {

public static void main(String[] args) throws Exception {


List<Future<Integer>> list = new ArrayList<>();
ExecutorService service = Executors.newFixedThreadPool(10);
for(int i = 0; i < 10; i++) {
list.add(service.submit(new MyTask((int) (Math.random() *
100))));
}

int sum = 0;
for(Future<Integer> future : list) {
// while(!future.isDone()) ;
sum += future.get();

第 277 页 共 485 页
}

System.out.println(sum);
}
}

62、synchronized 关键字的用法?

答:

synchronized 关 键 字 可 以 将 对 象 或 者 方 法 标 记 为 同 步 ,以 实 现 对 对 象 和 方 法 的 互

斥 访 问 , 可 以 用 synchronized(对 象 ) { … }定 义 同 步 代 码 块 , 或 者 在 声 明 方 法 时

将 synchronized 作 为 方 法 的 修 饰 符 。 在 第 60 题 的 例 子 中 已 经 展 示 了

synchronized 关 键 字 的 用 法 。

63、举例说明同步和异步。

答:

如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正

在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线

程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好

的例子)。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并

且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异

步途径往往更有效率。事实上,所谓的同步就是指阻塞式操作,而异步就是非阻

塞式操作。

64、启动一个线程是调用 run()还是 start()方法?

第 278 页 共 485 页
答:

启 动 一 个 线 程 是 调 用 start()方 法 , 使 线 程 所 代 表 的 虚 拟 处 理 机 处 于 可 运 行 状 态 ,

这 意 味 着 它 可 以 由 JVM 调 度 并 执 行 , 这 并 不 意 味 着 线 程 就 会 立 即 运 行 。 run()方

法 是 线 程 启 动 后 要 进 行 回 调 ( callback) 的 方 法 。

65、什么是线程池(thread pool)?

答:

在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内

存 资 源 或 者 其 它 更 多 资 源 。 在 Java 中 更 是 如 此 , 虚 拟 机 将 试 图 跟 踪 每 一 个 对 象 ,

以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽

可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就

是”池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的

线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完

毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。

Java 5+中 的 Executor 接 口 定 义 一 个 执 行 线 程 的 工 具 。 它 的 子 类 型 即 线 程 池 接 口

是 ExecutorService。要 配 置 一 个 线 程 池 是 比 较 复 杂 的 ,尤 其 是 对 于 线 程 池 的 原 理

不 是 很 清 楚 的 情 况 下 , 因 此 在 工 具 类 Executors 面 提 供 了 一 些 静 态 工 厂 方 法 , 生

成一些常用的线程池,如下所示:

 newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只
有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线
程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执
行顺序按照任务的提交顺序执行。

第 279 页 共 485 页
 newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创
建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就
会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
 newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小
超过了处理任务所需要的线程,那么就会回收部分空闲(60 秒不执行任务)的
线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程
池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)
能够创建的最大线程大小。
 newScheduledThreadPool:创建一个大小无限的线程池。此线程池支
持定时以及周期性执行任务的需求。
 newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持
定时以及周期性执行任务的需求。

第 60 题 的 例 子 中 演 示 了 通 过 Executors 工 具 类 创 建 线 程 池 并 使 用 线 程 池 执 行 线 程

的 代 码 。 如 果 希 望 在 服 务 器 上 使 用 线 程 池 , 强 烈 建 议 使 用 newFixedThreadPool

方法来创建线程池,这样能获得更好的性能。

66、线程的基本状态以及状态之间的关系?

答:

第 280 页 共 485 页
说 明 : 其 中 Running 表 示 运 行 状 态 , Runnable 表 示 就 绪 状 态 ( 万 事 俱 备 , 只 欠

CPU),Blocked 表 示 阻 塞 状 态 ,阻 塞 状 态 又 有 多 种 情 况 ,可 能 是 因 为 调 用 wait()

方法进入等待池,也可能是执行同步方法或同步代码块进入等锁池,或者是调用

了 sleep()方 法 或 join()方 法 等 待 休 眠 或 其 他 线 程 结 束 ,或 是 因 为 发 生 了 I/O 中 断 。

67、简述 synchronized 和 java.util.concurrent.locks.Lock

的异同?

答:

Lock 是 Java 5 以 后 引 入 的 新 的 API, 和 关 键 字 synchronized 相 比 主 要 相 同 点 :

Lock 能 完 成 synchronized 所 实 现 的 所 有 功 能 ; 主 要 不 同 点 : Lock 有 比

synchronized 更 精 确 的 线 程 语 义 和 更 好 的 性 能 ,而 且 不 强 制 性 的 要 求 一 定 要 获 得

锁 。 synchronized 会 自 动 释 放 锁 , 而 Lock 一 定 要 求 程 序 员 手 工 释 放 , 并 且 最 好

在 finally 块 中 释 放 ( 这 是 释 放 外 部 资 源 的 最 好 的 地 方 ) 。

第 281 页 共 485 页
68、Java 中如何实现序列化,有什么意义?

答:

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流

化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。

序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会

存在数据乱序的问题)。

要 实 现 序 列 化 , 需 要 让 一 个 类 实 现 Serializable 接 口 , 该 接 口 是 一 个 标 识 性 接 口 ,

标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通

过 writeObject(Object)方 法 就 可 以 将 实 现 对 象 写 出( 即 保 存 其 状 态 );如 果 需 要

反 序 列 化 则 可 以 用 一 个 输 入 流 建 立 对 象 输 入 流 ,然 后 通 过 readObject 方 法 从 流 中

读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆

( 可 以 参 考 第 29 题 ) 。

69、Java 中有几种类型的流?

答:

字 节 流 和 字 符 流 。 字 节 流 继 承 于 InputStream、 OutputStream, 字 符 流 继 承 于

Reader、 Writer。 在 java.io 包 中 还 有 许 多 其 他 的 流 , 主 要 是 为 了 提 高 性 能 和 使

用 方 便 。关 于 Java 的 I/O 需 要 注 意 的 有 两 点 :一 是 两 种 对 称 性( 输 入 和 输 出 的 对

称性,字节和字符的对称性);二是两种设计模式(适配器模式和装潢模式)。

另 外 Java 中 的 流 不 同 于 C#的 是 它 只 有 一 个 维 度 一 个 方 向 。

面 试 题 - 编 程 实 现 文 件 拷 贝 。( 这 个 题 目 在 笔 试 的 时 候 经 常 出 现 ,下 面 的 代 码 给

出了两种实现方案)

import java.io.FileInputStream;

第 282 页 共 485 页
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public final class MyUtil {

private MyUtil() {
throw new AssertionError();
}

public static void fileCopy(String source, String target) throws


IOException {
try (InputStream in = new FileInputStream(source)) {
try (OutputStream out = new FileOutputStream(target)) {
byte[] buffer = new byte[4096];
int bytesToRead;
while((bytesToRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesToRead);
}
}
}
}

public static void fileCopyNIO(String source, String target) throws


IOException {
try (FileInputStream in = new FileInputStream(source)) {
try (FileOutputStream out = new FileOutputStream(target)) {
FileChannel inChannel = in.getChannel();

第 283 页 共 485 页
FileChannel outChannel = out.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(4096);
while(inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
}
}
}
}

注 意 :上 面 用 到 Java 7 的 TWR,使 用 TWR 后 可 以 不 用 在 finally 中 释 放 外 部 资 源 ,

从而让代码更加优雅。

70、写一个方法,输入一个文件名和一个字符串,统计这个字

符串在这个文件中出现的次数。

答:

代码如下:

import java.io.BufferedReader;
import java.io.FileReader;

public final class MyUtil {

// 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象
(绝对好习惯)

第 284 页 共 485 页
private MyUtil() {
throw new AssertionError();
}

/**
* 统计给定文件中给定字符串的出现次数
*
* @param filename 文件名
* @param word 字符串
* @return 字符串在文件中出现的次数
*/
public static int countWordInFile(String filename, String word) {
int counter = 0;
try (FileReader fr = new FileReader(filename)) {
try (BufferedReader br = new BufferedReader(fr)) {
String line = null;
while ((line = br.readLine()) != null) {
int index = -1;
while (line.length() >= word.length() && (index =
line.indexOf(word)) >= 0) {
counter++;
line = line.substring(index + word.length());
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return counter;
}

第 285 页 共 485 页
}

71、如何用 Java 代码列出一个目录下所有的文件?

答:

如果只要求列出当前文件夹下的文件,代码如下所示:

import java.io.File;

class Test12 {

public static void main(String[] args) {


File f = new File("/Users/Hao/Downloads");
for(File temp : f.listFiles()) {
if(temp.isFile()) {
System.out.println(temp.getName());
}
}
}
}

如果需要对文件夹继续展开,代码如下所示:

import java.io.File;

class Test12 {

public static void main(String[] args) {


showDirectory(new File("/Users/Hao/Downloads"));

第 286 页 共 485 页
}

public static void showDirectory(File f) {


_walkDirectory(f, 0);
}

private static void _walkDirectory(File f, int level) {


if(f.isDirectory()) {
for(File temp : f.listFiles()) {
_walkDirectory(temp, level + 1);
}
}
else {
for(int i = 0; i < level - 1; i++) {
System.out.print("\t");
}
System.out.println(f.getName());
}
}
}

在 Java 7 中 可 以 使 用 NIO.2 的 API 来 做 同 样 的 事 情 , 代 码 如 下 所 示 :

class ShowFileTest {

public static void main(String[] args) throws IOException {


Path initPath = Paths.get("/Users/Hao/Downloads");
Files.walkFileTree(initPath, new SimpleFileVisitor<Path>() {

@Override

第 287 页 共 485 页
public FileVisitResult visitFile(Path file, BasicFileAttributes
attrs)
throws IOException {
System.out.println(file.getFileName().toString());
return FileVisitResult.CONTINUE;
}

});
}
}

72、用 Java 的套接字编程实现一个多线程的回显(echo)服

务器。

答:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class EchoServer {

private static final int ECHO_SERVER_PORT = 6789;

public static void main(String[] args) {

第 288 页 共 485 页
try(ServerSocket server = new
ServerSocket(ECHO_SERVER_PORT)) {
System.out.println("服务器已经启动...");
while(true) {
Socket client = server.accept();
new Thread(new ClientHandler(client)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}

private static class ClientHandler implements Runnable {


private Socket client;

public ClientHandler(Socket client) {


this.client = client;
}

@Override
public void run() {
try(BufferedReader br = new BufferedReader(new
InputStreamReader(client.getInputStream()));
PrintWriter pw = new
PrintWriter(client.getOutputStream())) {
String msg = br.readLine();
System.out.println("收到" + client.getInetAddress() + "
发送的: " + msg);
pw.println(msg);
pw.flush();
} catch(Exception ex) {

第 289 页 共 485 页
ex.printStackTrace();
} finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

注 意 : 上 面 的 代 码 使 用 了 Java 7 的 TWR 语 法 , 由 于 很 多 外 部 资 源 类 都 间 接 的 实

现 了 AutoCloseable 接 口 ( 单 方 法 回 调 接 口 ) , 因 此 可 以 利 用 TWR 语 法 在 try

结 束 的 时 候 通 过 回 调 的 方 式 自 动 调 用 外 部 资 源 类 的 close()方 法 , 避 免 书 写 冗 长 的

finally 代 码 块 。 此 外 , 上 面 的 代 码 用 一 个 静 态 内 部 类 实 现 线 程 的 功 能 , 使 用 多 线

程 可 以 避 免 一 个 用 户 I/O 操 作 所 产 生 的 中 断 影 响 其 他 用 户 对 服 务 器 的 访 问 , 简 单

的说就是一个用户的输入操作不会造成其他用户的阻塞。当然,上面的代码使用

线程池可以获得更好的性能,因为频繁的创建和销毁线程所造成的开销也是不可

忽视的。

下面是一段回显客户端测试代码:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class EchoClient {

第 290 页 共 485 页
public static void main(String[] args) throws Exception {
Socket client = new Socket("localhost", 6789);
Scanner sc = new Scanner(System.in);
System.out.print("请输入内容: ");
String msg = sc.nextLine();
sc.close();
PrintWriter pw = new PrintWriter(client.getOutputStream());
pw.println(msg);
pw.flush();
BufferedReader br = new BufferedReader(new
InputStreamReader(client.getInputStream()));
System.out.println(br.readLine());
client.close();
}
}

如 果 希 望 用 NIO 的 多 路 复 用 套 接 字 实 现 服 务 器 ,代 码 如 下 所 示 。 NIO 的 操 作 虽 然

带来了更好的性能,但是有些操作是比较底层的,对于初学者来说还是有些难于

理解。

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

第 291 页 共 485 页
public class EchoServerNIO {

private static final int ECHO_SERVER_PORT = 6789;


private static final int ECHO_SERVER_TIMEOUT = 5000;
private static final int BUFFER_SIZE = 1024;

private static ServerSocketChannel serverChannel = null;


private static Selector selector = null; // 多路复用选择器
private static ByteBuffer buffer = null; // 缓冲区

public static void main(String[] args) {


init();
listen();
}

private static void init() {


try {
serverChannel = ServerSocketChannel.open();
buffer = ByteBuffer.allocate(BUFFER_SIZE);
serverChannel.socket().bind(new
InetSocketAddress(ECHO_SERVER_PORT));
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private static void listen() {


while (true) {

第 292 页 共 485 页
try {
if (selector.select(ECHO_SERVER_TIMEOUT) != 0) {
Iterator<SelectionKey> it =
selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
handleKey(key);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

private static void handleKey(SelectionKey key) throws IOException {


SocketChannel channel = null;

try {
if (key.isAcceptable()) {
ServerSocketChannel serverChannel =
(ServerSocketChannel) key.channel();
channel = serverChannel.accept();
channel.configureBlocking(false);
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
channel = (SocketChannel) key.channel();
buffer.clear();
if (channel.read(buffer) > 0) {
buffer.flip();

第 293 页 共 485 页
CharBuffer charBuffer =
CharsetHelper.decode(buffer);
String msg = charBuffer.toString();
System.out.println("收到" +
channel.getRemoteAddress() + "的消息:" + msg);

channel.write(CharsetHelper.encode(CharBuffer.wrap(msg)));
} else {
channel.close();
}
}
} catch (Exception e) {
e.printStackTrace();
if (channel != null) {
channel.close();
}
}
}

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

public final class CharsetHelper {


private static final String UTF_8 = "UTF-8";

第 294 页 共 485 页
private static CharsetEncoder encoder =
Charset.forName(UTF_8).newEncoder();
private static CharsetDecoder decoder =
Charset.forName(UTF_8).newDecoder();

private CharsetHelper() {
}

public static ByteBuffer encode(CharBuffer in) throws


CharacterCodingException{
return encoder.encode(in);
}

public static CharBuffer decode(ByteBuffer in) throws


CharacterCodingException{
return decoder.decode(in);
}
}

73、XML 文档定义有几种形式?它们之间有何本质区别?解析

XML 文档有哪几种方式?

答:

XML 文 档 定 义 分 为 DTD 和 Schema 两 种 形 式 ,二 者 都 是 对 XML 语 法 的 约 束 ,其

本 质 区 别 在 于 Schema 本 身 也 是 一 个 XML 文 件 , 可 以 被 XML 解 析 器 解 析 , 而 且

可 以 为 XML 承 载 的 数 据 定 义 类 型 ,约 束 能 力 较 之 DTD 更 强 大 。对 XML 的 解 析 主

要 有 DOM( 文 档 对 象 模 型 ,Document Object Model)、SAX( Simple API for

XML)和 StAX( Java 6 中 引 入 的 新 的 解 析 XML 的 方 式 ,Streaming API for XML),

第 295 页 共 485 页
其 中 DOM 处 理 大 型 文 件 时 其 性 能 下 降 的 非 常 厉 害 ,这 个 问 题 是 由 DOM 树 结 构 占

用 的 内 存 较 多 造 成 的 ,而 且 DOM 解 析 方 式 必 须 在 解 析 文 件 之 前 把 整 个 文 档 装 入 内

存 ,适 合 对 XML 的 随 机 访 问( 典 型 的 用 空 间 换 取 时 间 的 策 略 );SAX 是 事 件 驱 动

型 的 XML 解 析 方 式 , 它 顺 序 读 取 XML 文 件 , 不 需 要 一 次 全 部 装 载 整 个 文 件 。 当

遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,

用 户 通 过 事 件 回 调 代 码 来 处 理 XML 文 件 , 适 合 对 XML 的 顺 序 访 问 ; 顾 名 思 义 ,

StAX 把 重 点 放 在 流 上 , 实 际 上 StAX 与 其 他 解 析 方 式 的 本 质 区 别 就 在 于 应 用 程 序

能 够 把 XML 作 为 一 个 事 件 流 来 处 理 。 将 XML 作 为 一 组 事 件 来 处 理 的 想 法 并 不 新

颖( SAX 就 是 这 样 做 的 ),但 不 同 之 处 在 于 StAX 允 许 应 用 程 序 代 码 把 这 些 事 件 逐

个拉出来,而不用提供在解析器方便时从解析器中接收事件的处理程序。

74、你在项目中哪些地方用到了 XML?

答:

XML 的 主 要 作 用 有 两 个 方 面 :数 据 交 换 和 信 息 配 置 。在 做 数 据 交 换 时 ,XML 将 数

据用标签组装成起来,然后压缩打包加密后通过网络传送给接收者,接收解密与

解 压 缩 后 再 从 XML 文 件 中 还 原 相 关 信 息 进 行 处 理 ,XML 曾 经 是 异 构 系 统 间 交 换 数

据 的 事 实 标 准 , 但 此 项 功 能 几 乎 已 经 被 JSON( JavaScript Object Notation) 取

而 代 之 。当 然 ,目 前 很 多 软 件 仍 然 使 用 XML 来 存 储 配 置 信 息 ,我 们 在 很 多 项 目 中

通 常 也 会 将 作 为 配 置 信 息 的 硬 代 码 写 在 XML 文 件 中 ,Java 的 很 多 框 架 也 是 这 么 做

的 , 而 且 这 些 框 架 都 选 择 了 dom4j 作 为 处 理 XML 的 工 具 , 因 为 Sun 公 司 的 官 方

API 实 在 不 怎 么 好 用 。

补 充 : 现 在 有 很 多 时 髦 的 软 件 ( 如 Sublime) 已 经 开 始 将 配 置 文 件 书 写 成 JSON

格 式 , 我 们 已 经 强 烈 的 感 受 到 XML 的 另 一 项 功 能 也 将 逐 渐 被 业 界 抛 弃 。

75、阐述 JDBC 操作数据库的步骤。

第 296 页 共 485 页
答:

下 面 的 代 码 以 连 接 本 机 的 Oracle 数 据 库 为 例 , 演 示 JDBC 操 作 数 据 库 的 步 骤 。

 加载驱动。

Class.forName("oracle.jdbc.driver.OracleDriver");

 创建连接。

Connection con =
DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl",
"scott", "tiger");

 创建语句。

PreparedStatement ps = con.prepareStatement("select * from emp


where sal between ? and ?");
ps.setInt(1, 1000);
ps.setInt(2, 3000);

 执行语句。

ResultSet rs = ps.executeQuery();

第 297 页 共 485 页
 处理结果。

while(rs.next()) {
System.out.println(rs.getInt("empno") + " - " +
rs.getString("ename"));
}

 关闭资源。

finally {
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

提 示 : 关 闭 外 部 资 源 的 顺 序 应 该 和 打 开 的 顺 序 相 反 , 也 就 是 说 先 关 闭 ResultSet、

再 关 闭 Statement、 在 关 闭 Connection。上 面 的 代 码 只 关 闭 了 Connection( 连

接),虽然通常情况下在关闭连接时,连接上创建的语句和打开的游标也会关闭,

但不能保证总是如此,因此应该按照刚才说的顺序分别关闭。此外,第一步加载

驱 动 在 JDBC 4.0 中 是 可 以 省 略 的( 自 动 从 类 路 径 中 加 载 驱 动 ),但 是 我 们 建 议 保

留。

76、Statement 和 PreparedStatement 有什么区别?哪个性

能更好?
第 298 页 共 485 页
答:

与 Statement 相 比 ,① PreparedStatement 接 口 代 表 预 编 译 的 语 句 ,它 主 要 的 优

势 在 于 可 以 减 少 SQL 的 编 译 错 误 并 增 加 SQL 的 安 全 性 ( 减 少 SQL 注 射 攻 击 的 可

能 性 );② PreparedStatement 中 的 SQL 语 句 是 可 以 带 参 数 的 ,避 免 了 用 字 符 串

连 接 拼 接 SQL 语 句 的 麻 烦 和 不 安 全 ;③ 当 批 量 处 理 SQL 或 频 繁 执 行 相 同 的 查 询 时 ,

PreparedStatement 有 明 显 的 性 能 上 的 优 势 , 由 于 数 据 库 可 以 将 编 译 优 化 后 的

SQL 语 句 缓 存 起 来 , 下 次 执 行 相 同 结 构 的 语 句 时 就 会 很 快 ( 不 用 再 次 编 译 和 生 成

执行计划)。

补 充 :为 了 提 供 对 存 储 过 程 的 调 用 ,JDBC API 中 还 提 供 了 CallableStatement 接

口 。存 储 过 程( Stored Procedure)是 数 据 库 中 一 组 为 了 完 成 特 定 功 能 的 SQL 语

句的集合,经编译后存储在数据库中,用户通过指定存储过程的名字并给出参数

(如果该存储过程带有参数)来执行它。虽然调用存储过程会在网络开销、安全

性、性能上获得很多好处,但是存在如果底层数据库发生迁移时就会有很多麻烦,

因为每种数据库的存储过程在书写上存在不少的差别。

77、使用 JDBC 操作数据库时,如何提升读取数据的性能?如

何提升更新数据的性能?

答:

要 提 升 读 取 数 据 的 性 能 ,可 以 指 定 通 过 结 果 集( ResultSet)对 象 的 setFetchSize()

方法指定每次抓取的记录数(典型的空间换时间策略);要提升更新数据的性能

可 以 使 用 PreparedStatement 语 句 构 建 批 处 理 , 将 若 干 SQL 语 句 置 于 一 个 批 处

理中执行。

第 299 页 共 485 页
78、在进行数据库编程时,连接池有什么作用?

答:

由于创建连接和释放连接都有很大的开销(尤其是数据库服务器不在本地时,每

次 建 立 连 接 都 需 要 进 行 TCP 的 三 次 握 手 ,释 放 连 接 需 要 进 行 TCP 四 次 握 手 ,造 成

的开销是不可忽视的),为了提升系统访问数据库的性能,可以事先创建若干连

接置于连接池中,需要时直接从连接池获取,使用结束时归还连接池而不必关闭

连接,从而避免频繁创建和释放连接所造成的开销,这是典型的用空间换取时间

的策略(浪费了空间存储连接,但节省了创建和释放连接的时间)。池化技术在

Java 开 发 中 是 很 常 见 的 ,在 使 用 线 程 时 创 建 线 程 池 的 道 理 与 此 相 同 。基 于 Java 的

开 源 数 据 库 连 接 池 主 要 有 : C3P0、 Proxool、 DBCP、 BoneCP、 Druid 等 。

补充:在计算机系统中时间和空间是不可调和的矛盾,理解这一点对设计满足性

能要求的算法是至关重要的。大型网站性能优化的一个关键就是使用缓存,而缓

存跟上面讲的连接池道理非常类似,也是使用空间换时间的策略。可以将热点数

据置于缓存中,当用户查询这些数据时可以直接从缓存中得到,这无论如何也快

过去数据库中查询。当然,缓存的置换策略等也会对系统性能产生重要影响,对

于这个问题的讨论已经超出了这里要阐述的范围。

79、什么是 DAO 模式?

答:

DAO( Data Access Object) 顾 名 思 义 是 一 个 为 数 据 库 或 其 他 持 久 化 机 制 提 供 了

抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访

问操作。在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在

一 个 公 共 API 中 。 用 程 序 设 计 语 言 来 说 , 就 是 建 立 一 个 接 口 , 接 口 中 定 义 了 此 应

用程序中将会用到的所有事务方法。在这个应用程序中,当需要和数据源进行交

第 300 页 共 485 页
互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该

类 对 应 一 个 特 定 的 数 据 存 储 。 DAO 模 式 实 际 上 包 含 了 两 个 模 式 , 一 是 Data

Accessor( 数 据 访 问 器 ) , 二 是 Data Object( 数 据 对 象 ) , 前 者 要 解 决 如 何 访

问数据的问题,而后者要解决的是如何用对象封装数据。

80、事务的 ACID 是指什么?

答:

 原子性(Atomic):事务中各项操作,要么全做要么全不做,任何一项操作
的失败都会导致整个事务的失败;
 一致性(Consistent):事务结束后系统状态是一致的;
 隔离性(Isolated):并发执行的事务彼此无法看到对方的中间状态;
 持久性(Durable):事务完成后所做的改动都会被持久化,即使发生灾难
性的失败。通过日志和同步备份可以在故障发生后重建数据。

补充:关于事务,在面试中被问到的概率是很高的,可以问的问题也是很多的。

首先需要知道的是,只有存在并发数据访问时才需要事务。当多个事务访问同一

数据时,可能会存在 5 类问题,包括 3 类数据读取问题(脏读、不可重复读和幻

读)和 2 类数据更新问题(第 1 类丢失更新和第 2 类丢失更新)。

脏 读( Dirty Read):A 事 务 读 取 B 事 务 尚 未 提 交 的 数 据 并 在 此 基 础 上 操 作 ,而 B

事务执行回滚,那么 A 读取到的数据就是脏数据。

时间 转账事务 A 取款事务 B

T1 开始事务

T2 开始事务

T3 查询账户余额为 1000 元

第 301 页 共 485 页
T4 取出 500 元余额修改为 500

T5 查询账户余额为 500 元(脏读)

T6 撤销事务余额恢复为 1000 元

T7 汇入 100 元把余额修改为 600

T8 提交事务

不 可 重 复 读 ( Unrepeatable Read) : 事 务 A 重 新 读 取 前 面 读 取 过 的 数 据 , 发 现

该数据已经被另一个已提交的事务 B 修改过了。

时间 转账事务 A 取款事务 B

T1 开始事务

T2 开始事务

T3 查询账户余额为 1000 元

T4 查询账户余额为 1000 元

T5 取出 100 元修改余额为 900

T6 提交事务

T7 查询账户余额为 900 元(不可重复读)

幻 读 ( Phantom Read) : 事 务 A 重 新 执 行 一 个 查 询 , 返 回 一 系 列 符 合 查 询 条 件

的行,发现其中插入了被事务 B 提交的行。

第 302 页 共 485 页
时间 统计金额事务 A 转账事务 B

T1 开始事务

T2 开始事务

T3 统计总存款为 10000 元

T4 新增一个存款账户存入 100

T5 提交事务

T6 再次统计总存款为 10100 元(幻读)

第 1 类丢失更新:事务 A 撤销时,把已经提交的事务 B 的更新数据覆盖了。

时间 取款事务 A 转账事务 B

T1 开始事务

T2 开始事务

T3 查询账户余额为 1000 元

T4 查询账户余额为 1000 元

T5 汇入 100 元修改余额为 1100

T6 提交事务

T7 取出 100 元将余额修改为 900 元

T8 撤销事务

T9 余额恢复为 1000 元(丢失更新)

第 303 页 共 485 页
第 2 类丢失更新:事务 A 覆盖事务 B 已经提交的数据,造成事务 B 所做的操作丢

失。

时间 转账事务 A 取款事务 B

T1 开始事务

T2 开始事务

T3 查询账户余额为 1000 元

T4 查询账户余额为 1000 元

T5 取出 100 元将余额修改为 900

T6 提交事务

T7 汇入 100 元将余额修改为 1100 元

T8 提交事务

T9 查询账户余额为 1100 元(丢失更新)

数据并发访问所产生的问题,在有些场景下可能是允许的,但是有些场景下可能

就是致命的,数据库通常会通过锁机制来解决数据并发访问问题,按锁定对象不

同可以分为表级锁和行级锁;按并发事务锁定关系可以分为共享锁和独占锁,具

体的内容大家可以自行查阅资料进行了解。

直接使用锁是非常麻烦的,为此数据库为用户提供了自动锁机制,只要用户指定

会 话 的 事 务 隔 离 级 别 , 数 据 库 就 会 通 过 分 析 SQL 语 句 然 后 为 事 务 访 问 的 资 源 加 上

合适的锁,此外,数据库还会维护这些锁通过各种手段提高系统的性能,这些对

用 户 来 说 都 是 透 明 的 ( 就 是 说 你 不 用 理 解 , 事 实 上 我 确 实 也 不 知 道 ) 。 ANSI/ISO

SQL 92 标 准 定 义 了 4 个 等 级 的 事 务 隔 离 级 别 , 如 下 表 所 示 :

隔离级别 脏读 不可重复读 幻读 第一类丢失更新 第二类丢失更新

第 304 页 共 485 页
READ 允许 允许 允许 不允许 允许

UNCOMMITED

READ 不允许 允许 允许 不允许 允许

COMMITTED

REPEATABLE 不允许 不允许 允许 不允许 不允许

READ

SERIALIZABLE 不允许 不允许 不允许 不允许 不允许

需要说明的是,事务隔离级别和数据访问的并发性是对立的,事务隔离级别越高

并发性就越差。所以要根据具体的应用来确定合适的事务隔离级别,这个地方没

有万能的原则。

**81、 JDBC 中 如 何 进 行 事 务 处 理 ?

答:

Connection 提 供 了 事 务 处 理 的 方 法 , 通 过 调 用 setAutoCommit(false)可 以 设 置

手 动 提 交 事 务 ;当 事 务 完 成 后 用 commit()显 式 提 交 事 务 ;如 果 在 事 务 处 理 过 程 中

发 生 异 常 则 通 过 rollback()进 行 事 务 回 滚 。 除 此 之 外 , 从 JDBC 3.0 中 还 引 入 了

Savepoint( 保 存 点 )的 概 念 ,允 许 通 过 代 码 设 置 保 存 点 并 让 事 务 回 滚 到 指 定 的 保

存点。

第 305 页 共 485 页
82、JDBC 能否处理 Blob 和 Clob?

答:

Blob 是 指 二 进 制 大 对 象 ( Binary Large Object) , 而 Clob 是 指 大 字 符 对 象

( Character Large Objec),因 此 其 中 Blob 是 为 存 储 大 的 二 进 制 数 据 而 设 计 的 ,

而 Clob 是 为 存 储 大 的 文 本 数 据 而 设 计 的 。 JDBC 的 PreparedStatement 和

ResultSet 都 提 供 了 相 应 的 方 法 来 支 持 Blob 和 Clob 操 作 。 下 面 的 代 码 展 示 了 如

何 使 用 JDBC 操 作 LOB:

下 面 以 MySQL 数 据 库 为 例 , 创 建 一 个 张 有 三 个 字 段 的 用 户 表 , 包 括 编 号 ( id) 、

姓 名 ( name) 和 照 片 ( photo) , 建 表 语 句 如 下 :

create table tb_user


(
id int primary key auto_increment,
name varchar(20) unique not null,
photo longblob
);

下 面 的 Java 代 码 向 数 据 库 中 插 入 一 条 记 录 :

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

class JdbcLobTest {

第 306 页 共 485 页
public static void main(String[] args) {
Connection con = null;
try {
// 1. 加载驱动(Java6 以上版本可以省略)
Class.forName("com.mysql.jdbc.Driver");
// 2. 建立连接
con =
DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root",
"123456");
// 3. 创建语句对象
PreparedStatement ps = con.prepareStatement("insert into
tb_user values (default, ?, ?)");
ps.setString(1, "骆昊"); // 将 SQL 语句中第一个
占位符换成字符串
try (InputStream in = new FileInputStream("test.jpg"))
{ // Java 7 的 TWR
ps.setBinaryStream(2, in); // 将 SQL 语句中第二个占
位符换成二进制流
// 4. 发出 SQL 语句获得受影响行数
System.out.println(ps.executeUpdate() == 1 ? "插入成功
" : "插入失败");
} catch(IOException e) {
System.out.println("读取照片失败!");
}
} catch (ClassNotFoundException | SQLException e) { // Java
7 的多异常捕获
e.printStackTrace();
} finally { // 释放外部资源的代码都应当放在 finally 中保证其能够得
到执行
try {

第 307 页 共 485 页
if(con != null && !con.isClosed()) {
con.close(); // 5. 释放数据库连接
con = null; // 指示垃圾回收器可以回收该对象
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

83、简述正则表达式及其用途。

答:

在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。

正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本

规则的代码。

说明:计算机诞生初期处理的信息几乎都是数值,但是时过境迁,今天我们使用

计算机处理的信息更多的时候不是数值而是字符串,正则表达式就是在进行字符

串匹配和处理的时候最为强大的工具,绝大多数语言都提供了对正则表达式的支

持。

84、Java 中是如何支持正则表达式操作的?

答:

第 308 页 共 485 页
Java 中 的 String 类 提 供 了 支 持 正 则 表 达 式 操 作 的 方 法 , 包 括 : matches()、

replaceAll()、replaceFirst()、split()。此 外 ,Java 中 可 以 用 Pattern 类 表 示 正 则

表 达 式 对 象 , 它 提 供 了 丰 富 的 API 进 行 各 种 正 则 表 达 式 操 作 , 请 参 考 下 面 面 试 题

的代码。

面 试 题 : - 如 果 要 从 字 符 串 中 截 取 第 一 个 英 文 左 括 号 之 前 的 字 符 串 ,例 如 :北 京

市 (朝 阳 区 )(西 城 区 )(海 淀 区 ), 截 取 结 果 为 : 北 京 市 , 那 么 正 则 表 达 式 怎 么 写 ?

import java.util.regex.Matcher;
import java.util.regex.Pattern;

class RegExpTest {

public static void main(String[] args) {


String str = "北京市(朝阳区)(西城区)(海淀区)";
Pattern p = Pattern.compile(".*?(?=\\()");
Matcher m = p.matcher(str);
if(m.find()) {
System.out.println(m.group());
}
}
}

说明:上面的正则表达式中使用了懒惰匹配和前瞻,如果不清楚这些内容,推荐

读 一 下 网 上 很 有 名 的 《 正 则 表 达 式 30 分 钟 入 门 教 程 》 。

85、获得一个类的类对象有哪些方式?

答:

第 309 页 共 485 页
 方法 1:类型.class,例如:String.class
 方法 2:对象.getClass(),例如:”hello”.getClass()
 方法 3:Class.forName(),例如:Class.forName(“java.lang.String”)

**86、 如 何 通 过 反 射 创 建 对 象 ?

答:

 方法 1:通过类对象调用 newInstance()方法,例如:
String.class.newInstance()
 方法 2:通过类对象的 getConstructor()或 getDeclaredConstructor()
方法获得构造器(Constructor)对象并调用其 newInstance()方法创建对象,
例如:String.class.getConstructor(String.class).newInstance(“Hello”);

**87、 如 何 通 过 反 射 获 取 和 设 置 对 象 私 有 字 段 的 值 ?

答:

可 以 通 过 类 对 象 的 getDeclaredField()方 法 字 段 ( Field) 对 象 , 然 后 再 通 过 字 段

对 象 的 setAccessible(true)将 其 设 置 为 可 以 访 问 , 接 下 来 就 可 以 通 过 get/set 方

法 来 获 取 /设 置 字 段 的 值 了 。 下 面 的 代 码 实 现 了 一 个 反 射 的 工 具 类 , 其 中 的 两 个 静

态方法分别用于获取和设置私有字段的值,字段可以是基本类型也可以是对象类

型 且 支 持 多 级 对 象 操 作 ,例 如 ReflectionUtil.get(dog, “ owner.car.engine.id”);

可 以 获 得 dog 对 象 的 主 人 的 汽 车 的 引 擎 的 ID 号 。

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;

第 310 页 共 485 页
import java.util.List;

/**
* 反射工具类
* @author 骆昊
*
*/
public class ReflectionUtil {

private ReflectionUtil() {
throw new AssertionError();
}

/**
* 通过反射取对象指定字段(属性)的值
* @param target 目标对象
* @param fieldName 字段的名字
* @throws 如果取不到对象指定字段的值则抛出异常
* @return 字段的值
*/
public static Object getValue(Object target, String fieldName) {
Class<?> clazz = target.getClass();
String[] fs = fieldName.split("\\.");

try {
for(int i = 0; i < fs.length - 1; i++) {
Field f = clazz.getDeclaredField(fs[i]);
f.setAccessible(true);
target = f.get(target);
clazz = target.getClass();
}

第 311 页 共 485 页
Field f = clazz.getDeclaredField(fs[fs.length - 1]);
f.setAccessible(true);
return f.get(target);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}

/**
* 通过反射给对象的指定字段赋值
* @param target 目标对象
* @param fieldName 字段的名称
* @param value 值
*/
public static void setValue(Object target, String fieldName, Object
value) {
Class<?> clazz = target.getClass();
String[] fs = fieldName.split("\\.");
try {
for(int i = 0; i < fs.length - 1; i++) {
Field f = clazz.getDeclaredField(fs[i]);
f.setAccessible(true);
Object val = f.get(target);
if(val == null) {
Constructor<?> c =
f.getType().getDeclaredConstructor();
c.setAccessible(true);
val = c.newInstance();
f.set(target, val);

第 312 页 共 485 页
}
target = val;
clazz = target.getClass();
}

Field f = clazz.getDeclaredField(fs[fs.length - 1]);


f.setAccessible(true);
f.set(target, value);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}

88、如何通过反射调用对象的方法?

答:

请看下面的代码:

import java.lang.reflect.Method;

class MethodInvokeTest {

public static void main(String[] args) throws Exception {


String str = "hello";
Method m = str.getClass().getMethod("toUpperCase");
System.out.println(m.invoke(str)); // HELLO

第 313 页 共 485 页
}
}

**89、 简 述 一 下 面 向 对 象 的 ” 六 原 则 一 法 则 ” 。

答:

 单一职责原则:一个类只做它该做的事情。
(单一职责原则想表达的就是”
高内聚”,写代码最终极的原则只有六个字”高内聚、低耦合”,就如同葵花宝
典或辟邪剑谱的中心思想就八个字”欲练此功必先自宫”,所谓的高内聚就是一
个代码模块只完成一项功能,在面向对象中,如果只让一个类完成它该做的事,
而不涉及与它无关的领域就是践行了高内聚的原则,这个类就只有单一职责。我
们都知道一句话叫”因为专注,所以专业”,一个对象如果承担太多的职责,那
么注定它什么都做不好。这个世界上任何好的东西都有两个特征,一个是功能单
一,好的相机绝对不是电视购物里面卖的那种一个机器有一百多种功能的,它基
本上只能照相;另一个是模块化,好的自行车是组装车,从减震叉、刹车到变速
器,所有的部件都是可以拆卸和重新组装的,好的乒乓球拍也不是成品拍,一定
是底板和胶皮可以拆分和自行组装的,一个好的软件系统,它里面的每个功能模
块也应该是可以轻易的拿到其他系统中使用的,这样才能实现软件复用的目标。)
 开闭原则:软件实体应当对扩展开放,对修改关闭。(在理想的状态下,
当我们需要为一个软件系统增加新功能时,只需要从原来的系统派生出一些新类
就可以,不需要修改原来的任何一行代码。要做到开闭有两个要点:①抽象是关
键,一个系统中如果没有抽象类或接口系统就没有扩展点;②封装可变性,将系
统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系
统将变得复杂而换乱,如果不清楚如何封装可变性,可以参考《设计模式精解》
一书中对桥梁模式的讲解的章节。)
 依赖倒转原则:面向接口编程。(该原则说得直白和具体一些就是声明方
法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不
用具体类型,因为抽象类型可以被它的任何一个子类型所替代,请参考下面的里
氏替换原则。)

第 314 页 共 485 页
里氏替换原则:任何时候都可以用子类型替换掉父类型。(关于里氏替换原则的
描述,Barbara Liskov 女士的描述比这个要复杂得多,但简单的说就是能用父
类型的地方就一定能使用子类型。里氏替换原则可以检查继承关系是否合理,如
果一个继承关系违背了里氏替换原则,那么这个继承关系一定是错误的,需要对
代码进行重构。例如让猫继承狗,或者狗继承猫,又或者让正方形继承长方形都
是错误的继承关系,因为你很容易找到违反里氏替换原则的场景。需要注意的是:
子类一定是增加父类的能力而不是减少父类的能力,因为子类比父类的能力更
多,把能力多的对象当成能力少的对象来用当然没有任何问题。)
 接口隔离原则:接口要小而专,绝不能大而全。(臃肿的接口是对接口的
污染,既然接口表示能力,那么一个接口只应该描述一种能力,接口也应该是高
度内聚的。例如,琴棋书画就应该分别设计为四个接口,而不应设计成一个接口
中的四个方法,因为如果设计成一个接口中的四个方法,那么这个接口很难用,
毕竟琴棋书画四样都精通的人还是少数,而如果设计成四个接口,会几项就实现
几个接口,这样的话每个接口被复用的可能性是很高的。Java 中的接口代表能
力、代表约定、代表角色,能否正确的使用接口一定是编程水平高低的重要标识。)
 合成聚合复用原则:优先使用聚合或合成关系复用代码。(通过继承来复
用代码是面向对象程序设计中被滥用得最多的东西,因为所有的教科书都无一例
外的对继承进行了鼓吹从而误导了初学者,类与类之间简单的说有三种关系,
Is-A 关系、Has-A 关系、Use-A 关系,分别代表继承、关联和依赖。其中,关
联关系根据其关联的强度又可以进一步划分为关联、聚合和合成,但说白了都是
Has-A 关系,合成聚合复用原则想表达的是优先考虑 Has-A 关系而不是 Is-A 关
系复用代码,原因嘛可以自己从百度上找到一万个理由,需要说明的是,即使在
Java 的 API 中也有不少滥用继承的例子,例如 Properties 类继承了 Hashtable
类,Stack 类继承了 Vector 类,这些继承明显就是错误的,更好的做法是在
Properties 类中放置一个 Hashtable 类型的成员并且将其键和值都设置为字符
串来存储数据,而 Stack 类的设计也应该是在 Stack 类中放一个 Vector 对象来
存储数据。记住:任何时候都不要继承工具类,工具是可以拥有并可以使用的,
而不是拿来继承的。)
 迪米特法则:迪米特法则又叫最少知识原则,一个对象应当对其他对象有
尽可能少的了解。(迪米特法则简单的说就是如何做到”低耦合”,门面模式和

第 315 页 共 485 页
调停者模式就是对迪米特法则的践行。对于门面模式可以举一个简单的例子,你
去一家公司洽谈业务,你不需要了解这个公司内部是如何运作的,你甚至可以对
这个公司一无所知,去的时候只需要找到公司入口处的前台美女,告诉她们你要
做什么,她们会找到合适的人跟你接洽,前台的美女就是公司这个系统的门面。
再复杂的系统都可以为用户提供一个简单的门面,Java Web 开发中作为前端控
制器的 Servlet 或 Filter 不就是一个门面吗,浏览器对服务器的运作方式一无所
知,但是通过前端控制器就能够根据你的请求得到相应的服务。调停者模式也可
以举一个简单的例子来说明,例如一台计算机,CPU、内存、硬盘、显卡、声卡
各种设备需要相互配合才能很好的工作,但是如果这些东西都直接连接到一起,
计算机的布线将异常复杂,在这种情况下,主板作为一个调停者的身份出现,它
将各个设备连接在一起而不需要每个设备之间直接交换数据,这样就减小了系统
的耦合度和复杂度,如下图所示。迪米特法则用通俗的话来将就是不要和陌生人
打交道,如果真的需要,找一个自己的朋友,让他替你和陌生人打交道。)

第 316 页 共 485 页
90、简述一下你了解的设计模式。

答:

所谓设计模式,就是一套被反复使用的代码设计经验的总结(情境中一个问题经

过证实的一个解决方案)。使用设计模式是为了可重用代码、让代码更容易被他

人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计

和体系结构。将已证实的技术表述成设计模式也会使新系统开发者更加容易理解

其设计思路。

在 GoF 的 《 Design Patterns: Elements of Reusable Object-Oriented

Software》 中 给 出 了 三 类 ( 创 建 型 [对 类 的 实 例 化 过 程 的 抽 象 化 ]、 结 构 型 [描 述 如

何 将 类 或 对 象 结 合 在 一 起 形 成 更 大 的 结 构 ]、行 为 型 [对 在 不 同 的 对 象 之 间 划 分 责 任

和 算 法 的 抽 象 化 ])共 23 种 设 计 模 式 ,包 括 :Abstract Factory( 抽 象 工 厂 模 式 ),

Builder( 建 造 者 模 式 ) , Factory Method( 工 厂 方 法 模 式 ) , Prototype( 原 始

模 型 模 式 ),Singleton( 单 例 模 式 ) ;Facade( 门 面 模 式 ) ,Adapter( 适 配 器

模 式 ) , Bridge( 桥 梁 模 式 ) , Composite( 合 成 模 式 ) , Decorator( 装 饰 模

式 ) , Flyweight( 享 元 模 式 ) , Proxy( 代 理 模 式 ) ; Command( 命 令 模 式 ) ,

第 317 页 共 485 页
Interpreter( 解 释 器 模 式 ) , Visitor( 访 问 者 模 式 ) , Iterator( 迭 代 子 模 式 ) ,

Mediator( 调 停 者 模 式 ),Memento( 备 忘 录 模 式 ),Observer( 观 察 者 模 式 ),

State( 状 态 模 式 ),Strategy( 策 略 模 式 ),Template Method( 模 板 方 法 模 式 ),

Chain Of Responsibility( 责 任 链 模 式 ) 。

面试被问到关于设计模式的知识时,可以拣最常用的作答,例如:

 工厂模式:工厂类可以根据条件生成不同的子类实例,这些子类有一个公
共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同
的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而
不必考虑到底返回的是哪一个子类的实例。
 代理模式:给一个对象提供一个代理对象,并由代理对象控制原对象的引
用。实际开发中,按照使用目的的不同,代理可以分为:远程代理、虚拟代理、
保护代理、Cache 代理、防火墙代理、同步化代理、智能引用代理。
 适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使
原本因接口不匹配而无法在一起使用的类能够一起工作。
 模板方法模式:提供一个抽象类,将部分逻辑以具体方法或构造器的形式
实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不
同的方式实现这些抽象方法(多态实现),从而实现不同的业务逻辑。

除此之外,还可以讲讲上面提到的门面模式、桥梁模式、单例模式、装潢模式
(Collections 工具类和 I/O 系统中都使用装潢模式)等,反正基本原则就是拣
自己最熟悉的、用得最多的作答,以免言多必失。

91、用 Java 写一个单例类。

答:

 饿汉式单例

第 318 页 共 485 页
public class Singleton {
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}

 懒汉式单例

public class Singleton {


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

注意:实现一个单例有两点注意事项,①将构造器私有,不允许外界通过构造器

创建对象;②通过公开的静态方法向外界返回类的唯一实例。这里有一个问题可

以 思 考 : Spring 的 IoC 容 器 可 以 为 普 通 的 类 创 建 单 例 , 它 是 怎 么 做 到 的 呢 ?

92、什么是 UML?

答:

第 319 页 共 485 页
UML 是 统 一 建 模 语 言 ( Unified Modeling Language) 的 缩 写 , 它 发 表 于 1997

年,综合了当时已经存在的面向对象的建模语言、方法和过程,是一个支持模型

化和软件系统开发的图形化语言,为软件开发的所有阶段提供模型化和可视化支

持 。使 用 UML 可 以 帮 助 沟 通 与 交 流 ,辅 助 应 用 设 计 和 文 档 的 生 成 ,还 能 够 阐 释 系

统的结构和行为。

93、UML 中有哪些常用的图?

答:

UML 定 义 了 多 种 图 形 化 的 符 号 来 描 述 软 件 系 统 部 分 或 全 部 的 静 态 结 构 和 动 态 结

构 ,包 括 :用 例 图( use case diagram)、类 图( class diagram)、时 序 图( sequence

diagram)、协 作 图( collaboration diagram)、状 态 图( statechart diagram)、

活 动 图( activity diagram)、构 件 图( component diagram)、部 署 图( deployment

diagram)等 。在 这 些 图 形 化 符 号 中 ,有 三 种 图 最 为 重 要 ,分 别 是 :用 例 图( 用 来

捕 获 需 求 ,描 述 系 统 的 功 能 ,通 过 该 图 可 以 迅 速 的 了 解 系 统 的 功 能 模 块 及 其 关 系 )、

类 图( 描 述 类 以 及 类 与 类 之 间 的 关 系 ,通 过 该 图 可 以 快 速 了 解 系 统 )、时 序 图( 描

述执行特定任务时对象之间的交互关系以及执行顺序,通过该图可以了解对象能

接收的消息也就是说对象能够向外界提供的服务)。

用例图:

第 320 页 共 485 页
类图:

时序图:

第 321 页 共 485 页
**94、 用 Java 写 一 个 冒 泡 排 序 。

答:

冒泡排序几乎是个程序员都写得出来,但是面试的时候如何写一个逼格高的冒泡

排序却不是每个人都能做到,下面提供一个参考代码:

import java.util.Comparator;

/**
* 排序器接口(策略模式: 将算法封装到具有共同接口的独立的类中使得它们可
以相互替换)
* @author 骆昊
*
*/
public interface Sorter {

/**
* 排序

第 322 页 共 485 页
* @param list 待排序的数组
*/
public <T extends Comparable<T>> void sort(T[] list);

/**
* 排序
* @param list 待排序的数组
* @param comp 比较两个对象的比较器
*/
public <T> void sort(T[] list, Comparator<T> comp);
}

import java.util.Comparator;

/**
* 冒泡排序
*
* @author 骆昊
*
*/
public class BubbleSorter implements Sorter {

@Override
public <T extends Comparable<T>> void sort(T[] list) {
boolean swapped = true;
for (int i = 1, len = list.length; i < len && swapped; ++i) {
swapped = false;
for (int j = 0; j < len - i; ++j) {
if (list[j].compareTo(list[j + 1]) > 0) {
T temp = list[j];
list[j] = list[j + 1];

第 323 页 共 485 页
list[j + 1] = temp;
swapped = true;
}
}
}
}

@Override
public <T> void sort(T[] list, Comparator<T> comp) {
boolean swapped = true;
for (int i = 1, len = list.length; i < len && swapped; ++i) {
swapped = false;
for (int j = 0; j < len - i; ++j) {
if (comp.compare(list[j], list[j + 1]) > 0) {
T temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
swapped = true;
}
}
}
}
}

95、用 Java 写一个折半查找。

答:

折半查找,也称二分查找、二分搜索,是一种在有序数组中查找某一特定元素的

搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,

第 324 页 共 485 页
则搜素过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小

于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一

步骤数组已经为空,则表示找不到指定的元素。这种搜索算法每一次比较都使搜

索 范 围 缩 小 一 半 , 其 时 间 复 杂 度 是 O(logN)。

import java.util.Comparator;

public class MyUtil {

public static <T extends Comparable<T>> int binarySearch(T[] x, T


key) {
return binarySearch(x, 0, x.length- 1, key);
}

// 使用循环实现的二分查找
public static <T> int binarySearch(T[] x, T key, Comparator<T> comp)
{
int low = 0;
int high = x.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int cmp = comp.compare(x[mid], key);
if (cmp < 0) {
low= mid + 1;
}
else if (cmp > 0) {
high= mid - 1;
}
else {
return mid;
}

第 325 页 共 485 页
}
return -1;
}

// 使用递归实现的二分查找
private static<T extends Comparable<T>> int binarySearch(T[] x, int
low, int high, T key) {
if(low <= high) {
int mid = low + ((high -low) >> 1);
if(key.compareTo(x[mid])== 0) {
return mid;
}
else if(key.compareTo(x[mid])< 0) {
return binarySearch(x,low, mid - 1, key);
}
else {
return binarySearch(x,mid + 1, high, key);
}
}
return -1;
}
}

说明:上面的代码中给出了折半查找的两个版本,一个用递归实现,一个用循环

实 现 。 需 要 注 意 的 是 计 算 中 间 位 置 时 不 应 该 使 用 (high+ low) / 2 的 方 式 , 因 为 加

法 运 算 可 能 导 致 整 数 越 界 , 这 里 应 该 使 用 以 下 三 种 方 式 之 一 : low + (high - low)

/ 2 或 low + (high – low) >> 1 或 (low + high) >>> 1( >>>是 逻 辑 右 移 , 是

不带符号位的右移)

Java 面试题(二)

第 326 页 共 485 页
下 面 列 出 这 份 Java 面 试 问 题 列 表 包 含 的 主 题

 多线程,并发及线程基础
 数据类型转换的基本原则
 垃圾回收(GC)
 Java 集合框架
 数组
 字符串
 GOF 设计模式
 SOLID
 抽象类与接口
 Java 基础,如 equals 和 hashcode
 泛型与枚举
 Java IO 与 NIO
 常用网络协议
 Java 中的数据结构和算法
 正则表达式
 JVM 底层
 Java 最佳实践
 JDBC
 Date, Time 与 Calendar
 Java 处理 XML
 JUnit
 编程

现 在 是 时 候 给 你 展 示 我 近 5 年 从 各 种 面 试 中 收 集 来 的 133 个 问 题 了 。我 确 定 你

在自己的面试中见过很多这些问题,很多问题你也能正确回答。

第 327 页 共 485 页
多线程、并发及线程的基础问题

1、Java 中能创建 volatile 数组吗?

能 , Java 中 可 以 创 建 volatile 类 型 数 组 , 不 过 只 是 一 个 指 向 数 组 的 引 用 , 而 不

是 整 个 数 组 。我 的 意 思 是 ,如 果 改 变 引 用 指 向 的 数 组 ,将 会 受 到 volatile 的 保 护 ,

但 是 如 果 多 个 线 程 同 时 改 变 数 组 的 元 素 , volatile 标 示 符 就 不 能 起 到 之 前 的 保 护

作用了。

2、volatile 能使得一个非原子操作变成原子操作吗?

一 个 典 型 的 例 子 是 在 类 中 有 一 个 long 类 型 的 成 员 变 量 。 如 果 你 知 道 该 成 员 变 量

会 被 多 个 线 程 访 问 ,如 计 数 器 、价 格 等 ,你 最 好 是 将 其 设 置 为 volatile。为 什 么 ?

因 为 Java 中 读 取 long 类 型 变 量 不 是 原 子 的 , 需 要 分 成 两 步 , 如 果 一 个 线 程 正

在 修 改 该 long 变 量 的 值 , 另 一 个 线 程 可 能 只 能 看 到 该 值 的 一 半 ( 前 32 位 ) 。

但 是 对 一 个 volatile 型 的 long 或 double 变 量 的 读 写 是 原 子 。

3、volatile 修饰符的有过什么实践?

一 种 实 践 是 用 volatile 修 饰 long 和 double 变 量 ,使 其 能 按 原 子 类 型 来 读 写 。

double 和 long 都 是 64 位 宽 ,因 此 对 这 两 种 类 型 的 读 是 分 为 两 部 分 的 ,第 一 次

读 取 第 一 个 32 位 ,然 后 再 读 剩 下 的 32 位 ,这 个 过 程 不 是 原 子 的 ,但 Java 中

volatile 型 的 long 或 double 变 量 的 读 写 是 原 子 的 。volatile 修 复 符 的 另 一 个

作 用 是 提 供 内 存 屏 障 ( memory barrier) , 例 如 在 分 布 式 框 架 中 的 应 用 。 简 单 的

说 ,就 是 当 你 写 一 个 volatile 变 量 之 前 ,Java 内 存 模 型 会 插 入 一 个 写 屏 障( write

barrier) , 读 一 个 volatile 变 量 之 前 , 会 插 入 一 个 读 屏 障 ( read barrier) 。 意

思 就 是 说 ,在 你 写 一 个 volatile 域 时 ,能 保 证 任 何 线 程 都 能 看 到 你 写 的 值 ,同 时 ,

第 328 页 共 485 页
在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其

他所有写的值更新到缓存。

4、volatile 类型变量提供什么保证?

volatile 变 量 提 供 顺 序 和 可 见 性 保 证 ,例 如 ,JVM 或 者 JIT 为 了 获 得 更 好 的 性 能

会 对 语 句 重 排 序 ,但 是 volatile 类 型 变 量 即 使 在 没 有 同 步 块 的 情 况 下 赋 值 也 不 会

与 其 他 语 句 重 排 序 。 volatile 提 供 happens-before 的 保 证 , 确 保 一 个 线 程 的

修 改 能 对 其 他 线 程 是 可 见 的 。某 些 情 况 下 ,volatile 还 能 提 供 原 子 性 ,如 读 64 位

数 据 类 型 ,像 long 和 double 都 不 是 原 子 的 ,但 volatile 类 型 的 double 和

long 就 是 原 子 的 。

5、10 个线程和 2 个线程的同步代码,哪个更容易写?

从写代码的角度来说,两者的复杂度是相同的,因为同步代码与线程数量是相互

独立的。但是同步策略的选择依赖于线程的数量,因为越多的线程意味着更大的

竞争,所以你需要利用同步技术,如锁分离,这要求更复杂的代码和专业知识。

6、你是如何调用 wait()方法的?使用 if 块还是循环?为什

么?

wait() 方 法 应 该 在 循 环 调 用 , 因 为 当 线 程 获 取 到 CPU 开 始 执 行 的 时 候 , 其 他 条

件可能还没有满足,所以在处理前,循环检测条件是否满足会更好。下面是一段

标 准 的 使 用 wait 和 notify 方 法 的 代 码 :

// The standard idiom for using the wait method


synchronized (obj) {

第 329 页 共 485 页
while (condition does not hold)
obj.wait(); // (Releases lock, and reacquires on wakeup)
... // Perform action appropriate to condition
}

参 见 [Effective Java]第 69 条 , 获 取 更 多 关 于 为 什 么 应 该 在 循 环 中 来 调 用 wait

方法的内容。

###7、 什 么 是 多 线 程 环 境 下 的 伪 共 享 ( false sharing) ?

伪共享是多线程系统(每个处理器有自己的局部缓存)中一个众所周知的性能问

题。伪共享发生在不同处理器的上的线程对变量的修改依赖于相同的缓存行,如

下图所示:

有经验程序员的 Java 面试题

伪共享问题很难被发现,因为线程可能访问完全不同的全局变量,内存中却碰巧

在很相近的位置上。如其他诸多的并发问题,避免伪共享的最基本方式是仔细审

查代码,根据缓存行来调整你的数据结构。

8、什么是 Busy spin?我们为什么要使用它?

Busy spin 是 一 种 在 不 释 放 CPU 的 基 础 上 等 待 事 件 的 技 术 。 它 经 常 用 于 避 免 丢

失 CPU 缓 存 中 的 数 据 ( 如 果 线 程 先 暂 停 , 之 后 在 其 他 CPU 上 运 行 就 会 丢 失 ) 。

所以,如果你的工作要求低延迟,并且你的线程目前没有任何顺序,这样你就可

以 通 过 循 环 检 测 队 列 中 的 新 消 息 来 代 替 调 用 sleep() 或 wait() 方 法 。 它 唯 一 的

好 处 就 是 你 只 需 等 待 很 短 的 时 间 , 如 几 微 秒 或 几 纳 秒 。 LMAX 分 布 式 框 架 是 一 个

高 性 能 线 程 间 通 信 的 库 ,该 库 有 一 个 BusySpinWaitStrategy 类 就 是 基 于 这 个 概

念 实 现 的 , 使 用 busy spin 循 环 EventProcessors 等 待 屏 障 。

第 330 页 共 485 页
9、Java 中怎么获取一份线程 dump 文件?

在 Linux 下 ,你 可 以 通 过 命 令 kill -3 PID ( Java 进 程 的 进 程 ID)来 获 取 Java

应 用 的 dump 文 件 。 在 Windows 下 , 你 可 以 按 下 Ctrl + Break 来 获 取 。 这

样 JVM 就 会 将 线 程 的 dump 文 件 打 印 到 标 准 输 出 或 错 误 文 件 中 ,它 可 能 打 印 在

控 制 台 或 者 日 志 文 件 中 , 具 体 位 置 依 赖 应 用 的 配 置 。 如 果 你 使 用 Tomcat。

10、Swing 是线程安全的?

不 是 , Swing 不 是 线 程 安 全 的 。 你 不 能 通 过 任 何 线 程 来 更 新 Swing 组 件 , 如

JTable、 JList 或 JPanel, 事 实 上 , 它 们 只 能 通 过 GUI 或 AWT 线 程 来 更 新 。

这 就 是 为 什 么 Swing 提 供 invokeAndWait() 和 invokeLater() 方 法 来 获 取 其

他 线 程 的 GUI 更 新 请 求 。 这 些 方 法 将 更 新 请 求 放 入 AWT 的 线 程 队 列 中 , 可 以

一直等待,也可以通过异步更新直接返回结果。你也可以在参考答案中查看和学

习到更详细的内容。

11、什么是线程局部变量?

线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共

享 。 Java 提 供 ThreadLocal 类 来 支 持 线 程 局 部 变 量 , 是 一 种 实 现 线 程 安 全 的 方

式 。但 是 在 管 理 环 境 下 ( 如 web 服 务 器 )使 用 线 程 局 部 变 量 的 时 候 要 特 别 小 心 ,

在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线

程 局 部 变 量 一 旦 在 工 作 完 成 后 没 有 释 放 , Java 应 用 就 存 在 内 存 泄 露 的 风 险 。

12、用 wait-notify 写一段代码来解决生产者-消费者问题?

答案

第 331 页 共 485 页
http://java67.blogspot.sg/2012/12/producer-consumer-problem-with-wai

t-and-notify-example.html

请 参 考 答 案 中 的 示 例 代 码 。只 要 记 住 在 同 步 块 中 调 用 wait() 和 notify()方 法 ,如

果阻塞,通过循环来测试等待条件。

13、用 Java 写一个线程安全的单例模式(Singleton)?

答案

http://javarevisited.blogspot.in/2012/12/how-to-create-thread-safe-singl

eton-in-java-example.html

请 参 考 答 案 中 的 示 例 代 码 , 这 里 面 一 步 一 步 教 你 创 建 一 个 线 程 安 全 的 Java 单 例

类。当我们说线程安全时,意思是即使初始化是在多线程环境中,仍然能保证单

个 实 例 。Java 中 ,使 用 枚 举 作 为 单 例 类 是 最 简 单 的 方 式 来 创 建 线 程 安 全 单 例 模 式

的方式。

14、Java 中 sleep 方法和 wait 方法的区别?

虽 然 两 者 都 是 用 来 暂 停 当 前 运 行 的 线 程 , 但 是 sleep() 实 际 上 只 是 短 暂 停 顿 , 因

为 它 不 会 释 放 锁 ,而 wait() 意 味 着 条 件 等 待 ,这 就 是 为 什 么 该 方 法 要 释 放 锁 ,因

为只有这样,其他等待的线程才能在满足条件时获取到该锁。

15、什么是不可变对象(immutable object)?Java 中怎么

创建一个不可变对象?

第 332 页 共 485 页
不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的

对 象 ,如 String、Integer 及 其 它 包 装 类 。详 情 参 见 答 案 ,一 步 一 步 指 导 你 在 Java

中创建一个不可变的类。

16、我们能创建一个包含可变对象的不可变对象吗?

是的,我们是可以创建一个包含可变对象的不可变对象的,你只需要谨慎一点,

不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。

最常见的例子就是对象中包含一个日期对象的引用。

数据类型和 Java 基础面试问题

17、Java 中应该使用什么数据类型来代表价格?

如 果 不 是 特 别 关 心 内 存 和 性 能 的 话 , 使 用 BigDecimal, 否 则 使 用 预 定 义 精 度 的

double 类 型 。

18、怎么将 byte 转换为 String?

可 以 使 用 String 接 收 byte[] 参 数 的 构 造 器 来 进 行 转 换 ,需 要 注 意 的 点 是 要 使 用

的正确的编码,否则会使用平台默认编码,这个编码可能跟原来的编码相同,也

可能不同。

19、Java 中怎样将 bytes 转换为 long 类型?

第 333 页 共 485 页
这 个 问 题 你 来 回 答 :-)

20、我们能将 int 强制转换为 byte 类型的变量吗?如果该值

大于 byte 类型的范围,将会出现什么现象?

是 的 ,我 们 可 以 做 强 制 转 换 ,但 是 Java 中 int 是 32 位 的 ,而 byte 是 8 位

的 ,所 以 ,如 果 强 制 转 化 是 ,int 类 型 的 高 24 位 将 会 被 丢 弃 ,byte 类 型 的 范 围

是 从 -128 到 128。

21、存在两个类,B 继承 A,C 继承 B,我们能将 B 转换为

C 么?如 C = (C) B;

答案

http://javarevisited.blogspot.sg/2012/12/what-is-type-casting-in-java-cla

ss-interface-example.html

22、哪个类包含 clone 方法?是 Cloneable 还是 Object?

java.lang.Cloneable 是 一 个 标 示 性 接 口 , 不 包 含 任 何 方 法 , clone 方 法 在

object 类 中 定 义 。并 且 需 要 知 道 clone() 方 法 是 一 个 本 地 方 法 ,这 意 味 着 它 是 由

c 或 c++ 或 其 他 本 地 语 言 实 现 的 。

23、Java 中 ++ 操作符是线程安全的吗?

第 334 页 共 485 页
答案:不是线程安全的操作。它涉及到多个指令,如读取变量值,增加,然后存

储回内存,这个过程可能会出现多个线程交差。

23、不是线程安全的操作。它涉及到多个指令,如读取变量值,

增加,然后存储回内存,这个过程可能会出现多个线程交差。

24、a = a + b 与 a += b 的区别

+= 隐 式 的 将 加 操 作 的 结 果 类 型 强 制 转 换 为 持 有 结 果 的 类 型 。 如 果 两 这 个 整 型 相

加 ,如 byte、short 或 者 int,首 先 会 将 它 们 提 升 到 int 类 型 ,然 后 在 执 行 加 法

操 作 。如 果 加 法 操 作 的 结 果 比 a 的 最 大 值 要 大 ,则 a+b 会 出 现 编 译 错 误 ,但 是

a += b 没 问 题 , 如 下 :

byte a = 127;
byte b = 127;
b = a + b; // error : cannot convert from int to byte
b += a; // ok

( 译 者 注 : 这 个 地 方 应 该 表 述 的 有 误 , 其 实 无 论 a+b 的 值 为 多 少 , 编 译 器 都 会

报 错 ,因 为 a+b 操 作 会 将 a、b 提 升 为 int 类 型 ,所 以 将 int 类 型 赋 值 给 byte

就会编译出错)

25、我能在不进行强制转换的情况下将一个 double 值赋值给

long 类型的变量吗?

第 335 页 共 485 页
不 行 ,你 不 能 在 没 有 强 制 类 型 转 换 的 前 提 下 将 一 个 double 值 赋 值 给 long 类 型

的 变 量 ,因 为 double 类 型 的 范 围 比 long 类 型 更 广 ,所 以 必 须 要 进 行 强 制 转 换 。

26、3*0.1 == 0.3 将会返回什么?true 还是 false?

false, 因 为 有 些 浮 点 数 不 能 完 全 精 确 的 表 示 出 来 。

27、int 和 Integer 哪个会占用更多的内存?

Integer 对 象 会 占 用 更 多 的 内 存 。 Integer 是 一 个 对 象 , 需 要 存 储 对 象 的 元 数 据 。

但 是 int 是 一 个 原 始 类 型 的 数 据 , 所 以 占 用 的 空 间 更 少 。

28、为什么 Java 中的 String 是不可变的(Immutable)?

Java 中 的 String 不 可 变 是 因 为 Java 的 设 计 者 认 为 字 符 串 使 用 非 常 频 繁 ,将 字

符串设置为不可变可以允许多个客户端之间共享相同的字符串。

29、 我 们 能 在 Switch 中 使 用 String 吗 ?

从 Java 7 开 始 , 我 们 可 以 在 switch case 中 使 用 字 符 串 , 但 这 仅 仅 是 一 个 语 法

糖 。 内 部 实 现 在 switch 中 使 用 字 符 串 的 hash code。

30、 Java 中 的 构 造 器 链 是 什 么 ?

当 你 从 一 个 构 造 器 中 调 用 另 一 个 构 造 器 ,就 是 Java 中 的 构 造 器 链 。这 种 情 况 只 在

重载了类的构造器的时候才会出现。

JVM 底层 与 GC(Garbage Collection) 的面试问题

第 336 页 共 485 页
31、64 位 JVM 中,int 的长度是多数?

Java 中 ,int 类 型 变 量 的 长 度 是 一 个 固 定 值 ,与 平 台 无 关 ,都 是 32 位 。意 思 就

是 说 , 在 32 位 和 64 位 的 Java 虚 拟 机 中 , int 类 型 的 长 度 是 相 同 的 。

32、Serial 与 Parallel GC 之间的不同之处?

Serial 与 Parallel 在 GC 执 行 的 时 候 都 会 引 起 stop-the-world。 它 们 之 间 主 要

不 同 serial 收 集 器 是 默 认 的 复 制 收 集 器 , 执 行 GC 的 时 候 只 有 一 个 线 程 , 而

parallel 收 集 器 使 用 多 个 GC 线 程 来 执 行 。

33、32 位和 64 位的 JVM,int 类型变量的长度是多数?

32 位 和 64 位 的 JVM 中 , int 类 型 变 量 的 长 度 是 相 同 的 , 都 是 32 位 或 者 4

个字节。

34、Java 中 WeakReference 与 SoftReference 的区别?

虽 然 WeakReference 与 SoftReference 都 有 利 于 提 高 GC 和 内 存 的 效 率 ,

但 是 WeakReference ,一 旦 失 去 最 后 一 个 强 引 用 ,就 会 被 GC 回 收 ,而 软 引 用

虽 然 不 能 阻 止 被 回 收 , 但 是 可 以 延 迟 到 JVM 内 存 不 足 的 时 候 。

35、WeakHashMap 是怎么工作的?

第 337 页 共 485 页
WeakHashMap 的 工 作 与 正 常 的 HashMap 类 似 , 但 是 使 用 弱 引 用 作 为 key,

意 思 就 是 当 key 对 象 没 有 任 何 引 用 时 , key/value 将 会 被 回 收 。

36、JVM 选项 -XX:+UseCompressedOops 有什么作用?

为什么要使用?

当 你 将 你 的 应 用 从 32 位 的 JVM 迁 移 到 64 位 的 JVM 时 ,由 于 对 象 的 指 针 从

32 位 增 加 到 了 64 位 , 因 此 堆 内 存 会 突 然 增 加 , 差 不 多 要 翻 倍 。 这 也 会 对 CPU

缓 存( 容 量 比 内 存 小 很 多 )的 数 据 产 生 不 利 的 影 响 。因 为 ,迁 移 到 64 位 的 JVM

主 要 动 机 在 于 可 以 指 定 最 大 堆 大 小 , 通 过 压 缩 OOP 可 以 节 省 一 定 的 内 存 。 通 过

-XX:+UseCompressedOops 选 项 ,JVM 会 使 用 32 位 的 OOP,而 不 是 64 位

的 OOP。

37、怎样通过 Java 程序来判断 JVM 是 32 位 还是 64

位?

你 可 以 检 查 某 些 系 统 属 性 如 sun.arch.data.model 或 os.arch 来 获 取 该 信 息 。

38、32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?

理 论 上 说 上 32 位 的 JVM 堆 内 存 可 以 到 达 2^32,即 4GB,但 实 际 上 会 比 这 个

小 很 多 。不 同 操 作 系 统 之 间 不 同 ,如 Windows 系 统 大 约 1.5 GB,Solaris 大 约

3GB。 64 位 JVM 允 许 指 定 最 大 的 堆 内 存 , 理 论 上 可 以 达 到 2^64, 这 是 一 个 非

常 大 的 数 字 ,实 际 上 你 可 以 指 定 堆 内 存 大 小 到 100GB。甚 至 有 的 JVM,如 Azul,

堆 内 存 到 1000G 都 是 可 能 的 。

第 338 页 共 485 页
39、JRE、JDK、JVM 及 JIT 之间有什么不同?

JRE 代 表 Java 运 行 时( Java run-time),是 运 行 Java 引 用 所 必 须 的 。JDK 代

表 Java 开 发 工 具( Java development kit),是 Java 程 序 的 开 发 工 具 ,如 Java

编 译 器 , 它 也 包 含 JRE。 JVM 代 表 Java 虚 拟 机 ( Java virtual machine) , 它

的 责 任 是 运 行 Java 应 用 。 JIT 代 表 即 时 编 译 ( Just In Time compilation) , 当

代 码 执 行 的 次 数 超 过 一 定 的 阈 值 时 , 会 将 Java 字 节 码 转 换 为 本 地 代 码 , 如 , 主

要 的 热 点 代 码 会 被 准 换 为 本 地 代 码 , 这 样 有 利 大 幅 度 提 高 Java 应 用 的 性 能 。

3 年工作经验的 Java 面试题

40、解释 Java 堆空间及 GC?

当 通 过 Java 命 令 启 动 Java 进 程 的 时 候 , 会 为 它 分 配 内 存 。 内 存 的 一 部 分 用 于

创 建 堆 空 间 ,当 程 序 中 创 建 对 象 的 时 候 ,就 从 对 空 间 中 分 配 内 存 。GC 是 JVM 内

部的一个进程,回收无效对象的内存用于将来的分配。

JVM 底层面试题及答案

41、你能保证 GC 执行吗?

不 能 ,虽 然 你 可 以 调 用 System.gc() 或 者 Runtime.gc(),但 是 没 有 办 法 保 证 GC

的执行。

42、怎么获取 Java 程序使用的内存?堆使用的百分比?

第 339 页 共 485 页
可 以 通 过 java.lang.Runtime 类 中 与 内 存 相 关 方 法 来 获 取 剩 余 的 内 存 ,总 内 存 及

最 大 堆 内 存 。通 过 这 些 方 法 你 也 可 以 获 取 到 堆 使 用 的 百 分 比 及 堆 内 存 的 剩 余 空 间 。

Runtime.freeMemory() 方 法 返 回 剩 余 空 间 的 字 节 数 , Runtime.totalMemory()

方 法 总 内 存 的 字 节 数 , Runtime.maxMemory() 返 回 最 大 内 存 的 字 节 数 。

43、Java 中堆和栈有什么区别?

JVM 中 堆 和 栈 属 于 不 同 的 内 存 区 域 ,使 用 目 的 也 不 同 。栈 常 用 于 保 存 方 法 帧 和 局

部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,

而 堆 被 整 个 JVM 的 所 有 线 程 共 享 。

关于内存的的面试问题和答案

Java 基本概念面试题

44、“a==b”和”a.equals(b)”有什么区别?

如 果 a 和 b 都 是 对 象 , 则 a==b 是 比 较 两 个 对 象 的 引 用 , 只 有 当 a 和 b 指

向 的 是 堆 中 的 同 一 个 对 象 才 会 返 回 true,而 a.equals(b) 是 进 行 逻 辑 比 较 ,所 以

通 常 需 要 重 写 该 方 法 来 提 供 逻 辑 一 致 性 的 比 较 。例 如 ,String 类 重 写 equals() 方

法,所以可以用于两个不同对象,但是包含的字母相同的比较。

45、a.hashCode() 有什么用?与 a.equals(b) 有什么关系?

第 340 页 共 485 页
hashCode() 方 法 是 相 应 对 象 整 型 的 hash 值 。 它 常 用 于 基 于 hash 的 集 合 类 ,

如 Hashtable、 HashMap、LinkedHashMap 等 等 。 它 与 equals() 方 法 关 系 特

别 紧 密 。根 据 Java 规 范 ,两 个 使 用 equal() 方 法 来 判 断 相 等 的 对 象 ,必 须 具 有

相 同 的 hash code。

46、final、finalize 和 finally 的不同之处?

final 是 一 个 修 饰 符 ,可 以 修 饰 变 量 、方 法 和 类 。如 果 final 修 饰 变 量 ,意 味 着 该

变 量 的 值 在 初 始 化 后 不 能 被 改 变 。finalize 方 法 是 在 对 象 被 回 收 之 前 调 用 的 方 法 ,

给 对 象 自 己 最 后 一 个 复 活 的 机 会 , 但 是 什 么 时 候 调 用 finalize 没 有 保 证 。 finally

是 一 个 关 键 字 ,与 try 和 catch 一 起 用 于 异 常 的 处 理 。finally 块 一 定 会 被 执 行 ,

无 论 在 try 块 中 是 否 有 发 生 异 常 。

47、Java 中的编译期常量是什么?使用它又什么风险?

公 共 静 态 不 可 变( public static final )变 量 也 就 是 我 们 所 说 的 编 译 期 常 量 ,这 里

的 public 可 选 的 。 实 际 上 这 些 变 量 在 编 译 时 会 被 替 换 掉 , 因 为 编 译 器 知 道 这 些

变量的值,并且知道这些变量在运行时不能改变。这种方式存在的一个问题是你

使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改

变 了 , 但 是 你 的 客 户 端 仍 然 在 使 用 老 的 值 , 甚 至 你 已 经 部 署 了 一 个 新 的 jar。 为 了

避 免 这 种 情 况 , 当 你 在 更 新 依 赖 JAR 文 件 时 , 确 保 重 新 编 译 你 的 程 序 。

Java 集合框架的面试题

这部分也包含数据结构、算法及数组的面试问题

48、List、Set、Map 和 Queue 之间的区别(答案)

第 341 页 共 485 页
List 是 一 个 有 序 集 合 , 允 许 元 素 重 复 。 它 的 某 些 实 现 可 以 提 供 基 于 下 标 值 的 常 量

访 问 时 间 , 但 是 这 不 是 List 接 口 保 证 的 。 Set 是 一 个 无 序 集 合 。

49、poll() 方法和 remove() 方法的区别?

poll() 和 remove() 都 是 从 队 列 中 取 出 一 个 元 素 , 但 是 poll() 在 获 取 元 素 失 败

的 时 候 会 返 回 空 , 但 是 remove() 失 败 的 时 候 会 抛 出 异 常 。

50、Java 中 LinkedHashMap 和 PriorityQueue 的区别是

什么?

PriorityQueue 保 证 最 高 或 者 最 低 优 先 级 的 的 元 素 总 是 在 队 列 头 部 , 但 是

LinkedHashMap 维 持 的 顺 序 是 元 素 插 入 的 顺 序 。 当 遍 历 一 个 PriorityQueue

时 ,没 有 任 何 顺 序 保 证 ,但 是 LinkedHashMap 课 保 证 遍 历 顺 序 是 元 素 插 入 的 顺

序。

51、ArrayList 与 LinkedList 的不区别?

最 明 显 的 区 别 是 ArrrayList 底 层 的 数 据 结 构 是 数 组 , 支 持 随 机 访 问 , 而

LinkedList 的 底 层 数 据 结 构 书 链 表 , 不 支 持 随 机 访 问 。 使 用 下 标 访 问 一 个 元 素 ,

ArrayList 的 时 间 复 杂 度 是 O(1),而 LinkedList 是 O(n)。更 多 细 节 的 讨 论 参 见

答案。

52、用哪两种方式来实现集合的排序?

第 342 页 共 485 页
你 可 以 使 用 有 序 集 合 ,如 TreeSet 或 TreeMap,你 也 可 以 使 用 有 顺 序 的 的 集 合 ,

如 list, 然 后 通 过 Collections.sort() 来 排 序 。

53、Java 中怎么打印数组?

你 可 以 使 用 Arrays.toString() 和 Arrays.deepToString() 方 法 来 打 印 数 组 。 由

于 数 组 没 有 实 现 toString() 方 法 ,所 以 如 果 将 数 组 传 递 给 System.out.println()

方 法 , 将 无 法 打 印 出 数 组 的 内 容 , 但 是 Arrays.toString() 可 以 打 印 每 个 元 素 。

54、Java 中的 LinkedList 是单向链表还是双向链表?

是 双 向 链 表 ,你 可 以 检 查 JDK 的 源 码 。在 Eclipse,你 可 以 使 用 快 捷 键 Ctrl + T,

直接在编辑器中打开该类。

55、Java 中的 TreeMap 是采用什么树实现的?(答案)

Java 中 的 TreeMap 是 使 用 红 黑 树 实 现 的 。

56、Hashtable 与 HashMap 有什么不同之处?

这两个类有许多不同的地方,下面列出了一部分:

a) Hashtable 是 JDK 1 遗 留 下 来 的 类 , 而 HashMap 是 后 来 增 加 的 。

b) Hashtable 是 同 步 的 , 比 较 慢 , 但 HashMap 没 有 同 步 策 略 , 所 以 会 更 快 。

c) Hashtable 不 允 许 有 个 空 的 key, 但 是 HashMap 允 许 出 现 一 个 null key。

更多的不同之处参见答案。

第 343 页 共 485 页
57、Java 中的 HashSet,内部是如何工作的?

HashSet 的 内 部 采 用 HashMap 来 实 现 。由 于 Map 需 要 key 和 value,所 以

所 有 key 的 都 有 一 个 默 认 value。 类 似 于 HashMap, HashSet 不 允 许 重 复 的

key,只 允 许 有 一 个 null key,意 思 就 是 HashSet 中 只 允 许 存 储 一 个 null 对 象 。

58、写一段代码在遍历 ArrayList 时移除一个元素?

该 问 题 的 关 键 在 于 面 试 者 使 用 的 是 ArrayList 的 remove() 还 是 Iterator 的

remove()方 法 。 这 有 一 段 示 例 代 码 , 是 使 用 正 确 的 方 式 来 实 现 在 遍 历 的 过 程 中 移

除 元 素 , 而 不 会 出 现 ConcurrentModificationException 异 常 的 示 例 代 码 。

59、我们能自己写一个容器类,然后使用 for-each 循环码?

可 以 , 你 可 以 写 一 个 自 己 的 容 器 类 。 如 果 你 想 使 用 Java 中 增 强 的 循 环 来 遍 历 ,

你 只 需 要 实 现 Iterable 接 口 。如 果 你 实 现 Collection 接 口 ,默 认 就 具 有 该 属 性 。

60、ArrayList 和 HashMap 的默认大小是多数?

在 Java 7 中 , ArrayList 的 默 认 大 小 是 10 个 元 素 , HashMap 的 默 认 大 小 是

16 个 元 素 ( 必 须 是 2 的 幂 ) 。 这 就 是 Java 7 中 ArrayList 和 HashMap 类 的

代码片段:

// from ArrayList.java JDK 1.7


private static final int DEFAULT_CAPACITY = 10;

//from HashMap.java JDK 7

第 344 页 共 485 页
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

61、有没有可能两个不相等的对象有有相同的 hashcode?

有 可 能 , 两 个 不 相 等 的 对 象 可 能 会 有 相 同 的 hashcode 值 , 这 就 是 为 什 么 在

hashmap 中 会 有 冲 突 。 相 等 hashcode 值 的 规 定 只 是 说 如 果 两 个 对 象 相 等 , 必

须 有 相 同 的 hashcode 值 , 但 是 没 有 关 于 不 相 等 对 象 的 任 何 规 定 。

62、两个相同的对象会有不同的的 hash code 吗?

不 能 , 根 据 hash code 的 规 定 , 这 是 不 可 能 的 。

63、我们可以在 hashcode() 中使用随机数字吗?

答案

http://javarevisited.blogspot.sg/2011/10/override-hashcode-in-java-exam

ple.html

不 行 ,因 为 对 象 的 hashcode 值 必 须 是 相 同 的 。参 见 答 案 获 取 更 多 关 于 Java 中

重 写 hashCode() 方 法 的 知 识 。

64、Java 中,Comparator 与 Comparable 有什么不同?

Comparable 接 口 用 于 定 义 对 象 的 自 然 顺 序 ,而 comparator 通 常 用 于 定 义 用 户

定 制 的 顺 序 。Comparable 总 是 只 有 一 个 ,但 是 可 以 有 多 个 comparator 来 定 义

对象的顺序。

第 345 页 共 485 页
65) 为 什 么 在 重 写 equals 方 法 的 时 候 需 要 重 写 hashCode 方 法 ? (答 案 )

因 为 有 强 制 的 规 范 指 定 需 要 同 时 重 写 hashcode 与 equal 是 方 法 ,许 多 容 器 类 ,

如 HashMap、 HashSet 都 依 赖 于 hashcode 与 equals 的 规 定 。

Java IO 和 NIO 的面试题

IO 是 Java 面 试 中 一 个 非 常 重 要 的 点 。你 应 该 很 好 掌 握 Java IO,NIO,NIO2 以

及 与 操 作 系 统 , 磁 盘 IO 相 关 的 基 础 知 识 。 下 面 是 Java IO 中 经 常 问 的 问 题 。

66、在我 Java 程序中,我有三个 socket,我需要多少个线

程来处理?

67、Java 中怎么创建 ByteBuffer?

byte[] bytes = new byte[10];

ByteBuffer buf = ByteBuffer.wrap(bytes);

68、Java 中,怎么读写 ByteBuffer ?

69、Java 采用的是大端还是小端?

70、ByteBuffer 中的字节序是什么?

第 346 页 共 485 页
71、Java 中,直接缓冲区与非直接缓冲器有什么区别?

答案

http://javarevisited.blogspot.sg/2015/08/difference-between-direct-non-

direct-mapped-bytebuffer-nio-java.html

72、Java 中的内存映射缓存区是什么?

答案

http://javarevisited.blogspot.sg/2012/01/memorymapped-file-and-io-in-j

ava.html

73、socket 选项 TCP NO DELAY 是指什么?

74、TCP 协议与 UDP 协议有什么区别?

答案

http://javarevisited.blogspot.com/2014/07/9-difference-between-tcp-and

-udp-protocol.html

75、Java 中,ByteBuffer 与 StringBuffer 有什么区别?(答

案)

第 347 页 共 485 页
Java 最佳实践的面试问题

包 含 Java 中 各 个 部 分 的 最 佳 实 践 , 如 集 合 , 字 符 串 , IO, 多 线 程 , 错 误 和 异 常

处理,设计模式等等。

76、Java 中,编写多线程程序的时候你会遵循哪些最佳实践?

这 是 我 在 写 Java 并 发 程 序 的 时 候 遵 循 的 一 些 最 佳 实 践 :

a) 给 线 程 命 名 , 这 样 可 以 帮 助 调 试 。

b) 最 小 化 同 步 的 范 围 , 而 不 是 将 整 个 方 法 同 步 , 只 对 关 键 部 分 做 同 步 。

c) 如 果 可 以 , 更 偏 向 于 使 用 volatile 而 不 是 synchronized。

d) 使 用 更 高 层 次 的 并 发 工 具 , 而 不 是 使 用 wait() 和 notify() 来 实 现 线 程 间 通

信 , 如 BlockingQueue, CountDownLatch 及 Semeaphore。

e) 优 先 使 用 并 发 集 合 , 而 不 是 对 集 合 进 行 同 步 。 并 发 集 合 提 供 更 好 的 可 扩 展 性 。

77、说出几点 Java 中使用 Collections 的最佳实践

这 是 我 在 使 用 Java 中 Collectionc 类 的 一 些 最 佳 实 践 :

a) 使 用 正 确 的 集 合 类 , 例 如 , 如 果 不 需 要 同 步 列 表 , 使 用 ArrayList 而 不 是

Vector。

b) 优 先 使 用 并 发 集 合 , 而 不 是 对 集 合 进 行 同 步 。 并 发 集 合 提 供 更 好 的 可 扩 展 性 。

c) 使 用 接 口 代 表 和 访 问 集 合 , 如 使 用 List 存 储 ArrayList, 使 用 Map 存 储

HashMap 等 等 。

d) 使 用 迭 代 器 来 循 环 集 合 。

e) 使 用 集 合 的 时 候 使 用 泛 型 。

78、说出至少 5 点在 Java 中使用线程的最佳实践。

第 348 页 共 485 页
答案

http://java67.blogspot.com/2014/01/10-points-about-thread-and-javalan

gthread-in-java.html

这个问题与之前的问题类似,你可以使用上面的答案。对线程来说,你应该:

a) 对 线 程 命 名

b) 将 线 程 和 任 务 分 离 , 使 用 线 程 池 执 行 器 来 执 行 Runnable 或 Callable。

c) 使 用 线 程 池

79、说出 5 条 IO 的最佳实践(答案)

IO 对 Java 应 用 的 性 能 非 常 重 要 。理 想 情 况 下 ,你 不 应 该 在 你 应 用 的 关 键 路 径 上

避 免 IO 操 作 。 下 面 是 一 些 你 应 该 遵 循 的 Java IO 最 佳 实 践 :

a) 使 用 有 缓 冲 区 的 IO 类 , 而 不 要 单 独 读 取 字 节 或 字 符 。

b) 使 用 NIO 和 NIO2

c) 在 finally 块 中 关 闭 流 , 或 者 使 用 try-with-resource 语 句 。

d) 使 用 内 存 映 射 文 件 获 取 更 快 的 IO。

80、列出 5 个应该遵循的 JDBC 最佳实践

答案

http://javarevisited.blogspot.sg/2012/08/top-10-jdbc-best-practices-for-j

ava.html))

有很多的最佳实践,你可以根据你的喜好来例举。下面是一些更通用的原则:

a) 使 用 批 量 的 操 作 来 插 入 和 更 新 数 据

第 349 页 共 485 页
b) 使 用 PreparedStatement 来 避 免 SQL 异 常 , 并 提 高 性 能 。

c) 使 用 数 据 库 连 接 池

d) 通 过 列 名 来 获 取 结 果 集 , 不 要 使 用 列 的 下 标 来 获 取 。

81、说出几条 Java 中方法重载的最佳实践?

下面有几条可以遵循的方法重载的最佳实践来避免造成自动装箱的混乱。

a) 不 要 重 载 这 样 的 方 法 : 一 个 方 法 接 收 int 参 数 , 而 另 个 方 法 接 收 Integer 参

数。

b) 不 要 重 载 参 数 数 量 一 致 , 而 只 是 参 数 顺 序 不 同 的 方 法 。

c) 如 果 重 载 的 方 法 参 数 个 数 多 于 5 个 , 采 用 可 变 参 数 。

Date、Time 及 Calendar 的面试题

82、在多线程环境下,SimpleDateFormat 是线程安全的吗?

不 是 , 非 常 不 幸 , DateFormat 的 所 有 实 现 , 包 括 SimpleDateFormat 都 不 是

线程安全的,因此你不应该在多线程序中使用,除非是在对外线程安全的环境中

使 用 , 如 将 SimpleDateFormat 限 制 在 ThreadLocal 中 。 如 果 你 不 这 么 做 ,

在解析或者格式化日期的时候,可能会获取到一个不正确的结果。因此,从日期、

时 间 处 理 的 所 有 实 践 来 说 , 我 强 力 推 荐 joda-time 库 。

83、Java 中如何格式化一个日期?如格式化为 ddMMyyyy

的形式?

答案

第 350 页 共 485 页
http://javarevisited.blogspot.com/2011/09/convert-date-to-string-simple

dateformat.html

Java 中 , 可 以 使 用 SimpleDateFormat 类 或 者 joda-time 库 来 格 式 日 期 。

DateFormat 类 允 许 你 使 用 多 种 流 行 的 格 式 来 格 式 化 日 期 。 参 见 答 案 中 的 示 例 代

码 ,代 码 中 演 示 了 将 日 期 格 式 化 成 不 同 的 格 式 ,如 dd-MM-yyyy 或 ddMMyyyy。

84、Java 中,怎么在格式化的日期中显示时区?

答案

http://java67.blogspot.sg/2013/01/how-to-format-date-in-java-simpledat

eformat-example.html

85、Java 中 java.util.Date 与 java.sql.Date 有什么区别?

答案

http://java67.blogspot.sg/2014/02/how-to-convert-javautildate-to-javasq

ldate-example.html

86、Java 中,如何计算两个日期之间的差距?

程序

http://javarevisited.blogspot.sg/2015/07/how-to-find-number-of-days-be

tween-two-dates-in-java.html

第 351 页 共 485 页
87、Java 中,如何将字符串 YYYYMMDD 转换为日期?

答案

http://java67.blogspot.sg/2014/12/string-to-date-example-in-java-multit

hreading.html

单元测试 JUnit 面试题

89、如何测试静态方法?(答案)

可 以 使 用 PowerMock 库 来 测 试 静 态 方 法 。

90、怎么利用 JUnit 来测试一个方法的异常?

答案

http://javarevisited.blogspot.sg/2013/04/JUnit-tutorial-example-test-exce

ption-thrown-by-java-method.html

91、你使用过哪个单元测试库来测试你的 Java 程序?

92、@Before 和 @BeforeClass 有什么区别?

答案

第 352 页 共 485 页
http://javarevisited.blogspot.sg/2013/04/JUnit-tutorial-example-test-exce

ption-thrown-by-java-method.html

编程和代码相关的面试题

93、怎么检查一个字符串只包含数字?解决方案

http://java67.blogspot.com/2014/01/java-regular-expression-to-check-nu

mbers-in-String.html

94、Java 中如何利用泛型写一个 LRU 缓存?

95、写一段 Java 程序将 byte 转换为 long?

95、在不使用 StringBuffer 的前提下,怎么反转一个字符串?

解决方案

http://java67.blogspot.com/2012/12/how-to-reverse-string-in-java-string

buffer-stringbuilder.htm

97、Java 中,怎么获取一个文件中单词出现的最高频率?

解决方案

第 353 页 共 485 页
http://java67.blogspot.com/2015/10/java-program-to-find-repeated-wor

ds-and-count.html

98、如何检查出两个给定的字符串是反序的?

解决方案

http://javarevisited.blogspot.sg/2013/03/Anagram-how-to-check-if-two-s

tring-are-anagrams-example-tutorial.html

99、Java 中,怎么打印出一个字符串的所有排列?

解决方案

http://javarevisited.blogspot.com/2015/08/how-to-find-all-permutations-

of-string-java-example.html

100、Java 中,怎样才能打印出数组中的重复元素?

解决方案

http://javarevisited.blogspot.com/2015/06/3-ways-to-find-duplicate-elem

ents-in-array-java.html

101、Java 中如何将字符串转换为整数?

第 354 页 共 485 页
String s="123";

int i;

第 一 种 方 法 : i=Integer.parseInt(s);

第 二 种 方 法 : i=Integer.valueOf(s).intValue();

102、在没有使用临时变量的情况如何交换两个整数变量的值?

解决方案

https://blog.csdn.net/zidane_2014/article/details/34180223

关于 OOP 和设计模式的面试题

这 部 分 包 含 Java 面 试 过 程 中 关 于 SOLID 的 设 计 原 则 ,OOP 基 础 ,如 类 ,对 象 ,

接口,继承,多态,封装,抽象以及更高级的一些概念,如组合、聚合及关联。

也 包 含 了 GOF 设 计 模 式 的 问 题 。

103、接口是什么?为什么要使用接口而不是直接使用具体类?

接 口 用 于 定 义 API。 它 定 义 了 类 必 须 得 遵 循 的 规 则 。 同 时 , 它 提 供 了 一 种 抽 象 ,

因 为 客 户 端 只 使 用 接 口 ,这 样 可 以 有 多 重 实 现 ,如 List 接 口 ,你 可 以 使 用 可 随 机

访 问 的 ArrayList, 也 可 以 使 用 方 便 插 入 和 删 除 的 LinkedList。 接 口 中 不 允 许 写

代 码 , 以 此 来 保 证 抽 象 , 但 是 Java 8 中 你 可 以 在 接 口 声 明 静 态 的 默 认 方 法 , 这

种方法是具体的。

104、Java 中,抽象类与接口之间有什么不同?

第 355 页 共 485 页
Java 中 ,抽 象 类 和 接 口 有 很 多 不 同 之 处 ,但 是 最 重 要 的 一 个 是 Java 中 限 制 一 个

类只能继承一个类,但是可以实现多个接口。抽象类可以很好的定义一个家族类

的默认行为,而接口能更好的定义类型,有助于后面实现多态机制。

105、除了单例模式,你在生产环境中还用过什么设计模式?

这需要根据你的经验来回答。一般情况下,你可以说依赖注入,工厂模式,装饰

模式或者观察者模式,随意选择你使用过的一种即可。不过你要准备回答接下的

基于你选择的模式的问题。

106、你能解释一下里氏替换原则吗?

答案

https://blog.csdn.net/pu_xubo565599455/article/details/51488323

107) 什么情况下会违反迪米特法则?为什么会有这个问题?

迪米特法则建议“只和朋友说话,不要陌生人说话”,以此来减少类之间的耦合。

108、适配器模式是什么?什么时候使用?

适配器模式提供对接口的转换。如果你的客户端使用某些接口,但是你有另外一

些接口,你就可以写一个适配去来连接这些接口。

109、什么是“依赖注入”和“控制反转”?为什么有人使用?

第 356 页 共 485 页
控 制 反 转 ( IOC) 是 Spring 框 架 的 核 心 思 想 , 用 我 自 己 的 话 说 , 就 是 你 要 做 一 件

事 , 别 自 己 可 劲 new 了 , 你 就 说 你 要 干 啥 , 然 后 外 包 出 去 就 好 ~

依 赖 注 入( DI) 在 我 浅 薄 的 想 法 中 ,就 是 通 过 接 口 的 引 用 和 构 造 方 法 的 表 达 ,将

一些事情整好了反过来传给需要用到的地方~

110、抽象类是什么?它与接口有什么区别?你为什么要使用过

抽象类?

接口用于规范,抽象类用于共性.

声明方法的存在而不去实现它的类被叫做抽象类

接 口 ( interface) 是 抽 象 类 的 变 体 。 在 接 口 中 , 所 有 方 法 都 是 抽 象 的 。

111、构造器注入和 setter 依赖注入,那种方式更好?

每种方式都有它的缺点和优点。构造器注入保证所有的注入都被初始化,但是

setter 注 入 提 供 更 好 的 灵 活 性 来 设 置 可 选 依 赖 。 如 果 使 用 XML 来 描 述 依 赖 ,

Setter 注 入 的 可 读 写 会 更 强 。 经 验 法 则 是 强 制 依 赖 使 用 构 造 器 注 入 , 可 选 依 赖 使

用 setter 注 入 。

112、依赖注入和工程模式之间有什么不同?

虽然两种模式都是将对象的创建从应用的逻辑中分离,但是依赖注入比工程模式

更 清 晰 。 通 过 依 赖 注 入 , 你 的 类 就 是 POJO, 它 只 知 道 依 赖 而 不 关 心 它 们 怎 么 获

取 。 使 用 工 厂 模 式 , 你 的 类 需 要 通 过 工 厂 来 获 取 依 赖 。 因 此 , 使 用 DI 会 比 使 用

工厂模式更容易测试。

第 357 页 共 485 页
113、适配器模式和装饰器模式有什么区别?

虽然适配器模式和装饰器模式的结构类似,但是每种模式的出现意图不同。适配

器模式被用于桥接两个接口,而装饰模式的目的是在不修改类的情况下给类增加

新的功能。

114、适配器模式和代理模式之前有什么不同?

这个问题与前面的类似,适配器模式和代理模式的区别在于他们的意图不同。由

于适配器模式和代理模式都是封装真正执行动作的类,因此结构是一致的,但是

适配器模式用于接口之间的转换,而代理模式则是增加一个额外的中间层,以便

支持分配、控制或智能访问。

115、什么是模板方法模式?

模板方法提供算法的框架,你可以自己去配置或定义步骤。例如,你可以将排序

算法看做是一个模板。它定义了排序的步骤,但是具体的比较,可以使用

Comparable 或 者 其 语 言 中 类 似 东 西 , 具 体 策 略 由 你 去 配 置 。 列 出 算 法 概 要 的 方

法就是众所周知的模板方法。

116、什么时候使用访问者模式?

访问者模式用于解决在类的继承层次上增加操作,但是不直接与之关联。这种模

式采用双派发的形式来增加中间层。

117、什么时候使用组合模式?

第 358 页 共 485 页
组合模式使用树结构来展示部分与整体继承关系。它允许客户端采用统一的形式

来对待单个对象和对象容器。当你想要展示对象这种部分与整体的继承关系时采

用组合模式。

118、继承和组合之间有什么不同?

虽然两种都可以实现代码复用,但是组合比继承共灵活,因为组合允许你在运行

时选择不同的实现。用组合实现的代码也比继承测试起来更加简单。

119、描述 Java 中的重载和重写?

重载和重写都允许你用相同的名称来实现不同的功能,但是重载是编译时活动,

而重写是运行时活动。你可以在同一个类中重载方法,但是只能在子类中重写方

法。重写必须要有继承。

120、Java 中,嵌套公共静态类与顶级类有什么不同?

类 的 内 部 可 以 有 多 个 嵌 套 公 共 静 态 类 , 但 是 一 个 Java 源 文 件 只 能 有 一 个 顶 级 公

共类,并且顶级公共类的名称与源文件名称必须一致。

121、 OOP 中的 组合、聚合和关联有什么区别?

如果两个对象彼此有关系,就说他们是彼此相关联的。组合和聚合是面向对象中

的两种形式的关联。组合是一种比聚合更强力的关联。组合中,一个对象是另一

个的拥有者,而聚合则是指一个对象使用另一个对象。如果对象 A 是由对象 B

第 359 页 共 485 页
组 合 的 ,则 A 不 存 在 的 话 ,B 一 定 不 存 在 ,但 是 如 果 A 对 象 聚 合 了 一 个 对 象 B,

则即使 A 不存在了,B 也可以单独存在。

122、给我一个符合开闭原则的设计模式的例子?

开闭原则要求你的代码对扩展开放,对修改关闭。这个意思就是说,如果你想增

加一个新的功能,你可以很容易的在不改变已测试过的代码的前提下增加新的代

码。有好几个设计模式是基于开闭原则的,如策略模式,如果你需要一个新的策

略,只需要实现接口,增加配置,不需要改变核心逻辑。一个正在工作的例子是

Collections.sort() 方 法 ,这 就 是 基 于 策 略 模 式 ,遵 循 开 闭 原 则 的 ,你 不 需 为 新 的

对 象 修 改 sort() 方 法 , 你 需 要 做 的 仅 仅 是 实 现 你 自 己 的 Comparator 接 口 。

123、抽象工厂模式和原型模式之间的区别?

抽象工厂模式:通常由工厂方法模式来实现。但一个工厂中往往含有多个工厂方

法生成一系列的产品。这个模式强调的是客户代码一次保证只使用一个系列的产

品。当要切换为另一个系列的产品,换一个工厂类即可。

原型模式:工厂方法的最大缺点就是,对应一个继承体系的产品类,要有一个同

样复杂的工厂类的继承体系。我们可以把工厂类中的工厂方法放到产品类自身之

中吗?如果这样的话,就可以将两个继承体系为一个。这也就是原型模式的思想,

原 型 模 式 中 的 工 厂 方 法 为 clone,它 会 返 回 一 个 拷 贝( 可 以 是 浅 拷 贝 ,也 可 以 是 深

拷 贝 , 由 设 计 者 决 定 ) 。 为 了 保 证 用 户 代 码 中 到 时 可 以 通 过 指 针 调 用 clone 来 动

态绑定地生成所需的具体的类。这些原型对象必须事先构造好。

原 型 模 式 想 对 工 厂 方 法 模 式 的 另 一 个 好 处 是 ,拷 贝 的 效 率 一 般 对 构 造 的 效 率 要 高 。

124、 什 么 时 候 使 用 享 元 模 式 ?

第 360 页 共 485 页
享元模式通过共享对象来避免创建太多的对象。为了使用享元模式,你需要确保

你 的 对 象 是 不 可 变 的 , 这 样 你 才 能 安 全 的 共 享 。 JDK 中 String 池 、 Integer 池

以 及 Long 池 都 是 很 好 的 使 用 了 享 元 模 式 的 例 子 。

Java 面试中其他各式各样的问题

这 部 分 包 含 Java 中 关 于 XML 的 面 试 题 ,正 则 表 达 式 面 试 题 ,Java 错 误 和 异 常

及序列化面试题

125、嵌套静态类与顶级类有什么区别?

一个公共的顶级类的源文件名称与类名相同,而嵌套静态类没有这个要求。一个

嵌套类位于顶级类内部,需要使用顶级类的名称来引用嵌套静态类,如

HashMap.Entry 是 一 个 嵌 套 静 态 类 , HashMap 是 一 个 顶 级 类 , Entry 是 一 个 嵌

套静态类。

126、你能写出一个正则表达式来判断一个字符串是否是一个数

字吗?

一 个 数 字 字 符 串 , 只 能 包 含 数 字 , 如 0 到 9 以 及 +、 - 开 头 , 通 过 这 个 信 息 ,

你可以下一个如下的正则表达式来判断给定的字符串是不是数字。

首先要 import java.util.regex.Pattern 和 java.util.regex.Matcher


public boolean isNumeric(String str){
Pattern pattern = Pattern.compile("[0-9]*");
Matcher isNum = pattern.matcher(str);
if( !isNum.matches() ){

第 361 页 共 485 页
return false;
}
return true;
}

127、Java 中,受检查异常 和 不受检查异常的区别?

受检查异常编译器在编译期间检查。对于这种异常,方法强制处理或者通过

throws 子 句 声 明 。 其 中 一 种 情 况 是 Exception 的 子 类 但 不 是

RuntimeException 的 子 类 。非 受 检 查 是 RuntimeException 的 子 类 ,在 编 译 阶

段不受编译器的检查。

128、Java 中,throw 和 throws 有什么区别

throw 用 于 抛 出 java.lang.Throwable 类 的 一 个 实 例 化 对 象 ,意 思 是 说 你 可 以 通

过 关 键 字 throw 抛 出 一 个 Error 或 者 一 个 Exception, 如 :

throw new IllegalArgumentException(“ size must be multiple of 2″ )

而 throws 的 作 用 是 作 为 方 法 声 明 和 签 名 的 一 部 分 , 方 法 被 抛 出 相 应 的 异 常 以 便

调 用 者 能 处 理 。 Java 中 , 任 何 未 处 理 的 受 检 查 异 常 强 制 在 throws 子 句 中 声 明 。

129、Java 中,Serializable 与 Externalizable 的区别?

Serializable 接 口 是 一 个 序 列 化 Java 类 的 接 口 , 以 便 于 它 们 可 以 在 网 络 上 传 输

或 者 可 以 将 它 们 的 状 态 保 存 在 磁 盘 上 ,是 JVM 内 嵌 的 默 认 序 列 化 方 式 ,成 本 高 、

脆 弱 而 且 不 安 全 。 Externalizable 允 许 你 控 制 整 个 序 列 化 过 程 , 指 定 特 定 的 二 进

制格式,增加安全机制。

第 362 页 共 485 页
130、Java 中,DOM 和 SAX 解析器有什么不同?

DOM 解 析 器 将 整 个 XML 文 档 加 载 到 内 存 来 创 建 一 棵 DOM 模 型 树 ,这 样 可 以

更 快 的 查 找 节 点 和 修 改 XML 结 构 , 而 SAX 解 析 器 是 一 个 基 于 事 件 的 解 析 器 ,

不 会 将 整 个 XML 文 档 加 载 到 内 存 。 由 于 这 个 原 因 , DOM 比 SAX 更 快 , 也 要

求 更 多 的 内 存 , 不 适 合 于 解 析 大 XML 文 件 。

131、说出 JDK 1.7 中的三个新特性?

虽 然 JDK 1.7 不 像 JDK 5 和 8 一 样 的 大 版 本 , 但 是 , 还 是 有 很 多 新 的 特 性 ,

如 try-with-resource 语 句 , 这 样 你 在 使 用 流 或 者 资 源 的 时 候 , 就 不 需 要 手 动 关

闭 ,Java 会 自 动 关 闭 。Fork-Join 池 某 种 程 度 上 实 现 Java 版 的 Map-reduce。

允 许 Switch 中 有 String 变 量 和 文 本 。 菱 形 操 作 符 (<>)用 于 类 型 推 断 , 不 再 需

要在变量声明的右边申明泛型,因此可以写出可读写更强、更简洁的代码。另一

个 值 得 一 提 的 特 性 是 改 善 异 常 处 理 , 如 允 许 在 同 一 个 catch 块 中 捕 获 多 个 异 常 。

132、说出 5 个 JDK 1.8 引入的新特性?

Java 8 在 Java 历 史 上 是 一 个 开 创 新 的 版 本 , 下 面 JDK 8 中 5 个 主 要 的 特 性 :

Lambda 表 达 式 , 允 许 像 对 象 一 样 传 递 匿 名 函 数

Stream API, 充 分 利 用 现 代 多 核 CPU, 可 以 写 出 很 简 洁 的 代 码

Date 与 Time API, 最 终 , 有 一 个 稳 定 、 简 单 的 日 期 和 时 间 库 可 供 你 使 用

扩展方法,现在,接口中可以有静态、默认方法。

重复注解,现在你可以将相同的注解在同一类型上使用多次。

133、Java 中,Maven 和 ANT 有什么区别?

第 363 页 共 485 页
虽 然 两 者 都 是 构 建 工 具 , 都 用 于 创 建 Java 应 用 , 但 是 Maven 做 的 事 情 更 多 ,

在 基 于“ 约 定 优 于 配 置 ”的 概 念 下 ,提 供 标 准 的 Java 项 目 结 构 ,同 时 能 为 应 用 自

动 管 理 依 赖( 应 用 中 所 依 赖 的 JAR 文 件 ),Maven 与 ANT 工 具 更 多 的 不 同 之

处请参见答案。

这就是所有的面试题,如此之多,是不是?我可以保证,如果你能回答列表中的

所 有 问 题 , 你 就 可 以 很 轻 松 的 应 付 任 何 核 心 Java 或 者 高 级 Java 面 试 。 虽 然 ,

这 里 没 有 涵 盖 Servlet、 JSP、 JSF、 JPA, JMS, EJB 及 其 它 Java EE 技 术 , 也

没 有 包 含 主 流 的 框 架 如 Spring MVC,Struts 2.0,Hibernate,也 没 有 包 含 SOAP

和 RESTful web service,但 是 这 份 列 表 对 做 Java 开 发 的 、准 备 应 聘 Java web

开 发 职 位 的 人 还 是 同 样 有 用 的 ,因 为 所 有 的 Java 面 试 ,开 始 的 问 题 都 是 Java 基

础 和 JDK API 相 关 的 。如 果 你 认 为 我 这 里 有 任 何 应 该 在 这 份 列 表 中 而 被 我 遗 漏 了

的 Java 流 行 的 问 题 , 你 可 以 自 由 的 给 我 建 议 。 我 的 目 的 是 从 最 近 的 面 试 中 创 建

一 份 最 新 的 、 最 优 的 Java 面 试 问 题 列 表 。

Spring 面试题(一)

1、一般问题

1.1、不同版本的 Spring Framework 有哪些主要功能?

Version Feature

Spring 2.5 发布于 2007 年。这是第一个支持注解的版本。

Spring 3.0 发布于 2009 年。它完全利用了 Java5 中的改进,并为 JEE6 提供了支

持。

Spring 4.0 发布于 2013 年。这是第一个完全支持 JAVA8 的版本。

第 364 页 共 485 页
1.2、什么是 Spring Framework?

Spring 是 一 个 开 源 应 用 框 架 ,旨 在 降 低 应 用 程 序 开 发 的 复 杂 度 。它 是 轻 量 级 、松

散 耦 合 的 。 它 具 有 分 层 体 系 结 构 , 允 许 用 户 选 择 组 件 , 同 时 还 为 J2EE 应 用 程 序

开 发 提 供 了 一 个 有 凝 聚 力 的 框 架 。它 可 以 集 成 其 他 框 架 ,如 Structs、Hibernate、

EJB 等 , 所 以 又 称 为 框 架 的 框 架 。

1.3、列举 Spring Framework 的优点。

由 于 Spring Frameworks 的 分 层 架 构 , 用 户 可 以 自 由 选 择 自 己 需 要 的 组 件 。

Spring Framework 支 持 POJO(Plain Old Java Object) 编 程 , 从 而 具 备 持 续 集

成 和 可 测 试 性 。 由 于 依 赖 注 入 和 控 制 反 转 , JDBC 得 以 简 化 。 它 是 开 源 免 费 的 。

1.4、Spring Framework 有哪些不同的功能?

轻 量 级 - Spring 在 代 码 量 和 透 明 度 方 面 都 很 轻 便 。IOC - 控 制 反 转 AOP - 面 向

切 面 编 程 可 以 将 应 用 业 务 逻 辑 和 系 统 服 务 分 离 ,以 实 现 高 内 聚 。容 器 - Spring 负

责 创 建 和 管 理 对 象 ( Bean) 的 生 命 周 期 和 配 置 。 MVC - 对 web 应 用 提 供 了 高

度 可 配 置 性 ,其 他 框 架 的 集 成 也 十 分 方 便 。事 务 管 理 - 提 供 了 用 于 事 务 管 理 的 通

用 抽 象 层 。 Spring 的 事 务 支 持 也 可 用 于 容 器 较 少 的 环 境 。 JDBC 异 常 - Spring

的 JDBC 抽 象 层 提 供 了 一 个 异 常 层 次 结 构 , 简 化 了 错 误 处 理 策 略 。

1.5、Spring Framework 中有多少个模块,它们分别是什么?

第 365 页 共 485 页
Spring 核 心 容 器 – 该 层 基 本 上 是 Spring Framework 的 核 心 。 它 包 含 以 下 模

块:

 Spring Core
 Spring Bean
 SpEL (Spring Expression Language)
 Spring Context

数 据 访 问 /集 成 – 该 层 提 供 与 数 据 库 交 互 的 支 持 。 它 包 含 以 下 模 块 :

 JDBC (Java DataBase Connectivity)


 ORM (Object Relational Mapping)
 OXM (Object XML Mappers)

第 366 页 共 485 页
 JMS (Java Messaging Service)
 Transaction

Web – 该 层 提 供 了 创 建 Web 应 用 程 序 的 支 持 。 它 包 含 以 下 模 块 :

 Web
 Web – Servlet
 Web – Socket
 Web – Portlet

AOP

 该层支持面向切面编程

Instrumentation

 该层为类检测和类加载器实现提供支持。

Test

 该层为使用 JUnit 和 TestNG 进行测试提供支持。

几个杂项模块:

第 367 页 共 485 页
Messaging – 该 模 块 为 STOMP 提 供 支 持 。 它 还 支 持 注 解 编 程 模 型 , 该 模 型 用

于 从 WebSocket 客 户 端 路 由 和 处 理 STOMP 消 息 。

Aspects – 该 模 块 为 与 AspectJ 的 集 成 提 供 支 持 。

1.6、什么是 Spring 配置文件?

Spring 配 置 文 件 是 XML 文 件 。该 文 件 主 要 包 含 类 信 息 。它 描 述 了 这 些 类 是 如 何

配 置 以 及 相 互 引 入 的 。但 是 ,XML 配 置 文 件 冗 长 且 更 加 干 净 。如 果 没 有 正 确 规 划

和编写,那么在大项目中管理变得非常困难。

1.7、Spring 应用程序有哪些不同组件?

Spring 应 用 一 般 有 以 下 组 件 :

 接口 - 定义功能。
 Bean 类 - 它包含属性,setter 和 getter 方法,函数等。
 Spring 面向切面编程(AOP) - 提供面向切面编程的功能。
 Bean 配置文件 - 包含类的信息以及如何配置它们。
 用户程序 - 它使用接口。

1.8、使用 Spring 有哪些方式?

使 用 Spring 有 以 下 方 式 :

 作为一个成熟的 Spring Web 应用程序。

第 368 页 共 485 页
 作为第三方 Web 框架,使用 Spring Frameworks 中间层。
 用于远程使用。
 作为企业级 Java Bean,它可以包装现有的 POJO(Plain Old Java
Objects)。

2、依赖注入(Ioc)

2.1、什么是 Spring IOC 容器?

Spring 框 架 的 核 心 是 Spring 容 器 。容 器 创 建 对 象 ,将 它 们 装 配 在 一 起 ,配 置 它

们 并 管 理 它 们 的 完 整 生 命 周 期 。Spring 容 器 使 用 依 赖 注 入 来 管 理 组 成 应 用 程 序 的

组件。容器通过读取提供的配置元数据来接收对象进行实例化,配置和组装的指

令 。 该 元 数 据 可 以 通 过 XML, Java 注 解 或 Java 代 码 提 供 。

2.2、什么是依赖注入?

第 369 页 共 485 页
在依赖注入中,您不必创建对象,但必须描述如何创建它们。您不是直接在代码

中 将 组 件 和 服 务 连 接 在 一 起 ,而 是 描 述 配 置 文 件 中 哪 些 组 件 需 要 哪 些 服 务 。由 IoC

容器将它们装配在一起。

2.3、可以通过多少种方式完成依赖注入?

通常,依赖注入可以通过三种方式完成,即:

 构造函数注入
 setter 注入
 接口注入

在 Spring Framework 中 , 仅 使 用 构 造 函 数 和 setter 注 入 。

2.4、区分构造函数注入和 setter 注入。

构造函数注入 setter 注入

没有部分注入 有部分注入

不会覆盖 setter 属性 会覆盖 setter 属性

任意修改都会创建一个新实例 任意修改不会创建一个新实例

适用于设置很多属性 适用于设置少量属性

2.5、spring 中有多少种 IOC 容器?

第 370 页 共 485 页
BeanFactory - BeanFactory 就 像 一 个 包 含 bean 集 合 的 工 厂 类 。它 会 在 客 户 端

要 求 时 实 例 化 bean。

ApplicationContext - ApplicationContext 接 口 扩 展 了 BeanFactory 接 口 。它

在 BeanFactory 基 础 上 提 供 了 一 些 额 外 的 功 能 。

2.6、区分 BeanFactory 和 ApplicationContext。

BeanFactory ApplicationContext

它使用懒加载 它使用即时加载

它使用语法显式提供资源对象 它自己创建和管理资源对象

不支持国际化 支持国际化

不支持基于依赖的注解 支持基于依赖的注解

2.7、列举 IoC 的一些好处。

IoC 的 一 些 好 处 是 :

 它将最小化应用程序中的代码量。
 它将使您的应用程序易于测试,因为它不需要单元测试用例中的任何单例
或 JNDI 查找机制。
 它以最小的影响和最少的侵入机制促进松耦合。
 它支持即时的实例化和延迟加载服务。

2.8、Spring IoC 的实现机制。

第 371 页 共 485 页
Spring 中 的 IoC 的 实 现 原 理 就 是 工 厂 模 式 加 反 射 机 制 。

示例:

interface Fruit {
public abstract void eat();
}
class Apple implements Fruit {
public void eat(){
System.out.println("Apple");
}
}
class Orange implements Fruit {
public void eat(){
System.out.println("Orange");
}
}
class Factory {
public static Fruit getInstance(String ClassName) {
Fruit f=null;
try {
f=(Fruit)Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
class Client {
public static void main(String[] a) {

第 372 页 共 485 页
Fruit f=Factory.getInstance("io.github.dunwu.spring.Apple");
if(f!=null){
f.eat();
}
}
}

3、Beans

3.1、什么是 spring bean?

 它们是构成用户应用程序主干的对象。
 Bean 由 Spring IoC 容器管理。
 它们由 Spring IoC 容器实例化,配置,装配和管理。
 Bean 是基于用户提供给容器的配置元数据创建。

3.2、spring 提供了哪些配置方式?

基 于 xml 配 置

bean 所 需 的 依 赖 项 和 服 务 在 XML 格 式 的 配 置 文 件 中 指 定 。 这 些 配 置 文 件 通 常

包 含 许 多 bean 定 义 和 特 定 于 应 用 程 序 的 配 置 选 项 。 它 们 通 常 以 bean 标 签 开

头。例如:

<bean id="studentbean" class="org.edureka.firstSpring.StudentBean">


<property name="name" value="Edureka"></property>
</bean>

第 373 页 共 485 页
基于注解配置

您 可 以 通 过 在 相 关 的 类 , 方 法 或 字 段 声 明 上 使 用 注 解 , 将 bean 配 置 为 组 件 类 本

身 , 而 不 是 使 用 XML 来 描 述 bean 装 配 。 默 认 情 况 下 , Spring 容 器 中 未 打 开

注 解 装 配 。 因 此 , 您 需 要 在 使 用 它 之 前 在 Spring 配 置 文 件 中 启 用 它 。 例 如 :

<beans>
<context:annotation-config/>
<!-- bean definitions go here -->
</beans>

基 于 Java API 配 置

Spring 的 Java 配 置 是 通 过 使 用 @Bean 和 @Configuration 来 实 现 。

1、 @Bean 注 解 扮 演 与 <bean/> 元 素 相 同 的 角 色 。
2、 @Configuration 类 允 许 通 过 简 单 地 调 用 同 一 个 类 中 的 其 他 @Bean 方 法

来 定 义 bean 间 依 赖 关 系 。

例如:

@Configuration
public class StudentConfig {
@Bean
public StudentBean myStudent() {
return new StudentBean();
}
}

第 374 页 共 485 页
3.3、spring 支持集中 bean scope?

Spring bean 支 持 5 种 scope:

Singleton - 每 个 Spring IoC 容 器 仅 有 一 个 单 实 例 。 Prototype - 每 次 请 求 都

会 产 生 一 个 新 的 实 例 。Request - 每 一 次 HTTP 请 求 都 会 产 生 一 个 新 的 实 例 ,并

且 该 bean 仅 在 当 前 HTTP 请 求 内 有 效 。Session - 每 一 次 HTTP 请 求 都 会 产

生 一 个 新 的 bean, 同 时 该 bean 仅 在 当 前 HTTP session 内 有 效 。

Global-session - 类 似 于 标 准 的 HTTP Session 作 用 域 , 不 过 它 仅 仅 在 基 于

portlet 的 web 应 用 中 才 有 意 义 。 Portlet 规 范 定 义 了 全 局 Session 的 概 念 ,

它 被 所 有 构 成 某 个 portlet web 应 用 的 各 种 不 同 的 portlet 所 共 享 。 在 global

session 作 用 域 中 定 义 的 bean 被 限 定 于 全 局 portlet Session 的 生 命 周 期 范

围 内 。 如 果 你 在 web 中 使 用 global session 作 用 域 来 标 识 bean, 那 么 web

会 自 动 当 成 session 类 型 来 使 用 。

仅 当 用 户 使 用 支 持 Web 的 ApplicationContext 时 , 最 后 三 个 才 可 用 。

3.4、spring bean 容器的生命周期是什么样的?

spring bean 容 器 的 生 命 周 期 流 程 如 下 :

1、Spring 容 器 根 据 配 置 中 的 bean 定 义 中 实 例 化 bean。2、Spring 使 用 依 赖

注 入 填 充 所 有 属 性 , 如 bean 中 所 定 义 的 配 置 。 3、 如 果 bean 实 现

BeanNameAware 接 口 , 则 工 厂 通 过 传 递 bean 的 ID 来 调 用

setBeanName()。 4、 如 果 bean 实 现 BeanFactoryAware 接 口 , 工 厂 通 过 传

递 自 身 的 实 例 来 调 用 setBeanFactory()。 5、 如 果 存 在 与 bean 关 联 的 任 何

BeanPostProcessors, 则 调 用 preProcessBeforeInitialization() 方 法 。 6、 如

果 为 bean 指 定 了 init 方 法 ( <bean> 的 init-method 属 性 ) , 那 么 将 调


用 它 。 7、 最 后 , 如 果 存 在 与 bean 关 联 的 任 何 BeanPostProcessors, 则 将 调

第 375 页 共 485 页
用 postProcessAfterInitialization() 方 法 。 8、 如 果 bean 实 现

DisposableBean 接 口 ,当 spring 容 器 关 闭 时 ,会 调 用 destory()。9、如 果 为

bean 指 定 了 destroy 方 法( <bean> 的 destroy-method 属 性 ),那 么 将


调用它。

3.5、什么是 spring 的内部 bean?

只 有 将 bean 用 作 另 一 个 bean 的 属 性 时 , 才 能 将 bean 声 明 为 内 部 bean。

为 了 定 义 bean, Spring 的 基 于 XML 的 配 置 元 数 据 在 <property> 或

<constructor-arg> 中 提 供 了 <bean> 元 素 的 使 用 。 内 部 bean 总 是 匿 名


的,它们总是作为原型。

例 如 , 假 设 我 们 有 一 个 Student 类 , 其 中 引 用 了 Person 类 。 这 里 我 们 将 只 创

建 一 个 Person 类 实 例 并 在 Student 中 使 用 它 。

Student.java

public class Student {


private Person person;
//Setters and Getters

第 376 页 共 485 页
}
public class Person {
private String name;
private String address;
//Setters and Getters
}

bean.xml

<bean id=“StudentBean" class="com.edureka.Student">


<property name="person">
<!--This is inner bean -->
<bean class="com.edureka.Person">
<property name="name" value=“Scott"></property>
<property name="address" value=
“Bangalore"></property>
</bean>
</property>
</bean>

3.6、什么是 spring 装配

当 bean 在 Spring 容 器 中 组 合 在 一 起 时 ,它 被 称 为 装 配 或 bean 装 配 。Spring

容 器 需 要 知 道 需 要 什 么 bean 以 及 容 器 应 该 如 何 使 用 依 赖 注 入 来 将 bean 绑 定

在 一 起 , 同 时 装 配 bean。

3.7、自动装配有哪些方式?

第 377 页 共 485 页
Spring 容 器 能 够 自 动 装 配 bean。 也 就 是 说 , 可 以 通 过 检 查 BeanFactory 的 内

容 让 Spring 自 动 解 析 bean 的 协 作 者 。

自动装配的不同模式:

no - 这 是 默 认 设 置 , 表 示 没 有 自 动 装 配 。 应 使 用 显 式 bean 引 用 进 行 装 配 。

byName - 它 根 据 bean 的 名 称 注 入 对 象 依 赖 项 。 它 匹 配 并 装 配 其 属 性 与 XML

文 件 中 由 相 同 名 称 定 义 的 bean。byType - 它 根 据 类 型 注 入 对 象 依 赖 项 。如 果 属

性 的 类 型 与 XML 文 件 中 的 一 个 bean 名 称 匹 配 ,则 匹 配 并 装 配 属 性 。构 造 函 数

- 它 通 过 调 用 类 的 构 造 函 数 来 注 入 依 赖 项 。它 有 大 量 的 参 数 。autodetect - 首 先

容 器 尝 试 通 过 构 造 函 数 使 用 autowire 装 配 ,如 果 不 能 ,则 尝 试 通 过 byType 自

动装配。

3.8、自动装配有什么局限?

覆 盖 的 可 能 性 - 您 始 终 可 以 使 用 <constructor-arg> 和 <property> 设
置 指 定 依 赖 项 ,这 将 覆 盖 自 动 装 配 。基 本 元 数 据 类 型 - 简 单 属 性( 如 原 数 据 类 型 ,

字 符 串 和 类 )无 法 自 动 装 配 。令 人 困 惑 的 性 质 - 总 是 喜 欢 使 用 明 确 的 装 配 ,因 为

自动装配不太精确。

4、注解

4.1、什么是基于注解的容器配置

不 使 用 XML 来 描 述 bean 装 配 ,开 发 人 员 通 过 在 相 关 的 类 ,方 法 或 字 段 声 明 上

使 用 注 解 将 配 置 移 动 到 组 件 类 本 身 。 它 可 以 作 为 XML 设 置 的 替 代 方 案 。 例 如 :

Spring 的 Java 配 置 是 通 过 使 用 @Bean 和 @Configuration 来 实 现 。

第 378 页 共 485 页
@Bean 注 解 扮 演 与 元 素 相 同 的 角 色 。 @Configuration 类 允 许 通 过 简 单 地 调

用 同 一 个 类 中 的 其 他 @Bean 方 法 来 定 义 bean 间 依 赖 关 系 。

例如:

@Configuration
public class StudentConfig {
@Bean
public StudentBean myStudent() {
return new StudentBean();
}
}

4.2、如何在 spring 中启动注解装配?

默 认 情 况 下 ,Spring 容 器 中 未 打 开 注 解 装 配 。因 此 ,要 使 用 基 于 注 解 装 配 ,我 们

必 须 通 过 配 置 <context:annotation-config/> 元 素 在 Spring 配 置 文 件
中启用它。

4.3、@Component, @Controller, @Repository,

@Service 有何区别?

@Component : 这 将 java 类 标 记 为 bean。 它 是 任 何 Spring 管 理 组 件 的 通

用 构 造 型 。 spring 的 组 件 扫 描 机 制 现 在 可 以 将 其 拾 取 并 将 其 拉 入 应 用 程 序 环 境

中 。 @Controller : 这 将 一 个 类 标 记 为 Spring Web MVC 控 制 器 。 标 有 它 的

Bean 会 自 动 导 入 到 IoC 容 器 中 。@Service :此 注 解 是 组 件 注 解 的 特 化 。它 不

会 对 @Component 注 解 提 供 任 何 其 他 行 为 。 您 可 以 在 服 务 层 类 中 使 用

第 379 页 共 485 页
@Service 而 不 是 @Component, 因 为 它 以 更 好 的 方 式 指 定 了 意 图 。

@Repository : 这 个 注 解 是 具 有 类 似 用 途 和 功 能 的 @Component 注 解 的 特

化 。 它 为 DAO 提 供 了 额 外 的 好 处 。 它 将 DAO 导 入 IoC 容 器 , 并 使 未 经 检 查

的 异 常 有 资 格 转 换 为 Spring DataAccessException。

4.4、@Required 注解有什么用?

@Required 应 用 于 bean 属 性 setter 方 法 。 此 注 解 仅 指 示 必 须 在 配 置 时 使 用

bean 定 义 中 的 显 式 属 性 值 或 使 用 自 动 装 配 填 充 受 影 响 的 bean 属 性 。 如 果 尚 未

填 充 受 影 响 的 bean 属 性 , 则 容 器 将 抛 出 BeanInitializationException。

示例:

public class Employee {


private String name;
@Required
public void setName(String name){
this.name=name;
}
public string getName(){
return name;
}
}

4.5、@Autowired 注解有什么用?

@Autowired 可 以 更 准 确 地 控 制 应 该 在 何 处 以 及 如 何 进 行 自 动 装 配 。 此 注 解 用 于

在 setter 方 法 , 构 造 函 数 , 具 有 任 意 名 称 或 多 个 参 数 的 属 性 或 方 法 上 自 动 装 配

bean。 默 认 情 况 下 , 它 是 类 型 驱 动 的 注 入 。

第 380 页 共 485 页
public class Employee {
private String name;
@Autowired
public void setName(String name) {
this.name=name;
}
public string getName(){
return name;
}
}

4.6、@Qualifier 注解有什么用?

当 您 创 建 多 个 相 同 类 型 的 bean 并 希 望 仅 使 用 属 性 装 配 其 中 一 个 bean 时 ,您 可

以 使 用 @Qualifier 注 解 和 @Autowired 通 过 指 定 应 该 装 配 哪 个 确 切 的 bean

来消除歧义。

例 如 , 这 里 我 们 分 别 有 两 个 类 , Employee 和 EmpAccount。 在 EmpAccount

中 , 使 用 @Qualifier 指 定 了 必 须 装 配 id 为 emp1 的 bean。

Employee.java

public class Employee {


private String name;
@Autowired
public void setName(String name) {
this.name=name;
}
public string getName() {

第 381 页 共 485 页
return name;
}
}

EmpAccount.java

public class EmpAccount {


private Employee emp;

@Autowired
@Qualifier(emp1)
public void showName() {
System.out.println(“Employee name : ”+emp.getName);
}
}

4.7、@RequestMapping 注解有什么用?

@RequestMapping 注 解 用 于 将 特 定 HTTP 请 求 方 法 映 射 到 将 处 理 相 应 请 求 的

控 制 器 中 的 特 定 类 /方 法 。 此 注 释 可 应 用 于 两 个 级 别 :

类 级 别 : 映 射 请 求 的 URL 方 法 级 别 : 映 射 URL 以 及 HTTP 请 求 方 法

5、数据访问

5.1、spring DAO 有什么用?

第 382 页 共 485 页
Spring DAO 使 得 JDBC, Hibernate 或 JDO 这 样 的 数 据 访 问 技 术 更 容 易 以 一

种统一的方式工作。这使得用户容易在持久性技术之间切换。它还允许您在编写

代码时,无需考虑捕获每种技术不同的异常。

5.2、列举 Spring DAO 抛出的异常。

5.3、spring JDBC API 中存在哪些类?

 JdbcTemplate
 SimpleJdbcTemplate
 NamedParameterJdbcTemplate
 SimpleJdbcInsert

第 383 页 共 485 页
 SimpleJdbcCall

5.4、使用 Spring 访问 Hibernate 的方法有哪些?

我 们 可 以 通 过 两 种 方 式 使 用 Spring 访 问 Hibernate:

1、 使 用 Hibernate 模 板 和 回 调 进 行 控 制 反 转

2、 扩 展 HibernateDAOSupport 并 应 用 AOP 拦 截 器 节 点

5.5、列举 spring 支持的事务管理类型

Spring 支 持 两 种 类 型 的 事 务 管 理 :

1、 程序化事务管理:在此过程中,在编程的帮助下管理事务。它为您提供极大

的灵活性,但维护起来非常困难。

2、 声 明 式 事 务 管 理 :在 此 ,事 务 管 理 与 业 务 代 码 分 离 。仅 使 用 注 解 或 基 于 XML

的配置来管理事务。

5.6、spring 支持哪些 ORM 框架

 Hibernate
 iBatis
 JPA
 JDO
 OJB

第 384 页 共 485 页
6、AOP

6.1、什么是 AOP?

AOP(Aspect-Oriented Programming), 即 面 向 切 面 编 程 , 它 与

OOP( Object-Oriented Programming, 面 向 对 象 编 程 ) 相 辅 相 成 , 提 供 了 与

OOP 不 同 的 抽 象 软 件 结 构 的 视 角 . 在 OOP 中 , 我 们 以 类 (class)作 为 我 们 的 基

本 单 元 , 而 AOP 中 的 基 本 单 元 是 Aspect(切 面 )

6.2、什么是 Aspect?

aspect 由 pointcount 和 advice 组 成 , 它 既 包 含 了 横 切 逻 辑 的 定 义 , 也 包


括 了 连 接 点 的 定 义 . Spring AOP 就 是 负 责 实 施 切 面 的 框 架 , 它 将 切 面 所 定 义 的 横

切 逻 辑 编 织 到 切 面 所 指 定 的 连 接 点 中 . AOP 的 工 作 重 心 在 于 如 何 将 增 强 编 织 目 标

对象的连接点上, 这里包含两个工作:

1、 如 何 通 过 pointcut 和 advice 定 位 到 特 定 的 joinpoint 上 2、 如 何 在

advice 中 编 写 切 面 代 码 .

第 385 页 共 485 页
可 以 简 单 地 认 为 , 使 用 @Aspect 注 解 的 类 就 是 切 面 .

6.3、什么是切点(JoinPoint)

程序运行中的一些时间点, 例如一个方法的执行, 或者是一个异常的处理.

在 Spring AOP 中 , join point 总 是 方 法 的 执 行 点 。

6.4、什么是通知(Advice)?

特 定 JoinPoint 处 的 Aspect 所 采 取 的 动 作 称 为 Advice。Spring AOP 使 用 一

个 Advice 作 为 拦 截 器 , 在 JoinPoint “ 周 围 ” 维 护 一 系 列 的 拦 截 器 。

第 386 页 共 485 页
6.5、有哪些类型的通知(Advice)?

 Before - 这些类型的 Advice 在 joinpoint 方法之前执行,并使用


@Before 注解标记进行配置。
 After Returning - 这些类型的 Advice 在连接点方法正常执行后执
行,并使用@AfterReturning 注解标记进行配置。
 After Throwing - 这些类型的 Advice 仅在 joinpoint 方法通过抛出
异常退出并使用 @AfterThrowing 注解标记配置时执行。
 After (finally) - 这些类型的 Advice 在连接点方法之后执行,无论方
法退出是正常还是异常返回,并使用 @After 注解标记进行配置。
 Around - 这些类型的 Advice 在连接点之前和之后执行,并使用
@Around 注解标记进行配置。

6.6、指出在 spring aop 中 concern 和 cross-cutting

concern 的不同之处。

concern 是 我 们 想 要 在 应 用 程 序 的 特 定 模 块 中 定 义 的 行 为 。 它 可 以 定 义 为 我 们 想

要实现的功能。

cross-cutting concern 是 一 个 适 用 于 整 个 应 用 的 行 为 , 这 会 影 响 整 个 应 用 程 序 。

例如,日志记录,安全性和数据传输是应用程序几乎每个模块都需要关注的问题,

因此它们是跨领域的问题。

6.7、AOP 有哪些实现方式?

实 现 AOP 的 技 术 , 主 要 分 为 两 大 类 :

第 387 页 共 485 页
静态代理

指 使 用 AOP 框 架 提 供 的 命 令 进 行 编 译 ,从 而 在 编 译 阶 段 就 可 生 成 AOP 代 理 类 ,

因此也称为编译时增强;

 编译时编织(特殊编译器实现)
 类加载时编织(特殊的类加载器实现)。

动态代理

在 运 行 时 在 内 存 中 “ 临 时 ” 生 成 AOP 动 态 代 理 类 , 因 此 也 被 称 为 运 行 时 增 强 。

 JDK 动态代理
 CGLIB

6.8、Spring AOP and AspectJ AOP 有什么区别?

Spring AOP 基 于 动 态 代 理 方 式 实 现 ; AspectJ 基 于 静 态 代 理 方 式 实 现 。 Spring

AOP 仅 支 持 方 法 级 别 的 PointCut; 提 供 了 完 全 的 AOP 支 持 , 它 还 支 持 属 性 级

别 的 PointCut。

6.9、如何理解 Spring 中的代理?

将 Advice 应 用 于 目 标 对 象 后 创 建 的 对 象 称 为 代 理 。 在 客 户 端 对 象 的 情 况 下 , 目

标对象和代理对象是相同的。

第 388 页 共 485 页
Advice + Target Object = Proxy

6.10、什么是编织(Weaving)?

为 了 创 建 一 个 advice 对 象 而 链 接 一 个 aspect 和 其 它 应 用 类 型 或 对 象 , 称 为 编

织 ( Weaving) 。 在 Spring AOP 中 , 编 织 在 运 行 时 执 行 。 请 参 考 下 图 :

7、MVC

7.1、Spring MVC 框架有什么用?

Spring Web MVC 框 架 提 供 模 型 -视 图 -控 制 器 架 构 和 随 时 可 用 的 组 件 ,用 于 开

发 灵 活 且 松 散 耦 合 的 Web 应 用 程 序 。 MVC 模 式 有 助 于 分 离 应 用 程 序 的 不 同 方

面 ,如 输 入 逻 辑 ,业 务 逻 辑 和 UI 逻 辑 ,同 时 在 所 有 这 些 元 素 之 间 提 供 松 散 耦 合 。

7.2、描述一下 DispatcherServlet 的工作流程

DispatcherServlet 的 工 作 流 程 可 以 用 一 幅 图 来 说 明 :

第 389 页 共 485 页
1、 向 服 务 器 发 送 HTTP 请 求 , 请 求 被 前 端 控 制 器 DispatcherServlet 捕 获 。

2、 DispatcherServlet 根 据 -servlet.xml 中 的 配 置 对 请 求 的 URL 进 行 解

析 , 得 到 请 求 资 源 标 识 符 ( URI) 。 然 后 根 据 该 URI, 调 用 HandlerMapping


获 得 该 Handler 配 置 的 所 有 相 关 的 对 象( 包 括 Handler 对 象 以 及 Handler 对

象 对 应 的 拦 截 器 ) , 最 后 以 HandlerExecutionChain 对 象 的 形 式 返 回 。

3、 DispatcherServlet 根 据 获 得 的 Handler , 选 择 一 个 合 适 的

HandlerAdapter 。( 附 注 :如 果 成 功 获 得 HandlerAdapter 后 ,此 时 将 开 始
执 行 拦 截 器 的 preHandler(...)方 法 ) 。

4、 提 取 Request 中 的 模 型 数 据 , 填 充 Handler 入 参 , 开 始 执 行 Handler

( Controller )。 在 填 充 Handler 的 入 参 过 程 中 , 根 据 你 的 配 置 , Spring 将


帮你做一些额外的工作:

第 390 页 共 485 页
 HttpMessageConveter:将请求消息(如 Json、xml 等数据)转换
成一个对象,将对象转换为指定的响应信息。
 数据转换:对请求消息进行数据转换。如 String 转换成 Integer、
Double 等。
 数据根式化:对请求消息进行数据格式化。如将字符串转换成格式化数字
或格式化日期等。
 数据验证:验证数据的有效性(长度、格式等),验证结果存储到
BindingResult 或 Error 中。

5、 Handler(Controller)执 行 完 成 后 , 向 DispatcherServlet 返 回 一 个

ModelAndView 对 象 ;

6、根 据 返 回 的 ModelAndView ,选 择 一 个 适 合 的 ViewResolver( 必 须 是 已

经 注 册 到 Spring 容 器 中 的 ViewResolver )返 回 给 DispatcherServlet 。

7、 ViewResolver 结 合 Model 和 View , 来 渲 染 视 图 。

8、 视 图 负 责 将 渲 染 结 果 返 回 给 客 户 端 。

7.3、介绍一下 WebApplicationContext

WebApplicationContext 是 ApplicationContext 的 扩 展 。 它 具 有 Web 应 用

程 序 所 需 的 一 些 额 外 功 能 。 它 与 普 通 的 ApplicationContext 在 解 析 主 题 和 决 定

与 哪 个 servlet 关 联 的 能 力 方 面 有 所 不 同 。

英文原文链接:

https://www.edureka.co/blog/interview-questions/spring-interview-questi

ons/

第 391 页 共 485 页
Spring 面试题(二)

1、什么是 spring?

Spring 是 个 java 企 业 级 应 用 的 开 源 开 发 框 架 。 Spring 主 要 用 来 开 发 Java 应 用 ,

但 是 有 些 扩 展 是 针 对 构 建 J2EE 平 台 的 web 应 用 。 Spring 框 架 目 标 是 简 化 Java

企 业 级 应 用 开 发 , 并 通 过 POJO 为 基 础 的 编 程 模 型 促 进 良 好 的 编 程 习 惯 。

2、使用 Spring 框架的好处是什么?

 轻量:Spring 是轻量的,基本的版本大约 2MB。


 控制反转:Spring 通过控制反转实现了松散耦合,对象们给出它们的依
赖,而不是创建或查找依赖的对象们。
 面向切面的编程(AOP):Spring 支持面向切面的编程,并且把应用业务
逻辑和系统服务分开。
 容器:Spring 包含并管理应用中对象的生命周期和配置。
 MVC 框架:Spring 的 WEB 框架是个精心设计的框架,是 Web 框架的
一个很好的替代品。
 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地
事务下至全局事务(JTA)。
 异常处理:Spring 提供方便的 API 把具体技术相关的异常(比如由 JDBC,
Hibernate or JDO 抛出的)转化为一致的 unchecked 异常。

3、Spring 由哪些模块组成?

以 下 是 Spring 框 架 的 基 本 模 块 :

第 392 页 共 485 页
 Core module
 Bean module
 Context module
 Expression Language module
 JDBC module
 ORM module
 OXM module
 Java Messaging Service(JMS) module
 Transaction module
 Web module
 Web-Servlet module
 Web-Struts module
 Web-Portlet module

4、核心容器(应用上下文) 模块。

这 是 基 本 的 Spring 模 块 , 提 供 spring 框 架 的 基 础 功 能 , BeanFactory 是 任 何

以 spring 为 基 础 的 应 用 的 核 心 。Spring 框 架 建 立 在 此 模 块 之 上 ,它 使 Spring 成

为一个容器。

5、BeanFactory – BeanFactory 实现举例。

Bean 工 厂 是 工 厂 模 式 的 一 个 实 现 ,提 供 了 控 制 反 转 功 能 ,用 来 把 应 用 的 配 置 和 依

赖从正真的应用代码中分离。

最 常 用 的 BeanFactory 实 现 是 XmlBeanFactory 类 。

第 393 页 共 485 页
6、XMLBeanFactory

最 常 用 的 就 是 org.springframework.beans.factory.xml.XmlBeanFactory , 它

根 据 XML 文 件 中 的 定 义 加 载 beans。该 容 器 从 XML 文 件 读 取 配 置 元 数 据 并 用 它

去创建一个完全配置的系统或应用。

7、解释 AOP 模块

AOP 模 块 用 于 发 给 我 们 的 Spring 应 用 做 面 向 切 面 的 开 发 , 很 多 支 持 由 AOP 联

盟 提 供 , 这 样 就 确 保 了 Spring 和 其 他 AOP 框 架 的 共 通 性 。 这 个 模 块 将 元 数 据 编

程 引 入 Spring。

8、解释 JDBC 抽象和 DAO 模块。

通 过 使 用 JDBC 抽 象 和 DAO 模 块 ,保 证 数 据 库 代 码 的 简 洁 ,并 能 避 免 数 据 库 资 源

错误关闭导致的问题,它在各种不同的数据库的错误信息之上,提供了一个统一

的 异 常 访 问 层 。 它 还 利 用 Spring 的 AOP 模 块 给 Spring 应 用 中 的 对 象 提 供 事 务

管理服务。

9、解释对象/关系映射集成模块。

Spring 通 过 提 供 ORM 模 块 ,支 持 我 们 在 直 接 JDBC 之 上 使 用 一 个 对 象 /关 系 映 射

映 射 (ORM)工 具 ,Spring 支 持 集 成 主 流 的 ORM 框 架 ,如 Hiberate,JDO 和 iBATIS

SQL Maps。 Spring 的 事 务 管 理 同 样 支 持 以 上 所 有 ORM 框 架 及 JDBC。

10、解释 WEB 模块。

第 394 页 共 485 页
Spring 的 WEB 模 块 是 构 建 在 application context 模 块 基 础 之 上 , 提 供 一 个 适

合 web 应 用 的 上 下 文 。这 个 模 块 也 包 括 支 持 多 种 面 向 web 的 任 务 ,如 透 明 地 处 理

多 个 文 件 上 传 请 求 和 程 序 级 请 求 参 数 的 绑 定 到 你 的 业 务 对 象 。 它 也 有 对 Jakarta

Struts 的 支 持 。

12、Spring 配置文件

Spring 配 置 文 件 是 个 XML 文 件 ,这 个 文 件 包 含 了 类 信 息 ,描 述 了 如 何 配 置 它 们 ,

以及如何相互调用。

13、什么是 Spring IOC 容器?

Spring IOC 负 责 创 建 对 象 ,管 理 对 象( 通 过 依 赖 注 入( DI),装 配 对 象 ,配 置 对

象,并且管理这些对象的整个生命周期。

14、IOC 的优点是什么?

IOC 或 依 赖 注 入 把 应 用 的 代 码 量 降 到 最 低 。它 使 应 用 容 易 测 试 ,单 元 测 试 不 再 需

要 单 例 和 JNDI 查 找 机 制 。 最 小 的 代 价 和 最 小 的 侵 入 性 使 松 散 耦 合 得 以 实 现 。 IOC

容器支持加载服务时的饿汉式初始化和懒加载。

15、ApplicationContext 通常的实现是什么?

 FileSystemXmlApplicationContext :此容器从一个 XML 文件中加


载 beans 的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。

第 395 页 共 485 页
 ClassPathXmlApplicationContext:此容器也从一个 XML 文件中加
载 beans 的定义,这里,你需要正确设置 classpath 因为这个容器将在 classpath
里找 bean 配置。
 WebXmlApplicationContext:此容器加载一个 XML 文件,此文件定
义了一个 WEB 应用的所有 bean。

16、Bean 工厂和 Application contexts 有什么区别?

Application contexts 提 供 一 种 方 法 处 理 文 本 消 息 , 一 个 通 常 的 做 法 是 加 载 文 件

资 源( 比 如 镜 像 ),它 们 可 以 向 注 册 为 监 听 器 的 bean 发 布 事 件 。另 外 ,在 容 器 或

容 器 内 的 对 象 上 执 行 的 那 些 不 得 不 由 bean 工 厂 以 程 序 化 方 式 处 理 的 操 作 ,可 以 在

Application contexts 中 以 声 明 的 方 式 处 理 。 Application contexts 实 现 了

MessageSource 接 口 , 该 接 口 的 实 现 以 可 插 拔 的 方 式 提 供 获 取 本 地 化 消 息 的 方

法。

17、一个 Spring 的应用看起来象什么?

 一个定义了一些功能的接口。
 这实现包括属性,它的 Setter , getter 方法和函数等。
 Spring AOP。
 Spring 的 XML 配置文件。
 使用以上功能的客户端程序。

依赖注入

18、什么是 Spring 的依赖注入?


第 396 页 共 485 页
依 赖 注 入 , 是 IOC 的 一 个 方 面 , 是 个 通 常 的 概 念 , 它 有 多 种 解 释 。 这 概 念 是 说 你

不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和

服 务 , 但 是 要 在 配 置 文 件 里 描 述 哪 些 组 件 需 要 哪 些 服 务 , 之 后 一 个 容 器 ( IOC 容

器)负责把他们组装起来。

19、有哪些不同类型的 IOC(依赖注入)方式?

 构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现
的,该类有一系列参数,每个参数代表一个对其他类的依赖。
 Setter 方法注入:Setter 方法注入是容器通过调用无参构造器或无参
static 工厂 方法实例化 bean 之后,调用该 bean 的 setter 方法,即实现了基
于 setter 的依赖注入。

20、哪种依赖注入方式你建议使用,构造器注入,还是 Setter

方法注入?

你 两 种 依 赖 方 式 都 可 以 使 用 , 构 造 器 注 入 和 Setter 方 法 注 入 。 最 好 的 解 决 方 案 是

用 构 造 器 参 数 实 现 强 制 依 赖 , setter 方 法 实 现 可 选 依 赖 。

Spring Beans

21.什么是 Spring beans?

第 397 页 共 485 页
Spring beans 是 那 些 形 成 Spring 应 用 的 主 干 的 java 对 象 。 它 们 被 Spring IOC

容 器 初 始 化 , 装 配 , 和 管 理 。 这 些 beans 通 过 容 器 中 配 置 的 元 数 据 创 建 。 比 如 ,

以 XML 文 件 中 的 形 式 定 义 。

Spring 框 架 定 义 的 beans 都 是 单 件 beans。 在 bean tag 中 有 个 属 性 ”

singleton” , 如 果 它 被 赋 为 TRUE, bean 就 是 单 件 , 否 则 就 是 一 个 prototype

bean。 默 认 是 TRUE, 所 以 所 有 在 Spring 框 架 中 的 beans 缺 省 都 是 单 件 。

22、一个 Spring Bean 定义 包含什么?

一 个 Spring Bean 的 定 义 包 含 容 器 必 知 的 所 有 配 置 元 数 据 , 包 括 如 何 创 建 一 个

bean, 它 的 生 命 周 期 详 情 及 它 的 依 赖 。

23、如何给 Spring 容器提供配置元数据?

这 里 有 三 种 重 要 的 方 法 给 Spring 容 器 提 供 配 置 元 数 据 。

XML 配 置 文 件 。

基于注解的配置。

基 于 java 的 配 置 。

24、你怎样定义类的作用域?

当 定 义 一 个 在 Spring 里 , 我 们 还 能 给 这 个 bean 声 明 一 个 作 用 域 。 它 可 以 通 过

bean 定 义 中 的 scope 属 性 来 定 义 。如 ,当 Spring 要 在 需 要 的 时 候 每 次 生 产 一 个

新 的 bean 实 例 ,bean 的 scope 属 性 被 指 定 为 prototype。另 一 方 面 ,一 个 bean

第 398 页 共 485 页
每 次 使 用 的 时 候 必 须 返 回 同 一 个 实 例 , 这 个 bean 的 scope 属 性 必 须 设 为

singleton。

25、解释 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.

26、Spring 框架中的单例 bean 是线程安全的吗?

不 , Spring 框 架 中 的 单 例 bean 不 是 线 程 安 全 的 。

27、解释 Spring 框架中 bean 的生命周期。

 Spring 容器 从 XML 文件中读取 bean 的定义,并实例化 bean。


 Spring 根据 bean 的定义填充所有的属性。

第 399 页 共 485 页
 如果 bean 实现了 BeanNameAware 接口,Spring 传递 bean 的 ID 到
setBeanName 方法。
 如果 Bean 实现了 BeanFactoryAware 接口, Spring 传递
beanfactory 给 setBeanFactory 方法。
 如果有任何与 bean 相关联的 BeanPostProcessors,Spring 会在
postProcesserBeforeInitialization()方法内调用它们。
 如果 bean 实现 IntializingBean 了,调用它的 afterPropertySet 方法,
如果 bean 声明了初始化方法,调用此初始化方法。
 如果有 BeanPostProcessors 和 bean 关联,这些 bean 的
postProcessAfterInitialization() 方法将被调用。
 如果 bean 实现了 DisposableBean,它将调用 destroy()方法。

28、哪些是重要的 bean 生命周期方法?你能重载它们吗?

有 两 个 重 要 的 bean 生 命 周 期 方 法 , 第 一 个 是 setup , 它 是 在 容 器 加 载 bean

的 时 候 被 调 用 。 第 二 个 方 法 是 teardown 它 是 在 容 器 卸 载 类 的 时 候 被 调 用 。

The bean 标 签 有 两 个 重 要 的 属 性( init-method 和 destroy-method)。用 它 们

你 可 以 自 己 定 制 初 始 化 和 注 销 方 法 。 它 们 也 有 相 应 的 注 解 ( @PostConstruct 和

@PreDestroy) 。

29、什么是 Spring 的内部 bean?

当 一 个 bean 仅 被 用 作 另 一 个 bean 的 属 性 时 , 它 能 被 声 明 为 一 个 内 部 bean, 为

了 定 义 inner bean, 在 Spring 的 基 于 XML 的 配 置 元 数 据 中 , 可 以 在 或 元

素 内 使 用 元 素 , 内 部 bean 通 常 是 匿 名 的 , 它 们 的 Scope 一 般 是 prototype。

30、在 Spring 中如何注入一个 java 集合?

第 400 页 共 485 页
Spring 提 供 以 下 几 种 集 合 的 配 置 元 素 :

 类型用于注入一列值,允许有相同的值。
 类型用于注入一组值,不允许有相同的值。
 类型用于注入一组键值对,键和值都可以为任意类型。
 类型用于注入一组键值对,键和值都只能为 String 类型。

31、什么是 bean 装配?

装 配 ,或 bean 装 配 是 指 在 Spring 容 器 中 把 bean 组 装 到 一 起 ,前 提 是 容 器 需 要

知 道 bean 的 依 赖 关 系 , 如 何 通 过 依 赖 注 入 来 把 它 们 装 配 到 一 起 。

32、什么是 bean 的自动装配?

Spring 容 器 能 够 自 动 装 配 相 互 合 作 的 bean, 这 意 味 着 容 器 不 需 要 和 配 置 , 能 通

过 Bean 工 厂 自 动 处 理 bean 之 间 的 协 作 。

33、解释不同方式的自动装配 。

有 五 种 自 动 装 配 的 方 式 ,可 以 用 来 指 导 Spring 容 器 用 自 动 装 配 方 式 来 进 行 依 赖 注

入。

 no:默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配。

第 401 页 共 485 页
 byName:通过参数名 自动装配,Spring 容器在配置文件中发现 bean
的 autowire 属性被设置成 byname,之后容器试图匹配、装配和该 bean 的属
性具有相同名字的 bean。
 byType::通过参数类型自动装配,Spring 容器在配置文件中发现 bean
的 autowire 属性被设置成 byType,之后容器试图匹配、装配和该 bean 的属
性具有相同类型的 bean。如果有多个 bean 符合条件,则抛出错误。
 constructor:这个方式类似于 byType, 但是要提供给构造器参数,如
果没有确定的带参数的构造器参数类型,将会抛出异常。
 autodetect:首先尝试使用 constructor 来自动装配,如果无法工作,
则使用 byType 方式。

34.自动装配有哪些局限性 ?

自动装配的局限性是:

 重写:你仍需用 和 配置来定义依赖,意味着总要重写自动装配。
 基本数据类型:你不能自动装配简单的属性,如基本数据类型,String
字符串,和类。
 模糊特性:自动装配不如显式装配精确,如果有可能,建议使用显式装配。

35、你可以在 Spring 中注入一个 null 和一个空字符串吗?

可以。

Spring 注解

第 402 页 共 485 页
36、什么是基于 Java 的 Spring 注解配置? 给一些注解的例子.

基 于 Java 的 配 置 , 允 许 你 在 少 量 的 Java 注 解 的 帮 助 下 , 进 行 你 的 大 部 分 Spring

配 置 而 非 通 过 XML 文 件 。

以 @Configuration 注 解 为 例 , 它 用 来 标 记 类 可 以 当 做 一 个 bean 的 定 义 , 被

Spring IOC 容 器 使 用 。 另 一 个 例 子 是 @Bean 注 解 , 它 表 示 此 方 法 将 要 返 回 一 个

对 象 , 作 为 一 个 bean 注 册 进 Spring 应 用 上 下 文 。

37、什么是基于注解的容器配置?

相 对 于 XML 文 件 ,注 解 型 的 配 置 依 赖 于 通 过 字 节 码 元 数 据 装 配 组 件 ,而 非 尖 括 号

的声明。

开发者通过在相应的类,方法或属性上使用注解的方式,直接组件类中进行配置,

而 不 是 使 用 xml 表 述 bean 的 装 配 关 系 。

38、怎样开启注解装配?

注 解 装 配 在 默 认 情 况 下 是 不 开 启 的 ,为 了 使 用 注 解 装 配 ,我 们 必 须 在 Spring 配 置

文 件 中 配 置 context:annotation-config/元 素 。

39、@Required 注解

这 个 注 解 表 明 bean 的 属 性 必 须 在 配 置 的 时 候 设 置 ,通 过 一 个 bean 定 义 的 显 式 的

属 性 值 或 通 过 自 动 装 配 , 若 @Required 注 解 的 bean 属 性 未 被 设 置 , 容 器 将 抛 出

BeanInitializationException。

第 403 页 共 485 页
40、@Autowired 注解

@Autowired 注 解 提 供 了 更 细 粒 度 的 控 制 , 包 括 在 何 处 以 及 如 何 完 成 自 动 装 配 。

它 的 用 法 和 @Required 一 样 ,修 饰 setter 方 法 、构 造 器 、属 性 或 者 具 有 任 意 名 称

和 /或 多 个 参 数 的 PN 方 法 。

41、@Qualifier 注解

当 有 多 个 相 同 类 型 的 bean 却 只 有 一 个 需 要 自 动 装 配 时 , 将 @Qualifier 注 解 和

@Autowire 注 解 结 合 使 用 以 消 除 这 种 混 淆 , 指 定 需 要 装 配 的 确 切 的 bean。

Spring 数据访问

42.在 Spring 框架中如何更有效地使用 JDBC?

使 用 SpringJDBC 框 架 , 资 源 管 理 和 错 误 处 理 的 代 价 都 会 被 减 轻 。 所 以 开 发 者 只

需 写 statements 和 queries 从 数 据 存 取 数 据 ,JDBC 也 可 以 在 Spring 框 架 提 供

的 模 板 类 的 帮 助 下 更 有 效 地 被 使 用 , 这 个 模 板 叫 JdbcTemplate ( 例 子 见 这 里

here)

43、JdbcTemplate

JdbcTemplate 类 提 供 了 很 多 便 利 的 方 法 解 决 诸 如 把 数 据 库 数 据 转 变 成 基 本 数 据

类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据错误处

理。

第 404 页 共 485 页
44、Spring 对 DAO 的支持

Spring 对 数 据 访 问 对 象 ( DAO) 的 支 持 旨 在 简 化 它 和 数 据 访 问 技 术 如 JDBC,

Hibernate or JDO 结 合 使 用 。这 使 我 们 可 以 方 便 切 换 持 久 层 。编 码 时 也 不 用 担 心

会捕获每种技术特有的异常。

45、使用 Spring 通过什么方式访问 Hibernate?

在 Spring 中 有 两 种 方 式 访 问 Hibernate:

 控制反转 Hibernate Template 和 Callback。


 继承 HibernateDAOSupport 提供一个 AOP 拦截器。

46、Spring 支持的 ORM

Spring 支 持 以 下 ORM:

 Hibernate
 iBatis
 JPA (Java Persistence API)
 TopLink
 JDO (Java Data Objects)
 OJB

第 405 页 共 485 页
47.如何通过 HibernateDaoSupport 将 Spring 和 Hibernate

结合起来?

用 Spring 的 SessionFactory 调 用 LocalSessionFactory。 集 成 过 程 分 三 步 :

 配置 the Hibernate SessionFactory。


 继承 HibernateDaoSupport 实现一个 DAO。
 在 AOP 支持的事务中装配。

48、Spring 支持的事务管理类型

Spring 支 持 两 种 类 型 的 事 务 管 理 :

 编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵
活性,但是难维护。
 声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用
注解和 XML 配置来管理事务。

49、Spring 框架的事务管理有哪些优点?

 它为不同的事务 API 如 JTA,JDBC,Hibernate,JPA 和 JDO,提供


一个不变的编程模式。
 它为编程式事务管理提供了一套简单的 API 而不是一些复杂的事务 API

第 406 页 共 485 页
 它支持声明式事务管理。
 它和 Spring 各种数据访问抽象层很好得集成。

50、你更倾向用那种事务管理类型?

大 多 数 Spring 框 架 的 用 户 选 择 声 明 式 事 务 管 理 , 因 为 它 对 应 用 代 码 的 影 响 最 小 ,

因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务

管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵

活性。

Spring 面向切面编程(AOP)

51、解释 AOP

面 向 切 面 的 编 程 , 或 AOP, 是 一 种 编 程 技 术 , 允 许 程 序 模 块 化 横 向 切 割 关 注 点 ,

或横切典型的责任划分,如日志和事务管理。

52、Aspect 切面

AOP 核 心 就 是 切 面 , 它 将 多 个 类 的 通 用 行 为 封 装 成 可 重 用 的 模 块 , 该 模 块 含 有 一

组 API 提 供 横 切 功 能 。比 如 ,一 个 日 志 模 块 可 以 被 称 作 日 志 的 AOP 切 面 。根 据 需

求 的 不 同 , 一 个 应 用 程 序 可 以 有 若 干 切 面 。 在 Spring AOP 中 , 切 面 通 过 带 有

@Aspect 注 解 的 类 实 现 。

52、在 Spring AOP 中,关注点和横切关注的区别是什么?

第 407 页 共 485 页
关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的

一个功能。

横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应

用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些

都属于横切关注点。

54、连接点

连 接 点 代 表 一 个 应 用 程 序 的 某 个 位 置 , 在 这 个 位 置 我 们 可 以 插 入 一 个 AOP 切 面 ,

它 实 际 上 是 个 应 用 程 序 执 行 Spring AOP 的 位 置 。

55、通知

通知是个在方法执行前或执行后要做的动作,实际上是程序执行时要通过

SpringAOP 框 架 触 发 的 代 码 段 。

Spring 切 面 可 以 应 用 五 种 类 型 的 通 知 :

 before:前置通知,在一个方法执行前被调用。
 after: 在方法执行之后调用的通知,无论方法执行是否成功。
 after-returning: 仅当方法成功完成后执行的通知。
 after-throwing: 在方法抛出异常退出时执行的通知。
 around: 在方法执行之前和之后调用的通知。

56、切点

第 408 页 共 485 页
切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的

方式指明切入点。

57、什么是引入?

引入允许我们在已存在的类中增加新的方法和属性。

58、什么是目标对象?

被一个或者多个切面所通知的对象。它通常是一个代理对象。也指被通知

( advised) 对 象 。

59、什么是代理?

代理是通知目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是

一样的。

60、有几种不同类型的自动代理?

BeanNameAutoProxyCreator

DefaultAdvisorAutoProxyCreator

Metadata autoproxying

61、什么是织入。什么是织入应用的不同点?

第 409 页 共 485 页
织入是将切面和到其他应用类型或对象连接或创建一个被通知对象的过程。

织入可以在编译时,加载时,或运行时完成。

62、解释基于 XML Schema 方式的切面实现。

在 这 种 情 况 下 , 切 面 由 常 规 类 以 及 基 于 XML 的 配 置 实 现 。

63、解释基于注解的切面实现

在 这 种 情 况 下 (基 于 @AspectJ 的 实 现 ),涉 及 到 的 切 面 声 明 的 风 格 与 带 有 java5 标

注 的 普 通 java 类 一 致 。

Spring 的 MVC

64、什么是 Spring 的 MVC 框架?

Spring 配 备 构 建 Web 应 用 的 全 功 能 MVC 框 架 。 Spring 可 以 很 便 捷 地 和 其 他

MVC 框 架 集 成 ,如 Struts,Spring 的 MVC 框 架 用 控 制 反 转 把 业 务 对 象 和 控 制 逻

辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。

65、DispatcherServlet

Spring 的 MVC 框 架 是 围 绕 DispatcherServlet 来 设 计 的 ,它 用 来 处 理 所 有 的 HTTP

请求和响应。

第 410 页 共 485 页
66、WebApplicationContext

WebApplicationContext 继 承 了 ApplicationContext 并 增 加 了 一 些 WEB 应 用

必 备 的 特 有 功 能 , 它 不 同 于 一 般 的 ApplicationContext , 因 为 它 能 处 理 主 题 ,

并 找 到 被 关 联 的 servlet。

67、什么是 Spring MVC 框架的控制器?

控制器提供一个访问应用程序的行为,此行为通常通过服务接口实现。控制器解

析 用 户 输 入 并 将 其 转 换 为 一 个 由 视 图 呈 现 给 用 户 的 模 型 。 Spring 用 一 个 非 常 抽 象

的方式实现了一个控制层,允许用户创建多种用途的控制器。

68、@Controller 注解

该 注 解 表 明 该 类 扮 演 控 制 器 的 角 色 , Spring 不 需 要 你 继 承 任 何 其 他 控 制 器 基 类 或

引 用 Servlet API。

69、@RequestMapping 注解

该 注 解 是 用 来 映 射 一 个 URL 到 一 个 类 或 一 个 特 定 的 方 处 理 法 上 。

微服务 面试题

1、您对微服务有何了解?

第 411 页 共 485 页
微 服 务 ,又 称 微 服 务 架 构 ,是 一 种 架 构 风 格 ,它 将 应 用 程 序 构 建 为 以 业 务 领 域 为

模型的小型自治服务集合 。

通俗地说,你必须看到蜜蜂如何通过对齐六角形蜡细胞来构建它们的蜂窝状物。

他们最初从使用各种材料的小部分开始,并继续从中构建一个大型蜂箱。这些细

胞形成图案,产生坚固的结构,将蜂窝的特定部分固定在一起。这里,每个细胞

独立于另一个细胞,但它也与其他细胞相关。这意味着对一个细胞的损害不会损

害其他细胞,因此,蜜蜂可以在不影响完整蜂箱的情况下重建这些细胞。

第 412 页 共 485 页
图 1: 微 服 务 的 蜂 窝 表 示 – 微 服 务 访 谈 问 题

请参考上图。这里,每个六边形形状代表单独的服务组件。与蜜蜂的工作类似,

每个敏捷团队都使用可用的框架和所选的技术堆栈构建单独的服务组件。就像在

蜂箱中一样,每个服务组件形成一个强大的微服务架构,以提供更好的可扩展性。

此外,敏捷团队可以单独处理每个服务组件的问题,而对整个应用程序没有影响

或影响最小。

2、微服务架构有哪些优势?

图 2: 微 服 务 的 优 点 – 微 服 务 访 谈 问 题

 独立开发 – 所有微服务都可以根据各自的功能轻松开发
 独立部署 – 基于其服务,可以在任何应用程序中单独部署它们
 故障隔离 – 即使应用程序的一项服务不起作用,系统仍可继续运行
 混合技术堆栈 – 可以使用不同的语言和技术来构建同一应用程序的不同
服务
 粒度缩放 – 单个组件可根据需要进行缩放,无需将所有组件缩放在一起

第 413 页 共 485 页
3。微服务有哪些特点?

图 3: 微 服 务 的 特 点 – 微 服 务 访 谈 问 题

 解耦 – 系统内的服务很大程度上是分离的。因此,整个应用程序可以轻
松构建,更改和扩展
 组件化 – 微服务被视为可以轻松更换和升级的独立组件
 业务能力 – 微服务非常简单,专注于单一功能
 自治 – 开发人员和团队可以彼此独立工作,从而提高速度
 持续交付 – 通过软件创建,测试和批准的系统自动化,允许频繁发布软

 责任 – 微服务不关注应用程序作为项目。相反,他们将应用程序视为他
们负责的产品
 分散治理 – 重点是使用正确的工具来做正确的工作。这意味着没有标准
化模式或任何技术模式。开发人员可以自由选择最有用的工具来解决他们的问题
 敏捷 – 微服务支持敏捷开发。任何新功能都可以快速开发并再次丢弃

第 414 页 共 485 页
4、设计微服务的最佳实践是什么?

以下是设计微服务的最佳实践:

图 4: 设 计 微 服 务 的 最 佳 实 践 – 微 服 务 访 谈 问 题

5、微服务架构如何运作?

微服务架构具有以下组件:

第 415 页 共 485 页
图 5: 微 服 务 架 构 – 微 服 务 面 试 问 题

 客户端 – 来自不同设备的不同用户发送请求。
 身份提供商 – 验证用户或客户身份并颁发安全令牌。
 API 网关 – 处理客户端请求。
 静态内容 – 容纳系统的所有内容。
 管理 – 在节点上平衡服务并识别故障。
 服务发现 – 查找微服务之间通信路径的指南。
 内容交付网络 – 代理服务器及其数据中心的分布式网络。
 远程服务 – 启用驻留在 IT 设备网络上的远程访问信息。

6、微服务架构的优缺点是什么?

第 416 页 共 485 页
7、单片,SOA 和微服务架构有什么区别?

图 6: 单 片 SOA 和 微 服 务 之 间 的 比 较 – 微 服 务 访 谈 问 题

 单片架构类似于大容器,其中应用程序的所有软件组件组装在一起并紧密
封装。

第 417 页 共 485 页
 一个面向服务的架构是一种相互通信服务的集合。通信可以涉及简单的数
据传递,也可以涉及两个或多个协调某些活动的服务。
 微服务架构是一种架构风格,它将应用程序构建为以业务域为模型的小型
自治服务集合。

8、在使用微服务架构时,您面临哪些挑战?

开发一些较小的微服务听起来很容易,但开发它们时经常遇到的挑战如下。

 自动化组件:难以自动化,因为有许多较小的组件。因此,对于每个组件,
我们必须遵循 Build,Deploy 和 Monitor 的各个阶段。
 易感性:将大量组件维护在一起变得难以部署,维护,监控和识别问题。
它需要在所有组件周围具有很好的感知能力。
 配置管理:有时在各种环境中维护组件的配置变得困难。
 调试:很难找到错误的每一项服务。维护集中式日志记录和仪表板以调试
问题至关重要。

9、SOA 和微服务架构之间的主要区别是什么?

SOA 和 微 服 务 之 间 的 主 要 区 别 如 下 :

第 418 页 共 485 页
10、微服务有什么特点?

您可以列出微服务的特征,如下所示:

图 7: 微 服 务 的 特 征 – 微 服 务 访 谈 问 题

11、什么是领域驱动设计?

第 419 页 共 485 页
图 8: DDD 原 理 – 微 服 务 面 试 问 题

12、为什么需要域驱动设计(DDD)?

图 9: 我 们 需 要 DDD 的 因 素 – 微 服 务 面 试 问 题

13、什么是无所不在的语言?

第 420 页 共 485 页
如 果 您 必 须 定 义 泛 在 语 言 ( UL) , 那 么 它 是 特 定 域 的 开 发 人 员 和 用 户 使 用 的 通 用

语言,通过该语言可以轻松解释域。

无处不在的语言必须非常清晰,以便它将所有团队成员放在同一页面上,并以机

器可以理解的方式进行翻译。

14、什么是凝聚力?

模块内部元素所属的程度被认为是凝聚力。

15、什么是耦合?

组件之间依赖关系强度的度量被认为是耦合。一个好的设计总是被认为具有高内

聚力和低耦合性。

16、什么是 REST / RESTful 以及它的用途是什么?

Representational State Transfer( REST) / RESTful Web 服 务 是 一 种 帮 助 计

算 机 系 统 通 过 Internet 进 行 通 信 的 架 构 风 格 。 这 使 得 微 服 务 更 容 易 理 解 和 实 现 。

微 服 务 可 以 使 用 或 不 使 用 RESTful API 实 现 , 但 使 用 RESTful API 构 建 松 散 耦 合

的微服务总是更容易。

17、你对 Spring Boot 有什么了解?

第 421 页 共 485 页
事 实 上 , 随 着 新 功 能 的 增 加 , 弹 簧 变 得 越 来 越 复 杂 。 如 果 必 须 启 动 新 的 spring 项

目 ,则 必 须 添 加 构 建 路 径 或 添 加 maven 依 赖 项 ,配 置 应 用 程 序 服 务 器 ,添 加 spring

配置。所以一切都必须从头开始。

Spring Boot 是 解 决 这 个 问 题 的 方 法 。 使 用 spring boot 可 以 避 免 所 有 样 板 代 码

和配置。因此,基本上认为自己就好像你正在烘烤蛋糕一样,春天就像制作蛋糕

所需的成分一样,弹簧靴就是你手中的完整蛋糕。

图 10: Spring Boot 的 因 素 – 微 服 务 面 试 问 题

18、什么是 Spring 引导的执行器?

Spring Boot 执 行 程 序 提 供 了 restful Web 服 务 ,以 访 问 生 产 环 境 中 运 行 应 用 程 序

的当前状态。在执行器的帮助下,您可以检查各种指标并监控您的应用程序。

19、什么是 Spring Cloud?

第 422 页 共 485 页
根 据 Spring Cloud 的 官 方 网 站 , Spring Cloud 为 开 发 人 员 提 供 了 快 速 构 建 分 布

式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,

领导选举,分布式会话,集群状态)。

20、Spring Cloud 解决了哪些问题?

在 使 用 Spring Boot 开 发 分 布 式 微 服 务 时 ,我 们 面 临 的 问 题 很 少 由 Spring Cloud

解决。

 与分布式系统相关的复杂性 – 包括网络问题,延迟开销,带宽问题,安
全问题。
 处理服务发现的能力 – 服务发现允许集群中的进程和服务找到彼此并进
行通信。
 解决冗余问题 – 冗余问题经常发生在分布式系统中。
 负载平衡 – 改进跨多个计算资源(例如计算机集群,网络链接,中央处
理单元)的工作负载分布。
 减少性能问题 – 减少因各种操作开销导致的性能问题。

21、在 Spring MVC 应用程序中使用 WebMvcTest 注释有什

么用处?

在 测 试 目 标 只 关 注 Spring MVC 组 件 的 情 况 下 ,WebMvcTest 注 释 用 于 单 元 测 试

Spring MVC 应 用 程 序 。在 上 面 显 示 的 快 照 中 ,我 们 只 想 启 动 ToTestController。

执行此单元测试时,不会启动所有其他控制器和映射。

第 423 页 共 485 页
22。你能否给出关于休息和微服务的要点?

虽 然 您 可 以 通 过 多 种 方 式 实 现 微 服 务 , 但 REST over HTTP 是 实 现 微 服 务 的 一 种

方 式 。REST 还 可 用 于 其 他 应 用 程 序 ,如 Web 应 用 程 序 ,API 设 计 和 MVC 应 用 程

序,以提供业务数据。

微服务是一种体系结构,其中系统的所有组件都被放入单独的组件中,这些组件

可以单独构建,部署和扩展。微服务的某些原则和最佳实践有助于构建弹性应用

程序。

简 而 言 之 , 您 可 以 说 REST 是 构 建 微 服 务 的 媒 介 。

23、什么是不同类型的微服务测试?

在使用微服务时,由于有多个微服务协同工作,测试变得非常复杂。因此,测试

分为不同的级别。

 在底层,我们有面向技术的测试,如单元测试和性能测试。这些是完全自
动化的。
 在中间层面,我们进行了诸如压力测试和可用性测试之类的探索性测试。
 在顶层, 我们的 验收测试数量很少。这些验收测试有助于利益相关者理
解和验证软件功能。

24、您对 Distributed Transaction 有何了解?

第 424 页 共 485 页
分布式事务是指单个事件导致两个或多个不能以原子方式提交的单独数据源的突

变的任何情况。在微服务的世界中,它变得更加复杂,因为每个服务都是一个工

作单元,并且大多数时候多个服务必须协同工作才能使业务成功。

25、什么是 Idempotence 以及它在哪里使用?

幂等性是能够以这样的方式做两次事情的特性,即最终结果将保持不变,即好像

它只做了一次。

用 法 :在 远 程 服 务 或 数 据 源 中 使 用 Idempotence,这 样 当 它 多 次 接 收 指 令 时 ,它

只处理指令一次。

26、什么是有界上下文?

有 界 上 下 文 是 域 驱 动 设 计 的 核 心 模 式 。 DDD 战 略 设 计 部 门 的 重 点 是 处 理 大 型 模 型

和 团 队 。 DDD 通 过 将 大 型 模 型 划 分 为 不 同 的 有 界 上 下 文 并 明 确 其 相 互 关 系 来 处 理

大型模型。

27、什么是双因素身份验证?

双因素身份验证为帐户登录过程启用第二级身份验证。

第 425 页 共 485 页
图 11: 双 因 素 认 证 的 表 示 – 微 服 务 访 谈 问 题

因此,假设用户必须只输入用户名和密码,那么这被认为是单因素身份验证。

28、双因素身份验证的凭据类型有哪些?

这三种凭证是:

第 426 页 共 485 页
图 12: 双 因 素 认 证 的 证 书 类 型 – 微 服 务 面 试 问 题

29、什么是客户证书?

客户端系统用于向远程服务器发出经过身份验证的请求的一种数字证书称为客户

端证书。客户端证书在许多相互认证设计中起着非常重要的作用,为请求者的身

份提供了强有力的保证。

30、PACT 在微服务架构中的用途是什么?

PACT 是 一 个 开 源 工 具 , 允 许 测 试 服 务 提 供 者 和 消 费 者 之 间 的 交 互 , 与 合 同 隔 离 ,

从而提高微服务集成的可靠性。

微服务中的用法

 用于在微服务中实现消费者驱动的合同。
 测试微服务的消费者和提供者之间的消费者驱动的合同。

查看即将到来的批次

31、什么是 OAuth?

OAuth 代 表 开 放 授 权 协 议 。这 允 许 通 过 在 HTTP 服 务 上 启 用 客 户 端 应 用 程 序( 例

如 第 三 方 提 供 商 Facebook, GitHub 等 ) 来 访 问 资 源 所 有 者 的 资 源 。 因 此 , 您 可

以在不使用其凭据的情况下与另一个站点共享存储在一个站点上的资源。

第 427 页 共 485 页
32、康威定律是什么?

“任 何 设 计 系 统 的 组 织 ( 广 泛 定 义 ) 都 将 产 生 一 种 设 计 , 其 结 构 是 组 织 通 信 结 构
的 副 本 。” – Mel Conw ay

图 13: Conway 定 律 的 表 示 – 微 服 务 访 谈 问 题

该法律基本上试图传达这样一个事实:为了使软件模块起作用,整个团队应该进

行良好的沟通。因此,系统的结构反映了产生它的组织的社会边界。

33、合同测试你懂什么?

根 据 Martin Flower 的 说 法 , 合 同 测 试 是 在 外 部 服 务 边 界 进 行 的 测 试 , 用 于 验 证

其是否符合消费服务预期的合同。

此外,合同测试不会深入测试服务的行为。更确切地说,它测试该服务调用的输

入&输出包含所需的属性和所述响应延迟,吞吐量是允许的限度内。

第 428 页 共 485 页
34、什么是端到端微服务测试?

端到端测试验证了工作流中的每个流程都正常运行。这可确保系统作为一个整体

协同工作并满足所有要求。

通俗地说,你可以说端到端测试是一种测试,在特定时期后测试所有东西。

图 14: 测 试 层 次 – 微 服 务 面 试 问 题

35、Container 在微服务中的用途是什么?

第 429 页 共 485 页
容器是管理基于微服务的应用程序以便单独开发和部署它们的好方法。您可以将

微 服 务 封 装 在 容 器 映 像 及 其 依 赖 项 中 ,然 后 可 以 使 用 它 来 滚 动 按 需 实 例 的 微 服 务 ,

而无需任何额外的工作。

图 15: 容 器 的 表 示 及 其 在 微 服 务 中 的 使 用 方 式 – 微 服 务 访 谈 问 题

36、什么是微服务架构中的 DRY?

DRY 代 表 不 要 重 复 自 己 。它 基 本 上 促 进 了 重 用 代 码 的 概 念 。这 导 致 开 发 和 共 享 库 ,

这反过来导致紧密耦合。

37、什么是消费者驱动的合同(CDC)?

这基本上是用于开发微服务的模式,以便它们可以被外部系统使用。当我们处理

微服务时,有一个特定的提供者构建它,并且有一个或多个使用微服务的消费者。

通 常 ,提 供 程 序 在 XML 文 档 中 指 定 接 口 。但 在 消 费 者 驱 动 的 合 同 中 ,每 个 服 务 消

费者都传达了提供商期望的接口。

第 430 页 共 485 页
38、Web,RESTful API 在微服务中的作用是什么?

微服务架构基于一个概念,其中所有服务应该能够彼此交互以构建业务功能。因

此 , 要 实 现 这 一 点 , 每 个 微 服 务 必 须 具 有 接 口 。 这 使 得 Web API 成 为 微 服 务 的 一

个 非 常 重 要 的 推 动 者 。 RESTful API 基 于 Web 的 开 放 网 络 原 则 , 为 构 建 微 服 务 架

构的各个组件之间的接口提供了最合理的模型。

39、您对微服务架构中的语义监控有何了解?

语义监控,也称为 综合监控, 将自动化测试与监控应用程序相结合,以检测业

务失败因素。

40、我们如何进行跨功能测试?

跨功能测试是对非功能性需求的验证,即那些无法像普通功能那样实现的需求。

41、我们如何在测试中消除非决定论?

非 确 定 性 测 试( NDT)基 本 上 是 不 可 靠 的 测 试 。所 以 ,有 时 可 能 会 发 生 它 们 通 过 ,

显然有时它们也可能会失败。当它们失败时,它们会重新运行通过。

从测试中删除非确定性的一些方法如下:

1、 隔离

2、 异步

3、 远程服务

4、 隔离

第 431 页 共 485 页
5、 时间

6、 资源泄漏

42、Mock 或 Stub 有什么区别?

存根

 一个有助于运行测试的虚拟对象。
 在某些可以硬编码的条件下提供固定行为。
 永远不会测试存根的任何其他行为。

例 如 ,对 于 空 堆 栈 ,您 可 以 创 建 一 个 只 为 empty( )方 法 返 回 true 的 存 根 。因 此 ,

这并不关心堆栈中是否存在元素。

嘲笑

 一个虚拟对象,其中最初设置了某些属性。
 此对象的行为取决于 set 属性。
 也可以测试对象的行为。

例 如 ,对 于 Customer 对 象 ,您 可 以 通 过 设 置 名 称 和 年 龄 来 模 拟 它 。您 可 以 将 age

设 置 为 12,然 后 测 试 isAdult( )方 法 ,该 方 法 将 在 年 龄 大 于 18 时 返 回 true。因

此 , 您 的 Mock Customer 对 象 适 用 于 指 定 的 条 件 。

43、您对 Mike Cohn 的测试金字塔了解多少?

第 432 页 共 485 页
Mike Cohn 提 供 了 一 个 名 为 Test Pyramid 的 模 型 。这 描 述 了 软 件 开 发 所 需 的 自

动化测试类型。

图 16: Mike Cohn 的 测 试 金 字 塔 – 微 服 务 面 试 问 题

根据金字塔,第一层的测试数量应该最高。在服务层,测试次数应小于单元测试

级别,但应大于端到端级别。

44、Docker 的目的是什么?

Docker 提 供 了 一 个 可 用 于 托 管 任 何 应 用 程 序 的 容 器 环 境 。在 此 ,软 件 应 用 程 序 和

支持它的依赖项紧密打包在一起。

因 此 , 这 个 打 包 的 产 品 被 称 为 Container, 因 为 它 是 由 Docker 完 成 的 , 所 以 它

被 称 为 Docker 容 器 !

第 433 页 共 485 页
45、什么是金丝雀释放?

Canary Releasing 是 一 种 降 低 在 生 产 中 引 入 新 软 件 版 本 的 风 险 的 技 术 。这 是 通 过

将变更缓慢地推广到一小部分用户,然后将其发布到整个基础架构,即将其提供

给每个人来完成的。

46、什么是持续集成(CI)?

持 续 集 成 ( CI) 是 每 次 团 队 成 员 提 交 版 本 控 制 更 改 时 自 动 构 建 和 测 试 代 码 的 过 程 。

这鼓励开发人员通过在每个小任务完成后将更改合并到共享版本控制存储库来共

享代码和单元测试。

47、什么是持续监测?

持续监控深入监控覆盖范围,从浏览器内前端性能指标,到应用程序性能,再到

主机虚拟化基础架构指标。

48、架构师在微服务架构中的角色是什么?

微服务架构中的架构师扮演以下角色:

 决定整个软件系统的布局。
 帮助确定组件的分区。因此,他们确保组件相互粘合,但不紧密耦合。
 与开发人员共同编写代码,了解日常生活中面临的挑战。
 为开发微服务的团队提供某些工具和技术的建议。

第 434 页 共 485 页
 提供技术治理,以便技术开发团队遵循微服务原则。

49、我们可以用微服务创建状态机吗?

我们知道拥有自己的数据库的每个微服务都是一个可独立部署的程序单元,这反

过来又让我们可以创建一个状态机。因此,我们可以为特定的微服务指定不同的

状态和事件。

例 如 , 我 们 可 以 定 义 Order 微 服 务 。 订 单 可 以 具 有 不 同 的 状 态 。 Order 状 态 的 转

换 可 以 是 Order 微 服 务 中 的 独 立 事 件 。

50、什么是微服务中的反应性扩展?

Reactive Extensions 也 称 为 Rx。 这 是 一 种 设 计 方 法 , 我 们 通 过 调 用 多 个 服 务 来

收 集 结 果 , 然 后 编 译 组 合 响 应 。 这 些 调 用 可 以 是 同 步 或 异 步 , 阻 塞 或 非 阻 塞 。 Rx

是分布式系统中非常流行的工具,与传统流程相反。

希望这些微服务面试问题可以帮助您进行微服务架构师访谈。

翻译来源:

https://www.edureka.co/blog/interview-questions/microservices-intervie

w-questions/

Linux 面试题

第 435 页 共 485 页
1、绝对路径用什么符号表示?当前目录、上层目录用什么表示?

主目录用什么表示? 切换目录用什么命令?

答案:

绝 对 路 径 : 如 /etc/init.d

当 前 目 录 和 上 层 目 录 : ./ ../

主 目 录 : ~/

切 换 目 录 : cd

2、怎么查看当前进程?怎么执行退出?怎么查看当前路径?

答案:

查 看 当 前 进 程 : ps

执 行 退 出 : exit

查 看 当 前 路 径 : pwd

3、怎么清屏?怎么退出当前命令?怎么执行睡眠?怎么查看当

前用户 id?查看指定帮助用什么命令?

答案:

清 屏 : clear

退 出 当 前 命 令 : ctrl+c 彻 底 退 出

执 行 睡 眠 : ctrl+z 挂 起 当 前 进 程 fg 恢 复 后 台

第 436 页 共 485 页
查 看 当 前 用 户 id: ” id“ : 查 看 显 示 目 前 登 陆 账 户 的 uid 和 gid 及 所 属 分 组

及用户名

查 看 指 定 帮 助 : 如 man adduser 这 个 很 全 而 且 有 例 子 ; adduser --help 这

个 告 诉 你 一 些 常 用 参 数 ; info adduesr;

4、Ls 命令执行什么功能? 可以带哪些参数,有什么区别?

答案:

ls 执 行 的 功 能 : 列 出 指 定 目 录 中 的 目 录 , 以 及 文 件

哪 些 参 数 以 及 区 别 : a 所 有 文 件 l 详 细 信 息 ,包 括 大 小 字 节 数 ,可 读 可 写 可 执 行

的权限等

5、建立软链接(快捷方式),以及硬链接的命令。

答案:

软 链 接 : ln -s slink source

硬 链 接 : ln link source

6、目录创建用什么命令?创建文件用什么命令?复制文件用什

么命令?

答案:

创 建 目 录 : mkdir

第 437 页 共 485 页
创 建 文 件 :典 型 的 如 touch,vi 也 可 以 创 建 文 件 ,其 实 只 要 向 一 个 不 存 在 的 文 件

输出,都会创建文件

复 制 文 件 : cp 7. 文 件 权 限 修 改 用 什 么 命 令 ? 格 式 是 怎 么 样 的 ?

文 件 权 限 修 改 : chmod

格式如下:

chmodu+xfile 给 file 的 属 主 增 加 执 行 权 限 chmod 751 file 给 file 的 属 主 分 配

读 、 写 、 执 行 (7)的 权 限 , 给 file 的 所 在 组 分 配 读 、 执 行 (5)的 权 限 , 给 其 他 用 户

分 配 执 行 (1)的 权 限

chmodu=rwx,g=rx,o=xfile 上 例 的 另 一 种 形 式 chmod =r file 为 所 有 用 户 分 配

读权限

chmod444file 同 上 例 chmod a-wx,a+r file 同 上 例

$ chmod -R u+r directory 递 归 地 给 directory 目 录 下 所 有 文 件 和 子 目 录 的 属

主分配读的权限

7、查看文件内容有哪些命令可以使用?

答案:

vi 文 件 名 #编 辑 方 式 查 看 , 可 修 改

cat 文 件 名 #显 示 全 部 文 件 内 容

more 文 件 名 #分 页 显 示 文 件 内 容

less 文 件 名 #与 more 相 似 , 更 好 的 是 可 以 往 前 翻 页

tail 文 件 名 #仅 查 看 尾 部 , 还 可 以 指 定 行 数

head 文 件 名 #仅 查 看 头 部 ,还 可 以 指 定 行 数

第 438 页 共 485 页
8、随意写文件命令?怎么向屏幕输出带空格的字符串,比如”

hello world”?

答案:

写 文 件 命 令 : vi

向 屏 幕 输 出 带 空 格 的 字 符 串 :echo hello world

9、终端是哪个文件夹下的哪个文件?黑洞文件是哪个文件夹下

的哪个命令?

答案:

终端 /dev/tty

黑洞文件 /dev/null

10、移动文件用哪个命令?改名用哪个命令?

答案:

mv mv

第 439 页 共 485 页
11、复制文件用哪个命令?如果需要连同文件夹一块复制呢?

如果需要有提示功能呢?

答案:

cp cp -r ????

12、删除文件用哪个命令?如果需要连目录及目录下文件一块

删除呢?删除空文件夹用什么命令?

答案:

rm rm -r rmdir

13、Linux 下命令有哪几种可使用的通配符?分别代表什么含

义?

答案:

“?”可替代单个字符。

“ *” 可 替 代 任 意 多 个 字 符 。

方 括 号 “ [charset]” 可 替 代 charset 集 中 的 任 何 单 个 字 符 , 如 [a-z], [abABC]

第 440 页 共 485 页
14、用什么命令对一个文件的内容进行统计?(行号、单词数、

字节数)

答案:

wc 命 令 - c 统 计 字 节 数 - l 统 计 行 数 - w 统 计 字 数 。

15、Grep 命令有什么用? 如何忽略大小写? 如何查找不含

该串的行?

答案:

是 一 种 强 大 的 文 本 搜 索 工 具 ,它 能 使 用 正 则 表 达 式 搜 索 文 本 ,并 把 匹 配 的 行 打 印

出来。

grep [stringSTRING] filename grep [^string] filename

16、Linux 中进程有哪几种状态?在 ps 显示出来的信息中,

分别用什么符号表示的?

答案:

1、 不 可 中 断 状 态 : 进 程 处 于 睡 眠 状 态 , 但 是 此 刻 进 程 是 不 可 中 断 的 。 不 可 中 断 ,

指进程不响应异步信号。

第 441 页 共 485 页
2、暂 停 状 态 /跟 踪 状 态 :向 进 程 发 送 一 个 SIGSTOP 信 号 ,它 就 会 因 响 应 该 信 号 而

进 入 TASK_STOPPED 状 态 ;当 进 程 正 在 被 跟 踪 时 , 它 处 于 TASK_TRACED 这 个

特殊的状态。

正被跟踪”指的是进程暂停下来,等待跟踪它的进程对它进行操作。

3、 就 绪 状 态 : 在 run_queue 队 列 里 的 状 态

4、 运 行 状 态 : 在 run_queue 队 列 里 的 状 态

5、 可 中 断 睡 眠 状 态 : 处 于 这 个 状 态 的 进 程 因 为 等 待 某 某 事 件 的 发 生 ( 比 如 等 待

socket 连 接 、 等 待 信 号 量 ) , 而 被 挂 起

6、zombie 状 态( 僵 尸 ):父 亲 没 有 通 过 wait 系 列 的 系 统 调 用 会 顺 便 将 子 进 程

的 尸 体 ( task_struct) 也 释 放 掉

7、 退 出 状 态

D 不 可 中 断 Uninterruptible( usually IO)

R 正在运行,或在队列中的进程

S 处于休眠状态

T 停止或被追踪

Z 僵尸进程

W 进 入 内 存 交 换 ( 从 内 核 2.6 开 始 无 效 )

X 死掉的进程

17、怎么使一个命令在后台运行?

答案:

一 般 都 是 使 用 & 在 命 令 结 尾 来 让 程 序 自 动 运 行 。 (命 令 后 可 以 不 追 加 空 格 )

第 442 页 共 485 页
18、利用 ps 怎么显示所有的进程? 怎么利用 ps 查看指定进

程的信息?

答案:

ps -ef (system v 输出)

ps -aux bsd 格式输出

ps -ef | grep pid

19、哪个命令专门用来查看后台任务?

答案:

job -l

20、把后台任务调到前台执行使用什么命令?把停下的后台任务

在后台执行起来用什么命令?

答案:

把 后 台 任 务 调 到 前 台 执 行 fg

把 停 下 的 后 台 任 务 在 后 台 执 行 起 来 bg

第 443 页 共 485 页
21、终止进程用什么命令? 带什么参数?

答案:

kill [-s <信 息 名 称 或 编 号 >][程 序 ] 或 kill [-l <信 息 编 号 >]

kill-9 pid

22、怎么查看系统支持的所有信号?

答案:

kill -l

23、搜索文件用什么命令? 格式是怎么样的?

答案:

find <指 定 目 录 > <指 定 条 件 > <指 定 动 作 >

whereis 加 参 数 与 文 件 名

locate 只 加 文 件 名

find 直 接 搜 索 磁 盘 , 较 慢 。

find / -name "string*"

第 444 页 共 485 页
24、查看当前谁在使用该主机用什么命令? 查找自己所在的终

端信息用什么命令?

答案:

查 找 自 己 所 在 的 终 端 信 息 : who am i

查 看 当 前 谁 在 使 用 该 主 机 : who

25、使用什么命令查看用过的命令列表?

答案:

history

26、使用什么命令查看磁盘使用空间? 空闲空间呢?

答案:

df -hl

文件系统 容量 已用 可用 已用% 挂载点

Filesystem Size Used Avail Use% Mounted on /dev/hda2 45G 19G 24G
44% /
/dev/hda1 494M 19M 450M 4% /boot

第 445 页 共 485 页
27、使用什么命令查看网络是否连通?

答案:

netstat

28、使用什么命令查看 ip 地址及接口信息?

答案:

ifconfig

29、查看各类环境变量用什么命令?

答案:

查 看 所 有 env

查 看 某 个 , 如 home: env $HOME

30、通过什么命令指定命令提示符?

答案:

 \u:显示当前用户账号
 \h:显示当前主机名

第 446 页 共 485 页
 \W:只显示当前路径最后一个目录
 \w:显示当前绝对路径(当前用户目录会以~代替)
 $PWD:显示当前全路径
 $:显示命令行’$'或者’#'符号
 #:下达的第几个命令
 \d:代表日期,格式为 week day month date,例如:"MonAug1"
 \t:显示时间为 24 小时格式,如:HH:MM:SS
 \T:显示时间为 12 小时格式
 \A:显示时间为 24 小时格式:HH:MM
 \v:BASH 的版本信息 如 export PS1=’[\u@\h\w#]$‘

31、查找命令的可执行文件是去哪查找的? 怎么对其进行设置

及添加?

答案:

whereis [-bfmsu][-B <目 录 >...][-M <目 录 >...][-S <目 录 >...][文 件 ...]

补 充 说 明 : whereis 指 令 会 在 特 定 目 录 中 查 找 符 合 条 件 的 文 件 。 这 些 文 件 的 烈 性

应属于原始代码,二进制文件,或是帮助文件。

 -b 只查找二进制文件。
 -B <目录> 只在设置的目录下查找二进制文件。 -f 不显示文件名前的
路径名称。
 -m 只查找说明文件。
 -M <目录> 只在设置的目录下查找说明文件。-s 只查找原始代码文件。
 -S <目录> 只在设置的目录下查找原始代码文件。 -u 查找不包含指定
类型的文件。

第 447 页 共 485 页
w -h ich 指令会在 PATH 变量指定的路径中,搜索某个系统命令的位置,并且
返回第一个搜索结果。
 -n 指定文件名长度,指定的长度必须大于或等于所有文件中最长的文件
名。
 -p 与-n 参数相同,但此处的包括了文件的路径。 -w 指定输出时栏位
的宽度。
 -V 显示版本信息

32、通过什么命令查找执行命令?

答案:

which 只 能 查 可 执 行 文 件

whereis 只 能 查 二 进 制 文 件 、 说 明 文 档 , 源 文 件 等

33、怎么对命令进行取别名?

答案:

alias la='ls -a'

34、du 和 df 的定义,以及区别?

答案:

du 显 示 目 录 或 文 件 的 大 小

第 448 页 共 485 页
df 显 示 每 个 <文 件 >所 在 的 文 件 系 统 的 信 息 , 默 认 是 显 示 所 有 文 件 系 统 。

(文件系统分配其中的一些磁盘块用来记录它自身的一些数据,如 i 节点,磁盘

分布图,间接块,超级块等。这些数据对大多数用户级的程序来说是不可见的,

通 常 称 为 Meta Data。) du 命 令 是 用 户 级 的 程 序 ,它 不 考 虑 Meta Data,而 df

命 令 则 查 看 文 件 系 统 的 磁 盘 分 配 图 并 考 虑 Meta Data。

df 命 令 获 得 真 正 的 文 件 系 统 数 据 , 而 du 命 令 只 查 看 文 件 系 统 的 部 分 情 况 。

35、awk 详解。

答案:

awk '{pattern + action}' {filenames}


#cat /etc/passwd |awk -F ':' '{print 1"\t"7}' //-F 的意思是以':'分隔 root
/bin/bash
daemon /bin/sh 搜索/etc/passwd 有 root 关键字的所有行

#awk -F: '/root/' /etc/passwd root:x:0:0:root:/root:/bin/bash

36、当你需要给命令绑定一个宏或者按键的时候,应该怎么做

呢?

答案:

可 以 使 用 bind 命 令 , bind 可 以 很 方 便 地 在 shell 中 实 现 宏 或 按 键 的 绑 定 。

在进行按键绑定的时候,我们需要先获取到绑定按键对应的字符序列。

第 449 页 共 485 页
比 如 获 取 F12 的 字 符 序 列 获 取 方 法 如 下 : 先 按 下 Ctrl+V,然 后 按 下 F12 .我 们 就 可

以 得 到 F12 的 字 符 序 列 ^[[24~。

接 着 使 用 bind 进 行 绑 定 。

[root@localhost ~]# bind ‘”\e[24~":"date"'

注意:相同的按键在不同的终端或终端模拟器下可能会产生不同的字符序列。

【 附 】 也 可 以 使 用 showkey -a 命 令 查 看 按 键 对 应 的 字 符 序 列 。

37、如果一个 linux 新手想要知道当前系统支持的所有命令的

列表,他需要怎么做?

答案:

使 用 命 令 compgen -c, 可 以 打 印 出 所 有 支 持 的 命 令 列 表 。

[root@localhost ~]$ compgen -c


l.
ll
ls
which
if
then
else
elif
fi
case

第 450 页 共 485 页
esac
for
select
while
until
do
done

38、如果你的助手想要打印出当前的目录栈,你会建议他怎么

做?

答案:

使 用 Linux 命 令 dirs 可 以 将 当 前 的 目 录 栈 打 印 出 来 。

[root@localhost ~]# dirs


/usr/share/X11

【 附 】 : 目 录 栈 通 过 pushd popd 来 操 作 。

39、你的系统目前有许多正在运行的任务,在不重启机器的条

件下,有什么方法可以把所有正在运行的进程移除呢?

答案:

使 用 linux 命 令 ’ disown -r ’ 可 以 将 所 有 正 在 运 行 的 进 程 移 除 。

第 451 页 共 485 页
40、bash shell 中的 hash 命令有什么作用?

答案:

linux 命 令 ’hash’管 理 着 一 个 内 置 的 哈 希 表 ,记 录 了 已 执 行 过 的 命 令 的 完 整 路 径 ,

用该命令可以打印出你所使用过的命令以及执行的次数。

[root@localhost ~]# hash

hits command

2 /bin/ls

2 /bin/su

41、哪一个 bash 内置命令能够进行数学运算。

答案:

bash shell 的 内 置 命 令 let 可 以 进 行 整 型 数 的 数 学 运 算 。

#! /bin/bash


let c=a+b

第 452 页 共 485 页
42、怎样一页一页地查看一个大文件的内容呢?

答案:

通 过 管 道 将 命 令 ” cat file_name.txt” 和 ’ more’ 连 接 在 一 起 可 以 实 现 这 个

需要.

[root@localhost ~]# cat file_name.txt | more

43、数据字典属于哪一个用户的?

答案:

数 据 字 典 是 属 于 ’SYS’用 户 的 ,用 户‘ SYS’ 和 ’SYSEM’是 由 系 统 默 认 自 动

创建的

44、怎样查看一个 linux 命令的概要与用法?假设你在/bin 目

录中偶然看到一个你从没见过的的命令,怎样才能知道它的作用

和用法呢?

答案:

使 用 命 令 whatis 可 以 先 出 显 示 出 这 个 命 令 的 用 法 简 要 ,比 如 ,你 可 以 使 用 whatis

zcat 去 查 看 ‘ zcat’ 的 介 绍 以 及 使 用 简 要 。

[root@localhost ~]# whatis zcat

第 453 页 共 485 页
zcat [gzip] (1) – compress or expand files

45、使用哪一个命令可以查看自己文件系统的磁盘空间配额

呢?

答案:

使 用 命 令 repquota 能 够 显 示 出 一 个 文 件 系 统 的 配 额 信 息

【 附 】 只 有 root 用 户 才 能 够 查 看 其 它 用 户 的 配 额 。

Spring Boot 面试题

1、什么是 Spring Boot?

多 年 来 , 随 着 新 功 能 的 增 加 , spring 变 得 越 来 越 复 杂 。 只 需 访 问

https://spring.io/projects 页 面 ,我 们 就 会 看 到 可 以 在 我 们 的 应 用 程 序 中 使 用 的

所 有 Spring 项 目 的 不 同 功 能 。 如 果 必 须 启 动 一 个 新 的 Spring 项 目 , 我 们 必 须 添

加 构 建 路 径 或 添 加 Maven 依 赖 关 系 , 配 置 应 用 程 序 服 务 器 , 添 加 spring 配 置 。

因 此 ,开 始 一 个 新 的 spring 项 目 需 要 很 多 努 力 , 因 为 我 们 现 在 必 须 从 头 开 始 做 所

有事情。

Spring Boot 是 解 决 这 个 问 题 的 方 法 。 Spring Boot 已 经 建 立 在 现 有 spring 框 架

之 上 。 使 用 spring 启 动 , 我 们 避 免 了 之 前 我 们 必 须 做 的 所 有 样 板 代 码 和 配 置 。 因

此 , Spring Boot 可 以 帮 助 我 们 以 最 少 的 工 作 量 , 更 加 健 壮 地 使 用 现 有 的 Spring

功能。

第 454 页 共 485 页
2、Spring Boot 有哪些优点?

Spring Boot 的 优 点 有 :

1、 减 少 开 发 , 测 试 时 间 和 努 力 。

2、 使 用 JavaConfig 有 助 于 避 免 使 用 XML。

3、 避 免 大 量 的 Maven 导 入 和 各 种 版 本 冲 突 。

4、 提 供 意 见 发 展 方 法 。

5、 通 过 提 供 默 认 值 快 速 开 始 开 发 。

6、 没 有 单 独 的 Web 服 务 器 需 要 。 这 意 味 着 你 不 再 需 要 启 动 Tomcat, Glassfish

或其他任何东西。

7、需 要 更 少 的 配 置 因 为 没 有 web.xml 文 件 。只 需 添 加 用 @ Configuration 注 释

的 类 ,然 后 添 加 用 @Bean 注 释 的 方 法 ,Spring 将 自 动 加 载 对 象 并 像 以 前 一 样 对 其

进 行 管 理 。您 甚 至 可 以 将 @Autowired 添 加 到 bean 方 法 中 ,以 使 Spring 自 动 装

入需要的依赖关系中。

8、基 于 环 境 的 配 置 使 用 这 些 属 性 ,您 可 以 将 您 正 在 使 用 的 环 境 传 递 到 应 用 程 序 :

-Dspring.profiles.active = {enviornment}。 在 加 载 主 应 用 程 序 属 性 文 件 后 ,

Spring 将 在( application{environment} .properties)中 加 载 后 续 的 应 用 程 序 属

性文件。

3、什么是 JavaConfig?

第 455 页 共 485 页
Spring JavaConfig 是 Spring 社 区 的 产 品 , 它 提 供 了 配 置 Spring IoC 容 器 的 纯

Java 方 法 。 因 此 它 有 助 于 避 免 使 用 XML 配 置 。 使 用 JavaConfig 的 优 点 在 于 :

1、 面 向 对 象 的 配 置 。 由 于 配 置 被 定 义 为 JavaConfig 中 的 类 , 因 此 用 户 可 以 充 分

利 用 Java 中 的 面 向 对 象 功 能 。 一 个 配 置 类 可 以 继 承 另 一 个 , 重 写 它 的 @Bean 方

法等。

2、减 少 或 消 除 XML 配 置 。基 于 依 赖 注 入 原 则 的 外 化 配 置 的 好 处 已 被 证 明 。但 是 ,

许 多 开 发 人 员 不 希 望 在 XML 和 Java 之 间 来 回 切 换 。JavaConfig 为 开 发 人 员 提 供

了 一 种 纯 Java 方 法 来 配 置 与 XML 配 置 概 念 相 似 的 Spring 容 器 。从 技 术 角 度 来 讲 ,

只 使 用 JavaConfig 配 置 类 来 配 置 容 器 是 可 行 的 , 但 实 际 上 很 多 人 认 为 将

JavaConfig 与 XML 混 合 匹 配 是 理 想 的 。

3、 类 型 安 全 和 重 构 友 好 。 JavaConfig 提 供 了 一 种 类 型 安 全 的 方 法 来 配 置 Spring

容 器 。由 于 Java 5.0 对 泛 型 的 支 持 ,现 在 可 以 按 类 型 而 不 是 按 名 称 检 索 bean,不

需要任何强制转换或基于字符串的查找。

4、如何重新加载 Spring Boot 上的更改,而无需重新启动服务

器?

这 可 以 使 用 DEV 工 具 来 实 现 。 通 过 这 种 依 赖 关 系 , 您 可 以 节 省 任 何 更 改 , 嵌 入 式

tomcat 将 重 新 启 动 。Spring Boot 有 一 个 开 发 工 具( DevTools)模 块 ,它 有 助 于

提 高 开 发 人 员 的 生 产 力 。 Java 开 发 人 员 面 临 的 一 个 主 要 挑 战 是 将 文 件 更 改 自 动 部

署 到 服 务 器 并 自 动 重 启 服 务 器 。 开 发 人 员 可 以 重 新 加 载 Spring Boot 上 的 更 改 ,

而 无 需 重 新 启 动 服 务 器 。这 将 消 除 每 次 手 动 部 署 更 改 的 需 要 。Spring Boot 在 发 布

它 的 第 一 个 版 本 时 没 有 这 个 功 能 。这 是 开 发 人 员 最 需 要 的 功 能 。DevTools 模 块 完

全 满 足 开 发 人 员 的 需 求 。该 模 块 将 在 生 产 环 境 中 被 禁 用 。它 还 提 供 H2 数 据 库 控 制

台以更好地测试应用程序。

第 456 页 共 485 页
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>

5、Spring Boot 中的监视器是什么?

Spring boot actuator 是 spring 启 动 框 架 中 的 重 要 功 能 之 一 。 Spring boot 监 视

器可帮助您访问生产环境中正在运行的应用程序的当前状态。有几个指标必须在

生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向

相 关 人 员 触 发 警 报 消 息 。 监 视 器 模 块 公 开 了 一 组 可 直 接 作 为 HTTP URL 访 问 的

REST 端 点 来 检 查 状 态 。

6、如何在 Spring Boot 中禁用 Actuator 端点安全性?

默 认 情 况 下 ,所 有 敏 感 的 HTTP 端 点 都 是 安 全 的 ,只 有 具 有 ACTUATOR 角 色 的 用

户 才 能 访 问 它 们 。安 全 性 是 使 用 标 准 的 HttpServletRequest.isUserInRole 方 法 实

施的。 我们可以使用

来禁用安全性。只有在执行机构端点在防火墙后访问时,才建议禁用安全性。

7、如何在自定义端口上运行 Spring Boot 应用程序?

为 了 在 自 定 义 端 口 上 运 行 Spring Boot 应 用 程 序 , 您 可 以 在

application.properties 中 指 定 端 口 。

server.port = 8090

第 457 页 共 485 页
8、什么是 YAML?

YAML 是 一 种 人 类 可 读 的 数 据 序 列 化 语 言 。 它 通 常 用 于 配 置 文 件 。

与 属 性 文 件 相 比 ,如 果 我 们 想 要 在 配 置 文 件 中 添 加 复 杂 的 属 性 ,YAML 文 件 就 更 加

结 构 化 , 而 且 更 少 混 淆 。 可 以 看 出 YAML 具 有 分 层 配 置 数 据 。

9、如何实现 Spring Boot 应用程序的安全性?

为 了 实 现 Spring Boot 的 安 全 性 , 我 们 使 用 spring-boot-starter-security 依 赖

项,并且必须添加安全配置。它只需要很少的代码。配置类将必须扩展

WebSecurityConfigurerAdapter 并 覆 盖 其 方 法 。

10、如何集成 Spring Boot 和 ActiveMQ?

对 于 集 成 Spring Boot 和 ActiveMQ, 我 们 使 用

依赖关系。 它只需要很少的配置,并且不需要样板代码。

11、如何使用 Spring Boot 实现分页和排序?

使 用 Spring Boot 实 现 分 页 非 常 简 单 。 使 用 Spring Data-JPA 可 以 实 现 将 可 分 页

传递给存储库方法。

第 458 页 共 485 页
12、什么是 Swagger?你用 Spring Boot 实现了它吗?

Swagger 广 泛 用 于 可 视 化 API,使 用 Swagger UI 为 前 端 开 发 人 员 提 供 在 线 沙 箱 。

Swagger 是 用 于 生 成 RESTful Web 服 务 的 可 视 化 表 示 的 工 具 ,规 范 和 完 整 框 架 实

现 。它 使 文 档 能 够 以 与 服 务 器 相 同 的 速 度 更 新 。当 通 过 Swagger 正 确 定 义 时 ,消

费 者 可 以 使 用 最 少 量 的 实 现 逻 辑 来 理 解 远 程 服 务 并 与 其 进 行 交 互 。因 此 ,Swagger

消除了调用服务时的猜测。

13、什么是 Spring Profiles?

Spring Profiles 允 许 用 户 根 据 配 置 文 件( dev,test,prod 等 )来 注 册 bean。因

此 ,当 应 用 程 序 在 开 发 中 运 行 时 ,只 有 某 些 bean 可 以 加 载 ,而 在 PRODUCTION

中 , 某 些 其 他 bean 可 以 加 载 。 假 设 我 们 的 要 求 是 Swagger 文 档 仅 适 用 于 QA 环

境 ,并 且 禁 用 所 有 其 他 文 档 。这 可 以 使 用 配 置 文 件 来 完 成 。Spring Boot 使 得 使 用

配置文件非常简单。

14、什么是 Spring Batch?

Spring Boot Batch 提 供 可 重 用 的 函 数 ,这 些 函 数 在 处 理 大 量 记 录 时 非 常 重 要 ,包

括 日 志 /跟 踪 , 事 务 管 理 , 作 业 处 理 统 计 信 息 , 作 业 重 新 启 动 , 跳 过 和 资 源 管 理 。

它还提供了更先进的技术服务和功能,通过优化和分区技术,可以实现极高批量

和高性能批处理作业。简单以及复杂的大批量批处理作业可以高度可扩展的方式

利用框架处理重要大量的信息。

15、什么是 FreeMarker 模板?

第 459 页 共 485 页
FreeMarker 是 一 个 基 于 Java 的 模 板 引 擎 ,最 初 专 注 于 使 用 MVC 软 件 架 构 进 行 动

态 网 页 生 成 。使 用 Freemarker 的 主 要 优 点 是 表 示 层 和 业 务 层 的 完 全 分 离 。程 序 员

可 以 处 理 应 用 程 序 代 码 , 而 设 计 人 员 可 以 处 理 html 页 面 设 计 。 最 后 使 用

freemarker 可 以 将 这 些 结 合 起 来 , 给 出 最 终 的 输 出 页 面 。

16、如何使用 Spring Boot 实现异常处理?

Spring 提 供 了 一 种 使 用 ControllerAdvice 处 理 异 常 的 非 常 有 用 的 方 法 。 我 们 通

过 实 现 一 个 ControlerAdvice 类 , 来 处 理 控 制 器 类 抛 出 的 所 有 异 常 。

17、您使用了哪些 starter maven 依赖项?

使用了下面的一些依赖项

spring-boot-starter-activemq
spring-boot-starter-security

这有助于增加更少的依赖关系,并减少版本的冲突。

18、什么是 CSRF 攻击?

CSRF 代 表 跨 站 请 求 伪 造 。 这 是 一 种 攻 击 , 迫 使 最 终 用 户 在 当 前 通 过 身 份 验 证 的

Web 应 用 程 序 上 执 行 不 需 要 的 操 作 。 CSRF 攻 击 专 门 针 对 状 态 改 变 请 求 , 而 不 是

数据窃取,因为攻击者无法查看对伪造请求的响应。

19、什么是 WebSockets?

第 460 页 共 485 页
WebSocket 是 一 种 计 算 机 通 信 协 议 , 通 过 单 个 TCP 连 接 提 供 全 双 工 通 信 信 道 。

1、 WebSocket 是 双 向 的 -使 用 WebSocket 客 户 端 或 服 务 器 可 以 发 起 消 息 发 送 。

2、 WebSocket 是 全 双 工 的 -客 户 端 和 服 务 器 通 信 是 相 互 独 立 的 。

3、单 个 TCP 连 接 -初 始 连 接 使 用 HTTP,然 后 将 此 连 接 升 级 到 基 于 套 接 字 的 连 接 。

然后这个单一连接用于所有未来的通信

4、 Light -与 http 相 比 , WebSocket 消 息 数 据 交 换 要 轻 得 多 。

20、什么是 AOP?

在软件开发过程中,跨越应用程序多个点的功能称为交叉问题。这些交叉问题与

应用程序的主要业务逻辑不同。因此,将这些横切关注与业务逻辑分开是面向方

面 编 程 ( AOP) 的 地 方 。

第 461 页 共 485 页
21、什么是 Apache Kafka?

Apache Kafka 是 一 个 分 布 式 发 布 - 订 阅 消 息 系 统 。 它 是 一 个 可 扩 展 的 , 容 错 的

发 布 - 订 阅 消 息 系 统 ,它 使 我 们 能 够 构 建 分 布 式 应 用 程 序 。这 是 一 个 Apache 顶

级 项 目 。 Kafka 适 合 离 线 和 在 线 消 息 消 费 。

22、我们如何监视所有 Spring Boot 微服务?

Spring Boot 提 供 监 视 器 端 点 以 监 控 各 个 微 服 务 的 度 量 。这 些 端 点 对 于 获 取 有 关 应

用程序的信息(如它们是否已启动)以及它们的组件(如数据库等)是否正常运

行很有帮助。但是,使用监视器的一个主要缺点或困难是,我们必须单独打开应

用 程 序 的 知 识 点 以 了 解 其 状 态 或 健 康 状 况 。想 象 一 下 涉 及 50 个 应 用 程 序 的 微 服 务 ,

管 理 员 将 不 得 不 击 中 所 有 50 个 应 用 程 序 的 执 行 终 端 。

为了帮助我们处理这种情况,我们将使用位于

的 开 源 项 目 。 它 建 立 在 Spring Boot Actuator 之 上 ,它 提 供 了 一 个 Web UI,使

我们能够可视化多个应用程序的度量。

Spring Cloud 面试题

1、什么是 Spring Cloud?

Spring cloud 流 应 用 程 序 启 动 器 是 基 于 Spring Boot 的 Spring 集 成 应 用 程 序 ,

提 供 与 外 部 系 统 的 集 成 。 Spring cloud Task, 一 个 生 命 周 期 短 暂 的 微 服 务 框 架 ,

用于快速构建执行有限数据处理的应用程序。

第 462 页 共 485 页
2、使用 Spring Cloud 有什么优势?

使 用 Spring Boot 开 发 分 布 式 微 服 务 时 , 我 们 面 临 以 下 问 题

1、 与 分 布 式 系 统 相 关 的 复 杂 性 -这 种 开 销 包 括 网 络 问 题 , 延 迟 开 销 , 带 宽 问 题 ,

安全问题。

2、 服 务 发 现 -服 务 发 现 工 具 管 理 群 集 中 的 流 程 和 服 务 如 何 查 找 和 互 相 交 谈 。 它 涉

及一个服务目录,在该目录中注册服务,然后能够查找并连接到该目录中的服务。

3、 冗 余 -分 布 式 系 统 中 的 冗 余 问 题 。

4、 负 载 平 衡 --负 载 平 衡 改 善 跨 多 个 计 算 资 源 的 工 作 负 荷 , 诸 如 计 算 机 , 计 算 机

集群,网络链路,中央处理单元,或磁盘驱动器的分布。

5、 性 能 -问 题 由 于 各 种 运 营 开 销 导 致 的 性 能 问 题 。

6、 部 署 复 杂 性 -Devops 技 能 的 要 求 。

3、服务注册和发现是什么意思?Spring Cloud 如何实现?

当我们开始一个项目时,我们通常在属性文件中进行所有的配置。随着越来越多

的服务开发和部署,添加和修改这些属性变得更加复杂。有些服务可能会下降,

而 某 些 位 置 可 能 会 发 生 变 化 。 手 动 更 改 属 性 可 能 会 产 生 问 题 。 Eureka 服 务 注 册

和 发 现 可 以 在 这 种 情 况 下 提 供 帮 助 。由 于 所 有 服 务 都 在 Eureka 服 务 器 上 注 册 并 通

过 调 用 Eureka 服 务 器 完 成 查 找 , 因 此 无 需 处 理 服 务 地 点 的 任 何 更 改 和 处 理 。

4、负载平衡的意义什么?

第 463 页 共 485 页
在计算中,负载平衡可以改善跨计算机,计算机集群,网络链接,中央处理单元

或磁盘驱动器等多种计算资源的工作负载分布。负载平衡旨在优化资源使用,最

大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负

载平衡而不是单个组件可能会通过冗余来提高可靠性和可用性。负载平衡通常涉

及专用软件或硬件,例如多层交换机或域名系统服务器进程。

5、什么是 Hystrix?它如何实现容错?

Hystrix 是 一 个 延 迟 和 容 错 库 ,旨 在 隔 离 远 程 系 统 ,服 务 和 第 三 方 库 的 访 问 点 ,当

出 现 故 障 是 不 可 避 免 的 故 障 时 ,停 止 级 联 故 障 并 在 复 杂 的 分 布 式 系 统 中 实 现 弹 性 。

通常对于使用微服务架构开发的系统,涉及到许多微服务。这些微服务彼此协作。

思考以下微服务

假设如果上图中的微服务 9 失败了,那么使用传统方法我们将传播一个异常。但

这仍然会导致整个系统崩溃。

第 464 页 共 485 页
随 着 微 服 务 数 量 的 增 加 , 这 个 问 题 变 得 更 加 复 杂 。 微 服 务 的 数 量 可 以 高 达 1000.

这 是 hystrix 出 现 的 地 方 我 们 将 使 用 Hystrix 在 这 种 情 况 下 的 Fallback 方 法 功 能 。

我 们 有 两 个 服 务 employee-consumer 使 用 由 employee-consumer 公 开 的 服 务 。

简化图如下所示

现 在 假 设 由 于 某 种 原 因 ,employee-producer 公 开 的 服 务 会 抛 出 异 常 。我 们 在 这

种 情 况 下 使 用 Hystrix 定 义 了 一 个 回 退 方 法 。这 种 后 备 方 法 应 该 具 有 与 公 开 服 务 相

同的返回类型。如果暴露服务中出现异常,则回退方法将返回一些值。

6、什么是 Hystrix 断路器?我们需要它吗?

由 于 某 些 原 因 , employee-consumer 公 开 服 务 会 引 发 异 常 。 在 这 种 情 况 下 使 用

Hystrix 我 们 定 义 了 一 个 回 退 方 法 。如 果 在 公 开 服 务 中 发 生 异 常 ,则 回 退 方 法 返 回

一些默认值。

第 465 页 共 485 页
如 果 firstPage method() 中 的 异 常 继 续 发 生 , 则 Hystrix 电 路 将 中 断 , 并 且 员 工

使 用 者 将 一 起 跳 过 firtsPage 方 法 , 并 直 接 调 用 回 退 方 法 。 断 路 器 的 目 的 是 给 第

一页方法或第一页方法可能调用的其他方法留出时间,并导致异常恢复。可能发

生的情况是,在负载较小的情况下,导致异常的问题有更好的恢复机会 。

7、什么是 Netflix Feign?它的优点是什么?

Feign 是 受 到 Retrofit,JAXRS-2.0 和 WebSocket 启 发 的 java 客 户 端 联 编 程 序 。

Feign 的 第 一 个 目 标 是 将 约 束 分 母 的 复 杂 性 统 一 到 http apis,而 不 考 虑 其 稳 定 性 。

在 employee-consumer 的 例 子 中 ,我 们 使 用 了 employee-producer 使 用 REST

模 板 公 开 的 REST 服 务 。

第 466 页 共 485 页
但是我们必须编写大量代码才能执行以下步骤

1、 使 用 功 能 区 进 行 负 载 平 衡 。

2、 获 取 服 务 实 例 , 然 后 获 取 基 本 URL。

3、 利 用 REST 模 板 来 使 用 服 务 。 前 面 的 代 码 如 下

@Controller
public class ConsumerControllerClient {

@Autowired
private LoadBalancerClient loadBalancer;

public void getEmployee() throws RestClientException, IOException {

ServiceInstance
serviceInstance=loadBalancer.choose("employee-producer");

System.out.println(serviceInstance.getUri());

String baseUrl=serviceInstance.getUri().toString();

baseUrl=baseUrl+"/employee";

RestTemplate restTemplate = new RestTemplate();


ResponseEntity<String> response=null;
try{
response=restTemplate.exchange(baseUrl,
HttpMethod.GET, getHeaders(),String.class);

第 467 页 共 485 页
}catch (Exception ex)
{
System.out.println(ex);
}
System.out.println(response.getBody());

之 前 的 代 码 ,有 像 NullPointer 这 样 的 例 外 的 机 会 ,并 不 是 最 优 的 。我 们 将 看 到 如

何 使 用 Netflix Feign 使 呼 叫 变 得 更 加 轻 松 和 清 洁 。 如 果 Netflix Ribbon 依 赖 关

系 也 在 类 路 径 中 , 那 么 Feign 默 认 也 会 负 责 负 载 平 衡 。

8、什么是 Spring Cloud Bus?我们需要它吗?

考 虑 以 下 情 况 : 我 们 有 多 个 应 用 程 序 使 用 Spring Cloud Config 读 取 属 性 , 而

Spring Cloud Config 从 GIT 读 取 这 些 属 性 。

下 面 的 例 子 中 多 个 员 工 生 产 者 模 块 从 Employee Config Module 获 取 Eureka 注

册的财产。

第 468 页 共 485 页
如 果 假 设 GIT 中 的 Eureka 注 册 属 性 更 改 为 指 向 另 一 台 Eureka 服 务 器 , 会 发 生 什

么情况。在这种情况下,我们将不得不重新启动服务以获取更新的属性。

还 有 另 一 种 使 用 执 行 器 端 点 /刷 新 的 方 式 。 但 是 我 们 将 不 得 不 为 每 个 模 块 单 独 调 用

这 个 url。例 如 ,如 果 Employee Producer1 部 署 在 端 口 8080 上 ,则 调 用 http:

// localhost: 8080 / refresh。 同 样 对 于 Employee Producer2 http: //

localhost: 8081 / refresh 等 等 。 这 又 很 麻 烦 。 这 就 是 Spring Cloud Bus 发 挥

作用的地方。

第 469 页 共 485 页
Spring Cloud Bus 提 供 了 跨 多 个 实 例 刷 新 配 置 的 功 能 。 因 此 , 在 上 面 的 示 例 中 ,

如 果 我 们 刷 新 Employee Producer1, 则 会 自 动 刷 新 所 有 其 他 必 需 的 模 块 。 如 果

我们有多个微服务启动并运行,这特别有用。这是通过将所有微服务连接到单个

消息代理来实现的。无论何时刷新实例,此事件都会订阅到侦听此代理的所有微

服 务 ,并 且 它 们 也 会 刷 新 。可 以 通 过 使 用 端 点 /总 线 /刷 新 来 实 现 对 任 何 单 个 实 例 的

刷新。

RabbitMQ 面试题

1、什么是 rabbitmq

采 用 AMQP 高 级 消 息 队 列 协 议 的 一 种 消 息 队 列 技 术 ,最 大 的 特 点 就 是 消 费 并 不 需

要 确 保 提 供 方 存 在 ,实 现 了 服 务 之 间 的 高 度 解 耦

第 470 页 共 485 页
2、为什么要使用 rabbitmq

1、 在 分 布 式 系 统 下 具 备 异 步 ,削 峰 ,负 载 均 衡 等 一 系 列 高 级 功 能 ;

2、 拥 有 持 久 化 的 机 制 , 进 程 消 息 , 队 列 中 的 信 息 也 可 以 保 存 下 来 。

3、 实 现 消 费 者 和 生 产 者 之 间 的 解 耦 。

4、对 于 高 并 发 场 景 下 ,利 用 消 息 队 列 可 以 使 得 同 步 访 问 变 为 串 行 访 问 达 到 一 定 量

的限流,利于数据库的操作。

5.可 以 使 用 消 息 队 列 达 到 异 步 下 单 的 效 果 , 排 队 中 , 后 台 进 行 逻 辑 下 单 。

3、使用 rabbitmq 的场景

1、 服 务 间 异 步 通 信

2、 顺 序 消 费

3、 定 时 任 务

4、 请 求 削 峰

4、如何确保消息正确地发送至 RabbitMQ? 如何确保消息接

收方消费了消息?

发送方确认模式

将 信 道 设 置 成 confirm 模 式 ( 发 送 方 确 认 模 式 ) , 则 所 有 在 信 道 上 发 布 的 消 息 都

会 被 指 派 一 个 唯 一 的 ID。

一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信

道 会 发 送 一 个 确 认 给 生 产 者 ( 包 含 消 息 唯 一 ID) 。

如 果 RabbitMQ 发 生 内 部 错 误 从 而 导 致 消 息 丢 失 , 会 发 送 一 条 nack( not

acknowledged, 未 确 认 ) 消 息 。

第 471 页 共 485 页
发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消

息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来

处理确认消息。

接收方确认机制

接收方消息确认机制

消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操

作 ) 。 只 有 消 费 者 确 认 了 消 息 , RabbitMQ 才 能 安 全 地 把 消 息 从 队 列 中 删 除 。

这 里 并 没 有 用 到 超 时 机 制 , RabbitMQ 仅 通 过 Consumer 的 连 接 中 断 来 确 认 是 否

需 要 重 新 发 送 消 息 。 也 就 是 说 , 只 要 连 接 不 中 断 , RabbitMQ 给 了 Consumer 足

够长的时间来处理消息。保证数据的最终一致性;

下面罗列几种特殊情况

如 果 消 费 者 接 收 到 消 息 , 在 确 认 之 前 断 开 了 连 接 或 取 消 订 阅 , RabbitMQ 会 认 为

消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消

费的隐患,需要去重)

如 果 消 费 者 接 收 到 消 息 却 没 有 确 认 消 息 , 连 接 也 未 断 开 , 则 RabbitMQ 认 为 该 消

费者繁忙,将不会给该消费者分发更多的消息。

5.如何避免消息重复投递或重复消费?

在 消 息 生 产 时 , MQ 内 部 针 对 每 条 生 产 者 发 送 的 消 息 生 成 一 个 inner-msg-id, 作

为去重的依据(消息投递失败并重传),避免重复的消息进入队列;

在 消 息 消 费 时 ,要 求 消 息 体 中 必 须 要 有 一 个 bizId( 对 于 同 一 业 务 全 局 唯 一 ,如 支

付 ID、 订 单 ID、 帖 子 ID 等 ) 作 为 去 重 的 依 据 , 避 免 同 一 条 消 息 被 重 复 消 费 。

第 472 页 共 485 页
6、消息基于什么传输?

由 于 TCP 连 接 的 创 建 和 销 毁 开 销 较 大 , 且 并 发 数 受 系 统 资 源 限 制 , 会 造 成 性 能 瓶

颈 。 RabbitMQ 使 用 信 道 的 方 式 来 传 输 数 据 。 信 道 是 建 立 在 真 实 的 TCP 连 接 内 的

虚 拟 连 接 , 且 每 条 TCP 连 接 上 的 信 道 数 量 没 有 限 制 。

7、消息如何分发?

若 该 队 列 至 少 有 一 个 消 费 者 订 阅 ,消 息 将 以 循 环( round-robin)的 方 式 发 送 给 消

费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息

并进行确认)。

通过路由可实现多消费的功能

8、消息怎么路由?

消 息 提 供 方 ->路 由 ->一 至 多 个 队 列

消 息 发 布 到 交 换 器 时 , 消 息 将 拥 有 一 个 路 由 键 ( routing key) , 在 消 息 创 建 时 设

定。

通过队列路由键,可以把队列绑定到交换器上。

消 息 到 达 交 换 器 后 , RabbitMQ 会 将 消 息 的 路 由 键 与 队 列 的 路 由 键 进 行 匹 配 ( 针

对不同的交换器有不同的路由规则);

常用的交换器主要分为一下三种

fanout: 如 果 交 换 器 收 到 消 息 , 将 会 广 播 到 所 有 绑 定 的 队 列 上

direct: 如 果 路 由 键 完 全 匹 配 , 消 息 就 被 投 递 到 相 应 的 队 列

topic: 可 以 使 来 自 不 同 源 头 的 消 息 能 够 到 达 同 一 个 队 列 。 使 用 topic 交 换 器 时 ,

可以使用通配符

第 473 页 共 485 页
9、如何确保消息不丢失?

消息持久化,当然前提是队列必须持久化

RabbitMQ 确 保 持 久 性 消 息 能 从 服 务 器 重 启 中 恢 复 的 方 式 是 , 将 它 们 写 入 磁 盘 上

的 一 个 持 久 化 日 志 文 件 , 当 发 布 一 条 持 久 性 消 息 到 持 久 交 换 器 上 时 , Rabbit 会 在

消息提交到日志文件后才发送响应。

一 旦 消 费 者 从 持 久 队 列 中 消 费 了 一 条 持 久 化 消 息 , RabbitMQ 会 在 持 久 化 日 志 中

把 这 条 消 息 标 记 为 等 待 垃 圾 收 集 。如 果 持 久 化 消 息 在 被 消 费 之 前 RabbitMQ 重 启 ,

那 么 Rabbit 会 自 动 重 建 交 换 器 和 队 列( 以 及 绑 定 ),并 重 新 发 布 持 久 化 日 志 文 件

中的消息到合适的队列。

10、使用 RabbitMQ 有什么好处?

1、 服 务 间 高 度 解 耦

2、 异 步 通 信 性 能 高

3、 流 量 削 峰

11、RabbitMQ 的集群

镜像集群模式

你 创 建 的 queue,无 论 元 数 据 还 是 queue 里 的 消 息 都 会 存 在 于 多 个 实 例 上 ,然 后

每 次 你 写 消 息 到 queue 的 时 候 ,都 会 自 动 把 消 息 到 多 个 实 例 的 queue 里 进 行 消 息

同步。

好处在于,你任何一个机器宕机了,没事儿,别的机器都可以用。坏处在于,第

一,这个性能开销也太大了吧,消息同步所有机器,导致网络带宽压力和消耗很

第 474 页 共 485 页
重 ! 第 二 , 这 么 玩 儿 , 就 没 有 扩 展 性 可 言 了 , 如 果 某 个 queue 负 载 很 重 , 你 加 机

器 ,新 增 的 机 器 也 包 含 了 这 个 queue 的 所 有 数 据 ,并 没 有 办 法 线 性 扩 展 你 的 queue

12、mq 的缺点

系统可用性降低

系 统 引 入 的 外 部 依 赖 越 多 ,越 容 易 挂 掉 ,本 来 你 就 是 A 系 统 调 用 BCD 三 个 系 统 的

接 口 就 好 了 , 人 ABCD 四 个 系 统 好 好 的 , 没 啥 问 题 , 你 偏 加 个 MQ 进 来 , 万 一

MQ 挂 了 咋 整 ? MQ 挂 了 , 整 套 系 统 崩 溃 了 , 你 不 就 完 了 么 。

系统复杂性提高

硬 生 生 加 个 MQ 进 来 ,你 怎 么 保 证 消 息 没 有 重 复 消 费 ? 怎 么 处 理 消 息 丢 失 的 情 况 ?

怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已

一致性问题

A 系 统 处 理 完 了 直 接 返 回 成 功 了 ,人 都 以 为 你 这 个 请 求 就 成 功 了 ;但 是 问 题 是 ,要

是 BCD 三 个 系 统 那 里 ,BD 两 个 系 统 写 库 成 功 了 ,结 果 C 系 统 写 库 失 败 了 ,咋 整 ?

你这数据就不一致了。

所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对

它带来的坏处做各种额外的技术方案和架构来规避掉,最好之后,你会发现,妈

呀 ,系 统 复 杂 度 提 升 了 一 个 数 量 级 ,也 许 是 复 杂 了 10 倍 。但 是 关 键 时 刻 ,用 ,还

是得用的

kafka 面试题

1、如何获取 topic 主题的列表

第 475 页 共 485 页
bin/kafka-topics.sh --list --zookeeper localhost:2181

2、生产者和消费者的命令行是什么?

生产者在主题上发布消息:

bin/kafka-console-producer.sh --broker-list 192.168.43.49:9092 --topic

Hello-Kafka

注 意 这 里 的 IP 是 server.properties 中 的 listeners 的 配 置 。接 下 来 每 个 新 行 就 是

输入一条新消息。

消费者接受消息:

bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic

Hello-Kafka --from-beginning

3、consumer 是推还是拉?

Kafka 最 初 考 虑 的 问 题 是 , customer 应 该 从 brokes 拉 取 消 息 还 是 brokers 将 消

息 推 送 到 consumer,也 就 是 pull 还 push。在 这 方 面 ,Kafka 遵 循 了 一 种 大 部 分

消 息 系 统 共 同 的 传 统 的 设 计 : producer 将 消 息 推 送 到 broker, consumer 从

broker 拉 取 消 息 。

一 些 消 息 系 统 比 如 Scribe 和 Apache Flume 采 用 了 push 模 式 , 将 消 息 推 送 到 下

游 的 consumer。这 样 做 有 好 处 也 有 坏 处 : 由 broker 决 定 消 息 推 送 的 速 率 , 对 于

不 同 消 费 速 率 的 consumer 就 不 太 好 处 理 了 。 消 息 系 统 都 致 力 于 让 consumer 以

最 大 的 速 率 最 快 速 的 消 费 消 息 , 但 不 幸 的 是 , push 模 式 下 , 当 broker 推 送 的 速

率 远 大 于 consumer 消 费 的 速 率 时 , consumer 恐 怕 就 要 崩 溃 了 。 最 终 Kafka 还

是 选 取 了 传 统 的 pull 模 式 。

第 476 页 共 485 页
Pull 模 式 的 另 外 一 个 好 处 是 consumer 可 以 自 主 决 定 是 否 批 量 的 从 broker 拉 取 数

据 。Push 模 式 必 须 在 不 知 道 下 游 consumer 消 费 能 力 和 消 费 策 略 的 情 况 下 决 定 是

立 即 推 送 每 条 消 息 还 是 缓 存 之 后 批 量 推 送 。 如 果 为 了 避 免 consumer 崩 溃 而 采 用

较 低 的 推 送 速 率 , 将 可 能 导 致 一 次 只 推 送 较 少 的 消 息 而 造 成 浪 费 。 Pull 模 式 下 ,

consumer 就 可 以 根 据 自 己 的 消 费 能 力 去 决 定 这 些 策 略 。

Pull 有 个 缺 点 是 ,如 果 broker 没 有 可 供 消 费 的 消 息 ,将 导 致 consumer 不 断 在 循

环 中 轮 询 ,直 到 新 消 息 到 t 达 。为 了 避 免 这 点 ,Kafka 有 个 参 数 可 以 让 consumer

阻 塞 知 道 新 消 息 到 达 (当 然 也 可 以 阻 塞 知 道 消 息 的 数 量 达 到 某 个 特 定 的 量 这 样 就 可

以 批 量 发 送 )。

4、讲讲 kafka 维护消费状态跟踪的方法

大 部 分 消 息 系 统 在 broker 端 的 维 护 消 息 被 消 费 的 记 录 : 一 个 消 息 被 分 发 到

consumer 后 broker 就 马 上 进 行 标 记 或 者 等 待 customer 的 通 知 后 进 行 标 记 。 这

样也可以在消息在消费后立马就删除以减少空间占用。

但是这样会不会有什么问题呢?如果一条消息发送出去之后就立即被标记为消费

过 的 , 一 旦 consumer 处 理 消 息 时 失 败 了 ( 比 如 程 序 崩 溃 ) 消 息 就 丢 失 了 。 为 了

解决这个问题,很多消息系统提供了另外一个个功能:当消息被发送出去之后仅

仅 被 标 记 为 已 发 送 状 态 , 当 接 到 consumer 已 经 消 费 成 功 的 通 知 后 才 标 记 为 已 被

消 费 的 状 态 。这 虽 然 解 决 了 消 息 丢 失 的 问 题 ,但 产 生 了 新 问 题 ,首 先 如 果 consumer

处 理 消 息 成 功 了 但 是 向 broker 发 送 响 应 时 失 败 了 ,这 条 消 息 将 被 消 费 两 次 。第 二

个 问 题 时 , broker 必 须 维 护 每 条 消 息 的 状 态 , 并 且 每 次 都 要 先 锁 住 消 息 然 后 更 改

状态然后释放锁。这样麻烦又来了,且不说要维护大量的状态数据,比如如果消

息发送出去但没有收到消费成功的通知,这条消息将一直处于被锁定的状态,

Kafka 采 用 了 不 同 的 策 略 。Topic 被 分 成 了 若 干 分 区 ,每 个 分 区 在 同 一 时 间 只 被 一

个 consumer 消 费 。 这 意 味 着 每 个 分 区 被 消 费 的 消 息 在 日 志 中 的 位 置 仅 仅 是 一 个

简 单 的 整 数 :offset。这 样 就 很 容 易 标 记 每 个 分 区 消 费 状 态 就 很 容 易 了 ,仅 仅 需 要

一个整数而已。这样消费状态的跟踪就很简单了。

第 477 页 共 485 页
这 带 来 了 另 外 一 个 好 处 : consumer 可 以 把 offset 调 成 一 个 较 老 的 值 , 去 重 新 消

费老的消息。这对传统的消息系统来说看起来有些不可思议,但确实是非常有用

的,谁规定了一条消息只能被消费一次呢?

5、讲一下主从同步**

https://blog.csdn.net/honglei915/article/details/37565289

6、为什么需要消息系统,mysql 不能满足需求吗?

1.解 耦 :

允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。

2.冗 余 :

消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据

丢 失 风 险 。许 多 消 息 队 列 所 采 用 的 ”插 入 -获 取 -删 除 ”范 式 中 ,在 把 一 个 消 息 从 队

列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保

你的数据被安全的保存直到你使用完毕。

3.扩 展 性 :

因 为 消 息 队 列 解 耦 了 你 的 处 理 过 程 ,所 以 增 大 消 息 入 队 和 处 理 的 频 率 是 很 容 易 的 ,

只要另外增加处理过程即可。

4.灵 活 性 & 峰 值 处 理 能 力 :

第 478 页 共 485 页
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不

常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪

费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负

荷的请求而完全崩溃。

5.可 恢 复 性 :

系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合

度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复

后被处理。

6.顺 序 保 证 :

在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,

并 且 能 保 证 数 据 会 按 照 特 定 的 顺 序 来 处 理 。 ( Kafka 保 证 一 个 Partition 内 的 消

息的有序性)

7.缓 冲 :

有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度

不一致的情况。

8.异 步 通 信 :

很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允

许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放

多少,然后在需要的时候再去处理它们。

7、Zookeeper 对于 Kafka 的作用是什么?

第 479 页 共 485 页
Zookeeper 是 一 个 开 放 源 码 的 、高 性 能 的 协 调 服 务 ,它 用 于 Kafka 的 分 布 式 应 用 。

Zookeeper 主 要 用 于 在 集 群 中 不 同 节 点 之 间 进 行 通 信

在 Kafka 中 , 它 被 用 于 提 交 偏 移 量 , 因 此 如 果 节 点 在 任 何 情 况 下 都 失 败 了 , 它 都

可以从之前提交的偏移量中获取

除 此 之 外 ,它 还 执 行 其 他 活 动 ,如 : leader 检 测 、分 布 式 同 步 、配 置 管 理 、识 别 新

节点何时离开或连接、集群、节点实时状态等等。

8、数据传输的事务定义有哪三种?

和 MQTT 的 事 务 定 义 一 样 都 是 3 种 。

( 1) 最 多 一 次 : 消 息 不 会 被 重 复 发 送 , 最 多 被 传 输 一 次 , 但 也 有 可 能 一 次 不 传 输

( 2) 最 少 一 次 : 消 息 不 会 被 漏 发 送 , 最 少 被 传 输 一 次 , 但 也 有 可 能 被 重 复 传 输 .

( 3) 精 确 的 一 次 ( Exactly once) : 不 会 漏 传 输 也 不 会 重 复 传 输 ,每 个 消 息 都 传 输

被一次而且仅仅被传输一次,这是大家所期望的

9、Kafka 判断一个节点是否还活着有那两个条件?

( 1)节 点 必 须 可 以 维 护 和 ZooKeeper 的 连 接 ,Zookeeper 通 过 心 跳 机 制 检 查 每

个节点的连接

( 2) 如 果 节 点 是 个 follower,他 必 须 能 及 时 的 同 步 leader 的 写 操 作 , 延 时 不 能 太

第 480 页 共 485 页
10、Kafka 与传统 MQ 消息系统之间有三个关键区别

(1).Kafka 持 久 化 日 志 , 这 些 日 志 可 以 被 重 复 读 取 和 无 限 期 保 留

(2).Kafka 是 一 个 分 布 式 系 统 :它 以 集 群 的 方 式 运 行 ,可 以 灵 活 伸 缩 ,在 内 部 通 过

复制数据提升容错能力和高可用性

(3).Kafka 支 持 实 时 的 流 式 处 理

11、讲一讲 kafka 的 ack 的三种机制

request.required.acks 有 三 个 值 0 1 -1(all)

0:生 产 者 不 会 等 待 broker 的 ack,这 个 延 迟 最 低 但 是 存 储 的 保 证 最 弱 当 server 挂

掉的时候就会丢数据。

1:服 务 端 会 等 待 ack 值 leader 副 本 确 认 接 收 到 消 息 后 发 送 ack 但 是 如 果 leader

挂 掉 后 他 不 确 保 是 否 复 制 完 成 新 leader 也 会 导 致 数 据 丢 失 。

-1(all): 服 务 端 会 等 所 有 的 follower 的 副 本 受 到 数 据 后 才 会 受 到 leader 发 出 的

ack, 这 样 数 据 不 会 丢 失

12、 消 费 者 如 何 不 自 动 提 交 偏 移 量 , 由 应 用 提 交 ?

将 auto.commit.offset 设 为 false, 然 后 在 处 理 一 批 消 息 后 commitSync() 或 者

异 步 提 交 commitAsync()

即:

ConsumerRecords<> records = consumer.poll();


for (ConsumerRecord<> record : records){
。。。
tyr{

第 481 页 共 485 页
consumer.commitSync()
}
。。。
}

13、消费者故障,出现活锁问题如何解决?

出现“活锁”的情况,是它持续的发送心跳,但是没有处理。为了预防消费者在

这 种 情 况 下 一 直 持 有 分 区 ,我 们 使 用 max.poll.interval.ms 活 跃 检 测 机 制 。 在 此

基 础 上 , 如 果 你 调 用 的 poll 的 频 率 大 于 最 大 间 隔 , 则 客 户 端 将 主 动 地 离 开 组 , 以

便 其 他 消 费 者 接 管 该 分 区 。 发 生 这 种 情 况 时 , 你 会 看 到 offset 提 交 失 败 ( 调 用

commitSync( ) 引 发 的 CommitFailedException) 。 这 是 一 种 安 全 机 制 , 保 障

只 有 活 动 成 员 能 够 提 交 offset。 所 以 要 留 在 组 中 , 你 必 须 持 续 调 用 poll。

消 费 者 提 供 两 个 配 置 设 置 来 控 制 poll 循 环 :

max.poll.interval.ms:增 大 poll 的 间 隔 ,可 以 为 消 费 者 提 供 更 多 的 时 间 去 处 理 返

回 的 消 息( 调 用 poll(long)返 回 的 消 息 ,通 常 返 回 的 消 息 都 是 一 批 )。缺 点 是 此 值

越大将会延迟组重新平衡。

max.poll.records: 此 设 置 限 制 每 次 调 用 poll 返 回 的 消 息 数 , 这 样 可 以 更 容 易 的

预 测 每 次 poll 间 隔 要 处 理 的 最 大 值 。通 过 调 整 此 值 ,可 以 减 少 poll 间 隔 ,减 少 重

新平衡分组的

对 于 消 息 处 理 时 间 不 可 预 测 地 的 情 况 ,这 些 选 项 是 不 够 的 。 处 理 这 种 情 况 的 推 荐

方 法 是 将 消 息 处 理 移 到 另 一 个 线 程 中 ,让 消 费 者 继 续 调 用 poll。 但 是 必 须 注 意 确

保 已 提 交 的 offset 不 超 过 实 际 位 置 。 另 外 , 你 必 须 禁 用 自 动 提 交 , 并 只 有 在 线 程

完 成 处 理 后 才 为 记 录 手 动 提 交 偏 移 量( 取 决 于 你 )。 还 要 注 意 ,你 需 要 pause 暂

停 分 区 , 不 会 从 poll 接 收 到 新 消 息 , 让 线 程 处 理 完 之 前 返 回 的 消 息 ( 如 果 你 的 处

理能力比拉取消息的慢,那创建新线程将导致你机器内存溢出)。

第 482 页 共 485 页
14、如何控制消费的位置

kafka 使 用 seek(TopicPartition, long)指 定 新 的 消 费 位 置 。 用 于 查 找 服 务 器 保 留

的 最 早 和 最 新 的 offset 的 特 殊 的 方 法 也 可 用 ( seekToBeginning(Collection) 和

seekToEnd(Collection))

15、kafka 分布式(不是单机)的情况下,如何保证消息的顺

序消费?

Kafka 分 布 式 的 单 位 是 partition,同 一 个 partition 用 一 个 write ahead log 组 织 ,

所 以 可 以 保 证 FIFO 的 顺 序 。 不 同 partition 之 间 不 能 保 证 顺 序 。 但 是 绝 大 多 数 用

户 都 可 以 通 过 message key 来 定 义 , 因 为 同 一 个 key 的 message 可 以 保 证 只 发

送 到 同 一 个 partition。

Kafka 中 发 送 1 条 消 息 的 时 候 , 可 以 指 定 (topic, partition, key) 3 个 参 数 。

partiton 和 key 是 可 选 的 。如 果 你 指 定 了 partition,那 就 是 所 有 消 息 发 往 同 1

个 partition,就 是 有 序 的 。并 且 在 消 费 端 ,Kafka 保 证 ,1 个 partition 只 能 被

1 个 consumer 消 费 。或 者 你 指 定 key( 比 如 order id),具 有 同 1 个 key 的

所 有 消 息 , 会 发 往 同 1 个 partition。

16、kafka 的高可用机制是什么?

这 个 问 题 比 较 系 统 ,回 答 出 kafka 的 系 统 特 点 ,leader 和 follower 的 关 系 ,消 息

读写的顺序即可。

https://www.cnblogs.com/qingyunzong/p/9004703.html

第 483 页 共 485 页
https://www.tuicool.com/articles/BNRza2E

https://yq.aliyun.com/articles/64703

17、kafka 如何减少数据丢失

https://www.cnblogs.com /huxi2b/p/6056364.htm l

18、kafka 如何不消费重复数据?比如扣款,我们不能重复的

扣。

其实还是得结合业务来思考,我这里给几个思路:

比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入

了 , update 一 下 好 吧 。

比 如 你 是 写 Redis, 那 没 问 题 了 , 反 正 每 次 都 是 set, 天 然 幂 等 性 。

比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据

的 时 候 , 里 面 加 一 个 全 局 唯 一 的 id, 类 似 订 单 id 之 类 的 东 西 , 然 后 你 这 里 消 费

到 了 之 后 ,先 根 据 这 个 id 去 比 如 Redis 里 查 一 下 ,之 前 消 费 过 吗 ? 如 果 没 有 消

费 过 , 你 就 处 理 , 然 后 这 个 id 写 Redis。 如 果 消 费 过 了 , 那 你 就 别 处 理 了 , 保

证别重复处理相同的消息即可。

比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束

了,重复数据插入只会报错,不会导致数据库中出现脏数据。

互联网 Java 工程师面试题


内容涵盖:Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、

第 484 页 共 485 页
Redis、MySQL、Spring、Spring Boot、Spring Cloud、RabbitMQ、Kafka、
Linux 等技术栈

第 485 页 共 485 页

You might also like