Java面试宝典:掌握高级技巧,征服职场殿堂(怒肝88小时整理合集)

目录

一、Java 基础

1.JDK 和 JRE 有什么区别?

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

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

4.final 在 java 中有什么作用?  (在 Java 中 final 可以修饰类、变量、方法)

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

6.String 属于基础的数据类型吗?  (引用数据类型)

1 、字符类型: char  

2 、基本整型:byte,short,int,long  

3 、浮点型:float,double 

4 布尔类型:boolean”

7.java  中操作字符串都有哪些类? (String、 StringBuffer、 StringBuilder)它们之间有什么区别?

8.Stringstr="i"与 Stringstr=new String(“i”)一样吗?

9.如何将字符串反转?

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

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

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

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

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

15.java 中 IO 流分为几种?

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

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

二、容器

18.java 容器都有哪些?

19.Collection 和 Collections 有什么区别?

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

21.HashMap 和 Hashtable 有什么区别?

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

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

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

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

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

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

28.Array 和 ArrayList 有何区别?

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

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

31.迭代器 Iterator 是什么?

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

33.Iterator 和 ListIterator 有什么区别?

34.怎么确保一个集合不能被修改?

三、多线程

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

36.线程和进程的区别?

37.守护线程是什么?

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

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

40.线程有哪些状态?

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

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

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

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

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

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

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

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

49.什么是死锁?

50.怎么防止死锁?

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

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

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

54.synchronized 和 Lock 有什么区别?

55.synchronized 和 ReentrantLock 区别是什么?

56.说一下 atomic 的原理?

四、反射

57.什么是反射?

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

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

60.怎么实现动态代理?

五、对象拷贝

61.为什么要使用克隆?

63.深拷贝和浅拷贝区别是什么?

六、 Java Web

64.jsp 和 servlet 有什么区别?

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

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

67.session 和 cookie 有什么区别?

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

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

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

71.如何避免 sql 注入?

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

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

七、异常

74.throw 和 throws 的区别?

75.fifinal、fifinally、fifinalize 有什么区别?

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

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

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

八、网络

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

80.forward 和 redirect 的区别?

81.简述 tcp 和 udp 的区别?

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

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

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

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

86.如何实现跨域?

87.说一下 JSONP 实现原理?

九、设计模式

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

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

十、 Spring/Spring MVC

90.为什么要使用 spring?

91.解释一下什么是 aop?

92.解释一下什么是 ioc?

93.spring 有哪些主要模块?

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

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

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

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

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

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

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

101.spring mvc 有哪些组件?

102.@RequestMapping 的作用是什么?

103.@Autowired 的作用是什么?

十一、Spring Boot/Spring Cloud

104.什么是 spring boot?

105.为什么要用 spring boot?

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

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

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

109.jpa 和 hibernate 有什么区别?

110.什么是 spring cloud?

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

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

十三、 Mybatis

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

127.RowBounds 是一次性查询全部结果吗?为什么?

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

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

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

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

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

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

十四、 RabbitMQ

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

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

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

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

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

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

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

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

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

144.rabbitmq 有几种广播类型?

145.rabbitmq 怎么实现延迟消息队列?

146.rabbitmq 集群有什么用?

147.rabbitmq 节点的类型有哪些?

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

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

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

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

十六、 Zookeeper

157.zookeeper 是什么?

158.zookeeper 都有哪些功能?

159.zookeeper 有几种部署模式?

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

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

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

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

十七、 MySql

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

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

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

167.说一下 ACID 是什么?

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

169.flfloat 和 double 的区别是什么?  

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

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

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

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

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

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

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

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

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

十八、 Redis

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

180.redis 有哪些功能?

181.redis 和 memecache 有什么区别?

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

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

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

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

186.jedis 和 redisson 有哪些区别?

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

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

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

192.redis 淘汰策略有哪些?  193.redis 常见的性能问题有哪些?该如何解决?

十九、 JVM

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

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

196.说一下堆栈的区别?

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

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

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

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

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

202.说一下 jvm 有哪些垃圾回收算法?

203.说一下 jvm 有哪些垃圾回收器?

204.详细介绍一下 CMS 垃圾回收器?

205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

206.简述分代垃圾回收器是怎么工作的?

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

208.常用的 jvm 调优的参数都有哪些?

算法题

Linux 常用命令

一、基本命令

二、目录操作命令

三、文件操作命令

四、压缩文件操作

五、查找命令

十、其他命令

十一、高薪挑战宝典

11.1 集合常见的面试题

1. Arraylist 与 LinkedList 区别

2. Collections.sort 和 Arrays.sort 的实现原理

3. HashMap 原理, java8 做了什么改变

4. List 和 Set , Map 的区别

5. poll()方法和 remove()方法的区别?

6. HashMap , HashTable ,ConcurrentHash 的共同点和区别

7. 写一段代码在遍历 ArrayList 时移除一个元素

8. Java 中怎么打印数组?

9. TreeMap 底层?

10. HashMap 的扩容过程

11. HashSet 是如何保证不重复的

12. HashMap 是线程安全的吗, 为什么不是线程安全的? 死循环问题?

13. LinkedHashMap 的应用,底层, 原理

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

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

16. Collection 与 Collections 的区别是什么?

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

19. 迭代器 Iterator 是什么?怎么用,有什么特点?

20. Iterator 和 ListIterator 有什么区别?

21. 怎么确保一个集合不能被修改?

22. 快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

23. 什么是 Java 优先级队列(Priority Queue)?

24. JAVA8 的 ConcurrentHashMap 为什么放弃了分段锁,有什么问题吗, 如果你来设计, 你如何设计。

25. 阻塞队列的实现, ArrayBlockingQueue 的底层实现?

26. Java 中的 LinkedList 是单向链表还是双向链表?

27. 说一说 ArrayList 的扩容机制吧

28. HashMap 的长度为什么是 2 的幂次方, 以及其他常量定义的含义~

29. ConcurrenHashMap 原理? 1.8 中为什么要用红黑树?

30. ArrayList 的默认大小

31. 为何 Collection 不从 Cloneable 和 Serializable 接口继承?

32. Enumeration 和 Iterator 接口的区别?

33. 我们如何对一组对象进行排序?

34. 当一个集合被作为参数传递给一个函数时,如何才可以确保函数不能修改它?

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

36. Array 和 ArrayList 有何区别?

37. 为什么 HashMap 中 String、Integer 这样的包装类适合作为 key?

38. 如果想用 Object 作为 hashMap 的 Key?;

39. 讲讲红黑树的特点?

40. Java 集合类框架的最佳实践有哪些?

41.谈谈线程池阻塞队列吧~

42. HashSet 和 TreeSet 有什么区别?

43. Set 里的元素是不能重复的, 那么用什么方法来区分重复与否呢? 是用 ==还是 equals()?

45. HashMap 在 JDK1.7 和 JDK1.8 中有哪些不同?

46. ArrayList 集合加入 1 万条数据, 应该怎么提高效率

47. 如何对 Object 的 list 排序

48. ArrayList 和 HashMap 的默认大小是多数?

49. 有没有有顺序的 Map 实现类,如果有,他们是怎么保证有序的

50. HashMap 是怎么解决哈希冲突的

11.2 线程常见面试题

1、并发编程三要素?

2、实现可见性的方法有哪些?

3、多线程的价值?

4、创建线程的有哪些方式?

5、创建线程的三种方式的对比?

6、线程的状态流转图

7、Java 线程具有五中基本状态

8、什么是线程池?有哪几种创建方式?

9、四种线程池的创建:

10、线程池的优点?

11、常用的并发工具类有哪些?

12、CyclicBarrier 和 CountDownLatch 的区别

13、synchronized 的作用?

14、volatile 关键字的作用

15、什么是 CAS

16、CAS 的问题

17、什么是 Future?

18、什么是 AQS

19、AQS 支持两种同步方式:

20、 ReadWriteLock 是什么

21、 FutureTask 是什么

22、synchronized 和 ReentrantLock 的区别

23、什么是乐观锁和悲观锁

24、线程 B 怎么知道线程 A 修改了变量

25、synchronized、volatile、CAS 比较

26、sleep 方法和 wait 方法有什么区别?

27、ThreadLocal 是什么?有什么用?

28、为什么 wait()方法和 notify()/notifyAll()方法要在同步块中被调用

29、多线程同步有哪几种方法?

30、线程的调度策略

31、ConcurrentHashMap 的并发度是什么

32、 Linux 环境下如何查找哪个线程使用 CPU 最长

33、Java 死锁以及如何避免?

34、死锁的原因

35、怎么唤醒一个阻塞的线程

37、什么是多线程的上下文切换

38、如果你提交任务时, 线程池队列已满,这时会发生什么

39、Java 中用到的线程调度算法是什么

40、什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)?

41、什么是自旋

42、Java

Concurrency API 中的 Lock 接口(Lock

43、单例模式的线程安全性

44、Semaphore 有什么作用

45、 Executors 类是什么?

46、线程类的构造方法、静态块是被哪个线程调用的

47、同步方法和同步块, 哪个是更好的选择?

48、Java 线程数过多会造成什么异常?

11.3 MySQL 常见面试题

1 事务

2 B 树和 B+树

3 建立索引

4 组合索引(多列索引)

5 聚簇索引和非聚簇索引(针对 B+树索引)

6 数据库引擎(主要就是 MyISAM 和 InnoDB 的区别)

7 主从复制(读写分离、数据备份)


一、Java 基础

1.JDK JRE 有什么区别?

JREJava Runtime Environment)是 Java 运行时环境……它是运行编译后的 Java 程序所必需的一切包,包括 Java 虚拟机(JVM)Java 基础类库、  Java 命令和其他基础设施。但是,它不能用于创建新程序。

JDK Java 开发工具包……功能齐全的 SDKforJava。它拥有 JRE 所拥有的一切, 还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具:  jconsolejvisualvm 等工具软件,还包含了 java 程序编写所需的文档和 demo 例子程序。它能够创建和编译程序,是提供给程序员使用的。

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

1、功能不同

"=="是判断两个变量或实例是不是指向同一个内存空间的值

"equals"是判断两个变量或实例所指向的内存空间的值是不是相同。

2、定义不同

"equals"JAVA 中是一个方法。

"=="JAVA 中只是一个运算符合

(1)  Java 中基本类型变量有 byteshortchar int longfloatdouble boolean,也称为原始数据类型。他们之间的比较应用双等号( ==)比较他们的值,当使用 ==来判断两个变量是否相等时,如果这两个变量是基本数据类型,则只要两个变量值相等就放回 true

(2)  复合数据类型(类)

当使用 ==比较他们两个的值,比较的是他们的变量指向的地址值,除非这两个变量是同一个 NEW 出来的对象,则他们的比较为 true,反之为 false

Equals()方法:

Java 中所有的类都继承与基类 Object,在 Object 中定义了 equals 方法,这个方法的初始行为是比较对象的内存地址。但 是在一些类库中这个方法被重写了,如 String Integer Date 这些类中的 equals 方法有自身的实现,  而不再是比较内地址值。

所有说对用复合型数据类型(引用类型),我们推荐使用重写了的 equals 方法进行比较,

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

两个对象 equals 相等,则它们的 hashcode 必须相等,反之则不一定。两个对象 ==相等,则其 hashcode一定相等, 反之不一定成立。

hashCode(哈希码值)的存在主要应用于查找的快捷性,如 hashTable hashMap 等,hashCode 码是确定对象在散列存储结构中的地址值。

如果两个对象通过 equals 方法比较相等,那么他们的 hashCode 值也一定相等。反之 hashCode 码相等,但是连个对象的equals比较不一定相等,也就是说这两个对象之间不一定适用于 equals 方法,只能说明这两个对象都存在于散列存储结构中。

4.final  java 中有什么作用?  (在 Java 中 final 可以修饰类、变量、方法)

(1)Final 修饰类:

当用 final 修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用 final 进行修饰。 final 类中的成员变量可以根据需要设为 final,但是要注意 final 类中的所有成员方法都会被隐式地指定为 final 方法。

(2) Final 修饰方法:final 修饰的方法后该方法不能被子类重写(可以重载多个 final 修饰的方法), 意:重写的前提是子类可 以从父类中继承此方法,如果父类中用 final 修饰的方法同时访问修饰符是 private 时,子类中则可以定义相同的方法名和

参数

(3) Final 修饰变量: final 在修饰基本数据类型变量时,该变量一经赋值则不许改变,如果修饰的引用数据类型时,则代表着引用的地址值不能改变,但地址值所指向的内容可以改变。

①修饰成员变量时:修饰成员变量时必须显示初始化,初始化方式有两种,第一就是变量声名时直接初始化,二就是变量声

明后不初始化,但必须要在这个变量所在的类的所有构造函数中对这个变量进行赋值。

②修饰局部变量时:当函数的参数通过 final 进行定义时,代表着该参数为只读,可以读取使用该参数,但不能修改该参数值。

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

Math round 方法是四舍五入,如果参数是负数,则往大的数如,Math.round(- 1.5)=-1

6.String 属于基础的数据类型吗?  (引用数据类型)

String 并不是基本数据类,  而是一个类(class),C++java 等编程语言中的字符串,

Java 中八大基本类型数据:

1 字符类型: char  
2 基本整型:byte,short,int,long  
3 浮点型:float,double 
4 布尔类型:boolean

7.java  中操作字符串都有哪些类? (String、 StringBuffer、 StringBuilder)它们之间有什么区别?

String : final 修饰,  String 类的方法都是返回 new String。即对 String 对象的任何改变都不影响到原对象,对字符串的修改操作都会生成新的对象。  StringBuffer : 对字符串的操作的方法都加了 synchronized,保证线程安全。

StringBuilder : 不保证线程安全,在方法体内需要进行字符串的修改操作,  可以 new StringBuilder 对象,调用 StringBuilder 象的 append replacedelete 等方法修改字符串。

(1)    String 是不可变字符窜 StringBufferStringBuilder 是可变字符窜。三者的底层都是以 char[]形式保存字符窜 String 底层初始化时默认 char[]数组长度为 0,StringBufferStringBuilder 初始化默认的 char[]数组长度为 16

(因为 String 不可变,每次对 String 进行操作都会生成新的字符窜)

(2)    StringBuffer 中大部分的方法都是被 synchronized 关键字这就代表着 StringBuffer 是线程安全的。   StringBuilder    中的方法则没有,代表着他是一个线程不安全的。所以在单线程的情况下选择 StringBuilder 要更快,然而在多线程当中

考虑使用 StringBuffer 更加安全些

(3)    在生命变量的时候如果根据情况这个变量声明之后基本上不做修改我自己考虑直接使用 String,不同 new String()的

方式进行声明那样就不会再堆内存中创建对象,此时 String 变量直接指向常量池,并且可以复用,效率更高

8.Stringstr="i"Stringstr=new String(“i”)一样吗?

String str =  “i”代表着声明了一个变量 str,此时在常量池中创建了一个内容为 i 的字符窜对象。

String str = new String(“I”);此时代表着创建了两个对象, str 引用地址指向堆内存,如果常量池中没有字符窜 i,则会

在常量池中创建第二个对象字符窜 i。

9.如何将字符串反转?

1. 利用 StringBuffer StringBuilder  reverse 成员方法:

public static String reverse(Stringstr) {

return new StringBuilder(str).reverse().toString();

}

2. 利用 String toCharArray 方法先将字符串转化为 char 类型数组,然后将各个字符进行重新拼接:

//自定义方法

public static String reverse(Stringstr){

char[] chars = str.toCharArray();

//创建StringBuilder 对象进行拼接

StringBuilder builder = new StringBuilder();

for (inti = chars.length - 1; i >= 0; i--) {

builder.append(chars[i]);

}

return builder.toString();

}

3. 利用 String CharAt 方法取出字符串中的各个字符:

public static String reverse1(Stringstr){

//创建StringBuilder 对象进行拼接

StringBuilder builder = new StringBuilder();

//获取字符窜长度

int length = str.length();

for (inti = length-1; i >=0; i--) {

builder.append(str.charAt(i));

}

return builder.toString();

}

4.使用递归的方式进行反转

public static String reverse2(Stringstr){

//获取字符窜长度

int length = str.length();

if(length<=1){

returnstr;

}

String left = str.substring(0, length/2);

String right = str.substring(length/2,length);

return reverse2(right)+reverse2(left);

}

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

indexOf(Stringstr):查找指定的字符在当前字符窜第一次出现的索引值

charAt(int index) 返回指定索引处得字符

replace(char oldChar,char newChar) : 它是通过用 newChar 替换此字符串中出现的所有 oldChartrim() 去除字符串两端的空白

split() 分割字符串 返回分割后的字符串数组

getBytes() 返回字符串的 byte 类型数组

length() 返回字符串的长度

toLowerCase() 字符串转小写

toUpperCase() 字符串转大写

substring() 截取字符串

equals() 字符串比较

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

抽象类可以没有抽象方法,  但是如果你的一个类已经声明成了抽象类,即使这个类中没有抽象方法,  它也不能再实例化,即不能直接构造一个该类的对象。 如果一个类中有了一个抽象方法,那么这个类必须声明为抽象类,否则编译通不过。

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

抽象类不能被实例化

抽象类可以有抽象方法,抽象方法只需申明,  无需实现

含有抽象方法的类必须申明为抽象类

抽象类的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类

抽象方法不能被声明为静态

抽象方法不能用 private 修饰

抽象方法不能用 final 修饰

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

不能,抽象类是被用于继承的, final 修饰代表不可修改、不可继承的。

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

1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向

实现所有接口方法的类对象。

2、抽象类要被子类继承,接口要被类实现。

3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现

4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,  一

个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。

6、抽象方法只能申明,  不能实现,接口是设计的结果 ,抽象类是重构的结果

7、抽象类里可以没有抽象方法

8、如果一个类里有抽象方法,那么这个类只能是抽象类

9、抽象方法要被实现,  所以不能是静态的,也不能是私有的。

10、接口可继承接口,并可多继承接口,但类只能单根继承。

15.java IO 流分为几种?

Java 中的流分为两种,  一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个) :InputStream OutputStream  Reader WriterJava 中其他多种多样变化的流均是由它们派生出来的。

16.BIO NIOAIO 有什么区别?

BIO 是一个连接一个线程。   NIO 是一个请求一个线程。  AIO 是一个有效请求一个线程。

BIO:同步并阻塞,  服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如

果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,  多路复用器轮

询到连接有 I/O 请求时才启动一个线程进行处理。

AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的 I/O 请求都是由 OS 先完成了再通知服务器应用去启

动线程进行处理。

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

Files.exists() 检测文件路径是否存在 Files.createFile()创建文件 Files.createDirectory()创建文件夹 Files.delete() 删除文件或者目 Files.copy() 复制文件 Files.move() 移动文件 Files.size()查看文件个数 Files.read() 读取文件 Files.write()写入文件

二、容器

18.java 容器都有哪些?

Java面试宝典:掌握高级技巧,征服职场殿堂(怒肝88小时整理合集)_第1张图片

List,Map,Set Collection  List  LinkedList ArrayList Vector Stack Set Map  Hashtable  HashMap WeakHashMap

