java常见面试题

java常见面试题

一、java基础

1.“==”和equals()的区别

“==”比的是地址,equals比的是内容

2.Get和post的区别是什么

  1. Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。
  2. Get传送的数据量较小,这主要是因为受URL长度限制;Post传送的数据量较大,一般被默认为不受限制。
  3. Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
  4. Get执行效率却比Post方法好。Get是form提交的默认方法。

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

不一定为true。 hashCode()相等,即两个键值对的哈希值相等。 然而哈希值相等,并不一定能得出键值对相等

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

(1)、final修饰类:表示该类不能被继承

  • 使用final修饰类的目的简单明确: 表明这个类不能被继承。
  • 当程序中有永远不会被继承的类时, 可以使用final关键字修饰。
  • 被final修饰的类所有成员方法都将被隐式修饰为final方法。

(2)、修饰方法:表示方法不能被重写

  • 使用final修饰方法有两个作用, 首要作用是锁定方法, 不让任何继承类对其进行修改.
  • 另外一个作用是在编译器对方法进行内联, 提升效率. 但是现在已经很少这么使用了, 近代的Java版本已经把这部分的优化处理得很好了. 但是为了满足求知欲还是了解一下什么是方法内敛.
  • 方法内敛: 当调用一个方法时, 系统需要进行保存现场信息, 建立栈帧, 恢复线程等操作, 这些操作都是相对比较耗时的. 如果使用final修饰一个了一个方法a, 在其他调用方法a的类进行编译时, 方法a的代码会直接嵌入到调用a的代码块中.

(3)、修饰变量:表示变量只能一次复制以后值不能被修改(常量)

  • 当final修饰的是一个基本数据类型数据时, 这个数据的值在初始化后将不能被改变; 当final修饰的是一个引用类型数据时, 也就是修饰一个对象时, 引用在初始化后将永远指向一个内存地址, 不可修改. 但是该内存地址中保存的对象信息, 是可以进行修改的

5.java八大基本类型

  • byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间。
  • short:16位,最大数据存储量是65536,数据范围是-32768~32767之间。
  • int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。
  • long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。
  • float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。
  • double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。
  • boolean:只有true和false两个取值。
  • char:16位,存储Unicode码,用单引号赋值。

6.java 中操作字符串都有哪些类?它们之间有什么区别?

  • String
  • StringBuffer
  • StringBuilder
StringStringBufferStringBuilder
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量可变类,速度更快
不可变可变可变
线程安全线程不安全
多线程操作字符串单线程操作字符串

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

  • 不一样,因为内存分配的方式不一样。
  • String str="i"方式,java虚拟机会将其分配到常量池中;
  • String str=new String(“i”)方式,则会被分到堆内存中。

8.Java如何将字符串反转

  1. 利用 StringBuffer 或 StringBuilder 的 reverse 成员方法:
  // StringBuffer
  public static String reverse1(String str) {
    return new StringBuilder(str).reverse().toString();
  }

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

  // toCharArray

  public static String reverse2(String str) {
    char[] chars = str.toCharArray();
    String reverse = "";
    for (int i = chars.length - 1; i >= 0; i--) {
      reverse += chars[i];
    }
    return reverse;
  }

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

  public static String reverse3(String str) {
    String reverse = "";
    int length = str.length();
    for (int i = 0; i < length; i++) {
      reverse = str.charAt(i) + reverse;
    }
    return reverse;
  }

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

  • indexOf():返回指定字符的索引。
  • charAt():返回指定索引处的字符。
  • replace():字符串替换。
  • trim():去除字符串两端空白。
  • split():分割字符串,返回一个分割后的字符串数组。
  • getBytes():返回字符串的 byte 类型数组。
  • length():返回字符串长度。
  • toLowerCase():将字符串转成小写字母。
  • toUpperCase():将字符串转成大写字符。
  • substring():截取字符串。
  • equals():字符串比较。

10.抽象类中是否一定要有抽象方法?

1.如果一个类使用了abstract关键字修饰,那么这个类就是一个抽象类。

2.抽象类可以没有抽象方法

3.一个类如果包含抽象方法,那么这个类必须是抽象类,否则编译就会报错。

4.最关键的一点就是如果一个类是抽象类,那么这个类是不能被实例化的。

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

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

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

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

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

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

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

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

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

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

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

参数抽象类接口
默认的方法实现它可以有默认的方法实现接口完全是抽象的。它根本不存在方法的实现
实现子类使用extends关键字来继承抽象类。如果子类不 是抽象类的话,它需要提供抽象类中所有声明的方法的实现。子类使用关键字implements来实现接口。 它需要提供接口中所有声明的方法的实现
构造器抽象类可以有构造器接口不能有构造器
与正常Java类的区别除了你不能实例化抽象类之外,它和普通Java类没有任何区别接口是完全不同的类型
访问修饰符抽象方法可以有public、protected和default这些修饰符接口方法默认修饰符是public。你不可以使用其它修饰符。
main方法抽象方法可以有main方法并且我们可以运行它接口没有main方法,因此我们不能运行它。 (java8以后接口可以有default和static方法,所以可以运行main方法)
多继承抽象方法可以继承一个类和实现多个接口接口只可以继承一个或多个其它接口
速度它比接口速度要快接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。
添加新方法如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。如果你往接口中添加方法,那么你必须改变实现该接口的类。

抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
一个类只能继承一个抽象类,而一个类却可以实现多个接口。

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

Files.exists():检测文件路径是否存在。

Files.createFile():创建文件。

Files.createDirectory():创建文件夹。

Files.delete():删除一个文件或目录。

Files.copy():复制文件。

Files.move():移动文件。

Files.size():查看文件个数。

Files.read():读取文件。

Files.write():写入文件。

13. .java 中 IO 流分为几种?

(1)按流向分类:
输入流
输出流
(2)按处理数据不同分类:
字节流:二进制,可以处理一切文件,包括:纯文本、doc、音频、视频等。
字符流:文本文件,只能处理纯文本。
(3)按功能不同分类:
节点流:包裹源头。
处理流:增强功能,提高性能**

二、容器

1.java 容器都有哪些?

Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
(1)Collection。一个独立元素的序列,这些元素都服从一条或多条规则。
在这里插入图片描述

(2)Map。一组成对的“键值对”对象,允许你使用键来查找值。
在这里插入图片描述

2.Collection 和 Collections 有什么区别?

Collection是集合类的上级接口,子接口主要有Set 和List、Map。

