Java 208 道面试题答案(待完善)

一、Java 基础

  1. JDK 和 JRE 有什么区别?

    • JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
    • JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。

    具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。

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

    == 对于基本类型和引用类型作用是不同的:

    • 基本类型:比较的是值是否相同
    • 引用类型:比较的是引用是否相同

    equals 本质上就是 ==,如下:

    public boolean equals(Object obj) {
          
          
        return (this == obj);
    }
    

    但所有包装类都重写了该实现,实际比较用的是其包装的基本类型值,对于 Long 类,其 equals 方法代码是:

    public boolean equals(Object obj) {
          
          
        if (obj instanceof Long) {
          
          
            return value == ((Long)obj).longValue();
        }
        return false;
    }
    
  3. 两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?

    不对,相同对象的 hashCode() 一定相同,但不同对象哈希值也有相同的情况。

    但相反,equals() 为 true,则两个对象的 hashCode() 一定相同,是对的。

  4. final 在 java 中有什么作用?

    • final 在修饰变量的时候表示常量,即变量赋值后就不能再修改了。
    • final 修饰方法,则这个方法不能被重写
    • final 修饰类,则这个类不能被继承
  5. java 中的 Math.round(-1.5) 等于多少?

    round:四舍五入。简单来说,就是返回最接近它的整数,若有两个返回接近的整数,则取最大的那个。

    答案是:-1

  6. String 属于基础的数据类型吗?

    不属于,String 属于对象类型。

    对 Java 语言而言,有如下基本数据类型:

    • 整数类型:有四种整型 byte/short/int/long
    • 小数类型 :有两种类型 float/double
    • 字符类型:char,表示单个字符
    • 布尔类型:boolean,表示真假
  7. java 中操作字符串都有哪些类?它们之间有什么区别?

    操作字符串的类有:String、StringBuffer、StringBuilder。

    • String 和 StringBuffer、StringBuilder 的区别在于:

      • String 类内部用一个字符数组表示字符串,实例变量定义为:

        private final char value[];
        

        所以 String 类也是不可变类,即对象一旦创建,就没有办法修改了。String 类中提供了很多看似修改的方法,其实是通过创建新的 String 对象来实现的,原来的 String 对象不会被修改。

      • 与 String 类似,StringBuilder 类也封装了一个字符数组,定义如下:

        char[] value;
        

        与 String 不同,它不是 final 的,可以修改。

      总结:如果字符串修改操作比较频繁,应该采用 StringBuilder 和 StringBuffer 类

    • StringBuilder 和 StringBuffer 的区别在于:StringBuffer 是线程安全的,而 StringBuilder 不是。线程安全是有成本的,影响性能,而字符串对象及操作,大部分情况下,没有线程安全的问题,适合使用 StringBuilder。

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

    不一样,因为内存的分配方式不一样。

    • String str=“i” 的方式,java 虚拟机会将其分配到字符串常量池中。字符串常量池,它保存所有的常量字符串,每个常量只会保存一份,被所有使用者共享。

      String name1 = "i";
      String name2 = "i";
      System.out.println(name1==name2); // true
      
    • String str=new String(“i”) 则会被分到堆内存中,即每次 new 生成一个对象。

      String name1 = new String("老马说编程");
      String name2 = new String("老马说编程");
      System.out.println(name1==name2); // false
      
  9. 如何将字符串反转?

    可以使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

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

    public boolean isEmpty() // 判断字符串是否为空
    public int length() // 获取字符串长度
    public String substring(int beginIndex) // 取子字符串
    public String substring(int beginIndex, int endIndex) // 取子字符串
    public int indexOf(int ch) // 在字符串中查找字符,返回第一个找到的索引位置,没找到返回-1
    public int indexOf(String str) // 在字符串中查找子字符串,返回第一个找到的索引位置,没找到返回-1
    public int lastIndexOf(int ch) // 从后面查找字符,返回从后面数的第一个索引位置,没找到返回-1
    public int lastIndexOf(String str) // 从后面查找子字符串,返回从后面数的第一个索引位置,没找到返回-1
    public boolean contains(CharSequence s)  // 判断字符串中是否包含指定的字符序列
    public boolean startsWith(String prefix) // 判断字符串是否以给定子字符串开头
    public boolean endsWith(String suffix) // 判断字符串是否以给定子字符串结尾
    public boolean equals(Object anObject) // 与其他字符串比较,看内容是否相同
    public boolean equalsIgnoreCase(String anotherString) // 忽略大小写,与其他字符串进行比较,看内容是否相同
    public int compareTo(String anotherString) // 比较字符串大小
    public int compareToIgnoreCase(String str) // 忽略大小写比较
    public String toUpperCase() // 所有字符转换为大写字符,返回新字符串,原字符串不变
    public String toLowerCase() // 所有字符转换为小写字符,返回新字符串,原字符串不变
    public String concat(String str) // 字符串连接,返回当前字符串和参数字符串合并后的字符串,原字符串不变
    public String replace(char oldChar, char newChar) // 字符串替换,替换单个字符,返回新字符串,原字符串不变
    public String replace(CharSequence target, CharSequence replacement) // 字符串替换,替换字符序列,返回新字符串,原字符串不变
    public String trim() // 删掉开头和结尾的空格,返回新字符串,原字符串不变
    public String[] split(String regex) // 分隔字符串,返回分隔后的子字符串数组,原字符串不变
    
  11. 抽象类必须要有抽象方法吗?

    不需要,抽象类不一定非要有抽象方法。

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

    • 普通类不能包含抽象方法,抽象类可以包含抽象方法。
    • 抽象类不能直接实例化,普通类可以直接实例化。
  13. 抽象类能使用 final 修饰吗?

    不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。

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

    • 实现:使用 extends 关键字来继承抽象类,使用关键字 implements 来实现接口
    • 实现数量:一个类可以实现多个接口,但只能继承一个抽象类
    • 变量类型:接口中变量的修饰符只能是 public static final,而抽象类没有限制
    • 构造函数:接口不能有构造函数,而抽象类可以有
    • 方法修饰符:抽象方法可以有 public、protected 和 default 这些修饰符,接口方法只能是 public
    • 方法实现:抽象类可以提供成员方法的实现细节,而接口中只能存在 public abstract 方法
  15. java 中 IO 流分为几种?

    • 按功能来分:输入流、输出流。

    • 按类型来分:字节流和字符流。

      字节流和字符流的区别:

      • 字节流:按字节(8位)为单位来处理数据,主要用在处理二进制数据
      • 字符流:按字符(16位)为单位来处理数据,主要用在处理文本数据

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

  1. Files 的常用方法都有哪些?
    • Files.createFile():创建文件。
    • Files.createDirectory():创建文件夹。
    • Files.exists():检测文件路径是否存在。
    • Files.copy():复制文件。
    • Files.move():移动文件。
    • Files.delete():删除一个文件或目录。
    • Files.size():查看文件个数。
    • Files.read():读取文件。
    • Files.write():写入文件。

