面试最常被问的 Java 后端题目及参考答案

Posted 柚子茶1990

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试最常被问的 Java 后端题目及参考答案相关的知识,希望对你有一定的参考价值。

一、Java 基础篇

1. Object 有哪些常用方法?大致说一下每个方法的含义

2. Java 创建对象有几种方式?

3. 获取一个类对象的方式有哪些?

4. ArrayList 和 LinkedList 的区别有哪些?

5. 用过 ArrayList 吗?说一下它有什么特点?

6. 有数组了为什么还要搞个 ArrayList 呢?

7. 说说什么是 fail-fast?

8. Hashtable 与 HashMap 的区别

9. HashMap 中的 key 我们可以使用任何类作为 key 吗?

10. HashMap 的长度为什么是 2 的 N 次方呢?

11. HashMap 与 ConcurrentHashMap 的异同

13. 红黑树有哪几个特征?

14. 说说你平时是怎么处理 Java 异常的

15. finally 模块执行了吗?是先执行 return 还是先执行 finally 模块?返回什么?

二、JVM 篇

16. Java 类加载器有几种?

17. 说一下有哪些类加载场景?

18. 说说 Java 类加载机制是什么?说说 new 创建一个普通对象的过程?

19. 说说类的生命周期?

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

21. 如何破坏双亲委派模型?

22. 能不能自己也写一个 java.lang.String 类?

23. 说一下 JVM 运行时数据区有哪些?分别说一下它们的功能

24. 方法区和永久代有什么区别?

24. JVM 运行时数据区哪些地方会产生内存溢出?

25. 为什么要用 metaspace 替换 permspace 呢?

26. 熟悉哪些 JVM 调优参数?

27. Java 对象的引用类型有哪些?

28. JVM 垃圾回收算法有哪些?

29. 垃圾收集器有哪些?

30. 说说 JVM 中内存的分配与回收策略

三、Dubbo 篇

31. 说说一次 Dubbo 服务请求流程?

32. 说说 Dubbo 工作原理

33. Dubbo 支持哪些协议?

34. 注册中心挂了,consumer 还能不能调用 provider?

35. 怎么实现动态感知服务下线的呢?

36. Dubbo 负载均衡策略?

37. Dubbo 容错策略

38. Dubbo 动态代理策略有哪些?

39. 说说 Dubbo 与 Spring Cloud 的区别?

40. 说说 TCP 与 UDP 的区别,以及各自的优缺点

41. 说一下 HTTP 和 HTTPS 的区别

42. 说说 HTTP、TCP、Socket 的关系是什么?

43. 说一下 HTTP 的长连接与短连接的区别

四、MyBatis 篇

44. 说说 MyBatis 的缓存

45. JDBC 编程有哪些步骤?

46. 说一下 MyBatis 中使用的 #和 $ 有什么区别

47. MyBatis 中比如 UserMapper.java 是接口,为什么没有实现类还能调用?

48. MyBatis 中见过什么设计模式?

五、mysql

49. 简单说说在 MySQL 中执行依据查询 SQL 是如何执行的?

50. MySQL 有哪些存储引擎?

51. MySQL 中 varchar 与 char 的区别?varchar(30) 中的 30 代表的涵义?

52. int(11) 中的 11 代表什么涵义?

53. 为什么 SELECT COUNT(*) FROM table 在 InnoDB 比 MyISAM 慢?

54. 说说数据库的三范式和反模式

55. 在设计数据库表的时候,字段用于存储金额、余额时,选择什么类型比较好?

56. 大概说说 InnoDB 与 MyISAM 有什么区别?

57. 什么是索引?

58. 索引有什么优缺点?

59. MySQL 索引类型有哪些?

60. 什么时候不要使用索引?

61. 使用 MySQL 的索引应该注意些什么?

62. 怎么知道一条查询语句是否用到了索引,用了什么类型的索引?

63. 说说什么是 MVCC?

64. MVCC 可以为数据库解决什么问题?

65. 说说 MVCC 的实现原理

66. 什么是死锁?

67. MySQL 事务隔离级别?

69. 请说说 MySQL 数据库的锁?

70. 说说什么是锁升级?

71. 说说悲观锁和乐观锁

72. 怎样尽量避免死锁的出现?

六、RabbitMQ 篇

73. 看你简历上写了 RabbitMQ,通常会问:为什么要用 RabbitMQ?

74. 可能你讲了上面三个 RabbitMQ 的优点后,会继续问:使用 RabbitMQ 容易带来什么问题?

75. 那么多消息队列,为什么选 RabbitMQ 呢?

75. RabbitMQ 中什么是死信队列?

76. 如何处理死信队列?

77. 怎么保证消息不会被丢失?

78. RabbitMQ 怎么高可用呢?

79. RabbitMq 怎么保证消息的顺序性?

80. 如果有大量消息持续积压在队列了,怎么处理?

七、Redis 篇

81. 为什么要用缓存

82. 为什么 使用 Redis 而不是用 Memcached 呢?

83. 为什么 Redis 单线程模型效率也能那么高?

84. 说说 Redis 的线程模型

85. 说一下 Redis 有什么优点和缺点

86. Redis 缓存刷新策略有哪些?

87. Redis 持久化方式有哪些?以及有什么区别?

88. 持久化有两种,那应该怎么选择呢?

89. 怎么使用 Redis 实现消息队列?

90. 熟悉哪些 Redis 集群模式?

91. 缓存和数据库谁先更新呢?

八、Spring Boot 篇

92. Spring Boot 提供了哪些核心功能?

93. Spring Boot 核心注解是什么?

94. 说说 Spring Boot 的自动装配原理

95. Spring Boot 常用 starter 有哪些?

96. Spring 中的 starter 是什么?

97. Spring Boot 有什么优缺点?

98. 读取配置文件中配置项的有哪些方法?

九、Spring 篇

99. Spring 中 ApplicationContext 和 BeanFactory 的区别

100. 说一下你对 Spring IOC 的理解

101. Spring IOC 有什么优点?

102. Bean 的生命周期

103. Spring Bean 的作用域有哪些?

104. Spring 是怎么管理事务的?

105. 说说你对 Spring AOP 的理解

106. Spring 中用到了哪些设计模式?

107. Spring 框架中的单例 Bean 是线程安全的么?

108. Spring 是怎么解决循环依赖的?

十、ZooKeeper 篇

109. 说说 ZooKeeper 是什么?

110. ZooKeeper 有哪些应用场景?

111. ZooKeeper 有哪些节点类型?

112. 请描述一下 ZooKeeper 的通知机制是什么?

113. ZooKeeper 对节点的 watch 监听通知是永久的吗?

114. ZooKeeper 集群中有哪些角色?

115. ZooKeeper 集群中 Server 有哪些工作状态?

116. ZooKeeper 集群中是怎样选举 leader 的?

117. ZooKeeper 是如何保证事务的顺序一致性的呢?

118. ZooKeeper 集群中各服务器之间是怎样通信的?

119. ZooKeeper 分布式锁怎么实现的?

十一、并发编程篇

120. 通常创建线程有几种方式?

121. 说说线程的生命周期

122. 说说 synchronized 的使用和原理

123. synchronized 和 ReentrantLock 区别

124. 什么是线程安全?

125. 线程安全需要保证几个基本特征

126. 说一下线程之间是如何通信的?

127. 说说你对 volatile 的理解

128. 说一下 volatile 和 synchronized 的区别?