数据容器主要分为了两类:   Collection: 存放独立元素的序列。   Map:存放 key-value 型的元素对。(这对于需要利用 key 找 value 的程序十分的重要  从类体系图中可以看出, Collection 定义了 Collection 类型数据的最基本、最共性的功能接口, List 对该接口进行了拓展。 LinkedList :其数据结构采用的是链表,此种结构的优势是删除和添加的效率很高,但随机访问元素时效率较 ArrayList 类低。 ArrayList:其数据结构采用的是线性表,此种结构的优势是访问和查询十分方便,但添加和删除的时候效率很低。 HashSet: Set 类不允许其中存在重复的元素(集),无法添加一个重复的元素(Set 中已经存在)。

HashSet 利用 Hash 函数进行了查询效率上的优化,其 contain  ()方法经常被使用,以用于判断相关元素是否已经被添加过。 HashMap: 提供了 key-value 的键值对数据存储机制,可以十分方便的通过键值查找相应的元素,而且通过 Hash 散列机制,查找十分的方便。

19.Collection Collections 有什么区别?

Collection 是集合的接口,其继承类又 List Set

Collections 是集合的工具类,定义了许多操作集合的静态方法。是帮助类

20.ListSetMap 之间的区别是什么?

List:有序集合

Set:不重复集合, LinkedHashSet 按照插入排序, SortedSet 可排序,  HashSet 无序

Map:键值对集合

(1)元素的重复性:

List 集合中可以出现重复元素

Set 集合中不可以出现重复元素,在向 Set 集合中存储多个重复的元素时会出现覆盖

Map 集合采用 key-value 的形式存储,在 Map 中不能出现重复的 Key 键,但可以出现多个不同的 Key 对应相同的 Value

(2)元素的有序性:

List 集合及其所有的实现类都确保了元素的有序性

Set 集合中的元素是无序的, 但是某些 Set 集合通过特定的形式对其中的元素进行排序,如 LinkedHashSet

Map 和 Set一样对元素进行了无序存储,如:TreeMap 根据 Key 键实现了元素的升序排序

(3)元素是否为空值:

List 集合中允许存在多个空值

Set 最多允许一个空值出现

Map 中只允许出现一个空键,但允许出现多个空值

21.HashMap Hashtable 有什么区别?

1 HashMap 不是线程安全的 hastmap 是一个接口 是 map 接口的子接口,是将键映射到值的对象,其中键和值都是对象,并且不能包含重复键,但可以包含重复值。  HashMap 允许 null key null value 2 HashTable 是线程安全的一个 Collection

HashMap Hashtable 的轻量级实现(非线程安全的实现),他们都完成了 Map 接口,主要区别在于 HashMap 允许空(null)键值(key,由于非线程安全,效率上可能高于 Hashtable。  HashMap 允许将 null 作为一个 entry  key 或者 value而 Hashtable 不允许。  HashMap Hashtable contains 方法去掉了,改成 containsvalue containsKey。因为 contains方法容易让人引起误解。   Hashtable 继承自 Dictionary 类,而 HashMap Java1.2 引进的 Map interface 的一个实现。  最大的不同是,  Hashtable 的方法是 Synchronize 的,而 HashMap 不是,在多个线程访问 Hashtable 时,不需要自己为它的方法实现同步,而 HashMap 必须为之提供外同步。   Hashtable  HashMap 采用的hash/rehash 算法都大概一样,所以性能不会有很大的差异。

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

TreeMap Key 值是要求实现 java.lang.Comparable ,所以迭代的时候 TreeMap 认是按照 Key 值升序排序的;TreeMap 的实现是基于红黑树结构。适用于按自然顺序或自定义顺序遍历键(key)。

HashMap Key 值实现散列 hashCode() ,分布是散列的、均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。适用于在 Map 中插入、删除和定位元素。

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

HashMap 使用数组加链表实现。每个数组中储存着链表。当使用 put 方法储存 key-value 键值对时,会先调用 key hashCode 方法,得到此 key 经特定哈希运算后的值,然后将此值通过其他运算(?  得到一个值,将这个值与(length- 1)做或操作(&),相当于对数组长度做取余操作。最终得到一个值作为此 key 在数组中的索引值,然后将  key-value 键值对储存进去。

通过这种方法将储存的不同 key-value 键值对散列到数组的不同位置。

在储存的时候,如果索引位置尚无元素,那么直接储存。如果有元素,那么就调用此 key equals 方法与原有的元素的 Key

进行比较。如果返回 true,说明在这个 equals 定义的规则上,这两个 Key 相同,那么将原有的 key 保留,用新的 value 代替

原来的 value。如果返回 false,那么就说明这两个 key equals 定义的规则下是不同元素,那么就与此链表的下一个结点行比较,知道最后一个结点都没有相同元素,  再下一个是 null 的时候,就用头插法将此 key-value 添加到链表上。

HashMap 对重复元素的处理方法是:  key 不变,value 覆盖。

当使用 get 方法获取 key 对应的 value 时, 会和储存 key-value 时用同样的方法,得到 key 在数组中的索引值,  如果此索引值 上没有元素,就返回 null。如果此索引值上有元素,那么就拿此 key equals 方法与此位置元素上的 key 进行比较,如果返回 true。就返回此位置元素对应的 value。如果返回 false,就一直按链表往下比较,  如果都是返回 false,那么就返回 null

另外:  HashMap JDK1.8 之后引入红黑树结构。 HashMap 是线程不安全的,线程安全的是 CurrentHashMap,不过此集合在多线程下效率低。

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

首先,我们需要知道它是 Set 的一个实现,所以保证了当中没有重复的元素。 一方面 Set 中最重要的一个操作就是查找。而且通常我们会选择 HashSet 使用的是散列函数,那么它当中的元素也就无序可寻。当中是允许元素为 null 的。

25.ArrayList LinkedList 的区别是什么?

数据结构实现:   ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。    

 随机访问效率:  ArrayList  LinkedList 在随机访问的时候效率要高, 因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往  后依次查找。  

 增加和删除效率:   在非首尾的增加和删除操作,   LinkedList 要比 ArrayList 效率要高,    因为 ArrayList 增删操作 要影响数组内的其他数据的下标。   

综合来说:   在需要频繁读取集合中的元素时,   更推荐使用 ArrayList , 而在插入和删除操

作较多时,更推荐使用 LinkedList

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

List 转数组:  toArray()方法

数组转 ListArrays asList(a)方法

27.ArrayList Vector 的区别是什么?

这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合,即存储在这两个集合中的元素的位置都

是有顺序的,相当于一种动态的数组,我们以后可以按位置索引号取出某个元素,  并且其中的数据是允许重复的,这是与HashSet 之类的集合的最大不同处,  HashSet 之类的集合不可以按索引号去检索其中的元素,也不允许有重复的元素。

ArrayList Vector 的区别主要包括两个方面:  .

1)同步性:的方法之间是线程不同步的。如果只有一个线程会访问到集合,那最好是使用 ArrayList,因为它不考虑线程安全,效率会高些;如果有多个线程会访问到集合,那最好是使用 Vector,因为不需要我们自己再去考虑和编写线程安全的代码。

2)数据增长:

要增加 ArrayList Vector 的存储空间,每次要增加存储空间时,不是只增加一个存储单元,而是增加多个存储单元,每次增 加的存储单元的个数在内存空间利用与程序效率之间要取得一定的平衡。  Vector 默认增长为原来两倍,而 ArrayList 的增长策略在文档中没有明确规定(从源代码看到的是增长为原来的 1.5 倍)  ArrayList Vector 都可以设置初始的空间大小,  Vector还可以设置增长的空间大小,而 ArrayList 没有提供设置增长空间的方法。

28.Array ArrayList 有何区别?

Array Java 中的数组,  声明数组有三种方式 int[] a=new int[10]; inta[]=new int[10]; int a[]={1,2,3,4}; 可以看出:在定义一个数组的时候,必须指定这个数组的数据类型及数组的大小,也就是说数组中存放的元素个数固定并且类型一样

ArrayList 是动态数组,也就是数组的复杂版本,它可以动态的添加和删除元素,被称为集合 ,集合的声明如下 ArrayList list= new ArrayList(10); ArrayList list1 = new ArrayList(); 可以看出:在不使用泛型的情况下,这个 list 是可以添加进不同类型的元素的,而且 arraylist 是可以不用指定长度的。在使用泛型时,我们就只能添加一种类型的数据了

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

相同点:都是返回第一个元素,并在队列中删除返回的对象。

poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。

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

Vector:就比 Arraylist 多了个同步化机制(线程安全)  。

Hashtable:就比 Hashmap 多了个线程安全。

ConcurrentHashMap:是一种高效但是线程安全的集合。

Stack:栈,也是线程安全的,继承于 Vector

31.迭代器 Iterator 是什么?

首先说一下迭代器模式,它是 Java 中常用的设计模式之一。用于顺序访问集合对象的元素,无需知道集合对象的底层实现。

Iterator 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦。

缺点是增加新的集合类需要对应增加新的迭代器类,迭代器类与集合类成对增加。

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

1 Iterator  ()要求容器返回一个 Iterator Iterator 将准备好返回序列的第一个元素。

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

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

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

有什么特点:

1 Iterator 遍历集合元素的过程中不允许线程对集合元素进行修改,否则会抛出

ConcurrentModifificationEception 的异常。

2 Iterator 遍历集合元素的过程中可以通过 remove 法来移除集合中

的元素,删除的是上一次 Iterator.next()方法返回的对象。

3 Iterator 必须依附于一个集合类对象而存在, Iterator 本身不具有装载数据对象的功能。

4 next  ()方法,该方法通过游标指向的形式返回 Iterator 下一个元

素。

33.Iterator ListIterator 有什么区别?

1)所属关系,  ListIterator 是一个 Iterator 的子类型。    

2)局限:只能应用于各种 List 类的访问。   

3)优势:Iterator 只能向前移动,而 ListIterator 可以双向移动。    

4 ListIterator add() 方法,可以向 List 中添加对象,而Iterator 不能。

34.怎么确保一个集合不能被修改?

我们可以采用 Collections 包下的 unmodifiableMap 方法,通过这个方法返回的 map,是不可以修改的。他会报java.lang.UnsupportedOperationException 错。

同理: Collections 包也提供了对 list set 集合的方法。    Collections.unmodififiableList(List)

Collections.unmodififiableSet(Set)

三、多线程

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

解释一:并行是指两个或者多个事件在同一时刻发生;  而并发是指两个或多个事件在同一时间间隔发生。

解释二:   并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。

解释三:在一台处理器上 同时处理多个任务,在多台处理器上同时处理多个任务。如 hadoop 分布式集群 所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

36.线程和进程的区别?

进程是执行着的应用程序,  而线程是进程内部的一个执行序列。  一个进程可以有多个线程。线程又叫轻量级进程。  线程的划分小于进程,线程是隶属于某个进程的。进程是程序的一种动态形式,是 CPU,内存等资源占用的基本单

位,而线程是不能占有这些资源的。

进程之间相互独立,通信比较困难,而线程之间共享一块内存区域,通信比较方便。

进程在执行过程中包含比较固定的入口,执行顺序,出口,而线程的这些过程会被应用程序所控制。

37.守护线程是什么?

1、守护线程,专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连 main 线程也执行完

毕,那么 jvm 就会退出(即停止运行)  此时,连 jvm 都停止运行了,守护线程当然也就停止执行了。

2、再换一种说法,如果有用户自定义线程存在的话, jvm 就不会退出——此时,守护线程也不能退出,  也就是它还

要运行,干嘛呢,  就是为了执行垃圾回收的任务啊。

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

1,继承 Thread 类,重写 run 方法;

2,实现 Runnable 接口,重写 run 方法,但是比继承 Thread 类好用,实现接口还 可以继承类,避免了单继承带来的局限性;

3,使用 Executor 框架创建线程池。  Executor 框架是 juc 里提供的线程池 的实现。   调用线程的 start():启动此线程;调用相

应的 run()方法

Thread 的常用方法:

1.start():启动线程并执行相应的 run()方法

2.run():子线程要执行的代码放入 run()方法中

3.currentThread():静态的,调取当前的线程

4.getName():获取此线程的名字

5.setName():设置此线程的名字

6.yield():调用此方法的线程释放当前 CPU 的执行权(很可能自己再次抢到资源)

7.join():A 线程中调用 B 线程的 join()

方法,表示:当执行到此方法, A 线程停止执行,直至 B 线程执行完毕,   A 线程再接着 join()之后的代码执行

8.isAlive():判断当前线程是否还存活

9.sleep(long l):显式的让当前线程睡眠 l 毫秒 (只能捕获异常,因为父类 run 方法没

有抛异常)

10.线程通信(方法在 Object 类中):  wait() notify() notifyAll() *设置线程的优先级(非绝对,只是相对几

率大些)

11.getPriority():返回线程优先值 setPriority(int newPriority):改变线程的优先级

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

相同点:

1. 两者都是接口;(废话)

2. 两者都可用来编写多线程程序;

3. 两者都需要调用 Thread.start()启动线程

不同点:

1. 两者最大的不同点是:实现 Callable 接口的任务线程能返回执行结果;  而实现 Runnable 接口的任务线程不能返回结果;

2. Callable 接口的 call()方法允许抛出异常;而 Runnable 接口的 run()方法的异常只能在内部消化,不能继续上抛;

40.线程有哪些状态?

线程状态有 5 种, 新建,就绪,运行,阻塞,  死亡

新建状态:创建一个新的线程对象

就绪:新的线程对象调用 start()方法,该状态下的线程位于可运行的线程池中,等待获取 CPU 调度时间运行:线程池中的线程获取到了 CPU 的资源,开始执行程序

阻塞: 指运行状态下的线程因为某种原因放弃了 CPU 使用权,暂时停止运行,直到线程再次进入就绪状态才有机会获取 CPU 的资源进入到运行状态

(1)、等待阻塞:运行中的线程执行了 wait()方法,JVM 会把该线程放入等待队列(等待池)中

(2)、同步阻塞:运行中的线程在获取对象的同步锁时,若当前的对象锁被其他线程所占有,则 JVM 会将该线程放入锁池中

(3)、其他阻塞:运行中的线程执行了 Thread.sleep()方法   或者 join()方法,或者发送了 I/O 请求时,JVM 会将该线程设置为阻塞状态

死亡:线程 run(),main()方法执行结束,或值 run()执行过程中出现异常,则该线程结束生命周期

1. 线程 start 方法执行后,并不表示该线程运行了,而是进入就绪状态,意思是随时准备运行,但是真正何时运行,是由操作系统决定的,  代码并不能控制,

2. 同样的,从运行状态的线程,也可能由于失去了 CPU 资源,回到就绪状态,也是由操作系统决定的。这一步中,也可以由程序主动失去 CPU 资源,只需调用 yield 方法。

3. 线程运行完毕,或者运行了一半异常了,或者主动调用线程的 stop 方法,那么就进入死亡。死亡的线程不可逆转。

4. 下面几个行为,会引起线程阻塞。

主动调用 sleep 方法。时间到了会进入就绪状态 主动调用 suspend 方法。主动调用 resume 方法,会进入就绪状态调用了阻塞式 IO 方法。调用完成后,会进入就绪状态。   试图获取锁。成功的获取锁之后,会进入就绪状态。   线程在等待某个通知。其它线程发出通知后,会进入就绪状态

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

1、同步锁 zd 的对待不同:

sleep()后,程序并不会不释放同步锁。

wait  ()后,程序会释放同步锁。

2、用法的不同:

sleep()可以用时间指定版来使他自动醒过来。如果时间不到你只能调用 interreput()来强行打断。

wait  ()可以用 notify()直接唤起。

(1)sleep()方法是 Thread 类中的静态方法,wait 属于 Object 基类的成员方法(他们都会是线程进入到阻塞状态)

(2)sleep()方法是线程类(Thread)的方法,不会涉及到线程通信,调用 sleep()方法后会使线程睡眠指定的时间,但此时线程不会释 放同步锁。  wait()涉及到线程通信问题,在调用 wait 方法后线程会主动释放同步锁.进入等待队列,可以通过 notify/notifyAll

唤醒线程。才会进入锁池中准备获取对象锁

(3)对象的 wait 方法即 notify()、 notifyAll()只能在同步代码块中执行,  sleep()能随处调用

(4)sleep()方法必须捕获异常(InterruptedException), wait 方法即 notify() notifyAll()则不需要

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

锁池:假设线程 A 已经拥有了某个对象(注意:不是类)的锁, 而其它的线程想要调用这个对象的某个 synchronized 方法   (或者 synchronized ),由于这些线程在进入对象的 synchronized 方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程 A 拥有,所以这些线程就进入了该对象的锁池中。

等待池:假设一个线程 A 调用了某个对象的 wait()方法,线程 A 就会释放该对象的锁后,进入到了该对象的等待池中

如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,  等待池中的线程不会去竞争该对象的锁。

当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒    的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,  调用了 notify 后只要一个线程会由  等待池进入锁池,而 notifyAll 会将该对象等待池内的所有线程移动到锁池中,等待锁竞争 优先级高的线程竞争到对象 锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()法,它才会重新回到

等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

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

调用 start() 方法是用来启动线程的,轮到该线程执行时,会自动调用 run();直接调用 run() 方法,无法达到启动多线程的目的,相当于主线程线性执行 Thread 对象的 run() 方法。   一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;  run() 方法没有限制。

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

1 newCachedThreadPool(),它是用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置时间超过 60 秒,则被终止并移除缓存;长时间闲置时,这种线程池,  不会消耗什么资源。其内部使用 SynchronousQueue 为工作队列。

2 newFixedThreadPoolint nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动线程数目,将在工作队列中等待空闲线程出现;如果工作线程退出,将会有新的工作线程被创建,  以补足指定数目 nThreads

3 newSingleThreadExecutor(),它的特点在于工作线程数目限制为 1,操作一个无界的工作队列,所以它保证了所   有的任务都是被顺序执行,  最多会有一个任务处于活动状态,并且不予许使用者改动线程池实例,因此可以避免改变线程数目。

4 newSingleThreadScheduledExecutor()newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。

5 newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建 ForkJoinPool,利用 Work-Stealing 算法,并行地处理任务,不保证处理顺序。

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

1. RUNNING:线程池一旦被创建,就处于 RUNNING 状态,任务数为 0,能够接收新任务,对已排队的任务进行处理。

2. SHUTDOWN:不接收新任务,但能处理已排队的任务。调用线程池的 shutdown() 方法,线程池由 RUNNING 变为 SHUTDOWN 状态。

3. STOP:不接收新任务,  不处理已排队的任务,并且会中断正在处理的任务。调用线程池的 shutdownNow() 法,线程池由(RUNNING SHUTDOWN ) 转变为 STOP 状态。

4. TIDYING

SHUTDOWN 状态下,任务数为 0 , 其他所有任务已终止,线程池会变为 TIDYING 状态,会执行 terminated() 法。线程池中的 terminated() 方法是空实现,可以重写该方法进行相应的处理。线程池在 SHUTDOWN 状态,任务队列为空且执行中任务为空,线程池就会由 SHUTDOWN 转变为 TIDYING 状态。线程池在 STOP 状态,线程池中执行中任务为空时,就会由STOP TIDYING 状态。

5. TERMINATED:线程池彻底终止。线程池在 TIDYING 状态执行完 terminated() 方法就会由 TIDYING 转变为TERMINATED 状态。

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

submit(Callable task)submit(Runnable task, T result)submit(Runnable task)归属ExecutorService 接口。execute(Runnable command)归属于 Executor 接口。 ExecutorService 继承了 Executor

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

线程的安全性问题体现在:

原子性:  一个或者多个操作在 CPU 执行的过程中不被中断的特性 可见性: 一个线程对共享变量的修改,另外一个线程能够立刻看到 有序性:  程序执行的顺序按照代码的先后顺序执行

导致原因:

缓存导致的可见性问题 线程切换带来的原子性问题 编译优化带来的有序性问题解决办法:

JDK Atomic 开头的原子类、  synchronized LOCK,可以解决原子性问synchronizedvolatile LOCK,可以解决可见性问题 Happens-Before 规则可以解决有序性问题

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

锁的级别从低到高:

无锁 - > 偏向锁 - > 轻量级锁 - > 重量级锁

锁分级别原因:

没有优化以前,  synchronized 是重量级锁(悲观锁),使用 wait notify notifyAll 来切换线程状态非常消耗系统  资源;线程的挂起和唤醒间隔很短暂,这样很浪费资源,影响性能。所以 JVM synchronized 关键字进行了优化,把锁分为 无锁、偏向锁、轻量级锁、重量级锁 状态。

无锁:没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失败的线程会不断重试直到修改成功。

偏向锁:对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续的执行中自动获取锁,降低获取锁带 来的性能开销。偏向锁,指的就是偏向第一个加锁线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争偏向锁才会被释放。偏向锁的撤销,需要在某个时间点上没有字节码正在执行时,先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁;如果线程处于活动状态,升级为轻量级锁的状态。

轻量级锁:轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁;当一个线程已持有锁,  另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁。

重量级锁:指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。

重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock 现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。

49.什么是死锁?

死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力 作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 

50.怎么防止死锁?

尽量使用 tryLock(longtimeout, TimeUnit unit)的方(ReentrantLock ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。 尽量使用 Java. util. concurrent 并发类代替自己手写锁。 尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。 尽量减少同步的代码块。

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

Thread Local 类是线程局部变量,是一种实现线程安全的方式。但是在管理环境下使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,  Java 应用就存在泄露大的风险

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

可以保证方法或代码块在运行时,同一时刻只有一个方法可以进入临时界区,同时它还可以保证共享变量的内存可见

普通同步方法,锁是当前实列对象

静态同步方法,锁是当前类的 class 对象

同步方法块,锁是括号里面的 对象

53.synchronized volatile 的区别是什么?

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

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

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

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

54.synchronized Lock 有什么区别?

首先 synchronized Java 内置关键字,在 jvm 层里面,  LOck 是个类;

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

synchronized 会自动释放锁,否则容易造成线程死锁

synchronized 会等待线程,  Lock 锁不会等待如果尝试获取不到锁,线程可以不用一直等待就结束了

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

55.synchronized ReentrantLock 区别是什么?

一个是关键字,  一个是类,  更灵活的特性,可被继承可以有方法

ReentrantLock 可以获取锁的等待时间设置,这样避免死锁,各种锁的信息,灵活实现多路通知

56.说一下 atomic 的原理?

当多个线程同时对该变量进行更新时,仅有一个线程能成功,而未成功的线程可以向自旋锁一样,继续尝试,  一直等待到执行成功

四、反射

57.什么是反射?

主要指程序可以访问,检测,修改本生状态或行为的一种能力

Java 运行是环境中,对任意一个类,是否知道这个类有属性和方法?

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

序列化:将 Java 对象转换成字节流的过程。

反序列化:将字节流转换成 Java 对象的过程。当 Java 对象需要在网络上传输 或者 持久化存储文件中时,就需要对 Java 象进行序列化处理。

序列化的实现:类实现 Serializable 接口,  这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉jvm 这个类的对象可以被序列化。

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

当想要给实现某个接口的类中的方法,加一些格外的处理。比如加日志,  加事务,  加权限等。可以给这个类创建一个代理,这个代理并不是定义好的,是动态生成的,具有解耦,灵活,扩展性强

60.怎么实现动态代理?

首先必须定义一个接口,还要有一个 InvocationHandler 处理类,再有一个工具类 Proxy。利用到 InvocationHandler处理类,拼接代理类源码,  将其编译生成代理类的二进制码,利用加载器加载,并将其实列化产生代理对象,  最后返

五、对象拷贝

61.为什么要使用克隆?

想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了。克隆分浅克隆和深克隆,浅克隆后的对象中非基本对象和原对象指向同一块内存,因此对这些非基本对象的修改会同时更改克隆前后的对象。深克隆可以实现完全的克隆,可以用反射的方式或序列化的方式实现。

62.如何实现对象克隆?

1 实现 Cloneable 接口并重写 object 类中的 clone  ()方法

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

63.深拷贝和浅拷贝区别是什么?

浅拷贝只是复制了对象的引用地址,两个对象指向同一内存地址,所有修复其中的任意值,另一个值都会随之变化深拷贝是将对象及值复制过来,两个对象修改其中任意值另一个值不会改变

六、 Java Web

64.jsp servlet 有什么区别?

jsp 编译后变成了 servlet

jsp 更擅长变现页面显示,  servlet 更擅长逻辑控制

servlet 没有内置对象,  jsp 的内置对象都是必须通过 httpservletrequst 对象,  httpservletrespose 对象以及

httpservlet 对象得到的

jsp servlet 的一种简化

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

request:封装客户请求 get post

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

pageContext:通过对该对象获取其他对象

session:封装用户会话的对象

application:封装服务器运行环境的对象 out:输出服务器响应的输出流对象

confifigweb 应用的配置对象

pagejsp 页面本身

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

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

page:jsp 页面本身

request:封装客户请求 get post

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

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

67.session cookie 有什么区别?

由于 HTTP 协议是无状态的协议,所以服务端余姚记录用户的状态 session 是保存在服务端的

cookie 是保存在客户端的

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

session 是一个存在服务器上的类似于一个散列表格的文件,里面存有我们需要信息,需要用的时候取出来,类似于大号的 map,里面的键存储的是用户的 sessionid,用户向服务器发送请求的时候会带上这个 sessionid。就可以从中取值

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

不能的到 session,禁用相当于失去 session ,一般认为两个是独立的东西,  cookie 采用的是客户端保持状态方案,session 采用的是客户端保持方案

70.spring mvc struts 的区别是什么?

struts2 是类级别的拦截器,  struts2 Action 的一个方法可以对应一个 url,而其类属性却被所有方法共享,这也就无法用注解或其他的方式标识其所属方法了,只能设计为多例

springmvc 是方法级别的拦截,在 spring 整合时,  springmvc controller Bean 默认单列模式 Singleton,所以默认对所有的请求,只会创建一个 controller

71.如何避免 sql 注入?

PreparedStatement使用正则表达式过滤传入的参数

字符串过滤

jsp 页面判断代码

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

跨站脚本攻击,攻击者向有 xss 漏洞的网站中输入恶意的 HTML 代码,当用户浏览该网站时,这段 HTML 就会自   动执行,类似于 sql 注入攻击,而 xss 攻击中,通过插入恶意脚本,实现对用户浏览器的控制,获取用户的一些信息。

总体思路:对输入 url 进行过滤,对输出进行编码。

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

跨站请求伪造,攻击者伪造用户的浏览器的请求,向访问一个用户自己曾认证访问的网站发出来,  使目标网站接收并误以为是用户的真是操作而执行的命令,常用于盗取 账号,转账,发送虚假信息等。攻击者利用请求网站的验证漏洞。网站不能验证请求是否源于用户的真实信息。

七、异常

74.throw throws 的区别?

throw

表示方法内抛出某种异常对象 如果异常对象是非 RuntimeException 则需要在方法申明时加上该异常的抛出 即需要 加上 throws 语句 或者 在方法体内 try catch 处理该异常,否则编译报错 执行到 throw 语句则后面的语句块不再执行 throws方法的定义上使用 throws 表示这个方法可能抛出某种异常 需要由方法的调用者进行异常处理

75.fifinalfifinallyfifinalize 有什么区别?

1.fifinal : 1、修饰符(关键字) 如果一个类被声明为 fifinal,意味着它不能再派生新的子类,不能作为父类被继承。因此一个类不能及被声明为 abstract,又被声明为 fifinal 的。  2、将变量或方法声明为...

2.fifinally: 在异常处理时提供 fifinally 块来执行清楚操作。如果抛出一个异常,那么相匹配的 catch 语句就会执行,然后控制...

3.fifinalize: 是方法名。java 技术允许使用 fifinalize()方法在垃圾收集器将对象从内存中清除之前做必要的清理工作。这

个方法是在垃圾收集器在确定了,被清理对象没有被引用的情况下调用的。 .

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

catch fifinally 语句块可以省略其中一个。

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

会执行,在 return 前执行

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

Error,即错误,代表 JVM 本身的错误,处理程序运行环境方面的异常,不能通过代码处理。比如

OutOfMemoryError AWTError 等。

Exception:即异常,程序运行时发生,可以被 java 异常处理机制使用。比如 IOException SQLEXception

RuntimeException 等等。

以上,  Error  RuntimeException 是非检查异常

八、网络

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

301 redirect: 301 代表永久性转移(Permanently Moved)

302 redirect: 302 代表暂时性转移(Temporarily Moved )

301 表示旧地址 A 的资源已经被永久地移除了(这个资源不可访问了),  搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址 302 表示旧地址 A 的资源还在(仍然可以访问),  这个重定向只是临时地从旧地址 A 跳转到地址 B 搜索引擎会抓取新的内容而保存旧的网址。

80.forward redirect 的区别?

1、请求方不同

redirect:客户端发起的请求

forward:服务端发起的请求

2、浏览器地址表现不同

redirect:浏览器地址显示被请求的

urlforward:浏览器地址不显示被请求的 url

3、参数传递不同

redirect:重新开始一个 request,原页面的 request 生命周期结束。

forwardforward 另一个连接的时候。  request 变量是在其生命周期内的。另一个页面也可以使用,其实质是把目标地址 include

81.简述 tcp udp 的区别?

UDP 是面向无连接的通讯协议, UDP 数据包括目的端口号和源端信息。   优点:  UDP 速度快、操作简单、要求   系统资源较少,由于通讯不需要连接,可以实现广播发送 缺点:  UDP 传送数据前并不与对方建立连接,对接收 到的数据也不发送确认信号,发送端不知道数据是否会正确接收,也不重复发送,  不可靠。   TCP 是面向连接的  通讯协议,通过三次握手建立连接,通讯完成时四次挥手 优点: TCP 在数据传递时,有确认、窗口、重传、阻塞等控制机制,能保证数据正确性,较为可靠。 缺点:TCP 相对于 UDP 速度慢一点,要求系统资源较多。

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

两次握手只能保证单向连接是畅通的。

只有经过第三次握手,才能确保双向都可以接收到对方的发送的 数据。

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

tcp 粘包可能发生在发送端或者接收端,分别来看两端各种产生粘包的原因:

发送端粘包:发送端需要等缓冲区满才发送出去,造成粘包;  接收方粘包:接收方不及时接收缓冲区的包,造成多个包接收。

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

1、物理层协议有:  EIA/TIA-232  EIA/TIA-499 V.35 V.24  RJ45  Ethernet  802.3

2、数据链路层协议有:  Frame Relay  HDLC  PPP  IEEE 802.3/802.2

3、网络层协议有:  IP  IPX AppleTalk DDP

4、传输层协议有:  TCP  UDP SPX

5、会话层协议有:  RPC SQL  NFS  NetBIOS  names AppleTalk

6、表示层协议有:  TIFF GIF JPEG  PICT ASCII  EBCDIC encryption

7、应用层协议有:  FTP WWW Telnet  NFS SMTP Gateway SNMP

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

1.get 是从服务器上获取数据,post 是向服务器传送数据。

2.get 请求时通过 URL 直接请求数据,数据信息可以在 URL 中直接看到,比如浏览器访问;post 请求是放在请求头中的,用户无法直接看到。

3.get 传送的数据量较小,有限制,不能大于 2KB;这主要是因为它受约于 URL 长度的限制。 p...

4.get 请求因为数据参数是暴露在 URL 中的,所以安全性比较低,如密码不能暴露的就不能用 get

86.如何实现跨域?

 1jsonp 利用了 script 不受同源策略的限制 缺点:只能 get 方式,易受到 XSS 攻击    2CORSCross-Origin Resource Sharing,跨域资源共享 当使用 XMLHttpRequest 发送请求时,如果浏览器 发现违反了同源策略就会自动加上一个请求头 origin; 后端在接受到请求后确定响应后会在后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-Allow-Origin; 浏览器判断响应中的Access-Control-Allow-Origin 值是否和当前的地址相同,  匹配成功后才继续响应处理,否则报错 缺点:忽略cookie,浏览器版本有一定要求

3、代理跨域请求 前端向发送请求,经过代理,  请求需要的服务器资源 缺点:需要额外的代理服务器

4 Html5 postMessage 方法 允许来自不同源的脚本采用异步方式进行有限的通信,  以实现跨文本、多窗口、跨域消息传递 缺点: 浏览器版本要求,部分浏览器要配置放开跨域限制

5、修改 document.domain 跨子域 相同主域名下的不同子域名资源,设置 document.domain 为 相同的一级域名 缺点:同一一级域名;相同协议;相同端口

6、基于 Html5 websocket 协议 websocket Html5一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,允许跨域请求 缺点:浏览器一定版本要求,  务器需要支持 websocket 协议

7document.xxx + iframe 通过 iframe 是浏览器非同源标签,加载内容中转,传到当前页面的属性中 缺点:页面的属性值有大小限制

87.说一下 JSONP 实现原理?

首先在客户端注册一个 callback,然后把 callback 的名字传给服务器。此时,服务器先生成 json 数据,然后以javascript 语法的方式,生成 function function 名字就是传递上来 I 带参数 jsonp。最后将 json 数据直接以入参  的方式,放置 function 中, 这样就生成 js 语法的文档, 返回给客户端。客户端浏览器,解script 变迁,并执行返回 javascript 文档,此时数据作为参数,传入了客户端预先定义好的 callback 函数里。简单的说,就是利用script 标签没有跨域限制的漏洞来达到与第三方通讯的目的。

九、设计模式

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

Java 中一般认为有 23 种设计模式,   总体来说设计模式分为三大类:

创建型模式,共五种:  工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。   结构型模式,共七 种:  适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。   行为型模式,共十一 种:  策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录 模式、状态模式、访问者模式、中介者模式、解释器模式。

// 直接创建对象 public static Singleton instance = new Singleton(); // 私有化构造函数 private Singleton() { }

// 返回对象实例 public static Singleton getInstance() { return instance; }

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

简单工厂模式 是由一个工厂对象创建产品实例,简单工厂模式的工厂类一般是使用静态方法,通过不同的参数的创

建不同的对象实例 可以生产结构中的任意产品,不能增加新的产品

抽象工厂模式 提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类,生产多个系列产品 生产

不同产品族的全部产品,不能新增产品,可以新增产品族

十、 Spring/Spring MVC

90.为什么要使用 spring

1.方便解耦,便于开发(Spring 就是一个大工厂,可以将所有对象的创建和依赖关系维护都交给 spring 管理)

2.spring 支持 aop 编程(spring 提供面向切面编程,可以很方便的实现对程序进行权限拦截和运行监控等功能)  

3.声明式事务的支持(通过配置就完成对事务的支持,不需要手动编程)

4.方便程序的测试,  spring  junit4 支持,可以通过注解方便的测试 spring 程序

5.方便集成各种优秀的框架()

6.降低 javaEE API 的使用难度(Spring  javaEE 开发中非常难用的一些 API 例如 JDBC,javaMail,远程调用等,都提供了封装,是这些 API 应用难度大大降低)

91.解释一下什么是 aop

在业务系统中,总有一些不得不处理的事情,  我们 将这些重复性的代码抽取出来,  放在专门的类中,   在通过 spring的 AOP 的核心对代码段进行增强处理。   在不改变原代码的基础上进行功能增强。有五种 增强方式,前置增强,后置增强,环绕增强,  引介增强。异常增强。

92.解释一下什么是 ioc

iocinverse of control )控制反转:所谓控制反转就是把对象(bean)对象和维护对象(bean)之间的关系的权利转移到 Sqring 容器中去了(ApplicationContext.xml)而程序本身不在维护了

didependency injection)依赖注入:实际上 DI IOC 是同一个概念,因为在 ApplicationContext.xml 配置文件中 bean bean 之间通过 ref 来维护的时候是相互依赖的,所以又叫做依赖注入。也就是控制反转

93.spring 有哪些主要模块?

Spring 有七大功能模块,  分别是 Spring Core AOP ORM  DAO  MVC WEB Context

 1 Spring CoreCore 模块是 Spring 的核心类库, Spring 的所有功能都依赖于该类库,  Core 主要实现 IOC 功能,  Sprign 的所有功能都是借助 IOC 实现的。  

 2 AOP AOP 模块是 Spring AOP 库,提供了 AOP (拦截器)机制,并提供常用的拦截

器,供用户自定义和配置。   

3 ORM Spring ORM 模块提供对常用的 ORM 框架的管理和辅助支持,   Spring 持常用的 Hibernate ibtasjdao 等框架的支持,  Spring 本身并不对 ORM 进行实现,  仅对常见的 ORM 框架进行封装,并对其进行管理

4  DAO 模块 Spring 提供对 JDBC 的支持,对 JDBC 进行封装,  允许 JDBC 使用 Spring 源,并能统一管理 JDBC 事物,并不对 JDBC 进行实现。(执行 sql 语句)  

5 WEB 模块 WEB 模块提供对常见框架如 Struts1 WEBWORKStruts 2), JSF 的支持,  Spring 能够管理这些框架,将 Spring 的资源注入给框架,   也能在这些框架的前后插入拦截器。   

6 Context 模块 Context 模块提供框架式的 Bean 访问方式,其他程序可以通过 Context 访问 Spring Bean 资源,相当于资源注入。    

7  MVC 模块 WEB MVC 模块Spring 提供了一套轻量级的 MVC 实现,在 Spring 的开发中,我们既可以用 Struts 也可以用 Spring 自己的 MVC 框架,相对于 Struts Spring自己的 MVC 框架更加简洁和方便。

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

构造方法注入,  setter 注入,  基于注解的注入

主要有四种注解可以注册 bean,每种注解可以任意使用,  只是语义上有所差异:

@Component:可以用于注册所有 bean @Repository:主要用于注册 dao 层的 bean @Controller主要用于注册控

制层的 bean @Service:主要用于注册服务层的 bean

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

容器本身并没有提供 Bean 的线程安全策略,因此可以说 Spring 容器中的 Bean 本身不具备线程安全的特性,但是具体还是要结合具体 scope Bean 去研究。

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

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

prototype:原型模式,每次通过容器的 getbean 方法获取 prototype 定义的 bean 时,都产生一个新的 bean实例

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

1)在 XML 中进行显式配置 (2)在 java 中进行显式配置 (3)隐式的 bean 发现和自动装配(一般推荐使用自动装

bean 的方式)

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

1)编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中调用 beginTransaction()commit() rollback()等事务管理相关的方法,  这就是编程式事务管理。 2)基于 TransactionProxyFactoryBean的声明式事务管理

(3)基于 @Transactional 的声明式事务管理

4)基于 Aspectj AOP 配置事务

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