二、容器

  1. java 容器都有哪些?

    在这里插入图片描述

  2. Collection 和 Collections 有什么区别?

    • java.util.Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法。
    • Collections 则是集合类的一个工具类,其中提供了一系列对集合进行操作的静态方法。
  3. List、Set、Map 之间的区别是什么?

    • List 接口和 Set 接口属于 Collection 接口,Map 接口和 Collection 接口并列存在
    • List 表示元素有顺序,且元素可以重复的容器接口
    • Set 表示元素不保证顺序,且没有重复元素的容器接口
    • Map 表示以键值对存储元素,一个键映射一个值,且键不能重复的容器接口
  4. HashMap 和 Hashtable 有什么区别?

    • HashMap 不是线程安全的,Hashtable 是线程安全的
    • 在 HashMap 中,键和值都可以为 null,而在 Hashtable 中不可以。
    • HashTable 是继承自 Dictionary 类,而 HashMap 是继承自 AbstractMap 类。
  5. 如何决定使用 HashMap 还是 TreeMap?

    不要求排序,优先考虑 HashMap,要求排序,考虑 TreeMap。

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

    HashMap 的内部有一个数组 table,每个元素 table[i] 指向一个单向链表,根据键存取值,用键算出 hash,取模得到数组中的索引位置 buketIndex,然后操作 table[buketIndex] 指向的单向链表。

    存取的时候依据键的 hash 值,只在对应的链表中操作,不会访问别的链表,在对应链表操作时也是先比较 hash 值,相同的话才用 equals 方法比较。

    需要说明的是,Java8 对 HashMap 的实现进行了优化,在哈希冲突比较严重的情况下,即大量元素映射到同一个链表的情况下(具体是至少 8 个元素,且总的键值对个数至少是 64),Java8 会将该链表转换为一个平衡的排序二叉树,以提高查询的效率。

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

    HashSet 内部是用 HashMap 实现的,它内部有一个 HashMap 实例变量,如下所示:

    private transient HashMap<E,Object> map;
    

    我们知道,Map 有键和值,HashSet 相当于只有键,值都是相同的固定值,这个值的定义为:

    private static final Object PRESENT = new Object();
    
  8. ArrayList 和 LinkedList 的区别是什么?

    • ArrrayList 底层的数据结构是数组,支持随机访问,而 LinkedList 的底层数据结构是双向循环链表,不支持随机访问。
    • 按照索引访问元素,ArrayList 的时间复杂度是 O(1),而 LinkedList 是 O(n)。
    • 插入和删除元素,ArrayList 的时间复杂度是 O(n),而 LinkedList 是 O(1)。
  9. 如何实现数组和 List 之间的转换?

    • List 转换成为数组:调用 ArrayList 的 toArray 方法。
    • 数组转换成为 List:调用 Arrays 的 asList 方法。
  10. ArrayList 和 Vector 的区别是什么?

    • ArrayList 不是线程安全的,Vector 比 ArrayList 多了个同步化机制,所以 Vector 线程安全的。
    • 由于线程的同步必然要影响性能,因此 ArrayList 的性能比 Vector 更好。
  11. Array 和 ArrayList 有何区别?

    • Array 可以包含基本类型和对象类型,ArrayList 只能包含对象类型。
    • Array 大小是固定的,ArrayList 的大小是动态变化的。
    • ArrayList 提供了更多的方法和特性,比如:addAll(),removeAll(),iterator() 等等。
  12. 在 Queue 中 poll() 和 remove() 有什么区别?

    poll() 和 remove() 都是从队列中取出一个元素,但在队列为空时,remove 会抛出异常 NoSuchElementException,而 poll 返回特殊值 null。

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

    • Vector:比 ArrayList 多了个同步化机制,因为效率较低,现在已经不太建议使用。
    • Stack:继承于 Vector,因为效率较低,现在已经不太建议使用。
    • Hashtable:比 HashMap 多了个同步化机制。
    • ConcurrentHashMap:在高并发的场景中,推荐使用。
  14. 迭代器 Iterator 是什么?

    • 迭代器是一种设计模式,用于顺序访问集合对象的元素,无需知道集合对象的底层实现。
    • 在 Java 中 Iterator 是遍历集合的接口,它提供了遍历集合时常用的接口方法。
  15. Iterator 怎么使用?有什么特点?

    • hasNext() 判断是否还有元素未访问
    • next() 返回下一个元素
    • remove() 删除最后返回的元素(需要注意调用 remove 方法前必须先调用 next 方法)

    特点:Iterator 只能单向遍历集合

  16. Iterator 和 ListIterator 有什么区别?

    • Iterator 可用来遍历 Set 和 List 集合,但是 ListIterator 只能用来遍历 List。
    • Iterator 对集合只能是后向遍历,ListIterator 既可以前向也可以后向遍历。
    • ListIterator 扩展了 Iterator 接口,增加了一些方法,向前遍历、添加元素、修改元素、返回索引位置等
  17. 怎么确保一个集合不能被修改?

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