Collections是针对集合类的一个帮助类,提供了操作集合的工具方法:一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-avhdzcXW-1635570122940)(C:/Users/HSL/AppData/Roaming/Typora/typora-user-images/image-20211026215146634.png)]

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

JDK7和JDK8中HashMap的大致变化是(这其实也是一个常被问道的面试题~):

1.7中采用数组+链表,1.8采用的是数组+链表/红黑树,即在1.7中链表长度超过一定长度后就改成红黑树存储。

1.7扩容时需要重新计算哈希值和索引位置,1.8并不重新计算哈希值,巧妙地采用和扩容后容量进行&操作来计算新的索引位置。

1.7是采用表头插入法插入链表,1.8采用的是尾部插入法。

在1.7中采用表头插入法,在扩容时会改变链表中元素原本的顺序,以至于在并发场景下导致链表成环的问题;在1.8中采用尾部插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。

HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

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

1、HashSet底层由HashMap实现

2、HashSet的值存放于HashMap的key上

3、HashMap的value统一为PRESENT

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

1 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
2 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
3 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。
综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

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

  • 数组转List使用Arrays.asList()方法
  • List转数组使用list.toArray()方法;

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

  1. ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
  2. Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
  3. LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

9.Array 和 ArrayList 有何区别

1.长度的区别

Array是数组,声明好之后,其长度就已经固定。

ArrayList底层是用数组实现的,但是ArrayList的长度是可变的,在每次添加时,如果发现空间不足的话,会创建一个长度大概是原来1.5倍的新数组(java8源码),然后把原来的数组元素复制过去。

2.存放数据的区别

Array可以除了可以存放对象类型的数据之外,还可以存放基本数据类型的数据。

而ArrayList只能存放对象数据类型的数据,因为它的类在定义时已经是针对Object的子类做了泛型的约束

10 队列

1、offer()和add()的区别

add()和offer()都是向队列中添加一个元素。但是如果想在一个满的队列中加入一个新元素,调用 add() 方法就会抛出一个 unchecked 异常,而调用 offer() 方法会返回 false。可以据此在程序中进行有效的判断!
2、peek()和element()的区别

peek()和element()都将在不移除的情况下返回队头,但是peek()方法在队列为空时返回null,调用element()方法会抛出NoSuchElementException异常。
3、poll()和remove()的区别

poll()和remove()都将移除并且返回对头,但是在poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。

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

Vector:就比ArrayList多了个同步化机制(线程安全)。
Hashtable:就被HashMap多了个线程安全。
ConcurrentHashMap:是一种高效但是线程安全的集合。
Stack:栈,也是线程安全的,继承于Vector,已过时,不建议使用。

12 迭代器 Iterator 是什么?

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

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

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

13 Iterator 和 ListIterator 有什么区别

ListIterator 继承 Iterator
ListIterator 比 Iterator多方法

  add(E e)//  将指定的元素插入列表,插入位置为迭代器当前位置之前
  set(E e)//  迭代器返回的最后一个元素替换参数e
  hasPrevious()//  迭代器当前位置,反向遍历集合是否含有元素
  previous()  //迭代器当前位置,反向遍历集合,下一个元素
  previousIndex()//  迭代器当前位置,反向遍历集合,返回下一个元素的下标
  nextIndex()  //迭代器当前位置,返回下一个元素的下标

使用范围不同,Iterator可以迭代所有集合;ListIterator 只能用于List及其子类
ListIterator 有 add 方法,可以向 List 中添加对象;Iterator 不能
ListIterator 有 hasPrevious() 和 previous() 方法,可以实现逆向遍历;Iterator不可以
ListIterator 有 nextIndex() 和previousIndex() 方法,可定位当前索引的位置;Iterator不可以
ListIterator 有 set()方法,可以实现对 List 的修改;Iterator 仅能遍历,不能修改

三、多线程

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

  • 并发:一个处理器可以同时处理多个任务。这是逻辑上的同时发生。
  • 并行:多个处理器同时处理多个不同的任务。这是物理上的同时发生。
  • 有一个清晰地比喻:
    并发:一个人同时吃三个苹果。并行:三个人同时吃三个苹果。

2.线程和进程的区别

进程:进程是并发执行程序在执行过程中资源分配和管理基本单位(资源分配的最小单位)。进程可以理解为一个应用程序的执行过程,应用程序一旦执行,就是一个进程。每个进程都有自己独立的地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段。

线程线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。

**区别:线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。**不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

3.守护线程是什么?

守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程,这是它的作用——而其他的线程只有一种,那就是用户线程。所以java里线程分2种,
1、守护线程,比如垃圾回收线程,就是最典型的守护线程。
2、用户线程,就是应用程序里的自定义线程。

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

  1. 继承Thread类(重要)
    • 自定义线程类继承Thread类;
    • 重写run()方法,编写线程执行体;
    • 创建线程对象,调用start()方法启动线程
  2. 实现Runnable接口,实现run方法,创建Thread时作为参数传入,调用start方法
  3. 实现Callable接口,重写call方法

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

  • Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
  • Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来拿到异步执行任务的返回值。。Callable用于产生结果,Future用于获取结果

6.线程有哪些状态?

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权即在就绪状态的进程除****CPU之外,其它的运行所需资源都已全部获得。

**3、运行状态(Running):**就绪状态的线程获取了CPU,执行程序代码。

**4、阻塞状态(Blocked):**阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

阻塞的情况分三种:

(1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“**等待池”**中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒,

(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入**“锁池”**中。

(3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

**5、死亡状态(Dead):**线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

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

都造成线程阻塞。

释放锁:sleep 方法没有释放锁,而 wait 方法释放了锁 。
作用:Wait 通常被用于线程间通信,sleep 用于暂停执行。
苏醒:wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可以使用wait(long timeout)超时后线程会自动苏醒。

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

前者是唤醒等待线程中的某一个线程,具体哪一个有随机性

后者的作用是唤醒等待线程中的所有线程,如果等待队列中只有一个线程,那么两个方法使用效果是一样的,如果等待队列中有多个线程,那么前者只会唤醒其中给一个,而后者会唤醒所有的等待线程。

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

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

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

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

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

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

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

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

11. 线程池包含哪些状态?

RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。

SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。

STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。

TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。

TERMINATED:terminated()方法结束后,线程池的状态就会变成这个

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

execute() 参数 Runnable ;submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)
execute() 没有返回值;而 submit() 有返回值
submit() 的返回值 Future 调用get方法时,可以捕获处理异

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

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

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

导致原因:

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

解决办法:

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

Happens-Before 规则如下:

  • 程序次序规则:在一个线程内,按照程序控制流顺序,书写在前面的操作先行发生于书写在后面的操作
  • 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作
  • volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作
  • 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作
  • 线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测
  • 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
  • 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始

14.死锁以及防止死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象

死锁发生的原因?

A:java 死锁产生的四个必要条件
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
B:产生原因:
竞争资源引起进程死锁

可剥夺资源和不可剥夺资源

竞争不可剥夺资源

竞争临时资源

死锁预防:

    1. 破坏“不可剥夺”条件:一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到 系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行。
    2. 破坏”请求与保持条件“:第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源。第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源。
    3. 破坏“循环等待”条件:采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。

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

ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。

经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session 管理 等问题。

16 synchronized 和 volatile 的区别是什么?

  • synchronized 可以作用于变量、方法、对象;volatile 只能作用于变量。
  • synchronized 可以保证线程间的有序性(猜测是无法保证线程内的有序性,即线程内的代码可能被 CPU 指令重排序)、原子性和可见性;volatile 只保证了可见性和有序性,无法保证原子性。
  • synchronized 线程阻塞,volatile 线程不阻塞。

17 .synchronized 和 Lock 有什么区别?

  • synchronized 可以作用于变量、方法、对象;volatile 只能作用于变量。
  • synchronized 可以保证线程间的有序性(猜测是无法保证线程内的有序性,即线程内的代码可能被 CPU 指令重排序)、原子性和可见性;volatile 只保证了可见性和有序性,无法保证原子性。
  • synchronized 线程阻塞,volatile 线程不阻塞。

18. synchronized 和 ReentrantLock 区别是什么?

四、反射

1.什么是反射?

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

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

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

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

当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。

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

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

动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法。
Java 中实现动态的方式:JDK 中的动态代理 和 Java类库 CGLib。

应用场景如:
统计每个 api 的请求耗时
统一的日志输出
校验被调用的 api 是否已经登录和权限鉴定
Spring的 AOP 功能模块就是采用动态代理的机制来实现切面编程

4.怎么实现动态代理?

1,JDK的动态代理,是基于接口的实现

2,基于CGLIB的动态代理,是基于继承当前类的子类来实现的(所以,这个类不能是final)。我们项目结构是没有接口的情况下,如果实现动态代理,那么就需要使用这种方法。

所以,我们的Spring默认会在以上两者根据代码的关系自动切换,当我们采用基于接口的方式编程时,则默认采用JDK的动态代理实现。如果不是接口的方式,那么会自动采用CGLIB。

SpringAOP的背后实现原理就是动态代理机制。

五、对象拷贝

1.为什么要使用克隆?

想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了,Java语言中克隆针对的是类的实例。

2.如何实现对象克隆?

有两种方式:

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

2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class UtilDemo {
  
    private UtilDemo() {        
      throw new AssertionError();    
    }
  
    @SuppressWarnings("unchecked")    
    public static <T extends Serializable> T clone(T obj) throws Exception {        
      ByteArrayOutputStream bout = new ByteArrayOutputStream();        
      ObjectOutputStream oos = new ObjectOutputStream(bout);        
      oos.writeObject(obj);
      
      ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());        
      ObjectInputStream ois = new ObjectInputStream(bin);        
      return (T) ois.readObject();
      
      // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义        
      // 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源,这一点不同于对外部资源(如文件流)的释放    
    }
}

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

浅克隆,在clone对象时,只会把基本数据类型的数据进行复制过去;如果是引用类型,只会把引用复制过去,也就是被克隆对象和原始对象,共同引用一个引用类型的属性。
深克隆:在克隆时,会把基本数据类型的数据和引用类型的数据,同时复制。克隆对象和原始对象不共同引用一个引用类型
 缺点:在深克隆时,如果引用对象关系比较复杂,克隆时会很麻烦,因为每一个对象都要克隆。
解决方案:可以使用序列化进行解决。

六、Java Web

1.jsp 和 servlet 有什么区别?

Servlet

  • 一种服务器端的Java应用程序
  • 由 Web 容器加载和管理
  • 用于生成动态 Web 内容
  • 负责处理客户端请求

Jsp

  • 是 Servlet 的扩展,本质上还是 Servlet
  • 每个 Jsp 页面就是一个 Servlet 实例
  • Jsp 页面会被 Web 容器编译成 Servlet,Servlet 再负责响应用户请求

区别

  • Servlet 适合动态输出 Web 数据和业务逻辑处理,对于 html 页面内容的修改非常不方便;Jsp 是在 Html 代码中嵌入 Java 代码,适合页面的显示
  • 内置对象不同,获取内置对象的方式不同

2.jsp 有哪些内置对象?作用分别是什么?[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t5CHfMB9-1635570122942)(面试题积累.assets/image-20211027111931667.png)]

3.说一下 JSP 的 4 种作用域?

page:代表与一个页面相关的对象和属性。
request:代表与客户端发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个 Web 组件;需要在页面显示的临时数据可以置于此作用域。

session:代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的 session 中。
application:代表与整个 Web 应用程序相关的对象和属性,它实质上是跨越整个 Web 应用程序,包括多个页面、请求和会话的一个全局作用域

4.session 和 cookie 有什么区别?

(1)Cookie以文本文件格式存储在浏览器中,而session存储在服务端它存储了限制数据量。它只允许4kb它没有在cookie中保存多个变量。

(2)cookie的存储限制了数据量,只允许4KB,而session是无限量的

(3)我们可以轻松访问cookie值但是我们无法轻松访问会话值,因此它更安全

(4)设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。

5.session的工作原理

  1. 用户第一次请求服务器时,服务器端会生成一个sessionid
  2. 服务器端将生成的sessionid返回给客户端,通过set-cookie
  3. 客户端收到sessionid会将它保存在cookie中,当客户端再次访问服务端时会带上这个sessionid
  4. 当服务端再次接收到来自客户端的请求时,会先去检查是否存在sessionid,不存在就新建一个sessionid重复1,2的流程,如果存在就去遍历服务端的session文件,找到与这个sessionid相对应的文件,文件中的键值便是sessionid,值为当前用户的一些信息
  5. 此后的请求都会交换这个 Session ID,进行有状态的会话。

6.客户端禁止 cookie,session 还能用吗?

一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。

如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。

但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session。

  • 通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
  • 服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
  • 通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。