READ UNCOMMITTED (读未提交数据):允许事务读取未被其他事务提交的变更数据,会出现脏读、不可重复读和幻读问题。  

 READ COMMITTED  (读已提交数据   只允许事务读取已经被其他事务提交的变更数据,可避免脏读,仍会出现不可重复读和幻读问题。 

REPEATABLE READ (可重复  确保事务可以多次从一个字 段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,  可以避免脏读和不可重复读,仍会出现幻读问题。 SERIALIZABLE   (序列化):确保事务可以从一个表中读取相同的行,  在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,  可避免所有并发问题,但性能非常低。 Spring 隔离级别

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

用户发送请求到前端控制器 DispatcherServlet,之后调用 HanderMapping 处理器找到具体的处理器返回,再调用

HanderlerAdapter 处理适配器,再调用具体的 Controller,执行完后返回 Modeandview  handlerAdate 将结果返回

DispatcherServlet,再传给 ViewResover 视图解析器解析后返回 View  DispatcherServlet 渲染后响应

101.spring mvc 有哪些组件?

HanderMapp 处理请求,  HanderAdapter 适配器,  ViewResolver 视图解析, Contoller 处理器,  DispatcherServlet

102.@RequestMapping 的作用是什么?

处理请求地址映射的注解,  如果用于类上,表示类中的所有响应请求方法都是以该路径作为父路径

103.@Autowired 的作用是什么?

l 类成员变量、方法和构造函数进行标注,完成自动装配的工作

十一、Spring Boot/Spring Cloud

104.什么是 spring boot

springboot spring 下的子项目,是 spring 的一站式解决方案,主要是简化 spring 的使用难度,降低对配置文件的要

求,使开发人员跟容易上手

105.为什么要用 spring boot

1.简化 spring 配置文件 2.没有代码和 xml 文件的生成 3.内置 Tomcat 4.能够独立运行 5.简化监控

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

核心配置文件有 application bootstarp 配置文件

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

 两种,  application 主要作用于 springboot 自动化配置文件bootstarp:使用 Spring Cloud Confifig 注册中心时 需要在 bootStarp 配置文件中添加链接到配置中心的配置属性来加载外部配置中心的配置信息。一些固定的不能被覆盖的属性。 一些加密/解密的场景

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

模板引擎热部署,  spring Loader 热部署,  JRebel 热部署工具

109.jpa hibernate 有什么区别?

主要区别是 jpa 是一个规范,  hibernate Red hat  jap 规范的实现

110.什么是 spring cloud

为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,  智能路由,领导选举,分布式会话,集群状态)。

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

当一个服务调用另一个服务由于网络原因或自身原因出现问题,调用者就会等待被调用者的响应 当更多的服务请求到这些资源导致更多的请求等待,发生连锁效应(雪崩效应)

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

服务发现 Netflflix Eureka,客户端负载均衡 Netflflix Ribbon,断路器 Netflflix Hystrix,服务网关 Netflflix Zuul分布式配置 Spring cloud confifig

十三、 Mybatis

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

占位符和拼接符

126.mybatis 有几种分页方式?

数组分页,  SQL 分页,拦截器分页,  RowBounds 分页

127.RowBounds 是一次性查询全部结果吗?为什么?

如果不使用 offffset limit,则返回该条件下的所有,如果使用则返回设置的

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

物理分页速度上并不一定快于逻辑分页,

逻辑分页速度上也并不一定快于物理分页。

物理分页总是优于逻辑分页:没有必要将属于数据库端的压力加诸到应用端来,

就算速度上存在优势,然而其它性能上的优点足以弥补这个缺点

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

支持,原理是使用 CGLIDB 创建目标对象的代理对象,当调用目标方法时,  进入拦截器方法

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

一级缓存是 Sqlsession 级别的缓存,也就是同一个 Sqlsession 内执行两次或多次相同结果的查询语句,  只会再第一次发送 sql 语句查询二级缓存默认是不开启的,  是 Mapper 级别的缓存,是多个 Sqlsession 之间可以共享数据

131.mybatis hibernate 的区别有哪些?

mybatis hibernate 不同, 它不完全是一个 ORM 框架,  因为 Mybatis 需要程序员自己编写 sqlmybatis 直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高,但是灵活的前提是 mybatis 无法做到数据库无关hibernate 对象/关系映射能力强,数据库无关性好

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

SImpleExecutor:每次执行一个 update select,就开启一个 statment 对象,用完就关闭

ReuseExecutor:执行 update select,以 sql 作为 key 查找 statment 对象,存在就是用,不存在就创建,用完后不关BatchExecutor:执行 update,将所有 sql 都添加到批处理中,等待统一执行

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

使用 mybatis 提供的插件接口,实现自定义插件,在插件拦截器方法内拦截器待执行的 sql然后再重写 sql,添加物                               理分页语句和物理分页参数

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

1. 编写 Interceptor 的实现类

2. 使用@Intercepts 注解完成插件签名 说明插件的拦截四大对象之一的哪一个对象的哪一个方法

3. 将写好的插件注册到全局配置文件中

十四、 RabbitMQ

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

跨系统的异步通信

多个应用之间的耦合

应用内的同步变异步

消息驱动的架构

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

生产者,消费者,  代理 

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

ConnectionFactory(连接管理器)

Channel (信道)

Exchange  (交换器)

Queue (队列)

RoutingKey  (路由键)

BindingKey  (绑定键)

138.rabbitmq vhost 的作用是什么?

虚拟的 broker,最重要的是拥有独立的权限系统,可以做到 vhost 范围的用户控制

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

必须连接到 RabbitMQ 服务器才能发布和消费消息,它们之间会创建一个 tcp 连接,  一旦 tcp 打开并通过了认证,  就会创建一条 amqp 信道,信道是创建在真实tcp 上的蓄奴链接,  amqp 命令都是通过信道发送出去

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

提供事务的功能,  通过将 channel 设置为 confifirm 模式

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

消息持久化,  ack 确认机制,  设置集群镜像模式,消息补偿机制

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

声明队列必须设置持久化 durable 设置为 rue

消息推送投递模式必须设置持久化,  deliverymode 设置为 2

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

消息已经到达持久化队列

以上都满足才能保证消息持久化成功

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

吃计划的缺点就是降低了服务器的吞吐量

因为使用的是磁盘而非内存存储

从而降低了吞吐量,可尽量使用 ssd 硬盘来缓解吞吐量问题

144.rabbitmq 有几种广播类型?

三种广播模式:  fanout direct topic

145.rabbitmq 怎么实现延迟消息队列?

通过消息过期后进入死信交换器再由交换器转发到延迟消费队列,实现延迟功能

使用 RabbitMQ - deleayed - message-exchange 插件实现

146.rabbitmq 集群有什么用?

主要作用:

高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用

高容量:集群可以承载更多的消息量

147.rabbitmq 节点的类型有哪些?

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

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

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

各节点之间使用 --link连接,不能忽略

各节点使用的 erlang cokkie 值必须相同,此值相当于密钥的功能,用于各节点的认证

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

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

不是

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

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

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

不能创建队列

不能创建交换器

不能创建绑定

不能添加用户

不能更改权限

不能添加和删除集群节点

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

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

十六、 Zookeeper

157.zookeeper 是什么?

是一个分布式的,  开发源码的分布式应用程序协调服务,一个分布式应用提供一致服务的软件,提供的功能:  配置服务,域名服务,分布式同步等

158.zookeeper 都有哪些功能?

集群管理,主节点选举,分布式锁,命名服务

159.zookeeper 有几种部署模式?

单机部署,集群部署,伪集群部署

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

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

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

在分布式环境中,  有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复

计算,提高性能,  所以就需要主节点

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

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

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

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

十七、 MySql

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

一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。   第二范式(2NF):首先是 1NF,另外包含两 部分内容,  一是表必须有一个主键;二是没有包含在主键中的列必须完全依 赖于主键,而不能只依赖于主键的一部   分。  第三范式(3NF):首先是 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:  非主键列 A 依赖 于非主键列 B,非主键列 B 依赖于主键的情况。注:关系实质上是一张二维表,其中每一行是一个元组,每一列是一个属性

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

一般情况下,我们创建的表的类型是 InnoDB,如果新增一条记录(不重启 mysql 的情况下),这条记录的 id 8;但 是如果重启(文中提到的)  MySQL 的话,这条记录的 ID 6因为 InnoDB 表只把自增主键的最大 ID 记录到内存中,所以重启数据库或者对表 OPTIMIZE 操作,都会使最大 ID 丢失。   但是,  如果我们使用表的类型是 MylSAM,那么这条记录的 ID 就是 8。因为 MylSAM 表会把自增主键的最大 ID 记录到数据文件里面,重启 MYSQL 后,自增主键的最大 ID 不会丢失。

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

一种方法:

打开 mysql 在命令提示符上输入 select version();如图

第二种方法:在 cmd 里面输入 mysql -V 来获取 mysql 版本号

167.说一下 ACID 是什么?

1.Atomicity 原子性

2.Consistency 一致性

3.Isolation 隔离性

4.Durability 耐久性

168.char varchar 区别是什么?

char 类型的长度是固定的,  varchar 的长度是可变的。

2.char 类型的效率比 varchar 的效率稍高

3.varchar 与 varchar2 的区别

varchar2 oracle 开发的一个数据类型。

169.flfloat double 的区别是什么?  

flfloat:4 个字节

double: 8 个字节

double flfloat 的区别是 double 精度高,有效数字 16 ,flfloat 精度 7 (可提供 7 位或 8 位有效数字,构成包括符号位、指数位和尾数位)

double 消耗内存是 flfloat 的两倍,double 的运算速度比 flfloat 慢得多,能用单精度时不要用双精度。

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

1.内连接只显示两表中有关联的数据

2.左连接显示左表所有数据, 右表没有对应的数据用 NULL 补齐,多了的数据删除

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

B+树索引

一、按表列属性分类:

1.单列索引

以表的单个列字段创建的索引

2.联合索引

以表的多个列字段组合创建的索引,在查询条件使用索引的从左字段顺序才会生效,遵循最左匹配原则。

单列索引和联合索引又包括:

普通索引

非主键,非唯一列的索引

主键索引

基于该表主键自动生成成的索引,如果未给表定义主键,会查找该表中是否存在非空、整形、唯一索引作为其

主键(可通过 select _rowid from 表名查看),若都不满足会隐式生成一个 rowid 作为主键(无法直接查到)

唯一索引

基于表的唯一列生成的索引,允许为空值

全文索引

将存储于数据库中的整本书或整篇文章中任意内容信息查找出来,如大量级的文字中如 like %关键字%,普通索

引的效率与全文索引相比是非常低的。

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

使用 explain 查看 SQL 是如何执行查询语句的,从而分析你的索引是否满足需求。    explain 语法:  explain select *  from table where type=1

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

Read uncommitted (读未提交):最低级别,以上问题均无法解决。

Read committed (读已提交):读已提交,可避免脏读情况发生。

Repeatable Read  (可重复读):确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,  禁止其他事务对此字段的更新,可以避免脏读和不可重复读,  仍会出现幻读问题。

Serializable   (串行化):最严格的事务隔离级别,要求所有事务被串行执行,不能并发执行,可避免脏读、不可重复读、幻读情况的发生。

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

1. InnoDB

InnoDB 的存储文件有两个,  后缀名分别是 .frm .idb,其中 .frm 表的定义文件,   而 idb 是数据文件。

InnoDB 中存在表锁和行锁, 不过行锁是在命中索引的情况下才会起作用  InnoDB 支持事务,且支持四种隔离级别(读未提交、读已提交、可重复读、串行化),默认的为可重复读;  在 Oracle 数据库中,只支持串行化级别和读已提交这两种级别,其中默认的为读已提交级别。

2. Myisam

Myisam 的存储文件有三个, 后缀名分别是 .frm .MYD MYI,其中 .frm 表的定义文件,  .MYD 是数据文件, .MYI 是索引文件。

Myisam 只支持表锁,  且不支持事务。  Myisam 由于有单独的索引文件,在读取数据方面的性能很高 。

3. 存储结构

InnoDB  Myisam 都是用 B+Tree 来存储数据的。

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

表级锁:开销小,  加锁快;  不会出现死锁;锁定粒度大,发生锁冲突的概率最高,  并发度最低。

行级锁:开销大,  加锁慢;  会出现死锁;锁定粒度最小,发生锁冲突的概率最低,  并发度也最高。

页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般

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

悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别   人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其 它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,  都是在做操作之前先上锁。  Java synchronized ReentrantLock 等独占锁就是悲观锁思想的实现。

乐观锁 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,  所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和 CAS 算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。在 Java java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。

两种锁的使用场景 从上面对两种锁的介绍,我们知道两种锁各有优缺点,  不可认为一种好于另一种, 像乐观锁适用   于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐

量。但如果是多写的情况,  一般会经常产生冲突,这就会导致上层应用会不断的进行 retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

乐观锁常见的两种实现方式 乐观锁一般会使用版本号机制或 CAS 算法实现。

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

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

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

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

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

为搜索字段创建索引。

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

垂直分割分表。

选择正确的存储引擎。

十八、 Redis

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

是一个高性能(key/value)分布式内存数据库,基于内存运行 并支持持久化的 NoSQL 数据库,是当前最热门的NoSql 数据库之一, 也被人们称为数据结构服务器。

优点:

Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。   Redis 不仅仅支

持简单的 key-value 类型的数据,同时还提供 list set zset  hash 等数据结构的储 Redis 支持数据的备份,即master-slave 模式的数据备份 应用场景:

内存存储和持久化: redis 支持异步将内存中的数据写到硬盘上,同时不影响继续服务 取最新 N 个数据的操作,如:

可以将最新的 10 条评论的 ID 放在 Redis List 集合里面 模拟类似于 HttpSession 这种需要设定过期时间的功能 发布、

订阅消息系统 定时器、计数器

180.redis 有哪些功能?

基于本机内存的缓存,服务端的 Redis,持久化(Persistence哨兵(Sentinel)和复制,(Replication)集群(Cluster

181.redis memecache 有什么区别?

区别:

1、存储方式不同

memecache 把数据全部存在内存之中,断电后会挂掉,  数据不能超过内存大小;  redis 有部份存在硬盘上,   样能保证数据的持久性,支持数据的持久化(笔者注:  有快照和 AOF 日志两种持久化方式,在实际应用的时候,要特别注意配置文件快照参数,要不就很有可能服务器频繁满载做 dump)。

2、数据支持类型不同

redis 在数据支持上要比 memecache 多的多。

3、使用底层模型不同

新版本的 redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

4、运行环境不同

redis 目前官方只支持 LINUX 上去 e5a48de588b6e799bee5baa6e79fa5e9819331333365666237 行,从而省去  了对于其它系统的支持,这样的话可以更好的把精力用于本系统 环境上的优化,  然后来微软有一个小组为其写了补丁。但是没有放到主干上。

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

redis 核心就是 如果我的数据全都在内存里,我单线程的去操作 就是效率最高的,  为什么呢,因为多线程的本质就是CPU 模拟出来多个线程的情况,这种模拟出来的情况就有一个代价,就是上下文的切换,对于一个内存的系统来说,它没有上下文的切换就是效率最高的。  redis 用 单个 CPU 绑定一块内存的数据,然后针对这块内存的数据进行多次读写的时候,都是在一个 CPU 上完成的,所以它是单线程处理这个事。在内存的情况下,这个方案就是最佳方案

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

 .缓存穿透:

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

解决办法:

1.布隆过滤对所有可能查询的参数以 hash 形式存储,在控制层先进行校验,  不符合则丢弃。还有最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中, 一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力。

2. 缓存空对象. null 变成一个值.也可以采用一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),  我们仍然把这个空结果进行缓存,但它的过期时间会很短,  最长不超过五分钟。

.缓存雪崩

解决方法

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 允许一个线程查询数据和写缓存,其他线程等待。

业界比较常用的做法,是使用 mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空  不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如 Redis SETNX 或者 Memcache ADD)去 set一个 mutex key,当操作返回成功时,  再进行 load db 的操作并回设缓存;否则,就重试整个 get 缓存的方法。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,  可以利用它来实现锁的效果。可以通过缓存 reload 机制,  预先去更新缓存,  再即将发生大并发访问前手动触发加载缓存不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀

3.做二级缓存,或者双缓存策略。

4.缓存永远不过期

这里的永远不过期包含两层意思:

从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,  其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。

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

如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。

这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。大多数系统设计者考虑用加锁或者队列的方式 

保证缓存的单线程(进程)写,  从而避免失效时大量的并发请求落到底层存储系统上。

1. 加锁排队 . 限流-- 限流算法 . 1.计数 2.滑动窗口 3. 令牌桶 Token Bucket 4.漏桶 leaky bucket [1]

2.数据预热.A1 为原始缓存,  A2 为拷贝缓存,A1 失效时,可以访问 A2A1 缓存失效时间设置为短期, A2 设置为长期。

(1) 从缓存上看,确实没有设置过期时间,这就保证了,不会出现热点 key 过期问题,也就是物理不过期。

(2) 从功能上看,如果不过期,  那不就成静态的了吗?所以我们把过期时间存在 key 对应的 value 里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑过期 .1string  (字符串)与 memcached 一样, 一个 key 对应一个 value  key 的最大存储值为 512MB value 的最大存储值也为 512MB string 类型是二进制安全的。意思是 redis string 可以包含任何数据。比如 jpg 图片或者序列化的对象。使用设置和获取的命令为 SET GET

命令为【SET key value】【GET key

2 hash  (哈希)

键值(key=>value)对集合。  是一个 string 类型的 fifield value 的映射表,   hash 特别适合用于存储对象(每个hash 可以存储 2 32 次方 - 1 键值对(40 多亿))。使用设置和获取的命令为 HMSET, HGET命令为【HMSET key key1 value1 key2 value2】【HGET key key1

3 list  (列表)

列表是简单的字符串列表,  按照插入顺序排序。你可以添加一个元素到列表的头部或者尾部(列表最多可存储 2 32 次方 - 1 元素 (4294967295, 每个列表可存储 40 多亿))。进值命令为 LPUSH 或者 RPUSH,获取值命令为

LRANGE

命令为【LPUSH key value】【LRANGE key 0 10】获取 key 列表从左边开始 0 10 value

4 set  (集合)

Set string 类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。集合中  最大的成员数为 2 32 次方 - 1(4294967295, 每个集合可存储 40 多亿个成员) SADD 添加一个 string 元素到

key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返0

命令为【SADD key value】【SMEMBERS key

5zset  (有序集合)

set一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。 redis 正是通过分数来为集合中的成员进行从小到大的排序。  zset 的成员是唯一的,但分(score)却可以重复。 ZADD 添加元素到集合,  元素在集合中存在则更新对应 score

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

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

186.jedis redisson 有哪些区别?

Jedis  Redisson 都是 Java 中对 Redis 操作的封装。Jedis 只是简单的封装了 Redis API 库,可以看作是 Redis 客户端,它的方法和 Redis 的命令很类似。  Redisson 不仅封装了 redis ,还封装了对更多数据结构的支持,以及锁等功能,相比于 Jedis 更加大。但 Jedis 相比于 Redisson 更原生一些,更灵活

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

将不一致分为三种情况:

1. 数据库有数据,缓存没有数据;

2. 数据库有数据,缓存也有数据,数据不相等;

3. 数据库没有数据,缓存有数据。

保证缓存和数据库数据的一致性:

\1. 首先尝试从缓存读取,读到数据则直接返回;如果读不到,就读数据库,并将数据会写到缓存,并返回。

\2. 需要更新数据时,先更新数据库,然后把缓存里对应的数据失效掉(删掉)。

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

RDB RDB 持久化机制,是对 redis 中的数据执行周期性的持久化。  AOFAOF 机制对每条写入命令作为日志,   以 append-

only 的模式写入一个日志文件中,在 redis 重启的时

候,可以通过回放 AOF 日志中的写入指令来重新构建整个数据集。

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

分布式锁常见的三种实现方式:

1. 数据库乐观锁;

2. 基于 Redis 的分布式锁;

3. 基于 ZooKeeper 的分布式锁。

Redis 的分布式锁;

1.加锁

最简单的方法是使用 setnx 命令。 key 是锁的唯一标识,按业务来决定命名。比如想要给一种商品的秒杀活动加

锁,可以给 key 命名为 lock_sale_商品 ID 。而 value 设置成什么呢?锁的 value 值为一个随机生成的 UUID。我们

可以姑且设置成 1。加锁的伪代码如下:

setnxkey  1 当一个线程执行 setnx 返回 1,说明 key 原本不存在,该线程成功得到了锁;当一个线程执行

setnx 返回 0,说明 key 已经存在,该线程抢锁失败。

2.解锁

有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式

是执行 del 指令,  伪代码如下:

delkey 释放锁之后,其他线程就可以继续执行 setnx 命令来获得锁。

3.锁超时

锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,  来不及显式地释放锁,这块资源将会

永远被锁住,别的线程再也别想进来。

所以,  setnx key 必须设置一个超时时间,单位为 second,以保证即使没有被显式释放,这把锁也要在一定时

间后自动释放,避免死锁。  setnx 不支持超时参数,所以需要额外的指令,  伪代码如下:

expirekey  30

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

1. Redis 分布式锁不能解决超时的问题,

2. 分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。

191.redis 如何做内存优化?

192.redis 淘汰策略有哪些?  193.redis 常见的性能问题有哪些?该如何解决?

十九、 JVM

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

1.类加载器(Class Loader 加载类文件到内存。 Class loader 只管加载,  只要符合文件结构就加载,至于能否运行,它不负责,那是有 Exectution Engine 负责的。

2.执行引擎(Execution Engine):也叫解释器,负责解释命令,交由操作系统执行。

3.本地库接口(Native Interface):本地接口的作用是融合不同的语言 java 所用

4.运行时数据区(Runtime Data Area): ** **

1。堆是 java 对象的存储区域,任何用 new 字段分配的 java 对象实例和数组,都被分配在堆上,  java 堆可用-Xms -Xmx 进行内存控制,  jdk1.7 以后,运行时常量池从方法区移到了堆上。新生代:老年代 = 1 2Eden: s1: s2=8:1:1

2方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。

误区:方法区不等于永生代

很多人原因把方法区称作永久代Permanent Generation),本质上两者并不等价,  只是 HotSpot 虚拟机垃

圾回收器团队把 GC 分代收集扩展到了方法区,  或者说是用来永久代来实现方法区而已,这样能省去专门为方法区编

写内存管理的代码,但是在 Jdk8 也移除了永久代 ,使用 Native Memory 来实现方法区。

3虚拟机栈:  虚拟机栈中执行每个方法的时候,都会创建一个栈桢用于存储局部变量表,操作数栈,动态链接,

方法出口等信息。

4本地方法栈:  与虚拟机发挥的作用相似,  相比于虚拟机栈为 Java 方法服务,  本地方法栈为虚拟机使用的 Native 方法服务,执行每个本地方法的时候,都会创建一个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信

息。

5程序计数器。  指示 Java 虚拟机下一条需要执行的字节码指令。

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

题外话,被网上各个版本的观点搞蒙了,整理一下。

Java 内存分 JVM 内存和本地内存(Native Memory)两块,这个不能搞混。  JVM 内存由 JVM 管理,  是虚拟的内存地址

本地内存直接使用进程内存,当进程无法申请内存时,  同样也会出现内存溢出和栈深度耗尽的异常。

JVM 内存, 从逻辑上划分为 JVM 栈区、 JVM 堆区、程序计数器。方法区、本地方法栈和本地堆都位于本地内存,但有些虚拟机(HotSpot)的本地方法栈和虚拟机栈是合并在一起的。

JVM 运行时数据区包括 JVM 栈区 、本地方法栈区、堆区、方法区、程序计数器。(网上通用的说法)

1. 程序计数器

程序计数器空间很小,各线程私有,只用于存放线程当前运行的行号,不存在内存溢出的问题。

2. 方法区

方法区用于存放类和方法的元数据、静态变量、 常量、即时编译后的代码。简单来讲,就是不常变化的东西。但要注意的java 对象本身永远都不会置入静态存储空间。  解释一下:元数据是类的代码信息,   保存在方法区;数值类型数据直接保存在代码内部;引用类型变量,引用放在代码内部,但是实例放在堆上。所以,自动装箱的整型、字符串数据在方法区只会保存一个实例常量池上的引用

面试中常问道的永久代(Permanent Generation space)是 HotSpot 类型的 JVM 虚拟机对方法区的物理实现,其他虚拟机有其他的实现方式。jsp 运行过程中容易出现内存溢出,就是因为即时编译的代码过多,导致永久代满了。

JDK1.7 后取消了永久代,方法区移动到了本地内存(Native Memory)中,称为 MetaSpace,与 JVM 内存无关了(受进程内存分配影响,还是有可能会内存溢出的)。

常量池位于方法区。JDK1.6 之前,常量池的具体实例也在方法区,很难找到标准答案,但从 String.intern()方法的变   动来看,这句话应该是对的。JDK1.7 之后,常量池在本地内存中只保存引用符号,  常量池的所有对象保存在堆上。在讲常量池的时候最好还是用符号常量池实例常量池区别一下。

插一段整型常量池的实现。整型的包装类 Integer,自动装箱过程调用的是 valueOf 方法(感谢这位兄台深入剖析 Java中的装箱和拆箱 - 海 子 - 博客园的文章),整型常量池使用 IntegerCache +整型数组实现的。

public static Integer valueOf(int i) {

if (i >= IntegerCache.low && i <= IntegerCache.high)

return IntegerCache.cache[i + (-IntegerCache.low)];

return new Integer(i);

}

//IntegerCache 的关键代码(在静态代码块中)

String integerCacheHighPropValue =

sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");

...

cache = new Integer[(high - low) + 1];

int j = low;

for(int k = 0; k < cache.length; k++)

cache[k] = new Integer(j++);

从源码中可以看到,常量池上的整型数据是在 IntegerCache 类加载时就生成完成的,  具体生成的范围是由 JVM 境决 定的。也就是,常量并不是动态添加到常量池的,呵~果然骚操作~Double 类就没有这样的操作,这就解释了,  为什么整型计算后的结果在常量池上,而浮点型计算结果为什么不在常量池,  人家“ 出生前就已经送到了常量池啊。

String 常量池的实现是看不到的,且 valueOf 方法返回的是 new 出来的新对象。  intern 方法是 native 方法,无法得知其实现。

3.栈区

栈区,是个临时的数据存放区。与 C 语言不同,栈区只在方法调用时才会被用到。栈区首先存放的是栈帧,每调用一 个方法,便会产生一个栈帧,并入栈。栈帧存放存放局部变量表、操作数栈、动态链接、返回地址。栈帧内部也有一

个栈的结构,根据元数据信息分配栈的深度。根据指令执行顺序,将操作数压栈保存、出栈运算,移动到局部变量表。方法执行完,  出栈,根据返回地址继续执行调用它的方法的栈帧。

4.堆区

堆区就是用来保存引用类型的实例。堆上的实例会存储它所属类的类信息,用于找到该类及该类的方法,在获取一个 对象的类名信息时,即时该对象被向上转型了,还是能够取得该实例化该对象的类的信息。通过类信息,准确的调用 类中的成员方法。 

196.说一下堆栈的区别?

堆栈与堆区别为:  空间不同、地址方向来不同、释放不同。

一、空间不同

1、堆栈:堆栈是自动分配变量,以及函数调用的时候所使用的一些空间。

2、堆:堆是是由 malloc 之类函数分配的空间所在地。

二、地址方向不同

1、堆栈:堆栈的地址方向是由高向低减少性扩展,有总长度自大小限制。

2、堆:堆的地址方向是由低向高增长性扩展  没有总长度大小限制。

三、释放不同

1、堆栈:堆栈由编译器自动释放,存放函数的参数值,  局部变量的 zhidao 值等。

2、堆:堆由程序员人工进行释放,  若程序员不释放,  程序结束时可能由 OS 回收 。

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

1.队列(Queue):是限定只能在表的一端进行插入和在另一端进行删除操作的线性表

2.(Stack):是限定只能在表的一端进行插入和删除操作的线性表

3.队列先进先出(FIFO),栈先进后出(FILO)

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

类加载器有哪些:  1、启动类加载器(Bootstrap ClassLoader):这个类加载器负责将存放在<JAVA_HOME>\lib 目录中的,或被-Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(例如 rt.jar)类库加载到虚拟机内存中;

2、扩展类加载器(Extension ClassLoader):它负责加载<JAVA_HOME>\lib\ext 目录中的,或者被 java.ext.dirs 统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器;

3、应用程序类加载器(Application ClassLoader):由于这个类加载器是 ClassLoader 中的

getSystemClassLoader()方法的返回值,所以一般也称为系统类加载器。它负责加载用户类路径(claspath上所指定的类库,开发者可以直接使用这个类加载器。

4、线程上下文类加载器,面试的时候知道的也可以说下,它是为了解决基础类掉调用回用户代码而引入的设计,也就是兼容双亲委派模型的缺陷。

类加载器双亲委派模型如下:

破坏双亲委派模型的三大情况:

1、双亲委派模型的第一次被破坏是发生在双亲委派模型出现之前——JDK1.2 发布之前;  

2、双亲委派模型的第二次坏”是由于模型自身的缺陷导致的;

一个典型的例子便是 JNDI 服务, JNDI 现在已经是 Java 的标准服务,它的代码由启动类加载器去加载(在 JDK1.3  放进去的 rt.jar,JNDI 的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的Classpath 下的 JNDI 接口提供者(SPI)的代码,但启动类记载不可能认识这些代码?

为了解决这个问题,Java 设计团队只好引入了一个不太优雅的设计,线程上下文类加载器。这个类加载器可以通     java.lang.Thread 类的 setContextClassLoader 方法设置,如果创建线程时还未设置,它将从父线程中继承一个,                                                                                                   如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序                      类加载器。

有了这个线程上下文来加载器,就可以做一些舞弊的事情了, JNDI 服务使用这个县城上下文类   加载器去加载所

需要的 SPI 代码,也就是父类加载器请求子类加载器去完成类加载器的动作,这种行为实际上打通了双亲委派模型的

层次结构来逆向使用类加载器,实际上已经违背了双亲委派模型的一般性原则;

java 中所有涉及 SPI 的加载懂基本上都采用这种方式,例JNDIJDBCJCEJAXB JBI 等。

3、双亲委派模型的第三次被破坏是由于用户对程序动态性的追求而导致的,即热部署(OSGI 实现模块化热部署)

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

java 程序在执行过程中,类,对象以及它们成员加载、初始化的顺序如下: 1、首先加载要创建对象的类及其直接复与间接父类。   2、在类被加载的同时会将静态成员进行加载,主要包括静态成员变量的初始化,静态语制句块的执

行,在加载时按代码的先后顺序进行。   3、需要的类加载完成后,开始创建对象,首先会加载非静态的成员,主要包   括非静态成员变量的初始化,非静态语句块的执行,在加载时按代码的先后顺序进行。  4、最后执 zd 行构造器,构造器执行完毕,对象生成。

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

这就是所谓的对象存活性判断,常用的方法有两种:  1.引用计数法; 2.对象可达性分析。由于引用计数法存在互相引用导致无法进行 GC 的问题,  所以目前 JVM 虚拟机多使用对象可达性分析算法。

1、引用计数法

引用计数法的逻辑非常简单,但是存在问题,  java 并不采用这种方式进行对象存活判断。

引用计数法的逻辑是:在堆中存储对象时,在对象头处维护一个 counter 计数器,如果一个对象增加了一个引用与之    相连,则将 counter++。如果一个引用关系失效则 counter 。如果一个对象的 counter 变为 0,则说明该对象已经被废

弃,不处于存活状态。

这种方法来标记对象的状态会存在很多问题:

jdk 1.2 开始增加了多种引用方式:软引用、弱引用、虚引用,且在不同引用情况下程序应进行不同的操作。如果我们只采用

一个引用计数法来计数无法准确的区分这么多种引用的情况。

2、可达性分析算法在主流的商用程序语言中(Java C#),都是使用可达性分析算法判断对象是否存活的。这个算

法的基本思路就是通过

一系列名为 GC Roots 的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference   Chain),当一个对象到 GC Roots 没有任何引用链相连时,  则证明此对象是不可用的,  下图对object5, object6,

object7 虽然有互相判断,但它们到 GC Roots 是不可达的,所以它们将会判定为是可回收对象。

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

1Java 中有哪几种引用?它们的含义和区别是什么?

JDK1.2 开始, Java 中的引用类型分为四种,  分别是:   

①强引用(StrongReference ②软引用(SoftRefernce

③弱引用(WeakReference ④虚引用(PhantomReference

强引用-StrongReference

这种引用是平时开发中最常用的,例如 String strong = new String("Strong Reference") ,当一个实例对象  具有强引用时,垃圾回收器不会回收该对象,  当内存不足时,宁愿抛出 OutOfMemeryError 异常也不会通过回收强引用的对象,因为 JVM 认为强引用的对象是用户正在使用的对象,它无法分辨出到底该回收哪个,强行回收有可能导致系统严重错误。

软引用-SoftReference

如果一个对象只有软引用,  那么只有当内存不足时, JVM 才会去回收该对象,其他情况不会回收。软引用可以结合   ReferenceQueue 来使用,当由于系统内存不足,导致软引用的对象被回收了, JVM 会把这个软引用加入到与之相关联的 ReferenceQueue 中。

ReferenceQueue referenceQueue = new ReferenceQueue();

SoftReference softReference = new SoftReference<>(new Book(), referenceQueue);

Book book = softReference.get();

Reference reference = referenceQueue.poll();

当系统内存不足时,触发 gc,这个 Book 就会被回收,   reference 将不为 null

弱引用-WeakReference

只有弱引用的对象,当 JVM 触发 gc 时,就会回收该对象。与软引用不同的是,不管是否内存不足,弱引用都会被回  收。弱引用可以结合 ReferenceQueue 来使用,当由于系统触发 gc,导致软引用的对象被回收了,  JVM 会把这个弱引 用加入到与之相关联的 ReferenceQueue 中,不过由于垃圾收集器线程的优先级很低,所以弱引用不一定会被很快回

收。下面通过一个主动触发 gc 的例子来验证此结论。

ReferenceQueue referenceQueue = new ReferenceQueue();

WeakReference weakReference = new WeakReference(new Book(), referenceQueue);

Book book = softReference.get();

System.gc();

//Runtime.getRuntime().gc();

Reference reference = referenceQueue.poll();weak_reference.png

当然这不是每次都能复现,  因为我们调用 System.gc()只是告诉 JVM 该回收垃圾了,  但是它什么时候做还是不一定的,但就我测试来看,  只要多写几次 System.gc(),复现的概率还是很高的。

虚引用-PhantomReference

如果一个对象只有虚引用在引用它,垃圾回收器是可以在任意时候对其进行回收的,虚引用主要用来跟踪对象被垃圾 回收器回收的活动,当被回收时, JVM 会把这个弱引用加入到与之相关联的 ReferenceQueue 中。与软引用和弱引用

不同的是,虚引用必须有一个与之关联的 ReferenceQueue,通过 phantomReference.get()得到的值为 null,试想一下,如果没有 ReferenceQueue 与之关联还有什么存在的价值呢?

202.说一下 jvm 有哪些垃圾回收算法?

垃圾回收算法有很多种,目前商业虚拟机常用的是分代回收算法,但最初并不是用这个算法的 我们来看一下垃圾收集算法的背景知识

标记-清除算法 最基础的垃圾回收算法,顾名思义,整个回收过程分两步:   1.逐个标记 2.统一回收 该算法可以算是后来所有垃圾回收算法的基石(后续所有算法都有标记和清除这两步,只不过策略上有了一些优化)   这里值得一说的是这个标记 虚拟机是如何判断一个对象是还是  因此又引出两种标记算法:   1.引用计数算法 引用计数算法    非常简单且高效,  当一个对象被引用一次则+1 不再被引用则- 1,当计数为 0 就是不可能在被使用的对象了,但是这种 算法存在一个致命的缺陷:  两个对象相互引用对方呢? 所以,这种算法肯定不能用, pass 2.可达性分析算法 目前    的标记算法主流实现都是用的可达性分析算法。就是以一个叫 GC Roots 的对象为起点,通过引用链向下搜索,  如果一个对象通过引用链无法与 GC Roots 对象链接,就视为可回收对象,上面说的那种相互引用的情况自然也解决了。

扩展: 即使是可达性分析中不可达的对象也并不是非死不可,只是暂处缓刑 ,真正宣告一个对象死亡至少还要经历

两次标记过程:当被判定不可达之后那么他被第一次标记并进行筛选,若对象没有覆盖 fifinalize()方法或者 fifinalize() 法已经被虚拟机调用过就放生 ,如果被判定需要执行 fifinalize()方法就会被放到一个叫 F-Queue 的队列中进行第二次   标记对象被再次被引用就会放生,否则就会被回收。   fifinalize()方法 fifinalize()Object 中的方法,当垃圾回收器将要 回收对象所占内存之前被调用,即当一个对象被虚拟机宣告死亡时会先调用它 fifinalize()方法, 让此对象处理它生前的最后事情(这个对象可以趁这个时机挣脱死亡的命运)说到这里敏锐的小伙伴可能以及察觉到了,上面都在说引用所以引用的定义就显得尤为关键了 JDK1.2 Java 对引用的概念进行了扩充,将引用分为:  强引用、软引用、弱引用、虚引用四种强引用:用处很大,无论如何都不会被 GC 回收

软引用:有一定用处但不大,内存实在不够才会在内存溢出之前回收掉

弱引用:比软引用强度更弱一些,GC 会让它多活一轮,下一轮就回收

虚引用:必回收,唯一作用就是被 GC 回收时会收到一个系统通知

复制算法 前面说的标记-清除算法其实两个过程效率都很低,并且回收之后内存被抠出很多洞 内存碎片化严重,  此时 如果过来了一个较大的对象,找不到一整块连续的内存空间就不得不提前触发另外一次 GC 回收。  而复制算法则选择 将内存一分为二每次只使用其中一半,满了之后将存活的对象整齐复制到另一块干净的内存上,将剩下的碎片一次性擦除,简单高效。但是也存在一个很大的缺陷,那就是可用内存变为原来的一半了。

分代收集算法 事实上后来 IBM 公司经过研究发现,  98%的对象都是朝生夕死 ,所以并不需要 1:1 的划分内存,   即我们现在常用的分代收集算法:   根据对象的存活周期将内存划分为两块,分别为新生代和老年代,然后对各代采用不同的回收算法,在新生代中大部分是‘朝生夕死对象,继续将新生代 8:2 划分为 Eden 区和 survival 区,其中 survival 1:1 分成 s0 s1 两块,采用之前说的复制算法,减少内存碎片的产生。   新生代满了会进行一次 minor GC  minor GC存活的对象转移到 survival 区, survival 区满了就会将 survival 区进行回收,存活的 survival 区对象复制到另外一块survival 区中,并且 survival 区对象每存活一轮年龄+1 当到达一定年龄就会前往老年代。

扩展 01JVM 何时会进行全局 GC

01.手动调用 System.GC 但也不是立即调用

02.老年代空间不足

03.永生代空间不足

04.计算得知新生代前往老年代平均值大于老年代剩余空间

扩展 02:在压力测试时,发现 FullGC 频率很高,如何解决

01.观察 GC 日志,判断是否有内存泄漏,或者存在内部不合理点

02.调整 JVM 参数,如新生代、老年代大小 S0+S1 大小比例, 选用不同的立即回收器

03.Dump 内存, 做进一步的对象分析

04.压测脚本的编写,性能问题解决前可以发现问题,并对解决方案进行验证

203.说一下 jvm 有哪些垃圾回收器?

新生代收集器:

Serial ParNew Parallel Scavenge

老年代收集器:

Serial Old CMS Parallel Old

堆内存垃圾收集器:

G1

204.详细介绍一下 CMS 垃圾回收器?

垃圾回收器从线程运行情况分类有三种串行回收, Serial 回收器,单线程回收,全程 stw; 并行回收,名称以 Parallel 开头的回收器,多线程回收,全程stw; 并发回收,  cms G1,多线程分阶段回收,只有某阶段会 stw CMS 垃圾回收特点 cms 只会回收老年代和永久带(1.8 开始为元数据区,需要设置 CMSClassUnloadingEnabled),不会收集年轻带;  cms 是一种预处理垃圾回收器,它不能等到 old 内存用尽时回收,需要在内存用尽前,完成回收操作,否则会导致并发回收失败;所以 cms 垃圾 回收器开始执行回收操作,  有一个触发阈值,  默认是老年代或永久带达到 92% 3. CMS cms 是最常用的垃圾垃圾回收器,下面分析下 CMS 垃圾回收器工作原理;

CMS 处理过程有七个步骤:

1. 初始标记(CMS-initial-mark) ,会导致 swt

2. 并发标记(CMS-concurrent-mark),与用户线程同时运行;

3. 预清理(CMS-concurrent-preclean),与用户线程同时运行;

4. 可被终止的预清理(CMS-concurrent-abortable-preclean) 与用户线程同时运行;

5. 重新标记(CMS-remark) ,会导致 swt

6. 并发清除(CMS-concurrent-sweep),与用户线程同时运行;

7. 并发重置状态等待下次 CMS 的触发(CMS-concurrent-reset),与用户线程同时运行;

205.新生代垃圾回收器和老生代垃圾回收器都有哪些?有什么区别?

新生代回收器:  Serial ParNew Parallel Scavenge

老年代回收器:  Serial Old Parallel OldCMS

整堆回收器:  G1

新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

206.简述分代垃圾回收器是怎么工作的?

分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。 新生代使用的是复制算法,新生代里有 3 个分区  EdenTo Survivor From Survivor,它们的默认占比是 8:1:1

它的执行流程如下:

 Eden + From Survivor 存活的对象放入 To Survivor 区;

清空 Eden  From Survivor 分区;   From Survivor To Survivor 分区交换,   From Survivor To Survivor To Survivor  From Survivor

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

老生代当空间占用到达某个值之后就会触发全局垃圾收回, 一般使用标记整理的执行算法。

以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

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

*Jconsole  : jdk 自带,功能简单,但是可以在系统有一定负荷的情况下使用。对垃圾回收算法有很详细的跟踪。**JProfifiler

商业软件,需要付费。功能强大。 *

*VisualVMJDK 自带,功能强大,与 JProfifiler 类似。推荐。 *

208.常用的 jvm 调优的参数都有哪些?

本文章参数根据后期用的参数会持续更新 ---

1 -Xms20M

表示设置 JVM 启动内存的最小值为 20M,必须以 M 单位

2 -Xmx20M

表示设置 JVM 启动内存的最大值为 20M,必须以 M 为单位。将-Xmx -Xms 设置为一样可以避免 JVM 内存自动扩展。大的项目-Xmx -Xms 一般都要设置到 10G20G 甚至还要高

3 -verbose:gc

表示输出虚拟机中 GC 的详细情况

4 -Xss128k

表示可以设置虚拟机栈的大小为 128k

5 -Xoss128k

表示设置本地方法栈的大小为 128k。不过 HotSpot 并不区分虚拟机栈和本地方法栈,因此对于 HotSpot 来说这个参数是无效的

6 -XX:PermSize=10M

表示 JVM 初始分配的永久代(方法区)的容量,必须以 M 为单位

7 -XX:MaxPermSize=10M

表示 JVM 允许分配的永久代(方法区)的最大容量,必须以 M 为单位,大部分情况下这个参数默认为 64M

8 -Xnoclassgc

表示关闭 JVM 对类的垃圾回收

9 -XX:+TraceClassLoading

表示查看类的加载信息

10 -XX:+TraceClassUnLoading

表示查看类的卸载信息

11 -XX:NewRatio=4

表示设置 年轻代(包括 Eden 和两个 Survivor 区) /老年代 的大小比值为 14,这意味着年轻代占整个堆的 1/5

12 -XX:SurvivorRatio=8

表示设置 2 Survivor 区: 1 Eden 区的大小比值为 2:8,这意味着 Survivor 区占整个年轻代

 1/5,这个参数默认为 8

13 -Xmn20M

表示设置年轻代的大小为 20M

14 -XX:+HeapDumpOnOutOfMemoryError

表示可以让虚拟机在出现内存溢出异常时 Dump 当前的堆内存转储快照

15 -XX:+UseG1GC

表示让 JVM 使用 G1 垃圾收集器

16 -XX:+PrintGCDetails

表示在控制台上打印出 GC 具体细节

17 -XX:+PrintGC

表示在控制台上打印出 GC 信息

18 -XX:PretenureSizeThreshold=3145728

表示对象大于 31457283M)时直接进入老年代分配,  这里只能以字节作为单位

19 -XX:MaxTenuringThreshold=1

表示对象年龄大于 1,自动进入老年代,如果设置为 0 的话,则年轻代对象不经过 Survivor 区,直接进入年老代。对于 年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行多次复制,这样可以增加对象在年轻代的存活时间,增加在年轻代被回收的概率。

20 -XX:CompileThreshold=1000

表示一个方法被调用 1000 次之后,会被认为是热点代码,并触发即时编译

21 -XX:+PrintHeapAtGC

表示可以看到每次 GC 前后堆内存布局

22 -XX:+PrintTLAB

表示可以看到 TLAB 的使用情况

23 -XX:+UseSpining

开启自旋锁

24 -XX:PreBlockSpin

更改自旋锁的自旋次数,使用这个参数必须先开启自旋锁

25 -XX:+UseSerialGC

表示使用 jvm 的串行垃圾回收机制,该机制适用于丹 cpu 的环境下

26 -XX:+UseParallelGC

表示使用 jvm 的并行垃圾回收机制,该机制适合用于多 cpu 机制,同时对响应时间无强硬要求的环境下,使用 -XX:ParallelGCThreads=设置并行垃圾回收的线程数,此值可以设置与机器处理器数量相等。

27 -XX:+UseParallelOldGC 

表示年老代使用并行的垃圾回收机制

28 -XX:+UseConcMarkSweepGC

表示使用并发模式的垃圾回收机制,该模式适用于对响应时间要求高,具有多 cpu 的环境下

29 -XX:MaxGCPauseMillis=100

设置每次年轻代垃圾回收的最长时间,如果无法满足此时间, JVM 会自动调整年轻代大小,以满足此值。

30 -XX:+UseAdaptiveSizePolicy

设置此选项后,并行收集器会自动选择年轻代区大小和相应的 Survivor 区比例,以达到目标系统规定的最低响应时间或者收集频率等,  此值建议使用并行收集器时,一直打开

算法题

1)请简单解释算法是什么?

算法是一个定义良好的计算过程,它将一些值作为输入并产生相应的输出值。简单来说,它是将输入转换为输出的一系列计算步骤。

2)解释什么是快速排序算法?

快速排序算法能够快速排序列表或查询。它基于分割交换排序的原则,这种类型的算法占用空间较小,它将待排序列

表分为三个主要部分:

小于 Pivot 的元素

枢轴元素 Pivot (选定的比较值)

大于 Pivot 的元素

3)解释算法的时间复杂度?

算法的时间复杂度表示程序运行完成所需的总时间,它通常用大 O 表示法来表示。

4)请问用于时间复杂度的符号类型是什么?

用于时间复杂度的符号类型包括:

Big Oh:它表示小于或等于目标多项式

Big Omega:它表示大于或等于目标多项式

Big Theta:它表示与目标多项式相等

Little Oh:它表示小于目标多项式

Little Omega:它表示大于目标多项式

5)解释二分法检索如何工作?

在二分法检索中,  我们先确定数组的中间位置,然后将要查找的值与数组中间位置的值进行比较,若小于数组中间值,则要查找的值应位于该中间值之前,依此类推,不断缩小查找范围,  直至得到最终结果。

6)解释是否可以使用二分法检索链表?