三、多线程

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

    • 并发:指两个或多个事件在同一时间间隔内发生。这些事件宏观上是同时发生的,但微观上是交替发生的。
    • 并行:指两个或多个事件在同一时刻同时发生。

    例如:

    • 单核 CPU 同一时刻只能执行一个程序,多个程序只能并发地执行
    • 多核 CPU 同一时刻可以同时执行多个程序,多个程序可以并行地执行
  2. 线程和进程的区别?

    • 进程是受操作系统管理的基本运行单元

    • 线程是在进程中独立运行的子任务,同一进程中的多个线程之间可以并发执行

      比如:QQ.exe 这个进程运行时就有很多子任务(好友视频,下载文件,发送表情)在同时运行,其中每一项任务都可以理解成是线程在工作

  3. 守护线程是什么?

    守护线程是一种特殊的线程,它的特性有陪伴的含义,当进程中不存在非守护进程了,则守护线程自动销毁。典型的守护进程就是垃圾回收线程,当进程中没有非守护进程了,则垃圾回收线程也没有存在的必要了,自动销毁。

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

    • 继承 Thread 类

      public class MyThread extends Thread {
              
              
      }
      
    • 实现 Runnable 接口

      public class MyThread implements Runnable{
              
              
          @Override
          public void run() {
              
              
          }
      }
      
    • 实现 Callable 接口

      public class MyThread implements Callable<Integer> {
              
              
          @Override
          public Integer call() throws Exception {
              
              
              int sum = 0;
              for (int i = 0; i < 10; i++) {
              
              
                  sum += i;
              }
              return sum;
          }
      
          public static void main(String[] args) throws ExecutionException, InterruptedException {
              
              
              FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
              Thread thread = new Thread(futureTask);
              thread.start();
              System.out.println(futureTask.get());
          }
      }
      
  5. 说一下 runnable 和 callable 有什么区别?

    • 需要实现的方法名称不同:runnable 是 run 方法,callable 是 call 方法

    • 返回值不同:run 方法无返回值,callable 的返回值类型和泛型是一致的。

    • 异常:run 方法无需抛出异常,callable 需要抛出异常。

  6. 线程有哪些状态?

    在这里插入图片描述

    线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。

    • 创建状态。在生成线程对象,并没有调用该对象的 start() 方法,这时线程处于创建状态。
    • 就绪状态。当调用了线程对象的 start() 方法之后,该线程就进入了就绪状态,但是此时线程规划器还没有把该线程设置为当前线程,此时处于就绪状态。
    • 运行状态。线程规划器将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行 run() 方法当中的代码。
    • 阻塞状态。在运行状态中,指线程因为某种原因放弃了 CPU 使用权,此时处于阻塞状态。
    • 死亡状态。如果一个线程的 run() 方法执行结束或者调用 stop() 方法后,该线程就会死亡。
  7. sleep() 和 wait() 有什么区别?

    • wait() 释放 CPU 资源,同时也释放了对象锁,并进入阻塞队列中;sleep() 释放 CPU 资源,但不释放对象锁,到时间会自动恢复
    • wait() 只能在同步方法或者同步代码块里面使用,而 sleep() 可以在任何地方使用
    • 这两个方法来自不同的类分别是 Thread 和 Object,sleep() 方法属于 Thread 类中的静态方法,wait 属于 Object 的成员方法。
  8. notify() 和 notifyAll() 有什么区别?

    • notify() 方法可以随机使阻塞队列中等待同一共享资源的 “一个” 线程退出,并进入就绪队列。
    • notifyAll() 方法可以使阻塞队列中等待同一共享资源的 “全部” 线程退出,并进入就绪队列。此时,优先级最高的那个线程最先执行,但也有可能是随机执行,因为这要取决与 JVM 虚拟机的实现。
  9. 线程的 run() 和 start() 有什么区别?

    • Thread 类中的 start() 方法的作用是通知 “线程规划器” 此线程已经准备就绪,等待调用线程对象的 run() 方法,具有异步效果。如果调用 thread.run() 就是同步了,那么此线程对象并不交给 “线程规划器” 来进行处理,而是由 main 主线程来调用 run() 方法,也就是说要等到 run() 方法执行完才可以执行后面的代码,也就无法达到启动多线程的目的。
    • 一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。
  10. 创建线程池有哪几种方式?

    • newFixedThreadPool : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂时存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
    • newSingleThreadExecutor : 该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
    • newCachedThreadPool : 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
    • newSingleThreadScheduledExecutor:该方法返回一个 ScheduledExecutorService 对象,线程池大小为 1。ScheduledExecutorService 接口在 ExecutorService 接口之上扩展了在给定时间执行某任务的功能,如在某个固定的延时之后执行,或者周期性执行某个任务。
    • newScheduledThreadPool:该方法也返回一个 ScheduledExecutorService 对象,但该线程池可以指定线程数量。
  11. 线程池都有哪些状态?

    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 状态。

    在这里插入图片描述

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

    • execute() 只能执行 Runnable 类型的任务,submit() 可以执行 Runnable 和 Callable 类型的任务。
    • execute() 没有返回值,而 submit() 有返回值。
    • submit() 的返回值 Future 调用get方法时,可以捕获处理异常
  13. 在 java 程序中怎么保证多线程的运行安全?

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

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

  1. 什么是死锁?

    死锁是指由于两个或者多个线程互相持有对方所需要的锁,而产生的阻塞现象。

  2. 怎么防止死锁?

    • 加锁顺序(线程按照一定的顺序加锁)
    • 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    • 死锁检测
  3. ThreadLocal 是什么?有哪些使用场景?

    ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。通过这种方式,避免资源在多线程间共享。

    经典的使用场景比如为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作。

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

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

    Java 中每一个对象都可以作为锁,这是 synchronized 实现同步的基础:

    • 普通同步方法,锁是当前实例对象
    • 静态同步方法,锁是当前类的 class 对象
    • 同步方法块,锁是括号里面的对象
  5. synchronized 和 volatile 的区别是什么?

    • volatile 解决的是变量在多个线程之间的可见性;而 synchronized 解决的是多个线程之间访问资源的同步性。
    • volatile 只能修饰于变量,而 synchronized 可以修饰方法以及代码块。
    • 多线程访问 volatile 不会发生阻塞,而 synchronized 会出现阻塞。
    • volatile 能保证数据的可见性,但不能保证原子性;而 synchronized 可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
  6. synchronized 和 Lock 有什么区别?

    • 存在层次:synchronized 是 Java 的一个关键字,而 Lock 是一个接口
    • 锁的状态:synchronized 无法判断锁的状态,Lock 可以通过 tryLock 方法判断锁的状态
    • 异常是否释放锁:synchronized 在发生异常时候会自动释放占有的锁,因此不会出现死锁;而 lock 发生异常时候,不会主动释放占有的锁,必须手动 unlock 来释放锁,可能引起死锁的发生。(所以最好将同步代码块用 try catch 包起来,finally 中写入 unlock,避免死锁的发生。)
    • 用 synchronized 关键字的两个线程 1 和线程 2,如果当前线程 1 获得锁,线程 2 线程等待。如果线程 1 阻塞,线程 2 则会一直等待下去;而 Lock 锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了。
    • Lock 锁适合大量同步的代码的同步问题,synchronized 锁适合代码少量的同步问题。

55.synchronized 和 ReentrantLock 区别是什么?

56.说一下 atomic 的原理?

四、反射

  1. 什么是反射?

    反射(reflection)机制是指在程序的运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。这种动态获取程序信息以及动态调用对象的功能称为 Java 语言的反射机制。

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

    序列化就是将内存中的 Java 对象持久保存到一个流中,反序列化就是从流中恢复 Java 对象到内存。

    序列化/反序列化主要有两个用处,一个是将对象持久化到文件中,另一个是网络远程调用,用于传递和返回对象。

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

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

    概念:动态代理就是用代理对象代理真实对象,达到增强真实对象功能的目的。

    特点:字节码随用随创建,随用随加载。

    作用:在不修改源码的基础上,对方法进行增强。

    应用:

    • Spring AOP
    • 添加事务
    • 添加日志
  4. 怎么实现动态代理?

    动态代理的分类:

    • 基于接口的动态代理
      • 提供者:JDK 官方
      • 要求:被代理类最少实现一个接口
    • 基于子类的动态代理
      • 提供者:第三方的 CGLib
      • 要求:被代理类是不能用 final 修饰的类

    基于接口的动态代理:

    1. 创建代理对象

      使用 Proxy 类中的 newProxyInstance 方法

      Producer producerProxy = (Producer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() {
              
              
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              
              
              return null;
          }
      });
      
    2. newProxyInstance 方法的参数

      • ClassLoader:类加载器
        它是用于加载代理对象字节码的,和被代理对象使用相同的类加载器。
      • Class[] :字节码数组
        它是用于让代理对象和被代理对象有相同方法
      • InvocationHandler
        他是用于提供增强的代码。一般都是写一个该接口的实现类 InvocationHandler,通常情况下都是匿名内部类,但不是必须的。
    3. invoke 方法的作用

      执行被代理对象的任何接口方法都会经过该方法

    4. invoke 方法的参数

      • proxy:代理对象的引用
      • method:当前执行的方法
      • args:当前执行方法所需的参数
    5. invoke 方法的返回值

      和被代理对象方法有相同的返回值

    6. 基于接口的动态代理的完整实现

      // 创建真实对象
      final Producer producer = new ProducerImpl();
      
      // 创建代理对象
      Producer producerProxy = (Producer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() {
              
              
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              
              
              Object returnValue = null;
              if("saleProduct".equals(method.getName())){
              
              
                  double money = (Double) args[0];
                  returnValue = method.invoke(producer, money * 0.8);
              }
              return returnValue;
          }
      });
      
      // 通过代理对象执行方法
      producerProxy.saleProduct(1000);
      

    基于子类的动态代理:

    1. 导入 CGLib 的 jar 包

      <dependency>
          <groupId>cglib</groupId>
          <artifactId>cglib</artifactId>
          <version>2.1_3</version>
      </dependency>
      
    2. 创建代理对象

      使用 Enhancer 类中的 create 方法

      Enhancer.create(producer.getClass(), new MethodInterceptor() {
              
              
          public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
              
              
              return null;
          }
      });
      
    3. create 方法的参数

      • Class:字节码

        它是用于指定被代理对象的字节码

      • Callback

        他是用于提供增强的代码。一般都是写一个该接口的实现类 MethodInterceptor,通常情况下都是匿名内部类,但不是必须的。

    4. intercept 的作用

      执行被代理对象的任何接口方法都会经过该方法

    5. intercept 的参数

      • o:代理对象的引用
      • method:当前执行的方法
      • objects:当前执行方法所需的参数
      • methodProxy:当前执行方法的代理对象
    6. intercept 的返回值

      和被代理对象方法有相同的返回值

    7. 基于子类的动态代理的完整实现

      // 创建真实对象
      final ProducerImpl producer = new ProducerImpl();
      
      // 创建代理对象
      ProducerImpl producerProxy = (ProducerImpl) Enhancer.create(producer.getClass(), new MethodInterceptor() {
              
              
          public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
              
              
              Object returnValue = null;
              if("saleProduct".equals(method.getName())){
              
              
                  double money = (Double) objects[0];
                  returnValue = method.invoke(producer,money*0.8);
              }
              return returnValue;
          }
      });
      
      // 通过代理对象执行方法
      producerProxy.saleProduct(1000);
      

