2026/6/20 8:51:23
网站建设
项目流程
二十一冶建设有限公司网站,电商商城系统免费版下载,做音乐网站之前的准备,信息发布平台推广有哪些一、Java 基础篇
1. Object 有哪些常用方法#xff1f;大致说一下每个方法的含义
java.lang.Object 下面是对应方法的含义。
clone 方法
保护方法#xff0c;实现对象的浅复制#xff0c;只有实现了 Cloneable 接口才可以调用该方法#xff0c;否则抛出 CloneNotSuppor…一、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) 设定一个超时间隔如果在规定时间内没有获得锁就返回。调用该方法后当前线程进入睡眠状态直到以下事件发生。其他线程调用了该对象的 notify 方法其他线程调用了该对象的 notifyAll 方法其他线程调用了 interrupt 中断该线程时间间隔到了。此时该线程就可以被调度了如果是被中断的话就抛出一个 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 都说清楚把线程状态也都说清楚尝试让面试官跟着你的节奏走。篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc需要全套面试笔记及答案【点击此处即可/免费获取】https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho2. Java 创建对象有几种方式这题目看似简单要好好回答起来还是有点小复杂的我们来看看到底有哪些方式可以创建对象1. 使用 new 关键字这也是我们平时使用的最多的创建对象的方式示例User usernew User();运行项目并下载源码2. 反射方式创建对象使用 newInstance()但是得处理两个异常 InstantiationException、IllegalAccessExceptionUser userUser.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 usernew User();//clazz就是一个User的类对象Class? clazzuser.getClass();运行项目并下载源码第二种通过类的静态成员表示每个类都有隐含的静态成员 class。//clazz就是一个User的类对象Class? clazzUser.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-fastfail-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。出生的版本不一样Hashtable 出生于 Java 发布的第一版本 JDK 1.0HashMap 出生于 JDK 1.2。都实现了 Map、Cloneable、Serializable当前 JDK 版本 1.8。HashMap 继承的是 AbstractMap并且 AbstractMap 也实现了 Map 接口。Hashtable 继承 Dictionary。Hashtable 中大部分 public 修饰普通方法都是 synchronized 字段修饰的是线程安全的HashMap 是非线程安全的。Hashtable 的 key 不能为 nullvalue 也不能为 null这个可以从 Hashtable 源码中的 put 方法看到判断如果 value 为 null 就直接抛出空指针异常在 put 方法中计算 key 的 hash 值之前并没有判断 key 为 null 的情况那说明这时候如果 key 为空照样会抛出空指针异常。HashMap 的 key 和 value 都可以为 null。在计算 hash 值的时候有判断如果 keynull则其 hash0至于 value 是否为 null根本没有判断过。Hashtable 直接使用对象的 hash 值。hash 值是 JDK 根据对象的地址或者字符串或者数字算出来的 int 类型的数值。然后再使用除留余数法来获得最终的位置。然而除法运算是非常耗费时间的效率很低。HashMap 为了提高计算效率将哈希表的大小固定为了 2 的幂这样在取模预算时不需要做除法只需要做位运算。位运算比除法的效率要高很多。Hashtable、HashMap 都使用了 Iterator。而由于历史原因Hashtable 还使用了 Enumeration 的方式。默认情况下初始容量不同Hashtable 的初始长度是 11之后每次扩充容量变为之前的 2n1n 为上一次的长度而 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 的异同都是 key-value 形式的存储数据HashMap 是线程不安全的ConcurrentHashMap 是 JUC 下的线程安全的HashMap 底层数据结构是数组 链表JDK 1.8 之前。JDK 1.8 之后是数组 链表 红黑树。当链表中元素个数达到 8 的时候链表的查询速度不如红黑树快链表会转为红黑树红黑树查询速度快HashMap 初始数组大小为 16默认当出现扩容的时候以 0.75 * 数组大小的方式进行扩容ConcurrentHashMap 在 JDK 1.8 之前是采用分段锁来现实的 Segment HashEntrySegment 数组大小默认是 162 的 n 次方JDK 1.8 之后采用 Node CAS Synchronized 来保证并发安全进行实现。13. 红黑树有哪几个特征紧接上个问题面试官很有可能会问红黑树下面把红黑树的几个特征列出来如果面试官还要继续问红黑树具体是怎么添加节点和删除节点的推荐看30 张图带你彻底理解红黑树篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc需要全套面试笔记及答案【点击此处即可/免费获取】https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho14. 说说你平时是怎么处理 Java 异常的try-catch-finallytry 块负责监控可能出现异常的代码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 创建一个普通对象的过程如下检测类是否被加载过为对象分配内存为分配的内存空间初始化为 0对对象进行其他相关设置执行 init 方法下面用一张图来描述19. 说说类的生命周期注意类生命周期和对象声明周期类生命周期主要有以下几个阶段20. 什么是双亲委派模型21. 如何破坏双亲委派模型篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc需要全套面试笔记及答案【点击此处即可/免费获取】https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho22. 能不能自己也写一个 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:S18:1:1。大多数对象在 Eden 区出生和死亡。Eden 区存活下来的对象进入 S0 区S0 区活下来的对象放到 S1S1 区活下来的对象放到 S0 区这过程中 S0 和 S1 至少有一个区域是空着的。并且对象每次倒腾一次自己的年龄就加 1直达加到 15 岁的时候就直接入老年代了。有的大对象可以直接进入老年代条件是把该对象的大小以及达到了能直接进入老年代的条件了阈值可以设置。方法区先按照图中的关键字回答。但是方法区由于 JDK 版本有所变动。回答的时候一定要说一下方法区由于 JDK 版本有所变动。版本变动情况如下24. 方法区和永久代有什么区别之前有小伙伴也问过我方法区和永久代到底是什么区别这么说吧永久代又叫 Perm 区只存在于 HotSpot JVM 中并且只存在于 JDK 1.7 和之前的版本中JDK 1.8 中已经彻底移除了永久代JDK 1.8 中引入了一个新的内存区域叫 metaspace。并不是所有的 JVM 中都有永久代IBM 的 9Oracle 的 JRocket 都没有永久代。永久代是实现层面的东西。永久代里面存的东西基本上就是方法区规定的那些东西。因此我们可以说在 JDK 1.7 中永久代是方法区的一种实现当然在 HotSpot JDK 1.8 中 metaspace 可以看成是方法区的一种实现。24. JVM 运行时数据区哪些地方会产生内存溢出简单回答就行Java 虚拟机栈、本地方法栈、Java 堆、方法区其实就是除了程序计数器以外的几个都会发生 OOM。建议把阈值对应的几个区也简要的说一下25. 为什么要用 metaspace 替换 permspace 呢在 JDK 1.8 之前的 HotSpot 实现中类的元数据如方法数据、方法信息字节码、栈和变量大小、运行时常量池、已确定的符号引用和虚方法表等被保存在永久代中32 位默认永久代的大小为 64M64 位默认为 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:MetaspaceSize256m -XX:MaxMetaspaceSize256m根据实际情况调整 可以使用命令 jstat -gcutil pid 查看当前使用率M 对应的列根据使用率来定制一个具体的值建议两个值设置成同样大小。JDK 1.7-XX:MaxPermSize256m -XX:MaxPermSize256m 永久带。GC 日志-Xloggc:$CATALINA_BASE/logs/gc.log-XX:PrintGCDetails-XX:PrintGCDateStamps运行项目并下载源码记录 GC 日志并不会特别地影响 Java 程序性能推荐你尽可能记录日志。GC 算法-XX:UseParNewGC-XX:CMSParallelRemarkEnabled-XX:UseConcMarkSweepGC-XX:CMSInitiatingOccupancyFraction75运行项目并下载源码一般来说推荐使用这些配置但是根据程序不同的特性其他的也有可能更好。任何一个 JVM 参数的默认值可以通过java -XX:PrintFlagsFinal -version |grep JVMParamName运行项目并下载源码获取例如java -XX:PrintFlagsFinal -version |grep MetaspaceSize运行项目并下载源码27. Java 对象的引用类型有哪些对象引用类型有四类强引用、软引用、弱引用、虚引用。28. JVM 垃圾回收算法有哪些垃圾回收算法共四种其实我更愿意说成三种因为分代回收其实不是算法。29. 垃圾收集器有哪些目前常见的有如下几种Serial 收集器ParNew 收集器Parallel scavenge 收集器Serial Old 收集器CMSConcurrent Mark Sweep 收集器Parallel Old 收集器G1Garbage-First 收集器篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc需要全套面试笔记及答案【点击此处即可/免费获取】https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho垃圾收集器整合G1 是新生代和老年代一起搞不和别人合伙。SerialCMS 或者 Serial OldParNewCMS 或者 Serial OldParallel ScavengeParallel Old 或者 Serial Old分代收集器对应新生代收集器Serial、ParNew、Parallel Scavenge老年代收集器Serial Old、Parallel Old、CMS整堆收集器G130. 说说 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 模式并行调用多个服务器只要一个成功即返回。通常用于实时性要求较高的读操作但需要浪费更多服务资源。可通过 forks2 来设置最大并行数。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-monitorSpring Cloud 使用的是 Spring Boot admin。断路器Dubbo 在断路器这方面还不完善Spring Cloud 使用的是 Spring Cloud Netflix Hystrix。分布式配置、网关服务、服务跟踪、消息总线、批量任务等。Dubbo 目前可以说还是空白而 Spring Cloud 都有相应的组件来支撑。40. 说说 TCP 与 UDP 的区别以及各自的优缺点41. 说一下 HTTP 和 HTTPS 的区别端口不同HTTP 和 HTTPS 的连接方式不同没用的端口也不一样HTTP 是 80HTTPS 用的是 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 的缓存一级缓存在应用运行过程中我们有可能在一次数据库会话中执行多次查询条件完全相同的 SQLMyBatis 提供了一级缓存的方案优化这部分场景如果是相同的 SQL 语句会优先命中一级缓存避免直接对数据库进行查询提高性能。每个 SqlSession 中持有了 Executor每个 Executor 中有一个 LocalCache。当用户发起查询时MyBatis 根据当前执行的语句生成 MappedStatement在 Local Cache 进行查询如果缓存命中的话直接返回结果给用户如果缓存没有命中的话查询数据库结果写入 Local Cache最后返回结果给用户。具体实现类的类关系图如下图所示MyBatis 一级缓存的生命周期和 SqlSession 一致。MyBatis 一级缓存内部设计简单只是一个没有容量限定的 HashMap在缓存的功能性上有所欠缺。MyBatis 的一级缓存最大范围是 SqlSession 内部有多个 SqlSession 或者分布式的环境下数据库写操作会引起脏数据建议设定缓存级别为 Statement。二级缓存在上文中提到的一级缓存中其最大的共享范围就是一个 SqlSession 内部如果多个 SqlSession 之间需要共享缓存则需要使用到二级缓存。开启二级缓存后会使用 CachingExecutor 装饰 Executor进入一级缓存的查询流程前先在 CachingExecutor 进行二级缓存的查询具体的工作流程如下所示。二级缓存开启后同一个 namespace 下的所有操作语句都影响着同一个 Cache即二级缓存被多个 SqlSession 共享是一个全局的变量。当开启缓存后数据的查询执行的流程为二级缓存 - 一级缓存 - 数据库MyBatis 的二级缓存相对于一级缓存来说实现了 SqlSession 之间缓存数据的共享同时粒度更加细能够到 namespace 级别通过 Cache 接口实现类不同的组合对 Cache 的可控性也更强。MyBatis 在多表查询时极大可能会出现脏数据有设计上的缺陷安全使用二级缓存的条件比较苛刻。在分布式环境下由于默认的 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?characterEncodingUTF-8, root, 123456);运行项目并下载源码3. 创建 Statement 或者 PreparedStatement 接口执行 SQL 语句//查询用户信息public ListUser findUserList(){String sql select * from t_user order by user_id;Connection conn null;PreparedStatement pstmt null;ResultSet rs null;//创建一个List用于存放查询到的User对象ListUser 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. 释放资源。