由于随机访问在链表中是不可接受的,所以不可能到达 O1)时间的中间元素。因此, 对于链表来说,二分法检索是不可以的(对顺序链表或排序后的链表是可以用的)  。

7)解释什么是堆排序? 

堆排序可以看成是选择排序的改进,它可以定义为基于比较的排序算法。它将其输入划分为未排序和排序的区域,通过不断消除最小元素并将其移动到排序区域来收缩未排序区域。

8)说明什么是 Skip list

Skip list 数据结构化的方法,  它允许算法在符号表或字典中搜索、删除和插入元素。在 Skip list 中,每个元素由一个节点表示。搜索函数返回与 key 相关的值的内容。插入操作将指定的键与新值相关联,  删除操作可删除指定的键。

9)解释插入排序算法的空间复杂度是多少?

插入排序是一种就地排序算法,这意味着它不需要额外的或仅需要少量的存储空间。对于插入排序,  它只需要将单个列表元素存储在初始数据的外侧,从而使空间复杂度为 O1)。

10)解释什么是哈希算法 ,它们用于什么?

哈希算法是一个哈希函数, 它使用任意长度的字符串,  并将其减少为唯一的固定长度字符串。它用于密码有效性、消息和数据完整性以及许多其他加密系统。

11)解释如何查找链表是否有循环?