五、对象拷贝

61.为什么要使用克隆?

62.如何实现对象克隆?

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

六、Java Web

  1. JSP 和 Servlet 有什么区别?

    JSP 经编译后就变成了 Servlet,JSP 的本质就是Servlet。JSP 是为了简化 Servlet 的工作出现的替代品,Servlet 输出 HTML 非常困难,JSP 就是替代 Servlet 输出 HTML 的。

    二者区别:

    • JSP 更擅长表现于页面显示,Servlet 更擅长于逻辑控制。
    • Servlet 中没有内置对象,JSP 中有内置对象。
  2. JSP 有哪些内置对象?作用分别是什么?

    1. pageContext:可以获取其他八个内置对象
    2. page:当前页面的对象
    3. request:请求对象
    4. response:响应对象
    5. session:一次会话的对象
    6. application:整个服务器的对象
    7. out:输出对象,将数据输出到页面上
    8. config:Servlet 的配置对象
    9. exception:异常的对象
  3. 说一下 JSP 的 4 种作用域?

    1. page:只在一个页面中保存属性,跳转页面无效
    2. requet:只在一次请求中保存属性,服务器跳转有效,浏览器跳转无效
    3. session:在一个会话范围中保存属性,无论何种跳转均有效,关闭浏览器后无效
    4. application:在整个服务器中保存属性,所有用户都可以使用
  4. Session 和 Cookie 有什么区别?

    • Cookie 保存在客户端,Session 保存在服务端
    • Cookie 相对不安全,Session 比较安全
    • Cookie 存在大小限制,Session 不存在大小限制
  5. 说一下 Session 的工作原理?

    服务器第一次获取 Session 时,如果没有 Cookie,会在内存中创建一个新的 Session 对象,并在响应头中携带 set-cookie: JSESSIONID=xxx,JSESSIONID 就对应着 Session 的 ID; 浏览器下一次请求时,就会携带 cookie: JSESSIONID=xxx,服务器就会根据 JSESSIONID 找到对应的 Session 对象。

    在这里插入图片描述

  6. 如果客户端禁止 Cookie 能实现 Session 还能用吗?

    不行,Session 是依赖于 Cookie 的。服务器需要通过 JSESSIONID 来找到当前会话对应的 Session 对象,而 JSESSIONID 是通过 Cookie 来传递的,禁用 Cookie 就相当于失去了 JSESSIONID,也就找不到当前会话对应的 Session 对象。

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

  1. 如何避免 SQL 注入?

    SQL 注入:在拼接 SQL 时,有 SQL 关键字参与了字符串的拼接。

    解决 SQL 注入方法:使用 PreparedStatement 对象。

    PreparedStatement 原理:SQL 的参数使用 ? 作为占位符,PreparedStatement 会先对 SQL 进行预编译,然后调用 setXX() 方法设置 SQL 语句中的参数。这样再传入特殊值,也不会出现 SQL 注入的问题了。

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

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

七、异常

  1. throw 和 throws 的区别?

    • throw 用于抛出的一个具体的异常类型
    • throws 用于声明一个方法可能抛出的异常。这个声明的含义是说,我这个方法内可能抛出这些异常,我没有进行处理,至少没有处理完,调用者必须进行处理。
  2. final、finally、finalize 有什么区别?

    • final 可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
    • finally 一般作用在 try-catch 代码块中,在处理异常的时候,通常我们将一定要执行的代码方法 finally 代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
    • finalize 是一个方法,属于 Object 类的一个方法,而 Object 类是所有类的父类,该方法一般由垃圾回收器来调用,回收垃圾。
  3. try-catch-finally 中哪个部分可以省略?

    catch 可以省略。

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

    finally 语句有一个执行细节,如果在 try 或者 catch 语句内有 return 语句,则 return 语句在 finally 语句执行结束后才执行,但 finally 并不能改变返回值,我们来看下代码:

    public static int test(){
          
          
        int ret = 0;
        try{
          
          
            return ret;
        }finally{
          
          
            ret = 2;
        }
    }
    

    这个函数的返回值是 0,而不是 2,实际执行过程是,在执行到 try 内的 return ret; 语句前,会先将返回值 ret 保存在一个临时变量中,然后才执行 finally 语句,最后 try 再返回那个临时变量,finally 中对 ret 的修改不会被返回。

    如果在 finally 中也有 return 语句呢?try 和 catch 内的 return 会丢失,实际会返回 finally 中的返回值。finally 中有 return 不仅会覆盖 try 和 catch 内的返回值,还会掩盖 try 和 catch 内的异常,就像异常没有发生一样,比如说:

    public static int test(){
          
          
        int ret = 0;
        try{
          
          
            int a = 5/0;
            return ret;
        }finally{
          
          
            return 2;
        }
    }
    

    以上代码中,5/0 会触发 ArithmeticException,但是 finally 中有 return 语句,这个方法就会返回 2,而不再向上传递异常了。

    finally 中不仅 return 语句会掩盖异常,如果 finally 中抛出了异常,则原异常就会被掩盖,看下面代码:

    public static void test(){
          
          
        try{
          
          
            int a = 5/0;
        }finally{
          
          
            throw new RuntimeException("hello");
        }
    }
    

    finally 中抛出了 RuntimeException,则原异常 ArithmeticException 就丢失了。

    所以,一般而言,为避免混淆,应该避免在 finally 中使用 return 语句或者抛出异常,如果调用的其他代码可能抛出异常,则应该捕获异常并进行处理。

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

    在这里插入图片描述

    在这里插入图片描述