7.如何避免 sql 注入?

  • 严格限制 Web 应用的数据库的操作权限,给连接数据库的用户提供满足需要的最低权限,最大限度的减少注入攻击对数据库的危害
  • 校验参数的数据格式是否合法(可以使用正则或特殊字符的判断)
  • 对进入数据库的特殊字符进行转义处理,或编码转换
  • 预编译 SQL(Java 中使用 PreparedStatement),参数化查询方式,避免 SQL 拼接
  • 发布前,利用工具进行 SQL 注入检测
  • 报错信息不要包含 SQL 信息输出到 Web 页面

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

XSS 攻击,即跨站脚本攻击(Cross Site Scripting),它是 web 程序中常见的漏洞。

原理

攻击者往 web 页面里插入恶意的 HTML 代码(Javascript、css、html 标签等),当用户浏览该页面时,嵌入其中的 HTML 代码会被执行,从而达到恶意攻击用户的目的。如盗取用户 cookie 执行一系列操作,破坏页面结构、重定向到其他网站等
预防思路

  • web 页面中可由用户输入的地方,如果对输入的数据转义、过滤处理
  • 后台输出页面的时候,也需要对输出内容进行转义、过滤处理(因为攻击者可能通过其他方式把恶意脚本写入数据库)
  • 前端对 html 标签属性、css 属性赋值的地方进行校验

9…什么是 CSRF 攻击,如何避免?

CSRF(Cross-site request forgery), 中文名称:跨站请求伪造。攻击者盗用了你的身份,以你的名义发送恶意请求。

避免:

  • 验证HTTP Referer字段
  • 使用验证码(关键页面加上验证码验证,这种方法对用户不友好,不推荐)
  • 在请求地址中加入token并验证
  • 在HTTP头中自定义属性并验证

七、异常

1.throw 和 throws 的区别?

throw:

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

throws:

方法的定义上使用 throws 表示这个方法可能抛出某种异常
需要由方法的调用者进行异常处理

2.final、finally、finalize 有什么区别?

final可以用来修饰类、方法、变量,分别有不同的意义所在,final修饰的class代表不可继续扩展,final修饰的变量代表不可修改,final修饰的方法代表不可重写。

finally则是java保证某一段重点代码一定要被执行的修饰符,例如:我们需要用try块让JDBC保证连接,保证unlock锁等动作

finalize是基础类java.lang.Object的一个方法,它的设计目的是为了保证对象在垃圾回收之前完成特定资源的回收

3.try-catch-finally 中哪个部分可以省略?

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

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

会执行,

​ 1、不管有木有出现异常,finally块中代码都会执行;
  2、当try和catch中有return时,finally仍然会执行;
  3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管finally中的代码怎么样, 返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;
  4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。
​ 5、如果finally 函数里面有return; 那么函数最终会返回finally 里面执行之后的return的值给函数,如果没有return,函数值会是try里面return的值,或者catch里面的

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

NullPointerException 空指针异常

ClassNotFoundException 指定类不存在

NumberFormatException 字符串转换为数字异常

IndexOutOfBoundsException 数组下标越界异常

ClassCastException 数据类型转换异常

FileNotFoundException 文件未找到异常

NoSuchMethodException 方法不存在异常

IOException IO 异常

SocketException Socket 异常

八、网络

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

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

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

301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;

302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。

2.forward 和 redirect 的区别?

1、请求方不同

redirect:客户端发起的请求

forward:服务端发起的请求

2、浏览器地址表现不同

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HiVMegly-1635570122944)(面试题积累.assets/810a19d8bc3eb1351031fc0fab1ea8d3fc1f448b)]

3、参数传递不同

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

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

4、底层运作不同

redirect:发送的请求信息又回送给客户机,让客户机再转发到另一个资源上,需要在服务器和客户机之间增加一次通信。

forward:服务器端直接找到目标,并include过来。

5、定义不同

直接转发方式(Forward):客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。

间接转发方式(Redirect)实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。

3.简述 tcp 和 udp的区别?

TCP:面向连接,提供可靠的服务,有流量控制,拥塞控制,无重复、无丢失、无差错,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),只能是点对点,首部 20 字节,全双工。

UDP:无连接,尽最大努力交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对多,首部 8 字节

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 的数据

5.tcp粘连包

1、什么是 tcp 粘包?

发送方发送的多个数据包,到接收方缓冲区首尾相连,粘成一包,被接收。

2、原因

TCP 协议默认使用 Nagle 算法可能会把多个数据包一次发送到接收方。

应用程读取缓存中的数据包的速度小于接收数据包的速度,缓存中的多个数据包会被应用程序当成一个包一次读取。

3、处理方法

发送方使用 TCP_NODELAY 选项来关闭 Nagle 算法

数据包增加开始符和结束,应用程序读取、区分数据包。

在数据包的头部定义整个数据包的长度,应用程序先读取数据包的长度,然后读取整个长度的包字节数据,保证读取的是单个包且完整。

6.OSI 的七层模型有哪些?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d0i4J4SV-1635570122948)(面试题积累.assets/image-20211027124832896.png)]

7.Get请求和post请求

  1. Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。
  2. Get传送的数据量较小,这主要是因为受URL长度限制;Post传送的数据量较大,一般被默认为不受限制。
  3. Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
  4. Get执行效率却比Post方法好。Get是form提交的默认方法。

九、设计模式

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

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录 模式、状态模式、访问者模式、中介者模式、解释器模式。

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

代理模式

一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理
  对象实现同一个接口,先访问代理类再访问真正要访问的对象。

代理模式分为静态代理、动态代理。

静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。

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

简单工厂模式

是由一个工厂对象创建产品实例,简单工厂模式的工厂类一般是使用静态方法,通过不同的参数的创建不同的对象实例
可以生产结构中的任意产品,不能增加新的产品

抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类,生产多个系列产品
生产不同产品族的全部产品,不能新增产品,可以新增产品族

十、Spring/Spring MVC

1.为什么要使用 spring?

spring 是一个开源的轻量级 JavaBean 容器框架。使用 JavaBean 代替 EJB ,并提供了丰富的企业应用功能,降低应用开发的复杂性。

  • 轻量:非入侵性的、所依赖的东西少、资源占用少、部署简单,不同功能选择不同的 jar 组合
  • 容器:工厂模式实现对 JavaBean 进行管理,通过控制反转(IOC)将应用程序的配置和依赖性与应用代码分开
  • 松耦合:通过 xml 配置或注解即可完成 bean 的依赖注入
  • AOP:通过 xml 配置 或注解即可加入面向切面编程的能力,完成切面功能,如:日志,事务…的统一处理
  • 方便集成:通过配置和简单的对象注入即可集成其他框架,如 Mybatis、Hibernate、Shiro…
  • 丰富的功能:JDBC 层抽象、事务管理、MVC、Java Mail、任务调度、JMX、JMS、JNDI、EJB、动态语言、远程访问、Web Service…