要知道链表是否有循环,我们将采用两个指针的方法。如果保留两个指针,并且在处理两个节点之后增加一个指针,并且在处理每个节点之后,  遇到指针指向同一个节点的情况,这只有在链表有循环时才会发生。

12)解释加密算法的工作原理?

加密是将明文转换为称为密文的密码格式的过程。要转换文本,算法使用一系列被称为的位来进行计算。密钥越大,创建密文的潜在模式数越多。大多数加密算法使用长度约为 64 128 位的固定输入块,而有些则使用流方法。

13)列出一些常用的加密算法?

一些常用的加密算法是:

3-way

Blowfifish

CAST

CMEA

GOST

DES Triple DES

IDEA

LOKI 等等

14)解释一个算法的最佳情况和最坏情况之间有什么区别?

·最佳情况:算法的最佳情况解释为算法执行最佳的数据排列。例如,我们进行二分法检索,如果目标值位于正在搜索的数据中心,则这就是最佳情况,最佳情况时间复杂度为 0

·最差情况:给定算法的最差输入参考。例如快速排序,  如果选择关键值的子列表的最大或最小元素,  则会导致最差情况出现,这将导致时间复杂度快速退化到 On2)。

15)解释什么是基数排序算法?

基数排序又称桶子法 ,是通过比较数字将其分配到不同的来排序元素的。它是线性排序算法之一。

16)解释什么是递归算法?

递归算法是一个解决复杂问题的方法,将问题分解成较小的子问题,直到分解的足够小,可以轻松解决问题为止。通常,它涉及一个调用自身的函数。  

17)提到递归算法的三个定律是什么?

所有递归算法必须遵循三个规律

1. 递归算法必须有一个基点

2. 递归算法必须有一个趋向基点的状态变化过程

3. 递归算法必须自我调用

18)解释什么是冒泡排序算法?

冒泡排序算法也称为下沉排序。在这种类型的排序中,  要排序的列表的相邻元素之间互相比较。如果它们按顺序排列错误,将交换值并以正确的顺序排列,直到最终结果“ 出水面。

Linux 常用命令

https://blog.csdn.net/qq_23329167/article/details/83856430

一、基本命令

1.1 关机和重启

关机 shutdown -h now 立刻关机 shutdown -h 5 5 分钟后关机 poweroffff 立刻关机 重启 shutdown -r now 立刻重启

shutdown -r 5 5 分钟后重启 reboot 立刻重启

1.2 帮助命令

--help 命令 shutdown --help ifconfifig --help:查看网卡信息

man 命令(命令说明书)   man shutdown 注意:  man shutdown 打开命令说明书之后,   使用按键 q 退出

二、目录操作命令

2.1 目录切换 cd

命令:  cd 目录

cd / 切换到根目录 cd /usr 切换到根目录下的 usr 目录 cd ../ 切换到上一级目录 或者 cd .. cd ~ 切换到 home 目录 cd 切换到上次访问的目录

2.2 目录查看 ls [-al]

命令:  ls [-al]

ls 查看当前目录下的所有目录和文件 ls -a 查看当前目录下的所有目录和文件(包括隐藏的文件)   ls -l  ll 列表查看

当前目录下的所有目录和文件(列表查看,显示更多信息)  ls /dir 查看指定目录下的所有目录和文件 如: ls /usr

2.3 目录操作【增,删,改,  查】  

2.3.1 创建目录【增】   

mkdir 命令:  mkdir  目录

mkdir aaa 在当前目录下创建一个名为 aaa 的目录 mkdir /usr/aaa 在指定目录下创建一个名为 aaa 的目录

2.3.2 删除目录或文件【删】 rm

命令:  rm [-rf] 目录

删除文件:   rm 文件 删除当前目录下的文件 rm -f 文件 删除当前目录的的文件(不询问)删除目录:    rm -r aaa 递归删除当前

目录下的 aaa 目录 rm -rfaaa 递归删除当前目录下的 aaa 目录(不询问)

全部删除:   rm -rf * 将当前目录下的所有目录和文件全部删除 rm -rf /*   【自杀命令!慎用!慎用!慎用!】将根目

录下的所有文件全部删除

注意:  rm 不仅可以删除目录,也可以删除其他文件或压缩包,为了方便大家的记忆,无论删除任何目录或文件,都直接使用 rm -rf 目录/文件/压缩包

2.3.3 目录修改【改】mv cp

一、重命名目录

命令:  mv 当前目录 新目录

例如:  mv aaa bbb 将目录 aaa 改为 bbb

注意:  mv 的语法不仅可以对目录进行重命名而且也可以对各种文件,压缩包等进行 重命名的操作

二、剪切目录

命令:  mv 目录名称 目录的新位置

示例:将/usr/tmp 目录下的 aaa 目录剪切到 /usr 目录下面 mv /usr/tmp/aaa /usr

注意:  mv 语法不仅可以对目录进行剪切操作,  对文件和压缩包等都可执行剪切操作

三、拷贝目录

命令: cp -r 目录名称 目录拷贝的目标位置 -r 代表递归

示例:将/usr/tmp 目录下的 aaa 目录复制到 /usr 目录下面 cp /usr/tmp/aaa /usr

注意: cp 命令不仅可以拷贝目录还可以拷贝文件,压缩包等,拷贝文件和压缩包时不 用写-r 递归

2.3.4 搜索目录【查】fifind

命令: fifind 目录 参数 文件名称 示例:  fifind /usr/tmp -name 'a*' 查找/usr/tmp 目录下的所有以 a 开头的目录或文件

三、文件操作命令

3.1 文件操作【增,删,改,  查】

3.1.1 新建文件【增】touch

命令: touch 文件名 示例:  在当前目录创建一个名为 aa.txt 的文件 touch aa.txt

3.1.2 删除文件 【删】 rm

命令:  rm -rf 文件名

3.1.3 修改文件【改】   vi vim

vi 编辑器的 3 种模式】  基本上 vi 可以分为三种状态,  分别是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能区分如下:   

1) 命令行模式 command mode) 控制屏幕光标   的移动,字符、字或行的删除,查找,移动复制某区段及进入 Insert mode 下,或者到 last line mode。 命令行模式下的常用命令:   【1】控制光标移动:    ,    ,  j 2】删除当前行:   dd 3】查找:  /字符 【4】进入编辑模式:  i o 【5】进入底行模式:  :

2) 编辑模式(Insert mode) 只有在 Insert mode 下,才可以做文字输入,按「ESC」键可回到命令行模式。 编辑模式下常用命令:   【1 ESC 退出编辑模式到命令行模式;

3) 底行模式(last line mode) 将文件保存或退出 vi,也可以设置编辑环境,如寻找字符串、列出行号 ……等。  底行模式下常用命令:   【1】退出编辑:   :q 2】强制退出:    :q! 3】保存并退出:    :wq打开文件命令:  vi 文件名 示例:打开当前目录下的 aa.txt 文件 vi aa.txt 或者 vim aa.txt

注意:使用 vi 编辑器打开文件后,并不能编辑,因为此时处于命令模式,  点击键盘 i/a/o 进入编辑模式。

编辑文件

使用 vi 编辑器打开文件后点击按键:  i a 或者 o 即可进入编辑模式。

i:在光标所在字符前开始插入 a:在光标所在字符后开始插入 o:在光标所在行的下面另起一新行插入

保存或者取消编辑

保存文件:

第一步: ESC 进入命令行模式 第二步:  : 进入底行模式 第三步:  wq 保存并退出编辑

取消编辑:

第一步: ESC 进入命令行模式 第二步:  : 进入底行模式 第三步:   q! 撤销本次修改并退出编辑

3.1.4 文件的查看【查】

文件的查看命令:  cat/more/less/tail

cat:看最后一屏

示例:使用 cat 查看/etc/sudo.conf 文件,只能显示最后一屏内容 cat sudo.conf

more:百分比显示

示例:使用 more 查看/etc/sudo.conf 文件,可以显示百分比,回车可以向下一行, 空格可以向下一页, q 可以退出查 more sudo.conf

less:翻页查看

示例:使用 less 查看/etc/sudo.conf 文件,可以使用键盘上的 PgUp PgDn 向上 和向下翻页,   q 结束查看 less sudo.conf

tail:指定行数或者动态查看

示例:使用 tail - 10 查看/etc/sudo.conf 文件的 10 行, Ctrl+C 结束

tail - 10 sudo.conf

3.2 权限修改 rwx r 代表可读,  w 代表可写 x 代表该文件是一个可执行文件,如果 rwx 任意位置变为-则代表不可读或不可写或不可执行文件。

示例:给 aaa.txt 文件权限改为可执行文件权限 aaa.txt 文件的权限是-rw-------

第一位: -就代表是文件, d 代表是文件夹 第一段(3 ):代表拥有者的权限 第二段(3 ):表拥有者所在的组,组员的权限 第三段(最后 3 位):代表的是其他用户的权限

421 421 421

rw- --- ---

命令: chmod +x aaa.txt 或者采用 8421 法 命令:  chmod 100 aaa.txt

四、压缩文件操作

4.1 打包和压缩 Windows 的压缩文件的扩展名 .zip/.rar linux 中的打包文件:  aa.tar

linux 中的压缩文件: bb.gz

linux 中打包并压缩的文件:  .tar.gz

Linux 中的打包文件一般是以.tar 结尾的,压缩的命令一般是以.gz 结尾的。   而一般情况下打包和压缩是一起进行的,

打包并压缩后的文件的后缀名一般.tar.gz

命令: tar -zcvf 打包压缩后的文件名 要打包的文件 其中:  z:调用 gzip 压缩命令进行压缩 c:打包文件 v:显示运行

过程 f:指定文件名

示例:打包并压缩/usr/tmp 下的所有文件 压缩后的压缩包指定名称为 xxx.tartar -zcvfab.tar aa.txt bb.txt 或: tar -

zcvf ab.tar *

4.2 解压

命令: tar [-zxvf] 压缩文件

其中: x:代表解压 示例:将/usr/tmp 下的 ab.tar 解压到当前目录下

示例:将/usr/tmp 下的 ab.tar 解压到根目录/usr tar -xvfab.tar -C /usr------C 代表指定解压的位置

五、查找命令

5.1 grep

grep 命令是一种强大的文本搜索工具

使用实例:

ps -ef | grep sshd 查找指定 ssh 服务进程 ps -ef | grep sshd | grep -v grep 查找指定服务进程,排除 gerp  ps -ef |

grep sshd -c 查找指定进程个数 5.2 fifind fifind 命令在目录结构中搜索文件,并对搜索结果执行指定的操作。

fifind 默认搜索当前目录及其子目录,并且不过滤任何结果(也就是返回所有文件  将它们全都显示在屏幕上。

使用实例:

fifind . -name ".log" -ls 在当前目录查找以.log 结尾的文件,并显示详细信息。 fifind /root/ -perm 600 查找/root/目录下

权限为 600 的文件 fifind . -type f -name ".log" 查找当目录,以.log 结尾的普通文件 fifind . -typed | sort 查找当前所有目

录并排序 fifind . -size +100M 查找当前目录大 100M 的文件

十、其他命令

10.1 查看当前目录:  pwd

命令:  pwd 查看当前目录路径

10.2 查看进程:  ps -ef

命令:  ps -ef 查看所有正在运行的进程

10.3 结束进程:  kill

命令:  kill pid 或者 kill -9 pid(强制杀死进程) pid:进程号 10.4 网络通信命令:

ifconfifig:查看网卡信息

命令:  ifconfifig  ifconfifig | more

ping:查看与某台机器的连接情况

命令:  ping ip

netstat -an:查看当前系统端口

命令:  netstat -an

搜索指定端口 命令:  netstat -an | grep 8080

10.5 配置网络

命令:  setup

10.6 重启网络

命令: service network restart

10.7 切换用户

命令:  su - 用户名

10.8 关闭防火墙

命令: chkconfifig iptables offff

或者:

iptables -L; iptables -F; service iptables stop 10.9 修改文件权限 命令:  chmod 777

10.10 清屏

命令:  ctrl + l

十一、高薪挑战宝典

11.1 集合常见的面试题

1. Arraylist 与 LinkedList 区别

可以从它们的底层数据结构、效率、开销进行阐述哈

ArrayList 是数组的数据结构, LinkedList 是链表的数据结构。

随机访问的时候,ArrayList 的效率比较高,因为 LinkedList 要移动指针,而 ArrayList 是基于索引(index)的数据 结构, 可以直接映射到。

插入、删除数据时, LinkedList 的效率比较高, 因为 ArrayList 要移动数据。

LinkedList 比 ArrayList 开销更大,因为 LinkedList 的节点除了存储数据,还需要存储引用。

2. Collections.sort 和 Arrays.sort 的实现原理

Collection.sort 是对 list 进行排序, Arrays.sort 是对数组进行排序。

Collections.sort 底层实现

Collections.sort 方法调用了 list.sort 方法 list.sort 方法调用了 Arrays.sort 的方法因此, Collections.sort 方法 底层就是调用的 Array.sort 方法

Arrays.sort 底层实现

Arrays  sort 方法, 如下:如果比较器为 null,进入 sort(a)方法。如下:因此, Arrays  sort 方法底层就 是:

legacyMergeSort(a),归并排序,

ComparableTimSort.sort():即 Timsort 排序。

Timesort 排序

Timsort 排序是结合了合并排序(merge.sort)和插入排序(insertion sort)而得出的排序方法;

1.当数组长度小于某个值,采用的是二分插入排序算法, 下:

找到各个 run,并入栈。

按规则合并 run。

3. HashMap 原理, java8 做了什么改变

HashMap 是以键值对存储数据的集合容器

HashMap 是非线性安全的。

HashMap 底层数据结构:数组+(链表、红黑树),jdk8 之前是用数组+链表的方式实现,jdk8 引进了红黑树

Hashmap 数组的默认初始长度是 16 ,key 和 value 都允许 null 的存在

HashMap 的内部实现数组是 Node[]数组, 上面存放的是 key-value 键值对的节点。 HashMap 通过 put 和 get 方法存储和获取。

HashMap  put 方法, 首先计算 key  hashcode 值,定位到对应的数组索引, 然后再在该索引的单向链表上 进行循环遍历, 用 equals 比较 key 是否存在,如果存在则用新的 value 覆盖原值, 如果没有则向后追加。

jdk8  put 方法: 先判断 Hashmap 是否为空,为空就扩容,不为空计算出 key 的 hash 值 i,然后看 table[i]是 否为空,为空就直接插入,不为空判断当前位置的 key  table[i]是否相同,相同就覆盖, 不相同就查看 table[i] 是否是红黑树节点,如果是的话就用红黑树直接插入键值对,如果不是开始遍历链表插入,如果遇到重复值就覆 盖,否则直接插入, 如果链表长度大于 8,转为红黑树结构, 执行完成后看  size 是否大于阈值 threshold,大于 就扩容,否则直接结束。

Hashmap 解决 hash 冲突,使用的是链地址法,即数组+链表的形式来解决。put 执行首先判断 table[i]位置,如 果为空就直接插入,不为空判断和当前值是否相等,相等就覆盖,如果不相等的话,判断是否是红黑树节点,  如 果不是,就从 table[i]位置开始遍历链表, 相等覆盖,不相等插入。

HashMap 的 get 方法就是计算出要获取元素的 hash 值,去对应位置获取即可。

HashMap 的扩容机制,Hashmap 的扩容中主要进行两步, 第一步把数组长度变为原来的两倍,第二部把旧数组 元素重新计算 hash 插入到新数组中, jdk8 , 不用重新计算 hash,只用看看原来的 hash 值新增的一位是 还是  1,如果是  1 这个元素在新数组中的位置, 是原数组的位置加原数组长度,如果是零就插入到原数组中。扩 容过程第二部一个非常重要的方法是 transfer 方法,采用头插法, 把旧数组的元素插入到新数组中。

HashMap 大小为什么是 2 的幂次方? 效率高+空间分布均匀

有关于 HashMap 这些常量设计目的,也可以看我这篇文章: 面试加分项-HashMap 源码中这些常量的设计目的

4. List 和 Set  Map 的区别

List 以索引来存取元素, 有序的,元素是允许重复的,可以插入多个 null。

Set 不能存放重复元素,无序的,只允许一个 null

Map 保存键值对映射, 映射关系可以一对一、多对一

List 有基于数组、链表实现两种方式

Set、 Map 容器有基于哈希存储和红黑树两种方式实现

Set 基于 Map 实现, Set 里的元素值就是 Map 的键值

5. poll()方法和 remove()方法的区别?

Queue 队列中,poll() 和 remove() 都是从队列中取出一个元素,在队列元素为空的情况下, remove() 方法会抛 出异常,poll() 方法只会返回 null 。

看一下源码的解释吧:

/**

* Retrieves and removes the head of this queue.  This method differs

* from {@link #poll poll} only in that it throws an exception if this

* queue is empty. *

* @return the head of this queue

* @throws NoSuchElementException if this queue is empty

*/