129. Thread 调用 run 方法和调 start 方法的区别?

130. 说一下 Java 创建线程池有哪些方式?

131. 说说 ThreadLocal 底层原理是什么,怎么避免内存泄漏?

132. 说说你对 JUC 下并发工具类

133. CyclicBarrier 和 CountdownLatch 有什么区别?

十二、设计模式篇

134. 你都熟悉哪些设计模式

十三、其他篇

135. 有 8 个球(大小颜色都一模一样),其中一个球比其他 7 个球中的任何一个都重,使用天平秤最多几次能找到最重的那个球?

136. 分布式幂等性如何设计?

137. 简单一次完整的 HTTP 请求所经历的步骤?

138. 说说分布式事务解决方案有哪些?

139. 说说常用的 JVM 调优命令和工具有哪些?

140. 说说你对 JVM 内存溢出和内存泄漏的理解

141. 说说 JVM 中有哪些常用参数?

一、Java 基础篇

1. Object 有哪些常用方法?大致说一下每个方法的含义

java.lang.Object

 

下面是对应方法的含义。

clone 方法

保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出
CloneNotSupportedException 异常,深拷贝也需要实现 Cloneable,同时其成员变量为引用类型的也需要实现 Cloneable,然后重写 clone 方法。

finalize 方法

该方法和垃圾收集器有关系,判断一个对象是否可以被回收的最后一步就是判断是否重写了此方法。

equals 方法

该方法使用频率非常高。一般 equals 和 == 是不一样的,但是在 Object 中两者是一样的。子类一般都要重写这个方法。

hashCode 方法

该方法用于哈希查找,重写了 equals 方法一般都要重写 hashCode 方法,这个方法在一些具有哈希功能的 Collection 中用到。

一般必须满足 obj1.equals(obj2)==true。可以推出 obj1.hashCode()==obj2.hashCode(),但是 hashCode 相等不一定就满足 equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

  • JDK 1.6、1.7 默认是返回随机数;
  • JDK 1.8 默认是通过和当前线程有关的一个随机数 + 三个确定值,运用 Marsaglia’s xorshift scheme 随机数算法得到的一个随机数。

wait 方法