2 IOC

IOC(Inversion Of Controll,控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC在其他语言中也有应用,并非Spring特有。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。

2.1作用

  • 管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事,在对象关系比较复杂时,如果依赖关系需要程序猿来维护的话,那是相当头疼的
  • 解耦,由容器去维护具体的对象
  • 托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的

2.2优点

  • IOC 或 依赖注入把应用的代码量降到最低。、
  • 它使应用容易测试,单元测试不再需要单例和JNDI查找机制。
  • 最小的代价和最小的侵入性使松散耦合得以实现。
  • IOC容器支持加载服务时的饿汉式初始化和懒加载。

2.3 实现机制

Spring 中的 IoC 的实现原理就是工厂模式加反射机制

2.4 支持功能

  • 依赖注入
  • 依赖检查
  • 自动装配
  • 支持集合
  • 指定初始化方法和销毁方法
  • 支持回调某些方法(但是需要实现 Spring 接口,略有侵入)

3 AOP

AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。

3.1 什么是AOP

OOP(Object-Oriented Programming)面向对象编程,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而不利于各个模块的重用。

AOP(Aspect-Oriented Programming),一般称为面向切面编程,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。

3.2 Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?

AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。

(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

4.spring 有哪些主要模块?

Spring框架的七大块

  1. Spring Core
    框架的最基础部分,提供 IoC 容器,对 bean 进行管理。

2.Spring Context
基于 bean,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化、校验和调度等功能。

3.Spring DAO
提供了JDBC的抽象层,它可消除冗长的JDBC编码和解析数据库厂商特有的错误代码,还提供了声明性事务管理方法。

4.Spring ORM
提供了常用的“对象/关系”映射APIs的集成层。 其中包括JPA、JDO、Hibernate、MyBatis 等。

5.Spring AOP
提供了符合AOP Alliance规范的面向方面的编程实现。

6.Spring Web
提供了基础的 Web 开发的上下文信息,可与其他 web 进行集成。

7.Spring Web MVC
提供了 Web 应用的 Model-View-Controller 全功能实现。

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

singleton:默认,每个容器中只有一个bean的实例
prototype:为每一个bean请求提供一个实例

以下三种99.99%不用

request:为每一个网络请求创建一个实例,在请求完成后,bean会失效并被垃圾回收器回收
session:确保每个session中有一个bean的实例,在session过期后,bean会随之消失
gloal-session

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

spring 配置文件中 节点的 autowire 参数可以控制 bean 自动装配的方式

default - 默认的方式和 "no" 方式一样
no - 不自动装配,需要使用 <ref />节点或参数
byName - 根据名称进行装配
byType - 根据类型进行装配
constructor - 根据构造函数进行装配

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

Spring提供了编程式事务和声明式事务两种实现方式,

编程式事务允许用户在代码中精确定义事务的边界,

而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。

简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管

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

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

9.SpringMvc执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wC6HI2Xk-1635570122950)(https://i.loli.net/2021/10/20/J1mb8YMH5dinQfr.png)]

1用户向 前端控制器(DispatcherServlet) 发送请求

2.前端控制器(DispatcherServlet)委托请求给处理器映射器(HandlerMapping)

3.处理器映射器(HandlerMapping)根据请求url找到具体的处理器Handler

4.Handler将执行链返回给处理器映射器(HandlerMapping)

5.前端控制器(DispatcherServlet)请求处理器适配器(HandlerAdapter)

7.处理器适配器(HandlerAdapter) 将Handler执行结果ModelAndView返回给前端控制器(DispatcherServlet)

8.前端控制器(DispatcherServlet) 将ModelAndView传给 视图解析器(ViewResolver) 进行解析;

9.视图解析器(ViewResolver) 解析后返回具体View:

获取ModeAndView的试图
解析ModeAndView的试图名字
拼接试图名字,找到对应视图
将数据返回给视图

10.前端控制器(DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中),将试图展现给用户

10.spring mvc 有哪些组件?

  • 前端控制器(DispatcherServlet)
  • 处理器映射器(HandlerMapping)
  • 处理器适配器(HandlerAdapter)
  • 拦截器(HandlerInterceptor)
  • 语言环境处理器(LocaleResolver)
  • 主题解析器(ThemeResolver)
  • 视图解析器(ViewResolver)
  • 文件上传处理器(MultipartResolver)
  • 异常处理器(HandlerExceptionResolver)
  • 数据转换(DataBinder)
  • 消息转换器(HttpMessageConverter)
  • 请求转视图翻译器(RequestToViewNameTranslator)
  • 页面跳转参数管理器(FlashMapManager)
  • 处理程序执行链(HandlerExecutionChain)

11.@RequestMapping 的作用是什么?

@RequestMapping 是一个注解,用来标识 http 请求地址与 Controller 类的方法之间的映射。

可作用于类和方法上,方法匹配的完整是路径是 Controller 类上 @RequestMapping 注解的 value 值加上方法上的 @RequestMapping 注解的 value 值。

12 @Autowired的作用是什么?

@Autowired 是一个注释,它可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。
@Autowired 默认是按照类去匹配,配合 @Qualifier 指定按照名称去装配 bean。

十一、Spring Boot/Spring Cloud

1.什么是Spring Boot

SpringBoot基本上是 Spring框架的扩展,它消除了设置 Spring应用程序所需的 XML配置,为更快,更高效的开发生态系统铺平了道路。

SpringBoot中的一些特征:
1、创建独立的 Spring应用。
2、嵌入式 Tomcat、 Jetty、Undertow容器(无需部署war文件)。
3、提供的 starters 简化构建配置
4、尽可能自动配置 spring应用。
5、提供生产指标,例如指标、健壮检查和外部化配置
6、完全没有代码生成和 XML配置要求

2.为什么要用springboot

(1)简化配置,不需要编写太多的xml配置文件;

(2)基于Spring构建,使开发者快速入门,门槛很低;

(3)SpringBoot可以创建独立运行的应用而不需要依赖于容器;

(4)内置tomcat服务器,不需要打包成war包,可以直接放到tomcat中运行;

(5)提供maven极简配置,以及可视化的相关监控功能,比如性能监控,应用的健康程度等;

(6)为微服务SpringCloud奠定了基础,使得微服务的构建变得简单;

(7)Spring可以整合很多各式各样的框架,并能很好的集成;

(8)活跃的社区与论坛,以及丰富的开发文档;

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

Spring Boot 有两种类型的配置文件,application 和 bootstrap 文件
Spring Boot会自动加载classpath目前下的这两个文件,文件格式为 properties 或 yml 格式

*.properties 文件是 key=value 的形式
*.yml 是 key: value 的形式
*.yml 加载的属性是有顺序的,但不支持 @PropertySource 注解来导入配置,一般推荐用yml文件,看下来更加形象

bootstrap 配置文件是系统级别的,用来加载外部配置,如配置中心的配置信息,也可以用来定义系统不会变化的属性.bootstatp 文件的加载先于application文件
application 配置文件是应用级别的,是当前应用的配置文件

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

.properties 和 .yml,它们的区别主要是书写格式不同。
1).properties
app.user.name = javastack
2).yml
app:
user:
name: javastack
另外,.yml 格式不支持 @PropertySource 注解导入配置。