八、网络

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

    301,302 都是 HTTP 状态的编码,都代表着某个 URL 发生了转移。

    区别:

    • 301 redirect: 301 代表永久性转移(Permanently Moved)。
    • 302 redirect: 302 代表暂时性转移(Temporarily Moved)。
  2. forward 和 redirect 的区别?

    • forward 地址栏不发生改变,redirect 地址栏发生改变
    • forward 只能转发到当前服务器内部资源中,redirect 重定向可以访问其他站点的资源
    • forward 是一次请求,redirect 是两次请求(不能使用 request 对象来共享数据)
  3. 简述 TCP 和 UDP 的区别?

    • TCP 面向连接,UDP 是无连接的,即发送数据之前不需要建立连接。
    • TCP 提供可靠的服务,UDP 尽最大努力交付,即不保证可靠交付。
    • TCP 面向字节流,UDP 是面向报文的。
    • TCP 数据传输慢,UDP 数据传输快。
  4. TCP 为什么要三次握手,两次不行吗?为什么?

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

      Step1 A -> B : 你好,B。

      Step2 A <- B : 收到。你好,A。

      这样的两次握手过程, A 向 B 打招呼得到了回应,即 A 向 B 发送数据,B 是可以收到的。

      但是 B 向 A 打招呼,A 还没有回应,B 没有收到 A 的反馈,无法确保 A 可以收到 B 发送的数据。

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

      Step3 A -> B : 收到,B。

      这样 B 才能确定 A 也可以收到 B 发送给 A 的数据。

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

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

    在这里插入图片描述

  2. GET 和 POST 请求有哪些区别?

    1. GET 请求参数在请求行中,POST 请求参数在请求体中
    2. GET 请求的 url 长度有限制的,POST 请求的 url 长度没有限制的
    3. GET 不太安全,POST 相对安全
  3. 如何实现跨域?

    跨域问题是浏览器对于 ajax 请求的一种安全限制:一个页面发起的 ajax 请求,只能是与当前页域名相同的路径,这能有效的阻止跨站攻击。

    但是这却给我们的开发带来了不便,而且在实际生产环境中,肯定会有很多台服务器之间交互,地址和端口都可能不同,下面就来解决跨域问题。

    目前比较常用的跨域解决方案有三种:

    • Jsonp

      最早的解决方案,利用 script 标签可以跨域的原理实现。

      缺点:

      • 需要服务的支持
      • 只能发起 GET 请求
    • Nginx 反向代理

      利用 Nginx 把跨域反向代理为不跨域,支持各种请求方式

      缺点:

      • 需要在 Nginx 进行额外配置,语义不清晰
    • CORS

      CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)。

      它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 Ajax 只能同源使用的限制。

      CORS 需要浏览器和服务器同时支持

      • 浏览器端

        目前,所有浏览器都支持该功能(IE10 以下不行)。整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。

      • 服务端

        CORS 通信与 AJAX 没有任何差别,因此你不需要改变以前的业务逻辑。但浏览器会在请求中携带一些头信息,我们需要以此判断是否允许其跨域,然后在响应头中加入一些信息即可。这一般通过过滤器完成即可。

      优点:

      • 在服务端进行控制是否允许跨域,可自定义规则
      • 支持各种请求方式

      缺点:

      • 会产生额外的请求
  4. 说一下 JSONP 实现原理?

    Jsonp 即 json+padding,动态创建 script 标签,利用 script 标签的 src 属性可以获取任何域下的 js 脚本,通过这个特性(也可以说漏洞),服务器端不再返回 json 格式,而是返回一段调用某个函数的 js 代码,在 src 中进行了调用,这样实现了跨域。

九、设计模式

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

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

十、Spring/Spring MVC

  1. 为什么要使用 Spring?

    • 方便解耦,简化开发

      通过 Spring 提供的 IOC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

    • AOP 编程的支持

      通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。

    • 声明式事务的支持

      可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理, 提高开发效率和质量。

    • 方便程序的测试

      可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

    • 方便集成各种优秀框架

      Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz 等)的直接支持。

    • 降低 JavaEE API 的使用难度

      Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。

  2. 解释一下什么是 AOP?

    AOP(Aspect Oriented Programming),面向切面编程。意思为通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

    它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改源码的基础上,对我们的已有方法进行增强。

  3. 解释一下什么是 IOC?

    原来是主动创建对象,现在我把创建对象的权力交给工厂,由工厂创建对象,此时控制权就发生了转移,这种思想也就是控制反转(IOC)。

    IOC 的作用:解耦,降低了程序间的依赖关系。做到了编译期不依赖,运行期才依赖。

  4. Spring 有哪些主要模块?

    Spring 框架有七大模块:

    • Spring Core:提供 IOC 容器,对 bean 进行管理。
    • Spring Context:提供上下文信息。
    • Spring DAO:提供了 JDBC 的抽象层。
    • Spring ORM:Spring 与所有的主要的 ORM 框架都集成的很好,其中包括 Hibernate、MyBatis 等。
    • Spring AOP:面向切面编程。
    • Spring Web:提供了基础的 Web 开发的上下文信息,Web 层框架可以是 Spring 自己的 MVC 框架,或者其他的 MVC 框架。
    • Spring MVC:提供了 Web 应用的 Model-View-Controller 全功能实现。
  5. Spring 常用的注入方式有哪些?

    常用的注入方式有三种:

    • 通过构造函数注入
    • 通过 setter 方法注入
    • 通过注解 @Autowired 方式注入
  6. Spring 中的 bean 是线程安全的吗?

    Spring 不保证 bean 的线程安全。

    Spring 管理的 bean 的线程安全跟 bean 的作用域有关,如果是单例的,则存在线程安全问题,如果是多例的,则不存在线程安全。

  7. Spring 支持几种 bean 的作用域?

    Spring 一共支持 5 种作用域:

    • singleton:单例的(默认值),一个应用只有一个对象的实例,它的作用范围就是整个引用
    • prototype:多例的,每次访问对象时,都会重新创建对象实例
    • request:WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中
    • session:WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中
    • global session:WEB 项目中,作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是 session
  8. Spring 自动装配 bean 有哪些方式?

    Spring 配置文件中 <bean> 标签的 autowire 参数可以控制 bean 自动装配的方式

    • default:默认的方式和 “no” 方式一样
    • no:不自动装配,应使用显式 bean 引用进行装配
    • byName:根据名称进行装配
    • byType:根据类型进行装配
    • constructor:根据构造函数进行装配
  9. Spring 事务实现方式有哪些?

    • 声明式事务:
      • 基于 XML 配置文件的方式:
        • 在 XML 文件中配置事务管理器 DataSourceTransactionManager
        • 然后配置事务的通知
        • 配置 AOP ,配置通用切入点表达式
        • 建立事务通知和切入点表达式的对应关系
        • 配置事务属性
      • 基于注解方式:
        • 配置事务管理器
        • 开启 Spring 对注解事务的支持
        • 在需要事务支持的地方使用 @Transactional 注解
    • 编程式事务:使用 TransactionTemplate 模板,把需要事务控制的代码放在 TransactionTemplate.execute 方法中,这个方法已经封装了事务控制。但这样我们业务层的代码会越来越多,因为每一个需要事务控制的地方,都要使用这个方法,它不像声明式事务控制是基于 AOP 来实现的那样简洁。
  10. 说一下 Spring 的事务隔离?

    Spring 的一共有五个事务隔离级别:

    • 默认级别(和数据库一致):ISOLATION_DEFAULT
    • 读未提交:SOLATION_READ_UNCOMMITTED
    • 读已提交:ISOLATION_READ_COMMITTED
    • 可重复读(MySQL 的默认级别):ISOLATION_REPEATABLE_READ
    • 可串行化:ISOLATION_SERIALIZABLE

    存在问题:

    1. 脏读:一个事务,读取到另一个事务中没有提交的数据。
    2. 不可重复读:在同一个事务中,两次读取到的数据内容不一样。
    3. 幻读:一个事务查询所有数据,另一个事务添加了几条数据,则第一个事务再次查询时数据总量不同。

    隔离级别:

    隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁读
    READ UNCOMMITED(读未提交) YES YES YES NO
    READ COMMITED(读已提交) NO YES YES NO
    REPEATABLE READ(可重复读) NO NO YES NO
    SERIALIZABLE(可串行化) NO NO NO YES

    注意:隔离级别从小到大安全性越来越高,但是效率越来越低。

  11. 说一下 SpringMVC 运行流程?

    在这里插入图片描述

    1. 用户发送请求至前端控制器 DispatcherServlet
    2. DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
    3. 处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
    4. DispatcherServlet 调用 HandlerAdapter 处理器适配器
    5. HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)。
    6. Controller 执行完成返回 ModelAndView
    7. HandlerAdapter 将 Controller 执行结果 ModelAndView 返回给 DispatcherServlet
    8. DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器
    9. ViewReslover 解析后返回具体 View
    10. DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
    11. DispatcherServlet 响应用户
  12. SpringMVC 有哪些组件?

    • DispatcherServlet:前端控制器

      用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。

    • HandlerMapping:处理器映射器

      HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

    • HandlAdapter:处理器适配器

      通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

    • Handler(Controller):处理器

      它就是我们开发中要编写的具体业务控制器,由 Handler 对具体的用户请求进行处理。

    • View Resolver:视图解析器

      View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

    • View:视图

      SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView 等。我们最常用的视图就是 jsp。

  13. @RequestMapping 的作用是什么?

    • 作用:用于建立请求 URL 和处理请求方法之间的对应关系
    • 作用位置:
      • 作用在类上:第一级的访问目录
      • 作用在方法上:第二级的访问目录
  14. @Autowired 的作用是什么?

    作用:自动按照类型注入。只要容器中有唯一的一个 bean 对象类型和要注入的变量类型匹配,就可以注入成功

    • 如果 IOC 容器中没有任何 bean 的类型和要注入的变量类型匹配,则报错。
    • 如果 IOC 容器中有多个类型匹配时,按变量名称匹配