配合 synchronized 使用,wait 方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 方法一直等待,直到获得锁或者被中断。wait(long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生。

  1. 其他线程调用了该对象的 notify 方法;
  2. 其他线程调用了该对象的 notifyAll 方法;
  3. 其他线程调用了 interrupt 中断该线程;
  4. 时间间隔到了。

此时该线程就可以被调度了,如果是被中断的话就抛出一个 InterruptedException 异常。

notify 方法

配合 synchronized 使用,该方法唤醒在该对象上等待队列中的某个线程(同步队列中的线程是给抢占 CPU 的线程,等待队列中的线程指的是等待唤醒的线程)。

notifyAll 方法

配合 synchronized 使用,该方法唤醒在该对象上等待队列中的所有线程。

总结

只要把上面几个方法熟悉就可以了,toString 和 getClass 方法可以不用去讨论它们。该题目考察的是对 Object 的熟悉程度,平时用的很多方法并没看其定义但是也在用,比如说:wait() 方法,equals() 方法等。

Class Object is the root of the class hierarchy.Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.

大致意思:Object 是所有类的根,是所有类的父类,所有对象包括数组都实现了 Object 的方法。

面试扩散

上面提到了 wait、notify、notifyAll 方法,或许面试官会问你为什么 sleep 方法不属于 Object 的方法呢?因为提到 wait 等方法,所以最好把 synchronized 都说清楚,把线程状态也都说清楚,尝试让面试官跟着你的节奏走。

2. Java 创建对象有几种方式?

这题目看似简单,要好好回答起来还是有点小复杂的,我们来看看,到底有哪些方式可以创建对象?

1. 使用 new 关键字,这也是我们平时使用的最多的创建对象的方式,示例:

User user=new User();

2. 反射方式创建对象,使用 newInstance(),但是得处理两个异常 InstantiationException、IllegalAccessException:

User user=User.class.newInstance();
Object object=(Object)Class.forName("java.lang.Object").newInstance()

3.使用 clone 方法,前面题目中 clone 是 Object 的方法,所以所有对象都有这个方法。

4.使用反序列化创建对象,调用 ObjectInputStream 类的 readObject() 方法。

我们反序列化一个对象,JVM 会给我们创建一个单独的对象。JVM 创建对象并不会调用任何构造函数。一个对象实现了 Serializable 接口,就可以把对象写入到文件中,并通过读取文件来创建对象。

总结

创建对象的方式关键字:new、反射、clone 拷贝、反序列化。

3. 获取一个类对象的方式有哪些?

搞清楚类对象和实例对象,但都是对象。

第一种:通过类对象的 getClass() 方法获取,细心点的都知道,这个 getClass 是 Object 类里面的方法。

User user=new User();
//clazz就是一个User的类对象
Class<?> clazz=user.getClass();

第二种:通过类的静态成员表示,每个类都有隐含的静态成员 class。

//clazz就是一个User的类对象
Class<?> clazz=User.class;

第三种:通过 Class 类的静态方法 forName() 方法获取。

Class<?> clazz = Class.forName("com.tian.User");    

面试扩散

可能面试官会问相关的题目,比如:

Class.forName 和 ClassLoader.loadClass 的区别是什么?

参考:

反射中 Class.forName() 和 ClassLoader.loadClass() 的区别

4. ArrayList 和 LinkedList 的区别有哪些?

ArrayList

  • 优点:ArrayList 是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
  • 缺点:因为地址连续,ArrayList 要移动数据,所以插入和删除操作效率比较低。

LinkedList

  • 优点:LinkedList 基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址。对于新增和删除操作,LinkedList 比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景。
  • 缺点:因为 LinkedList 要移动指针,所以查询操作性能比较低。

适用场景分析

  • 当需要对数据进行对随机访问的时候,选用 ArrayList。
  • 当需要对数据进行多次增加删除修改时,采用 LinkedList。

如果容量固定,并且只会添加到尾部,不会引起扩容,优先采用 ArrayList。

当然,绝大数业务的场景下,使用 ArrayList 就够了,但需要注意避免 ArrayList 的扩容,以及非顺序的插入。

5. 用过 ArrayList 吗?说一下它有什么特点?

只要是搞 Java 的肯定都会回答“用过”。所以,回答题目的后半部分——ArrayList 的特点。可以从这几个方面去回答:

Java 集合框架中的一种存放相同类型的元素数据,是一种变长的集合类,基于定长数组实现,当加入数据达到一定程度后,会实行自动扩容,即扩大数组大小。

底层是使用数组实现,添加元素。

  • 如果 add(o),添加到的是数组的尾部,如果要增加的数据量很大,应该使用 ensureCapacity() 方法,该方法的作用是预先设置 ArrayList 的大小,这样可以大大提高初始化速度。
  • 如果使用 add(int,o),添加到某个位置,那么可能会挪动大量的数组元素,并且可能会触发扩容机制。

高并发的情况下,线程不安全。多个线程同时操作 ArrayList,会引发不可预知的异常或错误。

ArrayList 实现了 Cloneable 接口,标识着它可以被复制。注意:ArrayList 里面的 clone() 复制其实是浅复制。

6. 有数组了为什么还要搞个 ArrayList 呢?

通常我们在使用的时候,如果在不明确要插入多少数据的情况下,普通数组就很尴尬了,因为你不知道需要初始化数组大小为多少,而 ArrayList 可以使用默认的大小,当元素个数到达一定程度后,会自动扩容。

可以这么来理解:我们常说的数组是定死的数组,ArrayList 却是动态数组。

7. 说说什么是 fail-fast?

fail-fast 机制是 Java 集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生 fail-fast 事件。

例如:当某一个线程 A 通过 iterator 去遍历某集合的过程中,若该集合的内容被其他线程所改变了,那么线程 A 访问集合时,就会抛出
ConcurrentModificationException 异常,产生 fail-fast 事件。这里的操作主要是指 add、remove 和 clear,对集合元素个数进行修改。

解决办法:建议使用“java.util.concurrent 包下的类”去取代“java.util 包下的类”。

可以这么理解:在遍历之前,把 modCount 记下来 expectModCount,后面 expectModCount 去和 modCount 进行比较,如果不相等了,证明已并发了,被修改了,于是抛出
ConcurrentModificationException 异常。

8. Hashtable 与 HashMap 的区别

本来不想这么写标题的,但是无奈,面试官都喜欢这么问 HashMap。

  1. 出生的版本不一样,Hashtable 出生于 Java 发布的第一版本 JDK 1.0,HashMap 出生于 JDK 1.2。
  2. 都实现了 Map、Cloneable、Serializable(当前 JDK 版本 1.8)。
  3. HashMap 继承的是 AbstractMap,并且 AbstractMap 也实现了 Map 接口。Hashtable 继承 Dictionary。
  4. Hashtable 中大部分 public 修饰普通方法都是 synchronized 字段修饰的,是线程安全的,HashMap 是非线程安全的。
  5. Hashtable 的 key 不能为 null,value 也不能为 null,这个可以从 Hashtable 源码中的 put 方法看到,判断如果 value 为 null 就直接抛出空指针异常,在 put 方法中计算 key 的 hash 值之前并没有判断 key 为 null 的情况,那说明,这时候如果 key 为空,照样会抛出空指针异常。
  6. HashMap 的 key 和 value 都可以为 null。在计算 hash 值的时候,有判断,如果 key==null,则其 hash=0;至于 value 是否为 null,根本没有判断过。
  7. Hashtable 直接使用对象的 hash 值。hash 值是 JDK 根据对象的地址或者字符串或者数字算出来的 int 类型的数值。然后再使用除留余数法来获得最终的位置。然而除法运算是非常耗费时间的,效率很低。HashMap 为了提高计算效率,将哈希表的大小固定为了 2 的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
  8. Hashtable、HashMap 都使用了 Iterator。而由于历史原因,Hashtable 还使用了 Enumeration 的方式。
  9. 默认情况下,初始容量不同,Hashtable 的初始长度是 11,之后每次扩充容量变为之前的 2n+1(n 为上一次的长度)而 HashMap 的初始长度为 16,之后每次扩充变为原来的两倍。

另外在 Hashtable 源码注释中有这么一句话:

Hashtable is synchronized.  If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable . If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.

大致意思:Hashtable 是线程安全,推荐使用 HashMap 代替 Hashtable;如果需要线程安全高并发的话,推荐使用 ConcurrentHashMap 代替 Hashtable。

这个回答完了,面试官可能会继续问:HashMap 是线程不安全的,那么在需要线程安全的情况下还要考虑性能,有什么解决方式?

这里最好的选择就是 ConcurrentHashMap 了,但面试官肯定会叫你继续说一下 ConcurrentHashMap 数据结构以及底层原理等。

9. HashMap 中的 key 我们可以使用任何类作为 key 吗?

平时可能大家使用的最多的就是使用 String 作为 HashMap 的 key,但是现在我们想使用某个自定义类作为 HashMap 的 key,那就需要注意以下几点:

  • 如果类重写了 equals 方法,它也应该重写 hashCode 方法。
  • 类的所有实例需要遵循与 equals 和 hashCode 相关的规则。
  • 如果一个类没有使用 equals,你不应该在 hashCode 中使用它。
  • 咱们自定义 key 类的最佳实践是使之为不可变的,这样,hashCode 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保 hashCode 和 equals 在未来不会改变,这样就会解决与可变相关的问题了。

10. HashMap 的长度为什么是 2 的 N 次方呢?

为了能让 HashMap 存数据和取数据的效率高,尽可能地减少 hash 值的碰撞,也就是说尽量把数据能均匀的分配,每个链表或者红黑树长度尽量相等。

我们首先可能会想到 % 取模的操作来实现。

下面是回答的重点哟:

取余(%)操作中如果除数是 2 的幂次,则等价于与其除数减一的与(&)操作(也就是说 hash % length == hash &(length - 1) 的前提是 length 是 2 的 n 次方)。并且,采用二进制位操作 &,相对于 % 能够提高运算效率。

这就是为什么 HashMap 的长度需要 2 的 N 次方了。

11. HashMap 与 ConcurrentHashMap 的异同

  1. 都是 key-value 形式的存储数据;
  2. HashMap 是线程不安全的,ConcurrentHashMap 是 JUC 下的线程安全的;
  3. HashMap 底层数据结构是数组 + 链表(JDK 1.8 之前)。JDK 1.8 之后是数组 + 链表 + 红黑树。当链表中元素个数达到 8 的时候,链表的查询速度不如红黑树快,链表会转为红黑树,红黑树查询速度快;
  4. HashMap 初始数组大小为 16(默认),当出现扩容的时候,以 0.75 * 数组大小的方式进行扩容;
  5. ConcurrentHashMap 在 JDK 1.8 之前是采用分段锁来现实的 Segment + HashEntry,Segment 数组大小默认是 16,2 的 n 次方;JDK 1.8 之后,采用 Node + CAS + Synchronized 来保证并发安全进行实现。

13. 红黑树有哪几个特征?

紧接上个问题,面试官很有可能会问红黑树,下面把红黑树的几个特征列出来:

 

 

如果面试官还要继续问红黑树具体是怎么添加节点和删除节点的,推荐看:

30 张图带你彻底理解红黑树

14. 说说你平时是怎么处理 Java 异常的

try-catch-finally

  • try 块负责监控可能出现异常的代码
  • catch 块负责捕获可能出现的异常,并进行处理
  • finally 块负责清理各种资源,不管是否出现异常都会执行
  • 其中 try 块是必须的,catch 和 finally 至少存在一个标准异常处理流程

 

抛出异常→捕获异常→捕获成功(当 catch 的异常类型与抛出的异常类型匹配时,捕获成功)→异常被处理,程序继续运行 抛出异常→捕获异常→捕获失败(当 catch 的异常类型与抛出异常类型不匹配时,捕获失败)→异常未被处理,程序中断运行

在开发过程中会使用到自定义异常,在通常情况下,程序很少会自己抛出异常,因为异常的类名通常也包含了该异常的有用信息,所以在选择抛出异常的时候,应该选择合适的异常类,从而可以明确地描述该异常情况,所以这时候往往都是自定义异常。

自定义异常通常是通过继承 java.lang.Exception 类,如果想自定义 Runtime 异常的话,可以继承
java.lang.RuntimeException 类,实现一个无参构造和一个带字符串参数的有参构造方法。

在业务代码里,可以针对性的使用自定义异常。比如说:该用户不具备某某权限、余额不足等。

15. finally 模块执行了吗?是先执行 return 还是先执行 finally 模块?返回什么?

public class FinallyDemo 
    public String method111() 
        String ret = "hello";
        try 
            return ret;
         finally 
            ret = "world";
        
    

把 FinallyDemo.java 编译成 class 文件后,找到该 class 文件的当前目录,执行 cmd 命令:

javap -verbose FinallyDemo.class >>test.txt

然后打开 test.txt,关键部分内容如下:

 

发现在字节码指令中,将 hello 保存在本地变量 2 中,然后直到把本地变量 2 加载到操作数栈中,然后就直接出栈,return 回去了,所以本题的返回去的是 hello,但是 finally 代码块也执行了,执行完 finally 模块后再返回一个临时变量 2。

二、JVM 篇

16. Java 类加载器有几种?

 

 

17. 说一下有哪些类加载场景?

 

18. 说说 Java 类加载机制是什么?说说 new 创建一个普通对象的过程?

类加载的过程包括了:

加载、验证、准备、解析、初始化。

 

new 创建一个普通对象的过程如下:

  1. 检测类是否被加载过
  2. 为对象分配内存
  3. 为分配的内存空间初始化为 0
  4. 对对象进行其他相关设置
  5. 执行 init 方法

下面用一张图来描述:

 

19. 说说类的生命周期?

注意类生命周期和对象声明周期,类生命周期主要有以下几个阶段:

 

 

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

 

21. 如何破坏双亲委派模型?

 

22. 能不能自己也写一个 java.lang.String 类?

可以写,能编译,但是不能 run。禁止使用包名:java.开头的包名。

定义一个普通类:

package java.lang;

public class MyTest 

    public MyTest() 
    
    public MyTest(String str, int a) 
    

    public int length()
        return 10;
    
    public static void main(String[] args) 
        MyTest myTest =new MyTest("lang",1);
        myTest.length();
    

运行:

 

具体校验的源码地方:

 

结论就是定义包目录的时候,不能以 java. 开头。

23. 说一下 JVM 运行时数据区有哪些?分别说一下它们的功能

此题我想用我的方法说,不像网上一堆一堆抄书上的,希望能对大家有所帮助,如果没多大帮助,那可以网上找个看看,只能说抱歉了。

 

下面我们直入主题:

  • 每个线程单独拥有的:程序计数器、Java 虚拟机栈、本地方法栈
  • 所有线程都共同有的:方法区、堆

如果你在 Java 代码里创建一个线程,相应 JVM 虚拟机中就创建与之对应的程序计数器、Java 虚拟机栈、本地方法栈,同时方法区和堆是在虚拟机启动就已经有了。

程序计数器

可以简单理解为:程序计数器是记录执行到你代码的的第几行了,每个线程各自对应自己的程序计数器。

Java 虚拟机栈

虚拟机会为每个线程分配一个虚拟机栈,每个虚拟机栈中都有若干个栈帧,每个栈帧中存储了局部变量表、操作数栈、动态链接、返回地址等。一个栈帧就对应 Java 代码中的一个方法,当线程执行到一个方法时,就代表这个方法对应的栈帧已经进入虚拟机栈并且处于栈顶的位置,每一个 Java 方法从被调用到执行结束,就对应了一个栈帧从入栈到出栈的过程。一个线程的生命周期和与之对应的 Java 虚拟机栈的生命周期相同。

一个线程进来就创建虚拟机栈,该线程调用的方法就是栈帧,进入方法,栈帧就入栈(虚拟机栈),出方法就是出虚拟机栈。可以通过下面两张图进行理解:

 

 

本地方法栈

和 Java 虚拟机栈类似,Java 虚拟机栈针对的是 Java 方法,而本地方法栈针对的 native 修饰的方法。

JVM 几乎所有的对象的内存分配都在堆里。由于对象是有生命周期的,所以把堆又分成了新生代和老年代。

新生代和老年代大小比例 = 1:2(默认)。新生代又分为 Eden、S0、S1 区域,Ede:S0:S1=8:1:1。

大多数对象在 Eden 区出生和死亡。Eden 区存活下来的对象进入 S0 区,S0 区活下来的对象放到 S1,S1 区活下来的对象放到 S0 区,这过程中 S0 和 S1 至少有一个区域是空着的。并且对象每次倒腾一次自己的年龄就加 1,直达加到 15 岁的时候,就直接入老年代了。有的大对象可以直接进入老年代,条件是把该对象的大小以及达到了能直接进入老年代的条件了(阈值可以设置)。

方法区

先按照图中的关键字回答。但是方法区由于 JDK 版本有所变动。

回答的时候,一定要说一下方法区由于 JDK 版本有所变动。版本变动情况如下:

24. 方法区和永久代有什么区别?

之前有小伙伴也问过我,方法区和永久代到底是什么区别?

这么说吧:永久代又叫 Perm 区,只存在于 HotSpot JVM 中,并且只存在于 JDK 1.7 和之前的版本中,JDK 1.8 中已经彻底移除了永久代,JDK 1.8 中引入了一个新的内存区域叫 metaspace。

  • 并不是所有的 JVM 中都有永久代,IBM 的 9,Oracle 的 JRocket 都没有永久代。
  • 永久代是实现层面的东西。
  • 永久代里面存的东西基本上就是方法区规定的那些东西。

因此,我们可以说,在 JDK 1.7 中永久代是方法区的一种实现,当然,在 HotSpot JDK 1.8 中 metaspace 可以看成是方法区的一种实现。

24. JVM 运行时数据区哪些地方会产生内存溢出?

简单回答就行:

Java 虚拟机栈、本地方法栈、Java 堆、方法区,其实就是除了程序计数器以外的几个都会发生 OOM。

建议把阈值对应的几个区也简要的说一下:

25. 为什么要用 metaspace 替换 permspace 呢?

在 JDK 1.8 之前的 HotSpot 实现中,类的元数据如方法数据、方法信息(字节码、栈和变量大小)、运行时常量池、已确定的符号引用和虚方法表等被保存在永久代中,32 位默认永久代的大小为 64M,64 位默认为 85M,可以通过参数 -XX:MaxPermSize 进行设置,一旦类的元数据超过了永久代大小,就会抛出 OOM 异常。

虚拟机团队在 JDK 1.8 的 HotSpot 中,把永久代从 Java 堆中移除了,并把类的元数据直接保存在本地内存区域(堆外内存),称之为元空间 metaspace(本地内存)。即就是说 JDK 1.8 之前,永久代是在虚拟机中的,而 JDK 1.8 引入的元空间是系统的内存的一部分。理论上取决于 32 位/64 位系统可虚拟的内存大小,可见也不是无限制的,需要配置参数。

另外一方面,咱们在对永久代进行调优的时候是相当费劲,因为永久代的大小不好控制,涉及到很多因素,比如:类的总数、常量池的大小、方法数量等,最无语的是永久代的内存不够了可能会伴随着一次 Full GC。

下面是 JDK 几个版本中方法区和堆存储的信息的关系:

总结

  • 字符串存在永久代中,容易出现性能问题和内存溢出。
  • 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
  • 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
  • 移除永久代是为融合 HotSpot JVM 与 JRockit VM 而做出的努力,因为 JRockit 没有永久代,不需要配置永久代。

26. 熟悉哪些 JVM 调优参数?

整个堆内存大小

-Xms(初始堆大小)、-Xmx(最大堆大小),为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值。

新生代空间大小

NewRadio:年轻代和年老代将根据默认的比例(1:2)分配堆内存,建议设置为 2 到 4,可以通过调整二者之间的比率 NewRadio 来调整二者之间的大小。也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize 来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把 -XX:newSize -XX:MaxNewSize 设置一样大小。

方法区(元空间)

JDK 1.8:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m,根据实际情况调整, 可以使用命令 jstat -gcutil pid 查看当前使用率,M 对应的列,根据使用率来定制一个具体的值,建议两个值设置成同样大小。

JDK 1.7:-XX:MaxPermSize=256m -XX:MaxPermSize=256m 永久带。

GC 日志

-Xloggc:$CATALINA_BASE/logs/gc.log 
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps

记录 GC 日志并不会特别地影响 Java 程序性能,推荐你尽可能记录日志。

GC 算法

-XX:+UseParNewGC 
-XX:+CMSParallelRemarkEnabled 
-XX:+UseConcMarkSweepGC 
-XX:CMSInitiatingOccupancyFraction=75

一般来说推荐使用这些配置,但是根据程序不同的特性,其他的也有可能更好。

任何一个 JVM 参数的默认值可以通过

java -XX:+PrintFlagsFinal -version |grep JVMParamName

获取,例如:

java -XX:+PrintFlagsFinal -version |grep MetaspaceSize

27. Java 对象的引用类型有哪些?

对象引用类型有四类:强引用、软引用、弱引用、虚引用

28. JVM 垃圾回收算法有哪些?

垃圾回收算法共四种:其实我更愿意说成三种,因为分代回收其实不是算法。

29. 垃圾收集器有哪些?

目前常见的有如下几种:

Serial 收集器

ParNew 收集器

Parallel scavenge 收集器

Serial Old 收集器

CMS=Concurrent Mark Sweep 收集器

Parallel Old 收集器

G1=Garbage-First 收集器

垃圾收集器整合

  • G1 是新生代和老年代一起搞,不和别人合伙。
  • Serial:CMS 或者 Serial Old
  • ParNew:CMS 或者 Serial Old
  • Parallel Scavenge:Parallel Old 或者 Serial Old

分代收集器对应

  • 新生代收集器:Serial、ParNew、Parallel Scavenge
  • 老年代收集器:Serial Old、Parallel Old、CMS
  • 整堆收集器:G1

30. 说说 JVM 中内存的分配与回收策略

三、Dubbo 篇

其实关于 Dubbo 的面试题,我觉得最好的文档应该还是官网,因为官网有中文版,照顾了很多阅读英文文档吃力的小伙伴。但是官网内容挺多的,于是这里就结合官网和平时面试被问的相对较多的题目整理了一下。

31. 说说一次 Dubbo 服务请求流程?

基本工作流程:

上图中角色说明:

32. 说说 Dubbo 工作原理

工作原理分 10 层:

  • 第一层:service 层,接口层,给服务提供者和消费者来实现的(留给开发人员来实现);
  • 第二层:config 层,配置层,主要是对 Dubbo 进行各种配置的,Dubbo 相关配置;
  • 第三层:proxy 层,服务代理层,透明生成客户端的 stub 和服务单的 skeleton,调用的是接口,实现类没有,所以得生成代理,代理之间再进行网络通讯、负责均衡等;
  • 第四层:registry 层,服务注册层,负责服务的注册与发现;
  • 第五层:cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务;
  • 第六层:monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控;
  • 第七层:protocol 层,远程调用层,封装 rpc 调用;
  • 第八层:exchange 层,信息交换层,封装请求响应模式,同步转异步;
  • 第九层:transport 层,网络传输层,抽象 mina 和 netty 为统一接口;
  • 第十层:serialize 层,数据序列化层。

这是个很坑爹的面试题,但是很多面试官又喜欢问,你真的要背么?你能背那还是不错的,我建议不要背,你就想想 Dubbo 服务调用过程中应该会涉及到哪些技术,把这些技术串起来就 OK 了。

面试扩散

如果让你设计一个 RPC 框架,你会怎么做?其实你就把上面这个工作原理中涉及的到技术点总结一下就行了。

33. Dubbo 支持哪些协议?

还有三种,混个眼熟就行:Memcached 协议、Redis 协议、Rest 协议。

上图基本上把序列化的方式也罗列出来了。

详细请参考:Dubbo 官网。

34. 注册中心挂了,consumer 还能不能调用 provider?

可以。因为刚开始初始化的时候,consumer 会将需要的所有提供者的地址等信息拉取到本地缓存,所以注册中心挂了可以继续通信。但是 provider 挂了,那就没法调用了。

关键字:consumer 本地缓存服务列表。

35. 怎么实现动态感知服务下线的呢?

服务订阅通常有 pull 和 push 两种方式:

  • pull 模式需要客户端定时向注册中心拉取配置;
  • push 模式采用注册中心主动推送数据给客户端。

Dubbo ZooKeeper 注册中心采用是事件通知与客户端拉取方式。服务第一次订阅的时候将会拉取对应目录下全量数据,然后在订阅的节点注册一个 watcher。一旦目录节点下发生任何数据变化,ZooKeeper 将会通过 watcher 通知客户端。客户端接到通知,将会重新拉取该目录下全量数据,并重新注册 watcher。利用这个模式,Dubbo 服务就可以做到服务的动态发现。

注意:ZooKeeper 提供了“心跳检测”功能,它会定时向各个服务提供者发送一个请求(实际上建立的是一个 socket 长连接),如果长期没有响应,服务中心就认为该服务提供者已经“挂了”,并将其剔除。

36. Dubbo 负载均衡策略?

  • 随机(默认):随机来
  • 轮训:一个一个来
  • 活跃度:机器活跃度来负载
  • 一致性 hash:落到同一台机器上

37. Dubbo 容错策略

failover cluster 模式

provider 宕机重试以后,请求会分到其他的 provider 上,默认两次,可以手动设置重试次数,建议把写操作重试次数设置成 0。

failback 模式

失败自动恢复会在调用失败后,返回一个空结果给服务消费者。并通过定时任务对失败的调用进行重试,适合执行消息通知等操作。

failfast cluster 模式

快速失败只会进行一次调用,失败后立即抛出异常。适用于幂等操作、写操作,类似于 failover cluster 模式中重试次数设置为 0 的情况。

failsafe cluster 模式

失败安全是指,当调用过程中出现异常时,仅会打印异常,而不会抛出异常。适用于写入审计日志等操作。

forking cluster 模式

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

broadcacst cluster 模式

广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。

38. Dubbo 动态代理策略有哪些?

默认使用 javassist 动态字节码生成,创建代理类,但是可以通过 SPI 扩展机制配置自己的动态代理策略。

39. 说说 Dubbo 与 Spring Cloud 的区别?

这是很多面试官喜欢问的问题,本人认为其实他们没什么关联之处,但是硬是要问区别,那就说说吧。

回答的时候主要围绕着四个关键点来说:通信方式、注册中心、监控、断路器,其余像 Spring 分布式配置、服务网关肯定得知道。

通信方式

Dubbo 使用的是 RPC 通信;Spring Cloud 使用的是 HTTP RestFul 方式。

注册中心

Dubbo 使用 ZooKeeper(官方推荐),还有 Redis、Multicast、Simple 注册中心,但不推荐。;

Spring Cloud 使用的是 Spring Cloud Netflix Eureka。

监控

Dubbo 使用的是 Dubbo-monitor;Spring Cloud 使用的是 Spring Boot admin。

断路器

Dubbo 在断路器这方面还不完善,Spring Cloud 使用的是 Spring Cloud Netflix Hystrix。

分布式配置、网关服务、服务跟踪、消息总线、批量任务等。

Dubbo 目前可以说还是空白,而 Spring Cloud 都有相应的组件来支撑。

40. 说说 TCP 与 UDP 的区别,以及各自的优缺点

41. 说一下 HTTP 和 HTTPS 的区别

  • 端口不同:HTTP 和 HTTPS 的连接方式不同没用的端口也不一样,HTTP 是 80,HTTPS 用的是 443;
  • 消耗资源:和 HTTP 相比,HTTPS 通信会因为加解密的处理消耗更多的 CPU 和内存资源;
  • 开销:HTTPS 通信需要证书,这类证书通常需要向认证机构申请或者付费购买。

42. 说说 HTTP、TCP、Socket 的关系是什么?

  • TCP/IP 代表传输控制协议/网际协议,指的是一系列协议族。
  • HTTP 本身就是一个协议,是从 Web 服务器传输超文本到本地浏览器的传送协议。
  • Socket 是 TCP/IP 网络的 API,其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面。对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议。

综上所述:

  • 需要 IP 协议来连接网络。
  • TCP 是一种允许我们安全传输数据的机制,使用 TCP 协议来传输数据的 HTTP 是 Web 服务器和客户端使用的特殊协议。
  • HTTP 基于 TCP 协议,所以可以使用 Socket 去建立一个 TCP 连接。

43. 说一下 HTTP 的长连接与短连接的区别

HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。

短连接

在 HTTP/1.0 中默认使用短链接,也就是说,浏览器和服务器每进行一次 HTTP 操作,就建立一次连接,但任务结束就中断连接。如果客户端访问的某个 html 或其他类型的 Web 资源,如 javascript 文件、图像文件、CSS 文件等。当浏览器每遇到这样一个 Web 资源,就会建立一个 HTTP 会话。

长连接

从 HTTP/1.1 起,默认使用长连接,用以保持连接特性。在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭。如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如 Apache)中设定这个时间。