E remove();

/**

* Retrieves and removes the head of this queue,

* or returns {@code null} if this queue is empty. *

* @return the head of this queue, or {@code null} if this queue is empty */

E poll();

6. HashMap  HashTable ,ConcurrentHash 的共同点和区别

HashMap

底层由链表+数组+红黑树实现

可以存储 null 键和 null 值

线性不安全

初始容量为 16,扩容每次都是 2 的 n 次幂

加载因子为 0.75,当 Map 中元素总数超过 Entry 数组的 0.75,触发扩容操作.

并发情况下, HashMap 进行 put 操作会引起死循环,导致 CPU 利用率接近 100%

HashMap 是对 Map 接口的实现

HashTable

HashTable 的底层也是由链表+数组+红黑树实现。

无论 key 还是 value 都不能为 null

它是线性安全的,使用了 synchronized 关键字。

HashTable 实现了 Map 接口和 Dictionary 抽象类

Hashtable 初始容量为 11

ConcurrentHashMap

ConcurrentHashMap 的底层是数组+链表+红黑树

不能存储 null 键和值

ConcurrentHashMap 是线程安全的

ConcurrentHashMap 使用锁分段技术确保线性安全

JDK8 为何又放弃分段锁,是因为多个分段锁浪费内存空间, 竞争同一个锁的概率非常小, 分段锁反而会造成效率 低。

7. 写一段代码在遍历 ArrayList 时移除一个元素

因为 foreach 删除会导致快速失败问题, fori 顺序遍历会导致重复元素没删除,所以正确解法如下:

第一种遍历,倒叙遍历删除

for(int i=list.size()-1; i>-1; i--){

if(list.get(i).equals("jay")){

list.remove(list.get(i));

}

}

第二种,迭代器删除

Iterator itr = list.iterator();