十一、Spring Boot/Spring Cloud

  1. 什么是 SpringBoot?

    SpringBoot 被称为搭建程序的 “脚手架”。其最主要作用就是帮我们快速的构建庞大的 Spring 项目,并且尽可能的减少一切 XML 配置,让我们关注于业务而非配置文件。

  2. 为什么要用 SpringBoot?

    SpringBoot 主要解决了以下两点问题:

    • 复杂的配置

      SpringBoot 采用默认配置,我们也可以在 application.properties 文件来覆盖这些默认属性,这样虽然使用的还是默认配置,但是配置中的值改成了我们自定义的。

    • 混乱的依赖管理

      SpringBoot 提供了 stater(启动器),引入后就会自动管理依赖及版本了。

  3. SpringBoot 核心配置文件是什么?

    Spring Boot 有两种类型的配置文件:

    • bootstrap 配置文件是系统级别的,用来加载外部配置,如配置中心的配置信息,也可以用来定义系统不会变化的属性,bootstatp 文件的加载先于 application 文件
    • application 配置文件是应用级别的,是当前应用的配置文件
  4. SpringBoot 配置文件有哪几种类型?它们有什么区别?

    配置文件有 .properties 格式和 .yml 格式,它们主要的区别是书法风格不同。

    properties:

    jdbc.driverClassName=com.mysql.jdbc.Driver
    

    yml:

    jdbc:
    	driverClassName: com.mysql.jdbc.Driver
    
  5. SpringBoot 有哪些方式可以实现热部署?

    • 使用 devtools 启动热部署,添加 devtools 库,在配置文件中把 spring.devtools.restart.enabled 设置为 true。
    • 使用 Intellij IDEA 编辑器,勾上自动编译或者手动重新编译。