四、MyBatis 篇

44. 说说 MyBatis 的缓存

一级缓存

在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的 SQL,MyBatis 提供了一级缓存的方案优化这部分场景,如果是相同的 SQL 语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。

每个 SqlSession 中持有了 Executor,每个 Executor 中有一个 LocalCache。当用户发起查询时,MyBatis 根据当前执行的语句生成 MappedStatement,在 Local Cache 进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入 Local Cache,最后返回结果给用户。具体实现类的类关系图如下图所示:

  1. MyBatis 一级缓存的生命周期和 SqlSession 一致。
  2. MyBatis 一级缓存内部设计简单,只是一个没有容量限定的 HashMap,在缓存的功能性上有所欠缺。
  3. MyBatis 的一级缓存最大范围是 SqlSession 内部,有多个 SqlSession 或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为 Statement。

二级缓存

在上文中提到的一级缓存中,其最大的共享范围就是一个 SqlSession 内部,如果多个 SqlSession 之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用 CachingExecutor 装饰 Executor,进入一级缓存的查询流程前,先在 CachingExecutor 进行二级缓存的查询,具体的工作流程如下所示。

二级缓存开启后,同一个 namespace 下的所有操作语句,都影响着同一个 Cache,即二级缓存被多个 SqlSession 共享,是一个全局的变量。