十二、mybatis

1. #{}和${}的区别

  • #{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。
  • Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。
  • Mybatis在处理${} 时 , 是 原 值 传 入 , 就 是 把 {}时,是原值传入,就是把 时,是原值传入,就是把{}替换成变量的值,相当于JDBC中的Statement编译
  • 变量替换后,#{} 对应的变量自动加上单引号 ‘’;变量替换后,${} 对应的变量不会加上单引号 ‘’
  • #{} 可以有效的防止SQL注入,提高系统安全性;${} 不能防止SQL 注入
  • #{} 的变量替换是在DBMS 中;${} 的变量替换是在 DBMS 外

2…mybatis 有几种分页方式?

一、数组分页

也就是说用这个函数可以和sql语句一样实现分页,原理是将查询出的数组,取出从指定下标开始到指定长度的数组

我们的数据未必都是存储在数据库中,很多时候是用数组来组织的。所以获取数组数据,进行分页是比较常见的编程要求

array_slice(原数组,开始下标,要取几条),这里用到三个参数(如果不写第三个参数 返回直到数组末端的所有元素)

二、sql分页

mysql分页使用limit,limit 子句可以被用于强制 select 语句返回指定的记录数。limit 接受一个或两个数字参数。参数必须是一个整数常量。 如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。

三、拦截器分页

使用拦截器拦截原始的sql,然后加上分页查询的关键字和属性,拼装成新的sql语句再交给mybatis去执行。

四、RowBounds分页

Mybatis提供RowBounds类来实现逻辑分页。RowBounds中有2个字段offset和limit。这种方式获取所有的ResultSet,从ResultSet中的offset位置开始获取limit个记录。但这并不意味着JDBC驱动器会将所有的ResultSet存放在内存,实际上只加载小部分数据到内存,如果需要,再加载部分数据到内存

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

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

4、mybatis 逻辑分页和物理分页的区别是什么?

  • 物理分页速度上并不一定快于逻辑分页,
  • 逻辑分页速度上也并不一定快于物理分页。
  • 物理分页总是优于逻辑分页:没有必要将属于数据库端的压力加诸到应用端来,
  • 就算速度上存在优势,然而其它性能上的优点足以弥补这个缺点。

5.mybatis是否支持延迟加载,如果支持他的实现原理是什么

1.mybatis仅支持关联对象association和关联集合对象collection的延迟加载,association是一对一,collection指的是一对多查询,在mybatis配置文件中可以配置lazyloadingEnable=true/false.

2.原理:使用CGLIB为目标对象建立代理对象,当调用目标对象的方法时进入拦截器方法。比如调用a.getb().getName(),拦截器方法invoke()发现a.getb()为null值,会单独发送事先保存好的查询关联b对象的sql语句,把b查询上来然后调用a.setB(b),于是a的对象的属性b就有值了,然后接着调用a.getb().getName(),这就是延迟加载的原理。

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

Mybatis的一级缓存是默认开启的,它只相对于同一个SqlSession有效,所以也称之为SqlSession缓存。当参数和SQL完全相同的情况下,使用同一个SqlSession对象调用同一个Mapper方法,当第1次执行SQL语句后,MyBatis会自动将其放在缓存中,后续再次查询时,如果没有声明需要刷新,且缓存没有超时,会直接取出此前缓存的数据,而不会再次发送SQL到数据库。

Mybatis的二级缓存是默认未开启的,如果希望开启,需要在配置SQL的XML文件中配置节点,由于每个XML都通过根节点的namespace属性对应一个Mapper接口,所以,二级存储也称之为namespace缓存!在使用二级存储时,查询数据的节点需要配置useCache=“true”,并且,查询返回的结果类型必须是实现了Serializable接口的!另外,当缓存 了数据后,如果执行了当前XML中配置的增、删、改操作,会自动刷新此前的缓存数据!
关于二级缓存的特点还有许多,例如Mybatis使用了LRU算法来管理缓存的数据,但是,由于Mybatis的缓存在一定程度上是不可控的,所以,在实际应用中,一般并不使用Mybatis的缓存机制来实现数据缓存,而是使用自定义的缓存机制,或第3方缓存服务器,例如Redis、MemCache等!

例如Mybatis会在删除数据后自动刷新缓存数据,其目的是为了保证缓存的数据是有效的,但是,在一些高频率的访问中,刚刚缓存数据就被刷新,再次缓存又再次被刷新,其实是非常浪费性能和资源的,而且,在实际应用中,也不一定真的需要数据是非常精准的,就好比在电商平台购买一部手机,手机的库存值是8000还是6000,都不影响用户的购买,没有必要因为某用户购买成功后就更新手机的库存值!

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

1)sql 优化方面

Hibernate 不需要编写大量的 SQL,就可以完全映射,提供了日志、缓存、级联(级联比 MyBatis 强大)等特性,此外还提供 HQL(Hibernate Query Language)对 POJO 进行操作。但会多消耗性能。
MyBatis 手动编写 SQL,支持动态 SQL、处理列表、动态生成表名、支持存储过程。工作量相对大些。
2)开发方面

MyBatis 是一个半自动映射的框架,因为 MyBatis 需要手动匹配 POJO、SQL 和映射关系。
Hibernate 是一个全表映射的框架,只需提供 POJO 和映射关系即可。
3)Hibernate 优势