109.jpa 和 hibernate 有什么区别?

  1. 什么是 SpringCloud?

    SpringCloud 是一套完整的微服务解决方案,基于 SpringBoot 框架。SpringCloud 将现在非常流行的一些技术整合到一起,实现了诸如:配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。

  2. SpringCloud 断路器的作用是什么?

    熔断机制的原理很简单,像家里的电路熔断器,如果电路发生故障能立刻熔断电路,避免发生整个电路的灾难。在分布式系统中应用这一模式之后,服务调用方可以自己进行判断某些服务反应慢或者存在大量超时的情况时,能够主动熔断,防止整个系统被拖垮。

    不同于电路熔断只能断,不能自动重连,Hystix 可以实现弹性容错,当情况好转之后,可以自动重连。

    通过断路的方式,可以将后续请求直接拒绝掉,一段时间之后允许部分请求通过,如果调用成功则回到电路闭合状态,否则继续断开。

    熔断状态机 3 个状态:

    • Closed:关闭状态,所有请求都正常访问。
    • Open:打开状态,所有请求都会被降级。Hystix 会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。默认失败比例的阈值是 50%,请求次数最少不低于 20 次。
    • Half Open:半开状态,open 状态不是永久的,打开后会进入休眠时间(默认是 5S)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持打开,再次进行休眠计时
  3. SpringCloud 的核心组件有哪些?

    • Eureka:是一个服务治理组件,它实现了服务的自动注册、发现、状态监控。

      它主要包括两个组件:Eureka Server 和 Eureka Client

      • Eureka Server 提供服务注册和发现的功能,也就是微服务中的注册中心。

      • Eureka Client 是一个 Java 客户端,用于简化与 Eureka Server 的交互,也就是微服务中的客户端和服务端。

        每个微服务启动时,都会通过 Eureka Client 向 Eureka Server 注册自己,Eureka Server 会存储该服务的信息。

      Eureka 的作用:

      • Eureka 负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉 Eureka,然后 Eureka 会把符合你需求的服务告诉你。

      • 同时,服务提供方与 Eureka 之间通过 “心跳” 机制进行监控,当某个服务提供方出现问题,Eureka 自然会把它从服务列表中剔除。

      Eureka 的原理:

      在这里插入图片描述

      • Eureka:就是服务注册中心(可以是一个集群),对外暴露自己的地址
      • 提供者:启动后向 Eureka 注册自己信息(地址,提供什么服务)
      • 消费者:向 Eureka 订阅服务,Eureka 会将对应服务的所有提供者地址列表发送给消费者,并且定期更新
      • 心跳(续约):提供者定期通过 http 方式向 Eureka 刷新自己的状态
    • Ribbon:是 Netflix 发布的负载均衡器,它有助于控制 HTTP 和 TCP 的客户端的行为。为 Ribbon 配置服务提供者地址后,Ribbon 就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon 默认为我们提供了很多负载均衡算法,例如轮询、随机等。当然,我们也可为 Ribbon 实现自定义的负载均衡算法。

    • Hystrix:是由 Netflix 开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性、容错性与局部应用的弹性,是一个实现了超时机制和断路器模式的工具类库。

      Hystix 解决雪崩问题的手段有两个:

      • 线程隔离
      • 服务熔断
    • Feign:服务调用,给予 Ribbon 和 Hystrix 的声明式服务调用组件 (声明式服务调用)

    • Zuul:是 Netflix 开源的微服务网关,它可以和 Eureka、Ribbon、hystrix 等组件配合使用。Zuul 的核心是一系列过滤器。

      Zuul 加入后的架构:

      在这里插入图片描述

      不管是来自于客户端(PC 或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过 Zuul 这个网关,然后再由网关来实现鉴权、动态路由等等操作。Zuul 就是我们服务的统一入口。

十二、Hibernate

113.为什么要使用 hibernate?

  1. 什么是 ORM 框架?

    ORM(Object Relation Mapping)对象关系映射,是把数据库中的关系数据映射成为程序中的对象。

    使用 ORM 的优点:提高了开发效率降低了开发成本、开发更简单更对象化、可移植更强。

115.hibernate 中如何在控制台查看打印的 sql 语句?

116.hibernate 有几种查询方式?

117.hibernate 实体类可以被定义为 final 吗?

118.在 hibernate 中使用 Integer 和 int 做映射有什么区别?

119.hibernate 是如何工作的?

120.get()和 load()的区别?

121.说一下 hibernate 的缓存机制?

122.hibernate 对象有哪些状态?

123.在 hibernate 中 getCurrentSession 和 openSession 的区别是什么?

124.hibernate 实体类必须要有无参构造函数吗?为什么?

十三、MyBatis

  1. MyBatis 中 #{} 和 ${} 的区别是什么?

    • #{} 是预编译处理,${} 是字符串替换
    • MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 号,调用 PreparedStatement 的 set 方法来赋值,使用 #{} 可以有效的防止 SQL 注入,提高系统安全性
    • MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值
  2. MyBatis 有几种分页方式?

    • 数组分页:查询出全部数据,然后在 List 中截取需要的部分。(逻辑分页)

    • SQL 分页:使用 LIMIT 只从数据库中查询当前页的数据。(物理分页)

    • 拦截器分页:创建拦截器,拦截 Mybatis 接口方法,添加分页 SQL。(物理分页)

    • RowBounds 分页:在 MyBatis 接口方法中加入 RowBounds 参数,适用于数据量较小的情况。(逻辑分页)

      public List<UserBean> queryUsersByPage(String userName, RowBounds rowBounds);
      
  3. RowBounds 是一次性查询全部结果吗?为什么?

    RowBounds 表面是在 “所有” 数据中检索数据,其实并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next() 的时候,去查询更多的数据。就好比你去自动取款机取 10000 元,但取款机每次最多能取 2500 元,所以你要取 4 次才能把钱取完。只是对于 jdbc 来说,当你调用 next() 的时候会自动帮你完成查询工作。这样做的好处可以有效的防止内存溢出。

  4. Mybatis 逻辑分页和物理分页的区别是什么?

    • 逻辑分页是一次性查询很多数据,然后再在结果中检索分页的数据。这样做弊端是需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。
    • 物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点,比如需要大量的内存,对数据库查询压力较大等问题。
  5. MyBatis 是否支持延迟加载?延迟加载的原理是什么?

    • 延迟加载:在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

      立即加载:不管用不用数据,只要一调用方法,马上发起查询。

    • 延迟加载的实现:

      • 设置 lazyLoadingEnabled=true
      • MyBatis 在实现多表操作时,主要是通过 association 实现一对一、collection 实现一对多映射,association、collection 具备延迟加载功能。
    • 延迟加载原理:先从单表查询,需要时再从关联表去关联查询。

    • 好处:大大提高数据库性能,因为查询单表要比关联查询多张表速度快。

    • 缺点:因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,由于查询工作也要消耗时间,所以可能让用户等待时间变长,造成用户体验下降。

    • 采用延迟加载和立即加载的情况:

      • 一对多,多对多:通常情况下,我们都是采用延迟加载

      • 一对一:通常情况下,我们都是采用立即加载

  6. 说一下 MyBatis 的一级缓存和二级缓存?

    • 一级缓存:指的是 MyBatis 中 SqlSession 对象的缓存。当我们执行查询之后,查询的结果会同时存入到 SqlSession 为我们提供一块缓存区域(HsahMap)中。当我们再次查询同样的数据,MyBatis 会先去 SqlSession 中查询是否有,有的话直接拿出来用,就不用再去数据库查询了。

      注意:

      • MyBatis 默认开启一级缓存。
      • 不同的 sqlSession 之间的缓存数据区域(HashMap)是互相不影响的。
      • 当调用 SqlSession 的修改,添加,删除,commit(),close(),clearCache() 等方法时,就会清空一级缓存。
    • 二级缓存:指的是 MyBatis 中 SqlSessionFactory 对象的缓存。由同一个 SqlSessionFactory 对象创建的 SqlSession 共享其缓存。

      在这里插入图片描述

      • sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
      • sqlSession2 去查询与 sqlSession1 相同的用户信息,首先会去二级缓存中找是否存在数据,如果存在直接从二级缓存中取出数据。
      • sqlSession3 去执行用户信息的增删改操作,将会清空该二级缓存区域的数据。

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

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

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

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

十四、RabbitMQ

  1. RabbitMQ 的使用场景有哪些?

    • 应用解耦

      现在有三个独立的微服务:

      • 商品微服务:原始数据保存在 MySQL 中,从 MySQL 中增删改查商品数据。
      • 搜索微服务:原始数据保存在 ES 的索引库中,从 ES 中查询商品数据。
      • 商品详情微服务:做了页面静态化,静态页面的商品数据不会随着数据库发生变化。

      假如我在商品微服务中,修改了商品的数据,也就是 MySQL 中数据发生了改变。但搜索微服务查询到的数据还是原来的,商品详情微服务的生成的静态页面也没有发生改变,这样显然不对。我们需要实现数据的同步,让搜索微服务和商品详情微服务的商品也发生修改。

      下面有两种解决方案:

      • 方案 1:每当商品微服务进行增删改操作,同时要修改索引库数据及静态页面
      • 方案 2:商品详情微服务和搜索微服务对外提供操作接口,在商品微服务进行增删改操作后,调用接口

      上面两种方式都有一个问题:就是代码耦合。商品微服务中需要调用搜索微服务和商品详情微服务,违背了微服务的独立原则。

      这时,就可以使用消息队列技术来解决这个问题。

      • 在商品微服务修改商品后,就发出一条消息给消息队列,也不关心谁接受消息。
      • 搜索微服务从消息队列接收消息,去修改索引库。
      • 商品详情微服务从消息队列接收消息,去修改静态页面。
      • 如果以后有其它微服务也依赖商品微服务的数据,同样监听消息即可,商品微服务无需任何代码修改。

      在这里插入图片描述

    • 异步处理

      用户注册后,需要发送注册邮件和注册短信,最后客户端返回注册成功。可以采用以下做法:

      • 串行

        注册信息写入数据库(50ms)+发送注册邮件(50ms)+发送短信(50ms)=150ms

      • 并行

        注册信息写入数据库(50ms)+同时发送注册邮件和短信(50ms)=100ms

      • 异步处理

        邮件和短信对我正常的使用网站没有任何影响,客户端没有必要等着其发送完成才显示注册成功,所以可以注册信息写入数据库就返回注册成功。

        注册信息写入数据库(50ms)+将发送短信和邮件的写入消息队列(5ms)=55ms

        由此可以看出,异步处理减少了客户端的响应时间。

    • 流量消峰

      秒杀系统,一般会因为并发量过大,导致系统崩溃。为了解决这个问题,可以加入消息队列。

      用户发送请求,服务器收到之后,首先写入消息队列,当消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面。

  2. RabbitMQ 有哪些重要的角色?

    • 生产者:消息的创建者,负责创建和推送数据到消息服务器
    • 消费者:消息的接收方,用于处理数据和确认消息
    • 代理:就是 RabbitMQ 本身,用于扮演快递的角色,本身并不生产消息
  3. RabbitMQ 有哪些重要的组件?

    • ConnectionFactory(连接管理器):应用程序与 Rabbit 之间建立连接的管理器,程序代码中使用。
    • Channel(信道):消息推送使用的通道。
    • Exchange(交换器):用于接受、分配消息。
    • Queue(队列):用于存储生产者的消息。
    • RoutingKey(路由键):用于把生成者的数据分配到交换器上。
    • BindingKey(绑定键):用于把交换器的消息绑定到队列上。
  4. Rabbitmq 中 vhost 的作用是什么?

    每个 Virtual Host 相当于一个相对独立的 RabbitMQ 服务器,它拥有独立的权限系统,并且和其他 Virtual Host 之间是相互隔离的,exchange、queue、message 不能互通。

  5. RabbitMQ 的消息是怎么发送的?

    • 建立客户端和 RabbitMQ 之间的连接

      // 获取到连接
      Connection connection = ConnectionUtil.getConnection();
      
    • 然后在此连接上,建立信道

      // 获取通道
      Channel channel = connection.createChannel();
      
    • 在此信道上就可以发送消息

      String message = "Hello World!";
      channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
      
  6. RabbitMQ 怎么保证消息的稳定性?

    • 事务机制
    • 生产者确认机制:确保消息会正确发送,如果发送失败会有错误回执,从而触发重试。
  7. RabbitMQ 怎么避免消息丢失?

    • 事务机制和发送者确认模式

      作用:防止消息在进入 RabbitMQ 之前丢失,可以保证生产者将消息送达到 RabbitMQ。

    • 消息持久化

      作用:防止消息在队列中丢失。

    • 消息确认机制(ACK)

      RabbitMQ 有一个 ACK 机制。当消费者获取消息后,会向 RabbitMQ 发送回执 ACK,告知消息已经被接收。

      作用:防止消费者丢失消息。

      ACK 分两种:

      • 自动 ACK:消息一旦被接收,消费者自动发送 ACK。缺点是出现异常,还是会消费掉消息。
      • 手动 ACK:消息接收后,不会发送 ACK,需要手动调用。
  8. 要保证消息持久化成功的条件有哪些?

    1. 交换机持久化

      在这里插入图片描述

    2. 队列持久化

      在这里插入图片描述

    3. 消息持久化

      在这里插入图片描述

    4. 要保证消息最终到达队列

  9. RabbitMQ 持久化有什么缺点?

    持久化会将消息保存到硬盘上,这样 RabbitMQ 的效率肯定会受到影响。所以是否使用持久化,取决于系统是更需要安全性还是更需要效率。

  10. RabbitMQ 有几种广播类型?

    • anout(广播):它会将消息交给所有绑定到交换机的队列。
    • direct(定向):它会把消息交给符合指定的队列。
    • Topic(通配符):它会把消息交给符合路由模式的队列。

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

  1. RabbitMQ 集群有什么用?

    集群主要有以下两个用途:

    • 高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用。
    • 高容量:集群可以承载更多的消息量。
  2. RabbitMQ 节点的类型有哪些?

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

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

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

    不是,原因有以下两个:

    1. 存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据。
    2. 性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,最多是保持和单节点相同的性能甚至是更糟。

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

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

十五、Kafka

152.kafka 可以脱离 zookeeper 单独使用吗?为什么?

153.kafka 有几种数据保留的策略?

154.kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka 将如何处理?

155.什么情况会导致 kafka 运行变慢?

156.使用 kafka 集群需要注意什么?

十六、Zookeeper

157.zookeeper 是什么?

158.zookeeper 都有哪些功能?

159.zookeeper 有几种部署模式?

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

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

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

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

十七、MySQL

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

    • 第一范式(1NF):每一列都是不可分割的原子数据项
    • 第二范式(2NF):在 1NF 的基础上,非主属性必须完全依赖于主属性(在 1NF 基础上消除非主属性对主属性的部分函数依赖)
    • 第三范式(3NF):在 2NF 的基础上,任何非主属性不依赖于其它非主属性(在 2NF 基础上消除传递依赖)
  2. 一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一条数据,此时 id 是几?

    • 数据库引擎如果是 MyISAM ,那 id 就是 8。

    • 数据库引擎如果是 InnoDB,那 id 就是 6。

      InnoDB 表只会把自增主键的最大 id 记录在内存中,所以重启之后会导致最大 id 丢失。

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

    select version();
    
  4. 说一下 ACID 是什么?

    事务的四大特性:

    ACID 表示原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。一个运行良好的事务处理系统,必须具备这些标准特性。

    • 原子性:一个事物必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事物来说,不可能只执行其中的一个部分操作,这就是事务的原子性。
    • 一致性:数据库总是从一个一致性状态转化到另一个一致性状态。在前面的例子中,一致性确保了,即使在执行第三、第四语句之间时系统崩溃,支票账户中也不会损失 200 美元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。
    • 隔离性:通常来说,一个事物所做的修改在最终提交以前,对其他事务是不可见的。在前面的例子中,当执行完第三条语句、第四条语句还未开始时,此时有另外一个账户汇总程序开始执行,则其看到的支票账户的余额并没有被减去 200 美元。后面我们讨论隔离级别(Isolation level)的时候,会发现为什么我们要说 “通常来说” 是不可见的。
    • 持久性:一旦事务提交,则其所作的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且不可能有能做到 100% 的持久性保证的策略。
  5. char 和 varchar 的区别是什么?

    • char(n) :固定长度类型,比如订阅 char(10),当你输入"abc" 三个字符的时候,它们占的空间还是 10 个字节,其他 7 个是空字节。

    • varchar(n) :可变长度,存储的值是每个值占用的字节再加上一个用来记录其长度的字节的长度。

      所以,从空间上考虑 varcahr 比较合适;从效率上考虑 char 比较合适,二者使用需要权衡。

  6. float 和 double 的区别是什么?

    • float 最多可以存储 8 位的十进制数,并在内存中占 4 字节。
    • double 最可可以存储 16 位的十进制数,并在内存中占 8 字节。
  7. MySQL 的内连接、左连接、右连接有什么区别?

    • 内连接:查询两表交集部分。
    • 左外连接:查询左表所有数据以及两表交集部分
    • 右外连接:查询右表所有数据以及两表交集部分

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

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

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

    设置隔离级别的作用

    多个事务之间,是相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。

    存在问题

    1. 脏读:一个事务,读取到另一个事务中没有提交的数据。
    2. 不可重复读:在同一个事务中,两次读取到的数据内容不一样。
    3. 幻读:一个事务查询所有数据,另一个事务添加了几条数据,则第一个事务再次查询时数据总量不同。

    隔离级别

    隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁读
    READ UNCOMMITED(读未提交) YES YES YES NO
    READ COMMITED(读已提交) NO YES YES NO
    REPEATABLE READ(可重复读) NO NO YES NO
    SERIALIZABLE(可串行化) NO NO NO YES

    注意:隔离级别从小到大安全性越来越高,但是效率越来越低。

  2. 说一下 MySQL 常用的引擎?

    • InnoDB 引擎:mysql 5.1 后默认的数据库引擎,提供了对数据库 ACID 事务的支持,并且还提供了行级锁和外键的约束,它的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候,InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎是不支持全文搜索,同时启动也比较的慢,它是不会保存表的行数的,所以当进行 select count() from table 指令的时候,需要进行扫描全表。由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。
    • MyIASM 引擎:不提供事务的支持,也不支持行级锁和外键。因此当执行插入和更新语句时,即执行写操作的时候需要锁定这个表,所以会导致效率会降低。不过和 InnoDB 不同的是,MyIASM 引擎是保存了表的行数,于是当进行 select count() from table 语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的,可以将 MyIASM 作为数据库引擎的首选。
  3. 说一下 MySQL 的行锁和表锁

    MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。

    • 表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低。
    • 行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高。
  4. 说一下乐观锁和悲观锁?

    • 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。

    • 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。

      数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。

  5. MySQL 问题排查都有哪些手段?

    • 使用 show processlist 命令查看当前所有连接信息。
    • 使用 explain 命令查询 SQL 语句执行计划。
    • 开启慢查询日志,查看慢查询的 SQL。
  6. 如何做 MySQL 的性能优化?

    • 为搜索字段创建索引。
    • 避免使用 select *,列出需要查询的字段。
    • 垂直分割分表。
    • 选择正确的存储引擎。

十八、Redis

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

180.redis 有哪些功能?

181.redis 和 memecache 有什么区别?

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

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

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

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

186.jedis 和 redisson 有哪些区别?

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

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

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

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

191.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 调优的参数都有哪些?

猜你喜欢

转载自blog.csdn.net/bm1998/article/details/108551186