当开启缓存后,数据的查询执行的流程为:

二级缓存 -> 一级缓存 -> 数据库

  1. MyBatis 的二级缓存相对于一级缓存来说,实现了 SqlSession 之间缓存数据的共享,同时粒度更加细,能够到 namespace 级别,通过 Cache 接口实现类不同的组合,对 Cache 的可控性也更强。
  2. MyBatis 在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
  3. 在分布式环境下,由于默认的 MyBatis Cache 实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将 MyBatis 的 Cache 接口实现,有一定的开发成本,直接使用 Redis、Memcached 等分布式缓存可能成本更低,安全性也更高。

45. JDBC 编程有哪些步骤?

1. 装载相应的数据库的 JDBC 驱动并进行初始化:

Class.forName("com.mysql.jdbc.Driver"); 

2. 建立 JDBC 和数据库之间的 Connection 连接:

Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8", "root", "123456");

3. 创建 Statement 或者 PreparedStatement 接口,执行 SQL 语句:

    //查询用户信息
    public List<User> findUserList()
        String sql = "select * from t_user order by user_id";
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        //创建一个List用于存放查询到的User对象
        List<User> userList = new ArrayList<>();
        try 
            conn = DbUtil.getConnection();
            pstmt =(PreparedStatement) conn.prepareStatement(sql);
            rs =(ResultSet) pstmt.executeQuery();
            while(rs.next())
                int courseId = rs.getInt("user_id");
                String courseName = rs.getString("user_name");
                //每个记录对应一个User对象
                User user = new User();
                user.setUserId(courseId);
                user.setUserName(courseName);
                //将对象放到集合中
                userList.add(course);
            
         catch(SQLException e) 
            e.printStackTrace();
        finally
            //资源关闭
            DbUtil.close(pstmt);
            DbUtil.close(conn);
        
         return userList;
    