Hibernate 的 DAO 层开发比 MyBatis 简单,Mybatis 需要维护 SQL 和结果映射。
Hibernate 对对象的维护和缓存要比 MyBatis 好,对增删改查的对象的维护要方便。
Hibernate 数据库移植性很好,MyBatis 的数据库移植性不好,不同的数据库需要写不同 SQL。
Hibernate 有更好的二级缓存机制,可以使用第三方缓存。MyBatis 本身提供的缓存机制不佳。
4)Mybatis优势

MyBatis 可以进行更为细致的 SQL 优化,可以减少查询字段。
MyBatis 容易掌握,而 Hibernate 门槛较高。

总的来说,MyBatis 是一个小巧、方便、高效、简单、直接、半自动化的持久层框架,Hibernate 是一个强大、方便、高效、复杂、间接、全自动化的持久层框架。

所以对于性能要求不太苛刻的系统,比如管理系统、ERP 等推荐使用 Hibernate,而对于性能要求高、响应快、灵活的系统则推荐使用 MyBatis。

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

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

CachingExecutor:CachingExecutor是一个Executor接口的装饰器,它为Executor对象增加了二级缓存的相关功能,委托的执行器对象可以是SimpleExecutor、ReuseExecutor、BatchExecutor中任一一个。执行 update 方法前判断是否清空二级缓存;执行 query 方法前先在二级缓存中查询,命中失败再通过被代理类查询。

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

分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql,然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

十三、MySql

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

第一范式( 1NF): 字段具有原子性,不可再分。 所有关系型数据库系统都满足第一范式)
数据库表中的字段都是单一属性的, 不可再分。 例如, 姓名字段, 其中的姓和名必须作为一
个整体, 无法区分哪部分是姓, 哪部分是名, 如果要区分出姓和名, 必须设计成两个独立的
字段。
第二范式( 2NF):
第二范式( 2NF) 是在第一范式( 1NF) 的基础上建立起来的, 即满足第二范式( 2NF) 必
须先满足第一范式( 1NF)。
要求数据库表中的每个实例或行必须可以被惟一地区分。 通常需要为表加上一个列, 以存储
各个实例的惟一标识。 这个惟一属性列被称为主关键字或主键。
第二范式( 2NF) 要求实体的属性完全依赖于主关键字。 所谓完全依赖是指不能存在仅依赖
主关键字一部分的属性, 如果存在, 那么这个属性和主关键字的这一部分应该分离出来形成
一个新的实体, 新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,
以存储各个实例的惟一标识。 简而言之, 第二范式就是非主属性非部分依赖于主关键字。
第三范式的要求如下:
满足第三范式( 3NF) 必须先满足第二范式( 2NF)。 简而言之, 第三范式( 3NF) 要求一个
数据库表中不包含已在其它表中已包含的非主关键字信息。
所以第三范式具有如下特征:
1, 每一列只有一个值
2, 每一行都能区分。
3, 每一个表都不包含其他表已经包含的非主关键字信息。

2.说一下 ACID 是什么?

  • Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
  • Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

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

1、char的长度是不可变的,而varchar的长度是可变的

字段b:类型char(10), 值为:abc,存储为:abc (abc+7个空格)

字段d:类型varchar(10), 值为:abc,存储为:abc (自动变为3个的长度)

2、超出长度自动截取

字段c:类型char(3), 值为:abcdefg,存储为:abc(defg自动删除)

字段e:类型varchar(3), 值为:abcdefg,存储为:abc (defg自动删除)

3、var(10)和char(10),都表示可存10个字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放10个

4、char最多可以存放255个字符

varchar的最大长度为65535个字节,varchar可存放的字符数跟编码有关

字符类型若为gbk,每个字符最多占2个字节,最大长度不能超过32766个字符

字符类型若为utf8,每个字符最多占3个字节,最大长度不能超过21845个字符

5、char和varchar的最大长度限制是mysql规定的

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

1.内连接,显示两个表中有联系的所有数据;

2.左链接,以左表为参照,显示所有数据,右表中没有则以null显示

3.右链接,以右表为参照显示数据,,左表中没有则以null显示

5.mysql 索引是怎么实现的

索引的数据结构和具体存储引擎的实现有关, 在MySQL中使用较多的索引有Hash索引,B+树索引等,而我们经常使用的InnoDB存储引擎的默认索引实现为:B+树索引.

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

为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

隔离级别脏读不可重复读幻影读
READ-UNCOMMITTED
READ-COMMITTED×
REPEATABLE-READ××
SERIALIZABLE×××

SQL 标准定义了四个隔离级别:

  • READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
  • READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
  • REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
  • SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别

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

a.Innodb引擎,Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。它本身实际上是基于Mysql后台的完整的系统。Mysql运行的时候,Innodb会在内存中建立缓冲池,用于缓冲数据和索引。但是,该引擎是不支持全文搜索的。同时,启动也比较的慢,它是不会保存表的行数的。当进行Select count(*) from table指令的时候,需要进行扫描全表。所以当需要使用数据库的事务时,该引擎就是首选。由于锁的粒度小,写操作是不会锁定全表的。所以在并发度较高的场景下使用会提升效率的。

b.MyIASM引擎,它是MySql的默认引擎,但不提供事务的支持,也不支持行级锁和外键。因此当执行Insert插入和Update更新语句时,即执行写操作的时候需要锁定这个表。所以会导致效率会降低。不过和Innodb不同的是,MyIASM引擎是保存了表的行数,于是当进行Select count(*) from table语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的。可以将MyIASM作为数据库引擎的首

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

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

–行锁:开销大,加锁慢,会出现死锁,锁定粒度小,发生锁冲突概率低,并发度高

–页锁:介于行锁和表锁之间,会出现死锁,并发度一般

表锁更适用于以查询为主,只有少量按索引条件更新数据的应用;行锁更适用于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用

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

乐观锁:每次去获取数据的时候都认为别人不会修改,不会上锁,但是在提交修改的时候会判断一下在此期间别人有没有修改这个数据。
悲观锁:每次去获取数据的时候都认为别人会修改,每次都会上锁,阻止其他线程获取数据,直到这个锁释放。

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

  • 使用 show processlist 命令查看当前所有连接信息。
  • 使用 explain 命令查询 SQL 语句执行计划。
  • 开启慢查询日志,查看慢查询的 SQL。

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

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

十四、 Redis

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

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis 使用场景:
数据高并发的读写
海量数据的读写
对扩展性要求高的数据

2.redis 有哪些功能?

数据缓存功能
分布式锁的功能
支持数据持久化
支持事务
支持消息队列

3.redis 和 memecache 有什么区别?

memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
redis的速度比memcached快很多
redis可以持久化其数据

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

因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。

关于 Redis 的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。