while(itr.hasNext()) {

if(itr.next().equals("jay") {

itr.remove();

}

}

8. Java 中怎么打印数组?

数组是不能直接打印的哈,如下:

public class Test {

public static void main(String[] args) {

String[] jayArray = {"jay", "boy"};

System.out.println(jayArray);

}

}

//output

[Ljava.lang.String;@1540e19d

打印数组可以用流的方式 Strem.of().foreach(),如下:

public class Test {

public static void main(String[] args) {

String[] jayArray = {"jay", "boy"};

Stream.of(jayArray).forEach(System.out::println);

}

}

//output

jay

boy

打印数组, 最优雅的方式可以用这个 APi,Arrays.toString()

public class Test {

public static void main(String[] args) {

String[] jayArray = {"jay", "boy"};

System.out.println(Arrays.toString(jayArray));

}

}

//output

[jay, boy]

9. TreeMap 底层?

TreeMap 实现了 SotredMap 接口,它是有序的集合。

TreeMap 底层数据结构是一个红黑树,每个 key-value 都作为一个红黑树的节点。

如果在调用 TreeMap 的构造函数时没有指定比较器,则根据 key 执行自然排序。

10. HashMap 的扩容过程

Hashmap 的扩容:

第一步把数组长度变为原来的两倍,

第二步把旧数组的元素重新计算 hash 插入到新数组中。

jdk8 时, 不用重新计算 hash,只用看看原来的 hash 值新增的一位是零还是 1,如果是 1 这个元素在新数组中的 位置,是原数组的位置加原数组长度,如果是零就插入到原数组中。扩容过程第二步一个非常重要的方法是 transfer 方法, 采用头插法,把旧数组的元素插入到新数组中。

11. HashSet 是如何保证不重复的

可以看一下 HashSet 的 add 方法,元素 E 作为 HashMap 的 key,我们都知道 HashMap 的可以是不允许重复的, 哈哈。

public boolean add(E e) {

return map.put(e, PRESENT)==null;

}

12. HashMap 是线程安全的吗, 为什么不是线程安全的? 死循环问题?

不是线性安全的。

并发的情况下, 扩容可能导致死循环问题。

13. LinkedHashMap 的应用,底层, 原理

LinkedHashMap 维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是 插入顺序(insert-order)或者是访问顺序,其中默认的迭代访问顺序就是插入顺序,即可以按插入的顺序遍历元

素,这点和 HashMap 有很大的不同。

LRU 算法可以用 LinkedHashMap 实现。

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

线性安全的

Vector:比 Arraylist 多了个同步化机制。

Hashtable:比 Hashmap 多了个线程安全。

ConcurrentHashMap:是一种高效但是线程安全的集合。

Stack:栈,也是线程安全的,继承于 Vector。

线性不安全的

Hashmap

Arraylist

LinkedList

HashSet

TreeSet

TreeMap

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

Vector 是线程安全的,ArrayList 不是线程安全的。

ArrayList 在底层数组不够用时在原来的基础上扩展 0.5 倍, Vector 是扩展 1 倍。

Vector 只要是关键性的操作,方法前面都加了 synchronized 关键字,来保证线程的安全性。

16. Collection 与 Collections 的区别是什么?

Collection 是 Java 集合框架中的基本接口,如 List 接口也是继承于它

public interface List extends Collection {

Collections 是 Java 集合框架提供的一个工具类, 其中包含了大量用于操作或返回集合的静态方法。如下:

public static > void sort(List> list) {

list.sort(null);

}

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

这个点,主要考察 HashMap 和 TreeMap 的区别。

TreeMap 实现 SortMap 接口, 能够把它保存的记录根据键排序, 默认是按 key 的升序排序,也可以指定排序的 比较器。当用 Iterator 遍历 TreeMap 时,得到的记录是排过序的。

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

List 转 Array

List 转 Array,必须使用集合的 toArray(T[] array),如下:

List list = new ArrayList();

list.add("jay");

list.add("tianluo");

// 使用泛型,无需显式类型转换

String[] array = list.toArray(new String[list.size()]);

System.out.println(array[0]);

如果直接使用 toArray 无参方法,返回值只能是 Object[] 类, 强转其他类型可能有问题,demo 如下:

List list = new ArrayList();

list.add("jay");

list.add("tianluo");

String[] array = (String[]) list.toArray();

System.out.println(array[0]);

运行结果:

Exception   in   thread   "main"   java.lang.ClassCastException:    [Ljava.lang.Object;   cannot   be   cast   to [Ljava.lang.String;

at Test.main(Test.java:14)

Array 转 List

使用 Arrays.asList() 把数组转换成集合时,不能使用修改集合相关的方法啦,如下:

String[] str = new String[] { "jay", "tianluo" };

List list = Arrays.asList(str);

list.add("boy");

运行结果如下:

Exception in thread "main" java.lang.UnsupportedOperationException

at java.util.AbstractList.add(AbstractList.java:148)

at java.util.AbstractList.add(AbstractList.java:108)

at Test.main(Test.java:13)

因为 Arrays.asList 不是返回 java.util.ArrayList,而是一个内部类 ArrayList。

可以这样使用弥补这个缺点:

//方式一:

ArrayList< String> arrayList = new ArrayList(strArray.length);

Collections.addAll(arrayList, strArray);

//方式二:

ArrayList list = new ArrayList(Arrays.asList(strArray)) ;

19. 迭代器 Iterator 是什么?怎么用,有什么特点?

public interface Collection extends Iterable {

Iterator iterator();

方法如下:

next() 方法获得集合中的下一个元素

hasNext() 检查集合中是否还有元素

remove() 方法将迭代器新返回的元素删除

forEachRemaining(Consumeruper E> action) 方法, 遍历所有元素

Iterator 主要是用来遍历集合用的, 它的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候, 就会抛出 ConcurrentModificationException 异常。

使用 demo 如下:

List list = new ArrayList<>();

Iterator it = list. iterator();

while(it. hasNext()){

String obj = it. next();

System. out. println(obj);

}

20. Iterator 和 ListIterator 有什么区别?

ListIterator 比 Iterator 有更多的方法。

ListIterator 只能用于遍历 List 及其子类, Iterator 可用来遍历所有集合,

ListIterator 遍历可以是逆向的, 因为有 previous()和 hasPrevious()方法, 而 Iterator 不可以。

ListIterator 有 add()方法,可以向 List 添加对象, 而 Iterator 却不能。

ListIterator 可以定位当前的索引位置,因为有 nextIndex()和 previousIndex()方法, 而 Iterator 不可以。

ListIterator 可以实现对象的修改,set()方法可以实现。 Iierator 仅能遍历, 不能修改哦。

21. 怎么确保一个集合不能被修改?

很多朋友很可能想到用 final 关键字进行修饰,  final  修饰的这个成员变量,如果是基本数据类型,表示这个变量 的值是不可改变的,如果是引用类型,则表示这个引用的地址值是不能改变的,但是这个引用所指向的对象里面 的内容还是可以改变滴~验证一下, 如下:

public class Test {

//final 修饰

private static final Map map = new HashMap();

{

map.put(1, "jay");

map.put(2, "tianluo");

}

public static void main(String[] args) {

map.put(1, "boy");

System.out.println(map.get(1));

}

}

运行结果如下:

//可以洗发现, final 修饰,集合还是会被修改呢

boy

嘻嘻, 那么,到底怎么确保一个集合不能被修改呢,看以下这三哥们~

unmodifiableMap

unmodifiableList

unmodifiableSet

再看一下 demo 吧

public class Test {

private static  Map map = new HashMap String>();

{

map.put(1, "jay");

map.put(2, "tianluo");

}

public static void main(String[] args) {

map = Collections.unmodifiableMap(map);

map.put(1, "boy");

System.out.println(map.get(1));

}

}

运行结果:

// 可以发现,unmodifiableMap 确保集合不能修改啦,抛异常了

Exception in thread "main" java.lang.UnsupportedOperationException

at java.util.Collections$UnmodifiableMap.put(Collections.java:1457)

at Test.main(Test.java:14)

22. 快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

快速失败

在用迭代器遍历一个集合对象时,  如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出 Concurrent Modification Exception。

public class Test {

public static void main(String[] args) {

List list = new ArrayList<>();

list.add(1);

list.add(2);

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

System.out.println(iterator.next());

list.add(3);

System.out.println(list.size());

}

}

}

运行结果:

1

Exception in thread "main" java.util.ConcurrentModificationException

3

at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)

at java.util.ArrayList$Itr.next(ArrayList.java:859)

at Test.main(Test.java:12)

安全失败

采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集 合上进行遍历。

public class Test {

public static void main(String[] args) {

List list = new CopyOnWriteArrayList<>();

list.add(1);

list.add(2);

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

System.out.println(iterator.next());

list.add(3);

System.out.println("list size:"+list.size());

}

}

}

运行结果:

1

list size:3

2

list size:4

其实,  java.util.concurrent 并发包的集合,如  ConcurrentHashMap, CopyOnWriteArrayList 等, 默认为都 是安全失败的。

23. 什么是 Java 优先级队列(Priority Queue)?

优先队列 PriorityQueue 是 Queue 接口的实现, 可以对其中元素进行排序

优先队列中元素默认排列顺序是升序排列

但对于自己定义的类来说,需要自己定义比较器

public class PriorityQueue extends AbstractQueue

implements java.io.Serializable {

...

private final Comparator E> comparator;

方法:

peek()//返回队首元素

poll()//返回队首元素, 队首元素出队列

add()//添加元素

size()//返回队列元素个数

isEmpty()//判断队列是否为空, 为空返回 true,不空返回 false

特点:

1.基于优先级堆

2.不允许 null 值

3.线程不安全

4.出入队时间复杂度 O(log(n))

5.调用 remove()返回堆内最小值

24. JAVA8 的 ConcurrentHashMap 为什么放弃了分段锁,有什么问题吗, 如果你来设计, 你如何设计。

jdk8 放弃了分段锁而是用了 Node 锁,减低锁的粒度,提高性能, 并使用 CAS 操作来确保 Node 的一些操作的 原子性,取代了锁。

可以跟面试官聊聊悲观锁和 CAS 乐观锁的区别,优缺点哈~

25. 阻塞队列的实现, ArrayBlockingQueue 的底层实现?

ArrayBlockingQueue 是数组实现的线程安全的有界的阻塞队列, 继承自 AbstractBlockingQueue,间接的实现了 Queue 接口和 Collection 接口。底层以数组的形式保存数据(实际上可看作一个循环数组)。常用的操作包括 add,offer,put ,remove,poll,take,peek。

可以结合线程池跟面试官讲一下哦~

26. Java 中的 LinkedList 是单向链表还是双向链表?

哈哈, 看源码吧,是双向链表

private static class Node {

E item;

Node next;

Node prev;

Node(Node prev, E element, Node next) {

this.item = element;

this.next = next;

this.prev = prev;

}

}

27. 说一说 ArrayList 的扩容机制吧

ArrayList 扩容的本质就是计算出新的扩容数组的 size 后实例化,并将原有数组内容复制到新数组中去。

public boolean add(E e) {

//扩容

ensureCapacityInternal(size + 1); // Increments modCount!!

elementData[size++] = e;

return true;

}

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}

private static int calculateCapacity(Object[] elementData, int minCapacity) {

//如果传入的是个空数组则最小容量取默认容量与 minCapacity 之间的最大值

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {

return Math.max(DEFAULT_CAPACITY, minCapacity);

}

return minCapacity;

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;

// 如果最小需要空间比 elementData 的内存空间要大,则需要扩容

// overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

private void grow(int minCapacity) {

// 获取 elementData 数组的内存空间长度

int oldCapacity = elementData.length;

// 扩容至原来的 1.5 倍

int newCapacity = oldCapacity + (oldCapacity >> 1);

//校验容量是否够

if (newCapacity - minCapacity < 0)

newCapacity = minCapacity;

//若预设值大于默认的最大值, 检查是否溢出

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

// 调用 Arrays.copyOf 方法将 elementData 数组指向新的内存空间

//并将 elementData 的数据复制到新的内存空间

elementData = Arrays.copyOf(elementData, newCapacity);

}

28. HashMap 的长度为什么是 2 的幂次方, 以及其他常量定义的含义~

为了能让 HashMap 存取高效, 数据分配均匀。

看着呢,以下等式相等, 但是位移运算比取余效率高很多呢~

hash%length=hash&(length-1)

可以看下我这篇文章哈~面试加分项-HashMap 源码中这些常量的设计目的

29. ConcurrenHashMap 原理? 1.8 中为什么要用红黑树?

聊到 ConcurrenHashMap,需要跟面试官聊到安全性,分段锁 segment,为什么放弃了分段锁, 与及选择 CAS, 其实就是都是从效率和安全性触发, 嘻嘻~

java8 不是用红黑树来管理 hashmap,而是在 hash 值相同的情况下(且重复数量大于 8),用红黑树来管理数据。 红黑树相当于排序数据。可以自动的使用二分法进行定位。性能较高。

30. ArrayList 的默认大小

ArrayList 的默认大小是 10 个元素

/**

* Default initial capacity.

*/

private static final int DEFAULT_CAPACITY = 10;

31. 为何 Collection 不从 Cloneable 和 Serializable 接口继承?

Collection 表示一个集合, 包含了一组对象元素。如何维护它的元素对象是由具体实现来决定的。因为集合的具 体形式多种多样,例如 list 允许重复,set 则不允许。而克隆(clone)和序列化(serializable)只对于具体的实 体,对象有意义,你不能说去把一个接口,抽象类克隆,序列化甚至反序列化。所以具体的 collection 实现类是

否可以克隆,是否可以序列化应该由其自身决定, 而不能由其超类强行赋予。

如果 collection 继承了 clone 和 serializable,那么所有的集合实现都会实现这两个接口,而如果某个实现它不需 要被克隆, 甚至不允许它序列化(序列化有风), 那么就与 collection 矛盾了。

32. Enumeration 和 Iterator 接口的区别?

public interface Enumeration {

boolean hasMoreElements();

E nextElement();

}

public interface Iterator {

boolean hasNext();

E next();

void remove();

}

函数接口不同

Enumeration 速度快, 占用内存少, 但是不是快速失败的,线程不安全。

Iterator 允许删除底层数据,枚举不允许

Iterator 安全性高,因为其他线程不能够修改正在被 Iterator 遍历的集合里面的对象。

33. 我们如何对一组对象进行排序?

可以用 Collections.sort() + Comparator.comparing(), 因为对对象排序,实际上是对对象的属性排序哈~

public class Student {

private String name;

private int score;

public Student(String name, int score){

this.name = name;

this.score = score;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getScore() {

return score;

}

public void setScore(int score) {

this.score = score;

}

@Override

public String toString() {

return "Student: " + this.name + " 分数: " + Integer.toString( this.score );

}

}

public class Test {

public static void main(String[] args) {

List studentList = new ArrayList<>();

studentList.add(new Student("D", 90));

studentList.add(new Student("C", 100));

studentList.add(new Student("B", 95));

studentList.add(new Student("A", 95));

Collections.sort(studentList,

Comparator.comparing(Student::getScore).reversed().thenComparing(Student::getName)); studentList.stream().forEach(p - > System.out.println(p.toString()));

}

}

34. 当一个集合被作为参数传递给一个函数时,如何才可以确保函数不能修改它?

这个跟之前那个不可变集合一样道理哈~

在作为参数传递之前, 使用 Collections.unmodifiableCollection(Collection c)方法创建一个只读集合,  这将确 保改变集合的任何操作都会抛出 UnsupportedOperationException。

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

不能保证元素的排列顺序,顺序有可能发生变化。

元素可以为 null

hashset 保证元素不重复~  (这个面试官很可能会问什么原理, 这个跟 HashMap 有关的哦)

HashSet,需要谈谈它俩 hashcode()和 equles()哦~

实际是基于 HashMap 实现的,  HashSet 底层使用 HashMap 来保存所有元素的

看看它的 add 方法吧~

public boolean add(E e) {

return map.put(e, PRESENT)==null;

}

36. Array 和 ArrayList 有何区别?

定义一个 Array 时, 必须指定数组的数据类型及数组长度,即数组中存放的元素个数固定并且类型相同。

ArrayList 是动态数组,长度动态可变,会自动扩容。不使用泛型的时候,可以添加不同类型元素。

37. 为什么 HashMap 中 String、Integer 这样的包装类适合作为 key?

String、 Integer 等包装类的特性能够保证 Hash 值的不可更改性和计算准确性,能够有效的减少 Hash 碰撞的几 率~

因为

它们都是 final 修饰的类, 不可变性, 保证 key 的不可更改性, 不会存在获取 hash 值不同的情况~

它们内部已重写了 equals()、hashCode()等方法, 遵守了 HashMap 内部的规范

38. 如果想用 Object 作为 hashMap 的 Key?;

重写 hashCode()和 equals()方法啦~ (这个答案来自互联网哈~)

重写 hashCode()是因为需要计算存储数据的存储位置, 需要注意不要试图从散列码计算中排除掉一个对象的关键 部分来提高性能,这样虽然能更快但可能会导致更多的 Hash 碰撞;

重写 equals()方法, 需要遵守自反性、对称性、传递性、 一致性以及对于任何非 null 的引用值 x ,x.equals(null) 必须返回 false 的这几个特性,目的是为了保证 key 在哈希表中的唯一性;

39. 讲讲红黑树的特点?

每个节点或者是黑色,或者是红色。

根节点是黑色。

每个叶子节点(NIL)是黑色。 [注意: 这里叶子节点,是指为空(NIL 或 NULL)的叶子节点!]

如果一个节点是红色的, 则它的子节点必须是黑色的。

从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

40. Java 集合类框架的最佳实践有哪些?

其实这些点,结合平时工作,代码总结讲出来,更容易吸引到面试官呢 (这个答案来自互联网哈~)

1.根据应用需要正确选择要使用的集合类型对性能非常重要, 比如:假如知道元素的大小是固定的,那么选用 Array 类型而不是 ArrayList 类型更为合适。

2.有些集合类型允许指定初始容量。因此,如果我们能估计出存储的元素的数目, 我们可以指定初始容量来避免重 新计算 hash 值或者扩容等。

3. 为 了    全 、         原 因    使   型 。 同 时 , 使          行 时 的 ClassCastException。

4.使用 JDK 提供的不变类(immutable class)作为 Map 的键可以避免为我们自己的类实现 hashCode()和 equals() 方法。

5.编程的时候接口优于实现

6.底层的集合实际上是空的情况下, 返回为长度是 0 的集合或数组而不是 null。

41.谈谈线程池阻塞队列吧~

ArrayBlockingQueue

LinkedBlockingQueue

DelayQueue

PriorityBlockingQueue

SynchronousQueue

ArrayBlockingQueue  有界队列)是一个用数组实现的有界阻塞队列, 按 FIFO 排序量。

LinkedBlockingQueue  可设置容量队列)基于链表结构的阻塞队列,按 FIFO 排序任务, 容量可以选择进行 设置,不设置的话,将是一个无边界的阻塞队列,最大长度为  Integer.MAX_VALUE 吞吐量通常要高于 ArrayBlockingQuene; newFixedThreadPool 线程池使用了这个队列

DelayQueue:(延迟队列) 是一个任务定时周期的延迟执行的队列。根据指定的执行时间从小到大排序,否则根 据插入到队列的先后排序。newScheduledThreadPool 线程池使用了这个队列。

PriorityBlockingQueue:(优先级队列) 是具有优先级的无界阻塞队列;

SynchronousQueue:(同步队列) 一个不存储元素的阻塞队列, 每个插入操作必须等到另一个线程调用移除操作, 否则插入操作一直处于阻塞状态,吞吐量通常要高于  LinkedBlockingQuene  newCachedThreadPool 线程池    使用了这个队列。针对面试题: 线程池都有哪几种工作队列?

我觉得,回答以上几种 ArrayBlockingQueue  LinkedBlockingQueue ,SynchronousQueue 等,说出它们的 特点, 并结合使用到对应队列的常用线程池(如 newFixedThreadPool 线程池使用 LinkedBlockingQueue),进行 展开阐述,  就可以啦。

有兴趣的朋友, 可以看看我的这篇文章哦~

面试必备: Java 线程池解析

42. HashSet 和 TreeSet 有什么区别?

Hashset 的底层是由哈希表实现的, Treeset 底层是由红黑树实现的。

HashSet 中的元素没有顺序, TreeSet 保存的元素有顺序性(实现 Comparable 接口)

HashSet 的 add() ,remove() ,contains()方法的时间复杂度是 O(1);TreeSet 中, add() ,remove() ,contains() 方法的时间复杂度是 O(logn)

43. Set 里的元素是不能重复的, 那么用什么方法来区分重复与否呢? 是用 ==还是 equals()?

元素重复与否是使用  equals()方法进行判断的,这个可以跟面试官说说 ==和 equals()的区别, hashcode()和 equals

44. 说出 ArrayList,LinkedList 的存储性能和特性

这道面试题,跟 ArrayList,LinkedList,就是换汤不换药的~

ArrayList,使用数组方式存储数据,查询时,ArrayList 是基于索引(index)的数据结构, 可以直接映射到,速度较 快;但是插入数据需要移动数据,效率就比 LinkedList 慢一点~

LinkedList,使用双向链表实现存储,按索引数据需要进行前向或后向遍历,查询相对 ArrayList 慢一点; 但是插入 数据速度较快。

LinkedList 比 ArrayList 开销更大,因为 LinkedList 的节点除了存储数据,还需要存储引用。

45. HashMap 在 JDK1.7 和 JDK1.8 中有哪些不同?

互联网上这个答案太详细啦( Java集合必会14问(精选面试题整理) - 简书

46. ArrayList 集合加入 1 万条数据, 应该怎么提高效率

因为 ArrayList 的底层是数组实现,并且数组的默认值是 10,如果插入 10000 条要不断的扩容,耗费时间,所以我们调 用 ArrayList 的指定容量的构造器方法 ArrayList(int size) 就可以实现不扩容,就提高了性能。

47. 如何对 Object 的 list 排序

看例子吧, 哈哈,这个跟对象排序也是一样的呢~

public class Person {

private String name;

private Integer age;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

public Person(String name, Integer age) {

this.name = name;

this.age = age;

}

}

public class Test {

public static void main(String[] args) {

List list = new ArrayList<>();

list.add(new Person("jay", 18));

list.add(new Person("tianLuo", 10));

list.stream().forEach(p - > System.out.println(p.getName()+" "+p.getAge()));

// 用 comparing 比较对象属性

list.sort(Comparator.comparing(Person::getAge));

System.out.println("排序后");

list.stream().forEach(p - > System.out.print(p.getName()+" "+p.getAge()+" "));

}

}

48. ArrayList 和 HashMap 的默认大小是多数?

在 Java 7 中, ArrayList 的默认大小是 10 个元素, HashMap 的默认大小是 16 个元素(必须是 2 的幂)。

49. 有没有有顺序的 Map 实现类,如果有,他们是怎么保证有序的

Hashmap 和 Hashtable 都不是有序的。

TreeMap 和 LinkedHashmap 都是有序的。(TreeMap 默认是 key 升序, LinkedHashmap 默认是数据插入顺序)

TreeMap 是基于比较器 Comparator 来实现有序的。

LinkedHashmap 是基于链表来实现数据插入有序的。

50. HashMap 是怎么解决哈希冲突的

Hashmap 解决 hash 冲突,使用的是链地址法,即数组+链表的形式来解决。put 执行首先判断 table[i]位置,如 果为空就直接插入,不为空判断和当前值是否相等,相等就覆盖,如果不相等的话,判断是否是红黑树节点,  如 果不是,就从 table[i]位置开始遍历链表, 相等覆盖,不相等插入。

11.2 线程常见面试题

1、并发编程三要素?

1)原子性

原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行。

2)可见性

可见性指多个线程操作一个共享变量时, 其中一个线程对变量进行修改后, 其他线程可以立即看到修改的结果。

3)有序性

有序性,即程序的执行顺序按照代码的先后顺序来执行。

2、实现可见性的方法有哪些?

synchronized 或者  Lock:保证同一个时刻只有一个线程获取锁执行代码,锁释放之前把最新的值刷新到主内存, 实现可见性。

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、创建线程的三种方式的对比?

1)采用实现 Runnable、Callable 接口的方式创建多线程。

优势是:

线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

在这种方式下, 多个线程可以共享同一个 target 对象, 所以非常适合多个相同线程来处理同一份资源的情况, 从 而可以将 CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势是:

编程稍微复杂, 如果要访问当前线程,则必须使用 Thread.currentThread()方法。

2)使用继承 Thread 类的方式创建多线程

优势是:

编写简单, 如果需要访问当前线程, 则无需使用 Thread.currentThread()方法,直接使用 this 即可获得当前线程。

劣势是:

线程类已经继承了 Thread 类, 所以不能再继承其他父类。

3) Runnable 和 Callable 的区别

Callable 规定(重写) 的方法是 call()  Runnable 规定(重写)的方法是 run()。

Callable 的任务执行后可返回值,而 Runnable 的任务是不能返回值的。

Call 方法可以抛出异常, run 方法不可以。

运行 Callable 任务可以拿到一个 Future 对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等 待计算的完成, 并检索计算的结果。通过 Future 对象可以了解任务执行情况,可取消任务的执行, 还可获取执行 结果。

6、线程的状态流转图

线程的生命周期及五种基本状态:

7、Java 线程具有五中基本状态

1)新建状态(New): 当线程对象对创建后, 即进入了新建状态, 如: Thread t = new MyThread();

2)就绪状态(Runnable): 当调用线程对象的 start()方法(t.start();), 线程即进入就绪状态。处于就绪状态的    线程, 只是说明此线程已经做好了准备, 随时等待 CPU 调度执行,并不是说执行了 t.start()此线程立即就会执行;

3)运行状态(Running): CPU 开始调度处于就绪状态的线程时, 此时线程才得以真正执行,即进入到运行状 态。注:就

绪状态是进入到运行状态的唯一入口,也就是说, 线程要想进入运行状态执行,首先必须处于就绪状态中;

4)阻塞状态(Blocked):处于运行状态中的线程由于某种原因, 暂时放弃对  CPU 使用权, 停止执行, 此时进 入阻塞状态,直到其进入到就绪状态,才 有机会再次被 CPU 调用以进入到运行状态。

根据阻塞产生的原因不同,阻塞状态又可以分为三种:

a.等待阻塞: 运行状态中的线程执行 wait()方法, 使本线程进入到等待阻塞状态;

b.同步阻塞 – 线程在获取 synchronized 同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

c.其他阻塞  通过调用线程的 sleep()或 join()或发出了 I/O 请求时,线程会进入到阻塞状态。当 sleep()状态超时、 join()等待线程终止或者超时、或者 I/O 处理完毕时,线程重新转入就绪状态。

5)死亡状态(Dead):线程执行完了或者因异常退出了 run()方法,该线程结束生命周期。

8、什么是线程池?有哪几种创建方式?

线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会 被销毁,而是等待下一个任务。由于创建和销毁线程都是消耗系统资源的,所以当你想要频繁的创建和销毁线程 的时候就可以考虑使用线程池来提升系统的性能。

java 提供了一个 java.util.concurrent.Executor 接口的实现用于创建线程池。

9、四种线程池的创建:

1) newCachedThreadPool 创建一个可缓存线程池

2) newFixedThreadPool 创建一个定长线程池, 可控制线程最大并发数。

3) newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

4) newSingleThreadExecutor 创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务。

10、线程池的优点?

1)重用存在的线程, 减少对象创建销毁的开销。

2)可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

3)提供定时执行、定期执行、单线程、并发数控制等功能。

11、常用的并发工具类有哪些?

CountDownLatch

CyclicBarrier

Semaphore

Exchanger

12、CyclicBarrier 和 CountDownLatch 的区别

1)CountDownLatch 简单的说就是一个线程等待,直到他所等待的其他线程都执行完成并且调用 countDown() 方法发出通知后,当前线程才可以继续执行。

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 中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前 获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处 理资源,比如通过给记录加 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 以便在任务执行成功或失败 后作出相应的操作。

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 接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的, 读和读之间不会互 斥,读和写、写和读、写和写之间才会互斥, 提升了读写的性能。

21、 FutureTask 是什么

这个其实前面有提到过, FutureTask 表示一个异步运算的任务。 FutureTask 里面可以传入一个 Callable 的具体 实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。当然, 由于 FutureTask 也是 Runnable 接口的实现类,所以 FutureTask 也可以放入线程池中。

22、synchronized 和 ReentrantLock 的区别

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

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

2) ReentrantLock 可以获取各种锁的信息

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

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

23、什么是乐观锁和悲观锁

1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生, 因此它不需要持有锁, 将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生   冲突, 那么就应该有相应的重试逻辑。

2)悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生, 因此每次对某资源进行操作时, 都会持有一个独占的锁, 就像 synchronized,不管三七二十一,直接上了锁就操   作资源了。

24、线程 B 怎么知道线程 A 修改了变量

volatile 修饰变量

synchronized 修饰修改变量的方法

wait/notify

while 轮询

25、synchronized、volatile、CAS 比较

synchronized 是悲观锁, 属于抢占式,会引起其他线程阻塞。

volatile 提供多线程共享变量可见性和禁止指令重排序优化。

CAS 是基于冲突检测的乐观锁(非阻塞)

26、sleep 方法和 wait 方法有什么区别?

这个问题常问, sleep 方法和 wait 方法都可以用来放弃 CPU一定的时间, 不同点在于如果线程持有某个对象的监

视器, sleep 方法不会放弃这个对象的监视器,wait 方法会放弃这个对象的监视器

27、ThreadLocal 是什么?有什么用?

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)在支持时间片的系统中,该线程的时间片用完

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)默认的锁申请操作是阻塞的。

所以要避免死锁,就要在一遇到多个对象锁交叉的情况,就要仔细审查这几个对象的类中的所有方法,是否存在 着导致锁依赖的环路的可能性。总之是尽量避免在一个同步方法中调用其它对象的延时方法和同步方法。

35、怎么唤醒一个阻塞的线程

  线       了  wait()  sleep() 或者  join()        塞 ,     线 程 ,     抛出 InterruptedException 来唤醒它;如果线程遇到了 IO 阻塞,无能为力, 因为 IO 是操作系统实现的,Java 代码并 没有办法直接接触到操作系统。

36、不可变对象对多线程有什么帮助

前面有提到过的一个问题,不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手 段,提升了代码执行效率。

37、什么是多线程的上下文切换

多线程的上下文切换是指 CPU 控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取 CPU 执行权的 线程的过程。

38、如果你提交任务时, 线程池队列已满,这时会发生什么

这里区分一下:

1)如果使用的是无界队列  LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到阻塞队列中 等待执行, 因为 LinkedBlockingQueue 可以近乎认为是一个无穷大的队列, 可以无限存放任务

2   果使  的是 有界队  比如  ArrayBlockingQueue , 任务 首先 会被添加到  ArrayBlockingQueue  中,  ArrayBlockingQueue 满了,会根据 maximumPoolSize 的值增加线程数量࿰

你可能感兴趣的:(java,职场和发展)