4. 处理和显示结果。

5. 释放资源。

46. 说一下 MyBatis 中使用的 #和 $ 有什么区别

动态 SQL 是 MyBatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 MyBatis 会对其进行动态解析。

MyBatis 为我们提供了两种支持动态 SQL 的语法:# 以及 $。

# 是预编译处理,$ 是字符替换。在使用 # 时,MyBatis 会将 SQL 中的 # 替换成 ?,配合 PreparedStatement 的 set 方法赋值,这样可以有效的防止 SQL 注入,保证程序的运行安全。

建议能不要用就不要用,“常在河边走哪能不湿鞋”

47. MyBatis 中比如 UserMapper.java 是接口,为什么没有实现类还能调用?

UserMapper.xml 中:

<mapper namespace="com.tian.UserMapper">

反射生成 namespace 的对象:

boundType = Resources.classForName(namespace);

JDK 动态代理:

 Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]mapperInterface, mapperProxy);

总结:XML 中的 namespace="com.user.UserMapper" 接口 com.user.UserMapper 本身反射 JDK 动态代理实现接口。

48. MyBatis 中见过什么设计模式?

五、MySQL 篇

49. 简单说说在 MySQL 中执行依据查询 SQL 是如何执行的?

select name from t_user where id=1
  1. 取得链接,使用使用到 MySQL 中的连接器。
  2. 查询缓存,key 为 SQL 语句,value 为查询结果,如果查到就直接返回。不建议使用次缓存,在 MySQL 8.0 版本已经将查询缓存删除,也就是说 MySQL 8.0 版本后不存在此功能。
  3. 分析器,分为词法分析和语法分析。此阶段只是做一些 SQL 解析,语法校验。所以一般语法错误在此阶段。
  4. 优化器,是在表里有多个索引的时候,决定使用哪个索引;或者一个语句中存在多表关联的时候(join),决定各个表的连接顺序。
  5. 执行器,通过分析器让 SQL 知道你要干啥,通过优化器知道该怎么做,于是开始执行语句。执行语句的时候还要判断是否具备此权限,没有权限就直接返回提示没有权限的错误;有权限则打开表,根据表的引擎定义,去使用这个引擎提供的接口,获取这个表的第一行,判断 id 是都等于 1。如果是,直接返回;如果不是继续调用引擎接口去下一行,重复相同的判断,直到取到这个表的最后一行,最后返回。