而且单线程并不代表就慢 nginx 和 nodejs 也都是高性能单线程的代表

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

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

解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

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

string、list、hash、set、zset。

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

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

8.jedis 和 redisson 有哪些区别?

Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。

Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

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

合理设置缓存的过期时间。
新增、更改、删除数据库操作时同步更新 Redis,可以使用事物机制来保证数据的一致性。

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

Redis 的持久化有两种方式,或者说有两种策略:

RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中

11.redis 怎么实现分布式锁

Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。

占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。

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

Redis 分布式锁不能解决超时的问题,分布式锁有一个超时时间,程序的执行如果超出了锁的超时时间就会出现问题。

13.redis 如何做内存优化?

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。

比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。

14. redis 淘汰策略有哪些?

  • volatile-lru:从已设置过期时间的数据集(server. db[i]. expires)中挑选最近最少使用的数据淘汰
  • volatile-ttl:从已设置过期时间的数据集(server. db[i]. expires)中挑选将要过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集(server. db[i]. expires)中任意选择数据淘汰。
  • allkeys-lru:从数据集(server. db[i]. dict)中挑选最近最少使用的数据淘汰。
  • allkeys-random:从数据集(server. db[i]. dict)中任意选择数据淘汰。
  • no-enviction(驱逐):禁止驱逐数据。

15.redis 常见的性能问题有哪些?该如何解决?

主服务器写内存快照,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以主服务器最好不要写内存快照。
Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,主从库最好在同一个局域网内。

十五、JVM

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

类加载器(ClassLoader)
运行时数据区(Runtime Data Area)
执行引擎(Execution Engine)
本地库接口(Native Interface)

组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

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

程序计数器
虚拟机栈
本地方法栈

方法区

有的区域随着虚拟机进程的启动而存在,有的区域则依赖用户进程的启动和结束而创建和销毁。

3.说一下堆栈的区别?

  • 物理地址

堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。比如,标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩)

使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。

  • 内存分别

因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈

连续的,所以分配的内存大小要在编译期就确认大小是固定的

  • 存放的内容

存放的是对象的实例和数组。因此该区更关注的是数据的存储

存放局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行

PS:

静态变量放在方法区
静态的对象还是放在堆。

程序的可见度

堆对于整个应用程序都是共享、可见的。

栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同

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

队列和栈都是被用来预存储数据的。

  • 操作的名称不同。队列的插入称为入队,队列的删除称为出队。栈的插入称为进栈,栈的删除称为出栈。
  • 可操作的方式不同。队列是在队尾入队,队头出队,即两边都可操作。而栈的进栈和出栈都是在栈顶进行的,无法对栈底直接进行操作。
  • 操作的方法不同。队列是先进先出(FIFO),即队列的修改是依先进先出的原则进行的。新来的成员总是加入队尾(不能从中间插入),每次离开的成员总是队列头上(不允许中途离队)。而栈为后进先出(LIFO),即每次删除(出栈)的总是当前栈中最新的元素,即最后插入(进栈)的元素,而最先插入的被放在栈的底部,要到最后才能删除。

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

5.1 工作原理

  • 当类加载器收到类加载请求时,他不会去加载该类,而是委托给他的父类去加载
  • 如果父类还有父类,则进一步向上委托,直到根加载器
  • 如果根加载器能够加载该类,则成功返回;否则,抛出异常

5.2 优势

  • 避免类的重复加载
  • 保护程序安全,防止核心API被随意篡改

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

  • 加载:根据查找路径找到相应的 class 文件然后导入;
  • 检查:检查加载的 class 文件的正确性;
  • 准备:给类中的静态变量分配内存空间;
  • 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址;
  • 初始化:对静态变量和静态代码块执行初始化工作。

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

一般有两种方法来判断:

引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。

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

  1. 强引用
  2. 软引用
  3. 弱引用
  4. 虚引用(幽灵引用/幻影引用)

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

  • 标记-清除算法
  • 标记-整理算法
  • 复制算法
  • 分代算法

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

  • Serial:最早的单线程串行垃圾回收器。
  • Serial Old:Serial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。
  • ParNew:是 Serial 的多线程版本。
  • Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系的吞吐量。
  • Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。
  • CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。
  • G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项

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

  • 新生代回收器:Serial、ParNew、Parallel Scavenge
  • 老年代回收器:Serial Old、Parallel Old、CMS
  • 整堆回收器:G1
  • 新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;
  • 老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

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

分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。

新生代使用的是复制算法,新生代里有 3 个分区:Eden、To 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)时,升级为老生代。大对象也会直接进入老生代。

老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

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

JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。

jconsole:用于对 JVM 中的内存、线程和类等进行监控;

jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。

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

-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。
erial 垃圾回收器的老年版本,同样也是单线程的,可以作为 CMS 垃圾回收器的备选预案。

  • ParNew:是 Serial 的多线程版本。
  • Parallel 和 ParNew 收集器类似是多线程的,但 Parallel 是吞吐量优先的收集器,可以牺牲等待时间换取系的吞吐量。
  • Parallel Old 是 Parallel 老生代版本,Parallel 使用的是复制的内存回收算法,Parallel Old 使用的是标记-整理的内存回收算法。
  • CMS:一种以获得最短停顿时间为目标的收集器,非常适用 B/S 系统。
  • G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项

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

  • 新生代回收器:Serial、ParNew、Parallel Scavenge
  • 老年代回收器:Serial Old、Parallel Old、CMS
  • 整堆回收器:G1
  • 新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;
  • 老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

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

分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。

新生代使用的是复制算法,新生代里有 3 个分区:Eden、To 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)时,升级为老生代。大对象也会直接进入老生代。

老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

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

JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。

jconsole:用于对 JVM 中的内存、线程和类等进行监控;

jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、gc 变化等。

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

-Xms2g:初始化推大小为 2g;
-Xmx2g:堆最大内存为 2g;
-XX:NewRatio=4:设置年轻的和老年代的内存比例为 1:4;
-XX:SurvivorRatio=8:设置新生代 Eden 和 Survivor 比例为 8:2;
–XX:+UseParNewGC:指定使用 ParNew + Serial Old 垃圾回收器组合;
-XX:+UseParallelOldGC:指定使用 ParNew + ParNew Old 垃圾回收器组合;
-XX:+UseConcMarkSweepGC:指定使用 CMS + Serial Old 垃圾回收器组合;
-XX:+PrintGC:开启打印 gc 信息;
-XX:+PrintGCDetails:打印 gc 详细信息。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱上晨间阳光

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值