MySQL 的典型的三层结构(连接器 + Server + 执行器):

50. MySQL 有哪些存储引擎?

51. MySQL 中 varchar 与 char 的区别?varchar(30) 中的 30 代表的涵义?

  • varchar 与 char 的区别,char 是一种固定长度的类型,varchar 则是一种可变长度的类型。
  • varchar(30) 中 30 的涵义最多存放 30 个字符。varchar(30) 和 (130) 存储 hello 所占空间一样,但后者在排序时会消耗更多内存,因为 ORDER BY col 采用 fixed_length 计算 col 长度(memory 引擎也一样)。
  • 对效率要求高用 char,对空间使用要求高用 varchar。

52. int(11) 中的 11 代表什么涵义?

int(11) 中的 11,不影响字段存储的范围,只影响展示效果。

53. 为什么 SELECT COUNT(*) FROM table 在 InnoDB 比 MyISAM 慢?

对于 SELECT COUNT(*) FROM table 语句,在没有 WHERE 条件的情况下,InnoDB 比 MyISAM 可能会慢很多,尤其在大表的情况下。因为,InnoDB 是去实时统计结果,会全表扫描;而 MyISAM 内部维持了一个计数器,预存了结果,所以直接返回即可。

面试扩散

此题还有另外一种问法:SELECT COUNT(*) FROM table 在使用存储引擎 InnoDB 和 MyISAM,谁更快,为什么?

54. 说说数据库的三范式和反模式

55. 在设计数据库表的时候,字段用于存储金额、余额时,选择什么类型比较好?

  1. 直接选择 int 或者 bigint 类型,但是得对金额进行乘 100,或者 1000;
  2. 使用 decimal 类型,避免精度丢失。如果使用 Java 语言时,需要使用 BigDecimal 进行对应,但是使用 BigDecimal 的时候也是容易出问题的,这是 Java 层面的,没遇到坑的,以后要留意点。

56. 大概说说 InnoDB 与 MyISAM 有什么区别?

  • 在 MySQL 5.1 及之前的版本中,MyISAM 是默认的存储引擎,而在 MySQL 5.5 版本以后,默认使用 InnoDB 存储引擎。
  • MyISAM 不支持行级锁,换句话说,MyISAM 会对整张表加锁,而不是针对行。同时,MyISAM 不支持事务和外键。MyISAM 可被压缩,存储空间较小,而且 MyISAM 在筛选大量数据时非常快。
  • InnoDB 是事务型引擎,当事务异常提交时,会被回滚。同时,InnoDB 支持行锁。此外,InnoDB 需要更多存储空间,会在内存中建立其专用的缓冲池用于高速缓冲数据和索引。InnoDB 支持自动奔溃恢复特性。

建议:一般情况下,个人建议优先选择 InnoDB 存储引擎,并且尽量不要将 InnoDB 与 MyISAM 混合使用。

57. 什么是索引?

索引,类似于书籍的目录,想找到一本书的某个特定的主题,需要先找到书的目录,定位对应的页码。

MySQL 中存储引擎使用类似的方式进行查询,先去索引中查找对应的值,然后根据匹配的索引找到对应的数据行。

58. 索引有什么优缺点?

59. MySQL 索引类型有哪些?

60. 什么时候不要使用索引?

  1. 经常增删改的列不要建立索引;
  2. 有大量重复的列不建立索引;
  3. 表记录太少不要建立索引。

61. 使用 MySQL 的索引应该注意些什么?

62. 怎么知道一条查询语句是否用到了索引,用了什么类型的索引?

使用方法,在 SELECT 语句前加上 EXPLAIN 执行,查看其执行计划, 可以帮助选择更好的索引和写出更优化的查询语句,explain 执行计划对应每一列的详情,这里就不用再提了,网上一堆资料,但还是推荐看官网:

索引详解

英语好的就直接看官网的说明,英语不好的可以使用浏览器搞个自动翻译的小插件,结合英文同步来看。

63. 说说什么是 MVCC?

多版本并发控制(MVCC=Multi-Version Concurrency Control),是一种用来解决读 - 写冲突的无锁并发控制。也就是为事务分配单向增长的时间戳,为每个修改保存一个版本。版本与事务时间戳关联,读操作只

面试官最常问的10道测试用例和5道思维面试题及答案,每1题都很经典

前言


软件测试面试中,测试用例是非常容被问到的一个点,今天小编就给大家把最常见的20道测试用例方面的问题给大家整理出来,希望对大家的面试提供帮助。

之前给大家整理了一套软件测试的学习资料以及各大厂的面试题,因文章的篇幅有限,所以图上的文件我都放在公众号【IT互联网二狗子】中了,关注之后回复777即可获取了。

测试用例10道面试题

1、什么是测试用例
答:测试用例的设计就是如何覆盖所有软件表现出来的状态,即在满足输入/输出的一组条件下,软件运行是一系列有次序的、受控制的状态变化过程。

2、设计用例的好处?
答:设计用例的过程可以更深刻的理解需求,熟悉各功能点,保证尽可能全的覆盖到各测试点。也便于用例评审。

3、用例必备4个方面?
答:预置条件、执行步骤、预期结果、测试结果;用例要点:需包括与其他模块耦合关系、用例的级别,考虑哪些需求必须完成,哪些需求可以后续完成。

4、测试用例的设计理念?
答:首先要保证产品的质量,测试用例的数量并不能决定质量的好坏,要做到覆盖全面,提倡高质量的自动化测试。

5、测试用例有哪些设计方法?
答:等价类划分法、边界值分析法、功能图法、错误推测法、因果图法、场景法等。

6、何时开始设计测试用例?
答:需求文档定版后,即可开始陈列测试点和编写测试用例。

7、用例写完,我们要先做什么?
答:先自检,自检完毕,列出有疑惑的点,评审之前,把用例提前发给相关的开发、产品,预留时间告诉他们先看,再统一时间进行评审。

8、什么时候写测试点,什么时候写用例?
答:

如果公司只有你一个测试员,就没必要写测试用例了,写测试点,提取关键要素;
如果需求老是频繁变化,写测试点吧;你的测试用例的更新速度永远跟不上需求的变化速度,每天都在改用例。太详细的用例,无太多的意义和价值;
如果你们的节奏控制的非常紧凑,完全没时间严格按照测试用例执行,写测试点吧,提取关键要素;
如果团队的整体测试员技能均衡,测试点已经能够充分覆盖了,写测试点吧,测试用例的意义不大;
如果这块的逻辑非常复杂,你未曾接触,尽量写详细点的测试用例,通过用例的梳理过程,是一个很好的梳理理解需求和产品的过程;
如何用更少的测试点,尽可能的充分考虑各种可能性呢?跟什么因素有关呢?与用例设计方法、经验、需求理解等等有关。我们要综合运用等价类、边界值、错误推测、场景法、因果图等测试用例的设计方法;
不要总找刁钻的用例,要把客户常用的流程弄好。产品上线之前无论经过多少轮测试,一定要把主体业务流程进行回归测试。
9、测试用例的更新?
答、评审后需要更新、测试过程中需要更新、测试结束后根据线上反馈情况进行更新。

10、如何保证用例的覆盖度?
答:首先一定要熟悉需求,需求分析,拆解非常重要,需求熟悉过程中,不理解的地方,一定要找产品进行及时沟通,确定结果。其次项目开发过程中,每期的用例都要不断总结,学会总结,尽可能的保证少漏。其实这个与测试思维关系密切,工作经验的积累,以及测试思维的形成,都有助于你设计一份较完整的测试用例。

5道思维面试题

1、创建坐席组的功能模块,如何进行测试用例设计?

解答:

功能测试,使用等价类划分法去分析创建坐席的每个输入项的有效及无效类,同步考虑边界值去设计对应的测试用例:

  1. 先进行冒烟测试,正常创建坐席(坐席组编号自动生成且确认6位数字唯一,名称输入 10 个汉字,类型选 A,抵押坐席组复选框全选,选择第一个现场经理-名下第一个团队长-第一个坐席),点击确认
  2. 正常创建坐席(坐席组编号自动生成且确认 6 位数字唯一,名称输入 1 个汉字,类型选 B,抵押坐席组复选框部分勾选,选择最后一个现场经理-名下最后一个团队长-最后一个坐席),点击确认
  3. 坐席编号可否支持手动输入,系统统一生成 6 位后,手动修改少一位/多一位/非数字/不唯一,为空验证,系统是否提示异常
  4. 坐席组名称输入超过 11 个汉字/11 位包含非汉字/已存在名称/为空,系统是否提示异常
  5. 坐席组类型不填,系统是否提示异常
  6. 是否有抵押坐席组不选,可正常进行创建坐席操作
  7. 现场经理-团队长-坐席,三级级联功能是否正常,关注下拉框选项的完整正确性验证;坐席为空,系统是否有合理提示

UI 测试: 关于界面排版布局颜色风格等是否正常合理,是否跟原型需求一致

2、抽奖的活动页面,抽奖按钮,抽奖成功后获得奖励扣除抽奖资格怎么写用例?

解答:

Ui: 抽奖页面(转盘/跑马等),且显示抽奖剩余次数
功能:

  1. 抽奖活动入口是否正确,例如通过首页 banner、推荐分享链接等
  2. 基本抽奖功能验证,中奖:中奖次数-1,奖品发放正常,且可进行奖品兑换
  3. 基本抽奖功能验证,未中奖:中奖次数-1
  4. 同一用户抽奖次数测试,每抽奖 1 次,次数减 1;抽奖次数为 0,不允许抽奖;退出重登/刷新页面/隔天登录都无抽奖资格
  5. 抽奖中奖概率,可通过 Jmeter 大并发调用抽奖接口,计算中奖概率是否正确
  6. 未登录/session 过期/未到抽奖时间,是否可抽奖
    兼容:
    不同浏览器、不同手机的兼容,查看抽奖页面操作是否正常
    性能:
    同一时间,大并发抽奖,服务器处理是否正常

3、给你一个微信上一个聊天的窗口你是怎么测试的?

微信聊天框的主要功能就是发消息和接收别人的消息。
消息的分类:纯文字,图片,文件,表情,语音、视频,文字 + 表情
聊天的其他功能:@符号,撤回功能,加好友功能,消息重发,发红包,转账,发送位置信息、发送名片、群聊等功能
功能测试:
正常网络下,发送纯文字,图片,文件,表情,语音、视频,文字 + 表情消息,发送及接收功能是否正常
正常网络下,测试图片,文件,语音,视频,文字的上限值测试

正常网络下,连续发消息消息统计数量功能正常

正常网络下,是否支持群发文件/群聊文件,是否支持语音转文字

正常网络下,消息发送后,在一定时间范围内是否支持撤回,超出指定时间,是否不支持撤回功能

正常网络下,发送失败后,是否支持消息重发
正常网络下,消息较多时,是否支持一键回到之前浏览位置

正常网络下,语音聊天、视频聊天相互转换功能是否正常,发送语音聊天、视频聊天时,是否有声音提示

正常网络下,发送语音聊天、视频聊天时,长时间未接听,是否有提示

正常网络下,发送名片、发送位置信息功能是否正常

正常网络下,发送红包、转账功能是否正常

正常网络下,未加好友情况下,加好友功能是否正常
正常网络下,群聊:@单个人,多个人,全部人时,对象是否会收到提醒
正常网络下,群聊:发起群视频,群语音时,所有被邀请的成员是否能加入群聊
正常网络下,群聊:群聊数量统计是否正确
网络测试: 在弱网或无网络的情况下各类功能是否正常,视频聊天、语音聊天是否有提示
中断测试: 发送消息/语音聊天/视频聊天场景中,断网重连/低电量/打电话/切换 app,处理是否正常

界面测试: 聊天消息界面,提示框等是否正常

兼容性测试: 不同手机系统,不同手机型号,分辨率屏幕尺寸下,发送消息功能是否正常
性能测试: 发送图片、文字、视频、语音等消息,对方收到的时间,是否在需求时间之内

4、举一个电商项目的模块你是怎么做的?

举例自己熟悉的,比如电商项目中的购物车,
功能:

  1. 分为已登录和未登录的场景;如果是未登录的话,添加购物车就提示登录页面先登录;已经登录的话,可以正常跳转至购物车;
  2. 进入购物车的入口验证:比如淘宝商品页点击“加入购物车”切入,或从导航栏顶部去往购物车;
  3. 购物车空页面验证:当购物车商品添加为 0 时,页面是否正常提示
  4. 购物车的商品清单验证: 一件/多件/多个商家多件商品的图片、名称、单价金额等是否正常显示,并且点击可跳转到商品详情页;
  5. 商品数量的增减和输入验证:测试 1/最大库存/限购数量的正常情况 ,测试 0/负数/超过库存/超过限购数量 /非数字的异常情况;
  6. 选择全选和部分勾选商品, 确认单件商品的小计金额、 结算里的数量和总价金额的正确性
  7. 购物车已添加的商品下架,是否正常显示失效
  8. 删除购物车商品,购物车不再显示,重新添加可正常显示
  9. 点击结算,可正常跳转到订单页面,并与优惠活动存在交互功能验证。

非功能:
界面: 页面的美观,没有错别字 和 页面排版等
兼容性: Web 端的能兼容主流浏览器,APP 端能兼容手机和系统版本
性能:多次访问,长时间访问 考虑稳定性等
安全:敏感信息的传输,漏洞扫描等

5、涉及到了库存方面的测试吗?

  1. 前台商品详情页、购物车商品存库量是否与后台管理员添加商品库存量一致
  2. 后台管理员对商品库存量进行增、改,前台商品详情页、购物车中库存显示是否正常
  3. 商品完成购买支付发货流程后,前后台库存量是否及时减少
  4. 商品完成购买并取消订单,查看前后台库存量变化情况
  5. 商品完成购买发货,并进行退货,查看前后台库存量变化情况
  6. 库存量与购买数量的约束测试
  7. 后台设置库存量报警阈值,验证低于该阈值是否有报警,及时添加存库
  8. 库存量为 0,查看前后台商品库存量是否正常显示

以上这些面试题都是非常容易被面试官问到的,建议保存,面试之前反复观看,最后祝愿所有的面试者都能够找到自己满意的工作。

以上是关于面试最常被问的 Java 后端题目及参考答案的主要内容,如果未能解决你的问题,请参考以下文章

面试中的最常被问到的两种锁

面试官最常问的10道测试用例和5道思维面试题及答案,每1题都很经典

面试技巧|Java面试常被问到的题目+解答

BAT 面试常被问的几个 Glide 问题,记录一下

Java面试常被问到的题目+解答

面试官最常问的面试题及答案,每1题都很经典