Java面试题

JAVA语言概述

需要会手写的代码!

饿汉式: 对象预先加载,线程是安全的,在类创建好的同时对象生成,调用获得对象实例的方法反应速度快,代码简练。

懒汉式: 对象延迟加载,效率高,只有在使用的时候才实例化对象,但若设计不当线程会不安全,代码相对于饿汉式复杂,

​ 第一次加载类对象的时候反应不快。

单例模式:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。

两种方式的对比:

  • 饿汉式:
    • 坏处:对象加载时间过长。
      
    • 好处:饿汉式是线程安全的
      
  • 懒汉式:
    • 好处:延迟对象的创建。
    • 	  线程不安全。--->修改
      

饿汉式1:

class Bank{
	//1.私有化类的构造器
	private Bank(){
	
	}

    //2.内部创建类的对象
    //4.要求此对象也必须声明为静态的
    private static Bank instance = new Bank();

    //3.提供公共的静态的方法,返回类的对象
    public static Bank getInstance(){
        return instance;
    }
}

饿汉式2:使用了静态代码块

class Order{
    //1.私有化类的构造器
    private Order(){

    }

    //2.声明当前类对象,没初始化
    //4.此对象也必须声明为static的
    private static Order instance = null;

    static{
        instance = new Order();
    //3.声明public、static的返回当前类对象的方法
    public static Order getInstance(){
        return instance;
    }
}

懒汉式:

class Bank {

    private Bank() {
    }

    private static Bank instance = null;

    public static Bank getInstance() 
    {
        if (instance == null) {
		  //同步代码块,解决线程安全问题
            synchronized (Bank.class) 
            {
                if (instance == null) 
                {
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}

在这里插入图片描述

冒泡排序

int[] arr = new int[]{43,32,76,-98,0,64,33,-21,32,99};
//冒泡排序
for(int i = 0;i < arr.length - 1;i++){

    for(int j = 0;j < arr.length - 1 - i;j++){

        if(arr[j] > arr[j + 1]){
            int temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j + 1] = temp;
        }
    }
}

for(int i = 0;i < arr.length;i++){
    System.out.print(arr[i] + "\t");
}

1、JVM、JRE和JDK的关系

JVM

Java Virtual Machine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

JRE

Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包

如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。

JDK

Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等

JVM&JRE&JDK关系图

在这里插入图片描述

2、什么是跨平台性?原理是什么

所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。

实现原理:Java程序是通过java虚拟机在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行java程序。

3、Java语言有哪些特点

简单易学(Java语言的语法与C语言和C++语言很接近)

面向对象(封装,继承,多态)

平台无关性(Java虚拟机实现平台无关性)

支持网络编程并且很方便(Java语言诞生本身就是为简化网络编程设计的)

支持多线程(多线程机制使应用程序在同一时间并行执行多项任务)

健壮性(Java语言的强类型机制、异常处理、垃圾的自动收集等)

安全性

4、什么是字节码?采用字节码的最大好处是什么

字节码:Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。

采用字节码的好处:

Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。
在这里插入图片描述

Java源代码--->编译器--->jvm可执行的Java字节码(即虚拟指令)--->jvm--->jvm中解释器--->机器可执行的二进制机器码--->程序运行。

5、什么是Java程序的主类?应用程序和小程序的主类有何不同?

一个程序中可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类。应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类是Java程序执行的入口点。

6、Java和C++的区别

  • 都是面向对象的语言,都支持封装、继承和多态
  • Java不提供指针来直接访问内存,程序内存更加安全
  • Java的类是单继承的,C++支持多重继承;虽然Java的类不可以多继承,但是接口可以多继承。
  • Java有自动内存管理机制,不需要程序员手动释放无用内存

7、Java有哪些数据类型

在这里插入图片描述

  1. 整型:byte(1字节=8bit) \ short(2字节) \ int(4字节) \ long(8字节)
  2. 浮点型:float(4字节) \ double(8字节)
  3. 字符型:char (1字符=2字节)
  4. 布尔型:boolean

8、谈谈你对面向对象中类和对象的理解,并指出二者的关系?

类:抽象的、概念上的内容。

对象:实实在在存在的一个个体。在生活中,就是摸得着,在编程中,在内存真正的创建了一个对象,占据了一定的内存空间,这就叫实实在在。

对象是类的一个实例。对象是由类派生出来的。(New出来的)

举一个开发的例子:

比如,当我们与控制台去交互时,需要造、需要提供一个类,这个API给我们提供好了,叫Scanner类,这就是一个类的概念,我们真正执行的时候,需要创建这个Scanner的对象,通过这个Scanner的对象去操作功能方法,去完成我们和控制台的交互。

8、权限修饰符

在这里插入图片描述
权限修饰符可用来修饰的结构说明:
具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public

9、成员变量与局部变量的区别

1、在类中的位置不同
成员变量:在类中方法外面
局部变量:在方法或者代码块中,或者方法的声明上(即在参数列表中)
2、在内存中的位置不同
成员变量:在堆中(方法区中的静态区)
局部变量:在栈中
3、生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用或者代码块的执行而存在,随着方法的调用完毕或者代码块的执行完毕而消失
4、初始值
成员变量:默认初始值
局部变量:没默认初始值,使用之前需要赋值,否则编译器会报错

java中参数传递机制:值传递

规则:

  • 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
  • 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。

重写和重载?

重载:我们可以在同一个类当中,可以声明多个方法名相同,形参列表不同的方法,彼此之间就构成重载。构造器也可以重载。

重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。

子类返回类型小于等于父类方法返回类型, 子类抛出异常小于等于父类方法抛出异常, 子类访问权限大于等于父类方法访问权限。

10、== 和 equals 的区别是什么?

== :运算符

1.可以使用在基本数据类型变量和引用数据类型变量中

2.如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)

如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

补充: == 符号使用时,必须保证符号左右两边的变量类型一致。

equals:

说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。

11、final 在 Java 中有什么作用?

  • final 修饰的类叫最终类,该类不能被继承。
    • 比如:String类、System类、StringBuffer类
  • final 修饰的方法不能被重写。
    • 比如:Object类中getClass();
  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

static final 用来修饰属性:全局常量

12、throw 和 throws区别:

throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。

13、final、finally、finalize 有什么区别?

  • final:是修饰符,如果修饰类,此类不能被继承;如果修饰方法和变量,则表示此方法和此变量不能在被改变,只能使用。
  • finally:是 try{} catch{} finally{} 最后一部分,表示不论发生任何情况都会执行,finally 部分可以省略,但如果 finally 部分存在,则一定会执行 finally 里面的代码。
  • finalize: 是 Object 类的一个protected方法,在垃圾收集器执行的时候会调用被回收对象的此方法。子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。

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

finally 一定会执行,即使是 catch 中 return 了,catch 中的 return 会等 finally 中的代码执行完之后,才会执行。

常见的异常类有哪些?

  • NullPointerException 空指针异常
  • ClassNotFoundException 指定类不存在
  • NumberFormatException 字符串转换为数字异常
  • IndexOutOfBoundsException 数组下标越界异常
  • ClassCastException 数据类型转换异常
  • FileNotFoundException 文件未找到异常
  • NoSuchMethodException 方法不存在异常
  • IOException IO 异常
  • SocketException Socket 异常

15、接口和抽象类有什么区别?

  • 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。

  • 构造函数:抽象类可以有构造函数;接口不能有。

  • 实现数量:类可以实现很多个接口;但是只能继承一个抽象类。

  • 访问修饰符:接口中的方法默认使用 public 修饰, public abstract;public static final

    ​ 抽象类中的方法可以是任意访问修饰符

16、String、StringBuffer、StringBuilder三者的对比

String(JDK1.0):不可变的字符序列;底层使用char[]存储
StringBuffer(JKD1.0):可变的字符序列、效率低、线程安全;底层使用char[]存储
StringBuilder(JDK5.0):可变的字符序列、效率高、线程不安全;底层使用char[]存储
注意:作为参数传递的话,方法内部String不会改变其值,StringBuffer和StringBuilder会改变其值。
相同点:都与字符串相关的,底层使用char[]存储

扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组.底层创建了一个长度是16的数组。
默认情况下,扩容为原来容量的2倍 + 2,同时将原数组的元素复制到新的数组中.
开发中,若需要频繁的对字符串进行修改,就不要择String,

应根据线程安全情况择StringBuffer或StringBuilder

StringBuffer:当涉及多线程;线程安全问题,推荐使用

17、 如何将字符串反转?

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

18、String 类的常用方法都有那些?

  • indexOf():返回指定字符的索引。

  • charAt():返回指定索引处的字符。

  • replace():字符串替换。

  • trim():去除字符串两端空白。

  • split():分割字符串,返回一个分割后的字符串数组。

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

  • length():返回字符串长度。

  • toLowerCase():将字符串转成小写字母。

  • toUpperCase():将字符串转成大写字符。

  • substring():截取字符串。

  • equals():字符串比较。

Object类的说明:

  • 方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()
  • wait() 、 notify()、notifyAll()
    

final、finally、finalize三者的区别?
前两个是关键字,finalize是方法名
类似:
throw 和 throws
Collection 和 Collections
String 、StringBuffer、StringBuilder
ArrayList 、 LinkedList
HashMap 、LinkedHashMap

多线程:

在这里插入图片描述

19、并行和并发有什么区别?

  • 并行:多个处理器或多核处理器同时处理多个任务。
  • 并发:一个CPU(采用时间片)同时执行多个任务,从逻辑上来看那些任务是同时执行。

20、进程和线程的区别?

一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。

进程:

  • 概念:程序的一次执行过程,或是正在运行的一个程序。
  • 说明:进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

线程:

  • 概念:进程可进一步细化为线程,是一个程序内部的一条执行路径。
  • 说明:线程作为调度和执行的最小单位,每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。

进程可以细化为多个线程。
每个线程,拥有自己独立的:栈、程序计数器
多个线程,共享同一个进程中的结构:方法区、堆。

21、多线程的创建一共四种方式

1.继承Thread类

2.实现Runnable接口

3.实现Callable接口

4.通过Executors工具类创建线程池

22、Runnable 和 Callable 有什么区别?

runnable 没有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的补充。

23、死锁的理解:

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

24、怎么防止死锁?

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

25、如何解决线程安全问题?有几种方式

synchronized:同步代码块、同步方法

Lock锁:实例化ReentrantLock,调用lock()、unlock()方法

synchronized 与 Lock 的对比

  1. Lock是显示锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放

  2. Lock只有代码块锁,synchronized有代码块锁和方法锁

  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

使用的优先顺序:

Lock —> 同步代码块(已经进入了方法体,分配了相应资源 ) —> 同步方法(在方法体之外)
3.利弊
同步的方式,解决了线程的安全问题。—好处
操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。

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

  • volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。

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

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

    synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。volatile不会造成线程的阻塞;

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

26、synchronized 与 Lock的异同?

相同:二者都可以解决线程安全问题

不同:synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。

synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器

Lock需要手动的启动同步(lock(),同时结束同步也需要手动的实现(unlock())

55. synchronized 和 ReentrantLock 区别是什么?

synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在 Java 6 中对 synchronized 进行了非常多的改进。

主要区别如下:

  • ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
  • ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;
  • ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。

56. 说一下 atomic 的原理?

atomic 主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。

27、线程有哪些状态?

线程的状态:

  • NEW 尚未启动
  • RUNNABLE 正在执行中
  • BLOCKED 阻塞的(被同步锁或者IO锁阻塞)
  • WAITING 永久等待状态
  • TIMED_WAITING 等待指定的时间重新被唤醒的状态
  • TERMINATED 执行完成

28、线程的生命周期

在这里插入图片描述

29、sleep() 和 wait()的异同?

1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。

2.不同点:

  • 类的不同:Thread类中声明sleep() , Object类中声明wait()
  • 释放锁:sleep() 不释放锁;wait() 释放锁。
  • 调用的要求不同:sleep()可以在任何需要的场景下调用。
    wait()必须使用在同步代码块或同步方法中
  • 用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。

30、notify()和 notifyAll()有什么区别?

  • wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
  • notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
  • notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。

notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。

31、线程的 run() 和 start() 有什么区别?

start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次

run():将此线程执行的操作声明在run()中

start():① 启动当前线程 ② 调用当前线程的run()

32、ThreadLocal 是什么?有哪些使用场景?

ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,

而不会影响其它线程所对应的副本。一种以空间换时间的做法 , 在每个Thread 里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了

ThreadLocal 的经典使用场景是数据库连接和 session 管理等

集合:

33. Java 容器都有哪些?

Java 容器分为 Collection 和 Map 两大类,其下又有很多子类,如下所示:

  • Collection 接口:单列集合,用来存储一个一个的对象
  • List
    • ArrayList :
      • LinkedList:
      • Vector:
    • Stack
  • Set
    • HashSet
    • LinkedHashSet
    • TreeSet
  • Map 双列数据,存储key-value一对键值对的数据
  • HashMap
    • LinkedHashMap
  • TreeMap
  • Hashtable

/ConcurrentHashMap

34、ArrayList、LinkedList、Vector者的异同?

同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据

|----Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储有序的、可重复的数据。 -->“动态”数组,替换原的数组
|----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
|----LinkedList:线程不安全;对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
|----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储

ArrayList:jdk 7情况下,在造对象的时候,就已经把底层数组实例化了,底层创建了长度是10的Object[]数组elementData

​ jdk 8中ArrayList的变化:当调用add()方法时候,才把底层数组创建好,延迟了数组的创建,节省内存空间

数组容量不够,则扩容。默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中(造新的)

Vector的源码分析:JDK7和JDK8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。

集合添加的对象,所在的类要重写equals()方法

35、Comparable与Comparator对比?

我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
使用两个接口中的任何一个:Comparable 或 Comparator

自然排序:使用Comparable接口,重写了compareTo(obj)方法

​ 像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。

定制排序:使用Comparator接口,重写compare(Object o1,Object o2)方法

如果是Srtring、包装类、Date就可以直接使用排序方法compareTo。若是自定义类需要重写compareTo(obj)方法

Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。
Comparator接口属于临时性的比较。

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

37、ArrayList 和 LinkedList 的区别是什么?

  • 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
  • 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
  • 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。

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

38、如何实现数组和 List 之间的转换?

  • 数组转 List:使用 Arrays. asList(array) 进行转换。
  • List 转数组:使用 List 自带的 toArray() 方法。

39、Array 和 ArrayList 有何区别?

  • Array 是数组,必须指定数组的数据类型及数组长度,即数组中存放的元素个数固定并且类型相同;

    ArrayList 动态数组,长度动态可变,会自动扩容

  • Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。

  • Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。

  • Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。

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

HashSet 线程不安全的;可以存储null值,HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,New一个HashSet,就相当于new了一个HashMap,HashSet当调用add()添加元素的时候,相当于把元素放到HashMap的key中,所有的key指向同一个value。因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 存储无序的不可重复的值。

41、HashMap的底层实现原理?

4.1 HashMap在JDK7中实现原理:(重点!)
HashMap map = new HashMap():

  •  在实例化以后,底层创建了长度是16的一维数组Entry[] table。
    
  •  ...可能已经执行过多次put...
    
  •  map.put(key1,value1):
    
  •  首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
    
  •  如果此位置上的数据为空,此时的key1-value1(Entry)添加成功。 ----情况1
    
  •  如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
    
  •          如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。----情况2
    
  •          如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:
    
  •                  如果equals()返回false:此时key1-value1添加成功。----情况3
    
  •  如果equals()返回true:使用value1替换value2。
    
  •  补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
    
  • 在不断的添加过程中,会涉及到扩容问题,当超出临界值12(且要存放的位置非空)时,扩容。
    
    默认的扩容方式:扩容为原来容量的2倍,并将原的数据复制过来。

4.2 HashMap在jdk8中相较于jdk7在底层实现方面的不同:

  1. new HashMap():底层没创建一个长度为16的数组

  2. jdk 8底层的数组是:Node[],而非Entry[]

  3. 首次调用put()方法时,底层创建长度为16的数组

  4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
    4.1 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
    4.2 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64 时 —>添加使用红黑树
    此时此索引位置上的所有数据改为使用红黑树存储。便于查找,效率高。

    红黑树:有序的,二叉排序数;小的放左边,大的放右边。

    *当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 < 64 时 —>选择扩容

42、HashMap 和 Hashtable的异同?

  • 存储:HashMap 运行 key 和 value 为 null,而 Hashtable 不允许。
  • 线程安全:Hashtable 是线程安全的,而 HashMap 是非线程安全的。
  • 推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。

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

对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。

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

Vector、Hashtable、Stack 都是线程安全的,而像 HashMap 则是非线程安全的,不过在 JDK 1.5 之后随着 Java. util. concurrent 并发包的出现,它们也有了自己对应的线程安全类,比如 HashMap 对应的线程安全类就是 ConcurrentHashMap。

45、Iterator 怎么使用?有什么特点?

1.遍历Collection的两种方式:
① 使用迭代器Iterator ② foreach循环(或增强for循环)

hasNext():判断是否还有下一个元素

next():①指针下移 ②将下移以后集合位置上的元素返回

Iterator 的特点是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出异常

46、序列化和反序列化

序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
使用ObjectOutputStream实现

反序列化ObjectInputStream过程:将磁盘文件中的对象还原为内存中的一个java对象
使用ObjectInputStream实现

对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,

或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象

要求:要求类必须实现序列化接口,并且序列化和反序列化必须保持序列化的ID一致

47、Java 中 IO 流分为几种?

按类型来分:字节流、字符流

按照流的流向分:输入流、输出流

流的角色:节点流、处理流

字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。

虽然流很多,但是有4个抽象类基类中派生出来的

在这里插入图片描述

48、BIO、NIO、AIO 有什么区别?

  • BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
  • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
  • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

同步:一定要等任务执行完了,得到结果,才执行下一个任务。 异步:不等任务执行完,直接执行下一个任务。

49、Files的常用方法都有哪些?

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

Java Web

64. JSP 和 servlet 有什么区别?

JSP 是 servlet 技术的扩展,本质上就是 servlet 的简易方式。servlet 和 JSP 最主要的不同点在于,servlet 的应用逻辑是在 Java 文件中,并且完全从表示层中的 html 里分离开来,而 JSP 的情况是 Java 和 html 可以组合成一个扩展名为 JSP 的文件。JSP 侧重于视图,servlet 主要用于控制逻辑。

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

JSP 有 9 大内置对象:

  • request:封装客户端的请求,其中包含来自 get 或 post 请求的参数;
  • response:封装服务器对客户端的响应;
  • pageContext:通过该对象可以获取其他对象;
  • session:封装用户会话的对象;
  • application:封装服务器运行环境的对象;
  • out:输出服务器响应的输出流对象;
  • config:Web 应用的配置对象;
  • page:JSP 页面本身(相当于 Java 程序中的 this);
  • exception:封装页面抛出异常的对象。

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

  • page:代表与一个页面相关的对象和属性。
  • request:代表与客户端发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个 Web 组件;需要在页面显示的临时数据可以置于此作用域。
  • session:代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的 session 中。
  • application:代表与整个 Web 应用程序相关的对象和属性,它实质上是跨越整个 Web 应用程序,包括多个页面、请求和会话的一个全局作用域。

67. cookie 和 session 有什么区别?

  • 存储位置不同:cookie 存储在客户端,session 存储在服务器端;
  • 安全性不同:cookie 安全性一般,在浏览器存储,可以被伪造和修改;所以登录信息一般放在session中;
  • 容量和个数限制:cookie 有容量限制4k,每个站点下的 cookie 也有个数限制20。
  • 存储的多样性:session 可以存储在 Redis 中、数据库中、应用程序中;而 cookie 只能存储在浏览器中。
  • Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号,这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,
  • Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
  • Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。

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

session 的工作原理是客户端登录完成之后,服务器会创建对应的 session,session 创建完之后,会把 session 的 id 发送给客户端,客户端再存储到浏览器中。这样客户端每次访问服务器时,都会带着 sessionid,服务器拿到 sessionid 之后,在内存找到与之对应的 session 这样就可以正常工作了。

Session 技术,底层其实是基于 Cookie 技术来实现的

在这里插入图片描述

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

可以用,session 只是依赖 cookie 存储 sessionid,如果 cookie 被禁用了,可以使用 url 中添加 sessionid 的方式保证 session 能正常使用。

71. 如何避免 SQL 注入?

  • 使用预处理 PreparedStatement。
  • 使用正则表达式过滤掉字符中的特殊字符。

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

XSS 攻击:即跨站脚本攻击,它是 Web 程序中常见的漏洞。原理是攻击者往 Web 页面里插入恶意的脚本代码(css 代码、Javascript 代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的,如盗取用户 cookie、破坏页面结构、重定向到其他网站等。

预防 XSS 的核心是必须对输入的数据做过滤处理。

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

CSRF:Cross-Site Request Forgery(中文:跨站请求伪造),可以理解为攻击者盗用了你的身份,以你的名义发送恶意请求,比如:以你名义发送邮件、发消息、购买商品,虚拟货币转账等。

防御手段:

  • 验证请求来源地址;
  • 关键操作添加验证码;
  • 在请求地址添加 token 并验证。

网络

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

301:永久重定向。

302:暂时重定向。

它们的区别是,301 对搜索引擎优化(SEO)更加有利;302 有被提示为网络拦截的风险。

常用的响应码说明

200 表示请求成功

302 表示请求重定向

404 表示请求服务器已经收到了,但是你要的数据不存在(请求地址错误)

500 表示服务器已经收到请求,但是服务器内部错误(代码错误)

80. forward 和 redirect 的区别?

forward 是转发 和 redirect 是重定向:

①转发的特点: 转发地址栏路径不变、 转发只能访问当前服务器下的资源、转发是一次请求,可以使用request对象来共享数据。②重定向的特点:地址栏发生变化、重定向可以访问其他站点(服务器)的资源、重定向是两次请求,不能使用request对象来共享数据

81. 简述 tcp 和 udp的区别?

tcp 和 udp 是 OSI 模型中的运输层中的协议。tcp 提供可靠的通信传输,而 udp 则常被用于让广播和细节控制交给应用的通信传输。

两者的区别大致如下:

  • tcp 面向连接,udp 面向非连接即发送数据前不需要建立链接;
  • tcp 提供可靠的服务(数据传输),udp 无法保证;
  • tcp 面向字节流,udp 面向报文;
  • tcp 需释放已建立的连接,效率低,传输慢;udp 无需释放资源,开销小,数据传输快;

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

如果采用两次握手,那么只要服务器发出确认数据包就会建立连接,但由于客户端此时并未响应服务器端的请求,那此时服务器端就会一直在等待客户端,这样服务器端就白白浪费了一定的资源。若采用三次握手,服务器端没有收到来自客户端的再此确认,则就会知道客户端并没有要求建立请求,就不会浪费服务器的资源。

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

tcp 粘包可能发生在发送端或者接收端:

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

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

  • 应用层:直接向用户提供服务,完成用户希望在网络上完成的各种工作。
  • 表示层:处理用户信息的表示问题,如编码、数据格式转换和加密解密等。
  • 会话层:向两个实体的表示层提供建立和使用连接的方法。
  • 传输层:向用户提供可靠的端到端的差错和流量控制,保证报文的正确传输。
  • 网络层:通过路由选择算法,为报文或分组通过通信子网选择最适当的路径。
  • 数据链路层:负责建立和管理节点间的链路。
  • 物理层:利用传输介质为数据链路层提供物理连接,实现比特流的透明传输。

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

  • get 请求会被浏览器主动缓存,而 post 不会。

  • get 传递参数有大小限制,而 post 没有。

  • get请求:请求行,请求头;post请求:请求行,请求头,空行,请求体(发送给服务器的数据)

  • get 的参数会明文限制在 url 上,post 不会,post 参数传输更安全。

  • get和post的区别:

    get数据放在url上,即HTTP协议头上;相对不安全,传输快,传输的数据量有限,不能用于文件上传;

    post把数据放在HTTP的请求体内(request body);安全,传输慢,传输的数据量可以认为是无限的,可以用于文件上传;

86. 如何实现跨域?

实现跨域有以下几种方案:

  • 服务器端运行跨域 设置 CORS 等于 *;
  • 在单个接口使用注解 @CrossOrigin 运行跨域;
  • 使用 jsonp 跨域;

87. 说一下 JSONP 实现原理?

jsonp:JSON with Padding,它是利用script标签的 src 连接可以访问不同源的特性,加载远程返回的“JS 函数”来执行的。

设计模式

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

  • 单例模式:保证被创建一次,节省系统开销。
  • 工厂模式(简单工厂、抽象工厂):解耦代码。
  • 观察者模式:定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。
  • 外观模式:提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层的接口,让子系统更容易使用。
  • 模版方法模式:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。
  • 状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

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

  • 简单工厂:用来生产同一等级结构中的任意产品,对于增加新的产品,无能为力。
  • 抽象工厂:用来生产不同产品族的全部产品,对于增加新的产品,无能为力;支持增加产品族
  • 工厂方法:用来生产同一等级结构中的固定产品,支持增加任意产品。

MySQL

Q1:MySQL主要有哪些存储引擎,分别适合哪些应用场景?

答:主要有①MyISAM,是5.5版本之前的默认存储引擎,支持表级锁,不支持事务和外键,并发效率较低,读取数据快,更新数据慢。适合以读操作为主,并且对并发性要求较低的应用。②InnoDB,MySQL目前的默认存储引擎,支持行级锁、事务和外键,并发效率好。适合对事务的完整性和并发性、数据的准确性要求比较高,增删操作多的应用。③Memory,所有的数据都保存在内存中,访问速度快,一旦服务关闭数据将丢失。适合更新不太频繁的数据量小的表用来快速得到访问结果。④Archive、Federated等。

Q2:索引是什么?

MySQL官方对索引定义:索引(index)是帮助MySQL高效获取数据的数据结构(有序)。索引就类似于书本的目录,是存储引擎用于提高数据库表访问速度的一种数据结构。数据是存储在磁盘上的,查询数据时,如果没有索引,会加载所有的数据到内存,依次进行检索,读取磁盘次数较多。有了索引,就不需要加载所有的数据,因为B+树的高度一般是在2~4层,最多需要读取2-4次磁盘,查询速度提高。

Q3:索引的优缺点有哪些?

答:①优势:提高数据检索的效率,降低数据库的IO成本。通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。②劣势:实际上索引也是一张表,该表中保存了主键与索引字段,并指向实体类的记录,所以索引列也是要占用空间的。 虽然索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE。因为更新表时,MySQL 不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。

Q4:MySQL都有哪些类型索引?按照数据结构的维度分类

答:①BTREE 索引 : 最常见的索引类型,大部分索引都支持 B 树索引。②HASH 索引:只有Memory引擎支持 , 使用场景简单 。③R-tree 索引(空间索引):空间索引是MyISAM引擎的一个特殊索引类型,相对于BTREE的优势在于范围查找。④Full-text (全文索引) :全文索引也是MyISAM的一个特殊索引类型,主要用于全文索引,InnoDB从Mysql5.6版本开始支持全文索引。

  • BTREE索引:MySQL数据库使用最多的索引类型是’BTREE’索引,底层基于B+树数据结构来实现。B+树是基于B树和叶子节点顺序访问指针进行实现,它具有B树的平衡性,并旦通过顺序访问指针来提高区间查询的性能。
  • 哈希索引:哈希索引是基于哈希表实现的,对于每一行数据,存储引擎会对索引列进行哈希计算得到哈希码,并且哈希算法要尽量保证不同的列值计算出的哈希码值是不同的,将哈希码的值作为哈希表的key值,将指向数据行的指针作为哈希表的value值。
  • 全文奈引 (FULLTEXT):目前只有MyISAM引擎支持。只有CHAR、 VARCHAR、TEXT列上可以创建全文索引。
  • RTREE索引:RTREE在MySQL很少使用,仅支持geometry数据类型。相对于BTREE,RTREE的优势在于范围查找。

Q5:B+树是什么和B树有什么区别?

答:①BTree又叫多路平衡搜索树,一颗m叉的BTree特性如下: 树中每个节点最多包含m个孩子。 除根节点与叶子节点外,每个节点至少有[ceil(m/2)]个孩子。若根节点不是叶子节点,则至少有两个孩子。所有的叶子节点都在同一层。每个非叶子节点由n个key与n+1个指针组成,其中[ceil(m/2)-1] <= n <= m-1。②B+Tree为BTree的变种,B+Tree与BTree的区别为: n叉B+Tree最多含有n个key,而BTree最多含有n-1个key。

B+Tree的叶子节点保存所有的key信息,依key大小顺序排列。所有的非叶子节点都可以看作是key的索引部分。③由于B+Tree只有叶子节点保存key信息,查询任何key都要从root走到叶子。所以B+Tree的查询效率更加稳定。

Q6:MySQL的索引对B+树做了哪些优化?

MySql索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。

Q7:索引有哪些分类?

①单值索引 :即一个索引只包含单个列,一个表可以有多个单列索引。

②唯一索引 :索引列的值必须唯一,但允许有空值。

③复合索引 :即一个索引包含多个列。

Q8:数据库的索引创建有哪些设计原则?

答:①对查询频次较高,数据量较大的表创建索引。②限制索引数量:对于增删改操作较多的表,如果索引过多将需要很高的维护代价,降低操作效率,增加操作耗时。③利用最左前缀:如果索引字段值过长,会降低索引的执行效率。④删除不常用索引。⑤使用唯一索引,区分度越高,效率越高。⑤使用短索引,如果索引值很长则占用磁盘变大,会影响效率。⑥为常作为查询条件、经常需要排序、分组和联合操作的字段建立索引。⑦尽量扩展现有索引,联合索引的效率高于多个独立索引。

Q9:索引在什么情况下会失效?

①复合索引未使用最左列索引时或跳跃使用时失效,例如以name,age和sex字段建立索引,只使用age和sex或只使用name和sex时索引失效。

②在索引上进行运算或函数操作时索引失效。

③数字字符没有加单引号索引失效,因为MySQL查询优化器会自动进行类型转换。

④LIKE以%开头的查询索引失效,%在前时执行计划更倾向于使用全表扫描。

⑤OR的前后没有同时使用索引时索引失效。

⑥当全表扫描比使用索引速度更快时会使用全表扫描。

Q10:简述数据库三大范式

  • 第一范式:强调的是列的原子性,即数据库表的每一列都是不可分割的原子数据项。
  • 第二范式:要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。
  • 第三范式:任何非主属性不依赖于其它非主属性。

Q11:MySQL数据库的隔离级别有哪些?分别有什么特点?

①读未提交,一个事务会读取到另一个事务没有提交的数据,存在脏读、不可重复读、幻读的问题。

②读已提交,一个事务可以读取到另一个事务已经提交的数据,解决了脏读的问题,存在不可重复读、幻读的问题。

③可重复读,MySQL默认的隔离级别,在一次事务中读取同一个数据结果是一样的,可以解决脏读,不可重复读。

④可串行化,每次读都需要获得表级共享锁,读写互相阻塞,效率低,解决了脏读,不可重复读,幻读问题。

Q12:读取数据库时可能出现哪些问题?

①脏读,一个事务中会读取到另一个事务中还没有提交的数据,读到的是其他事务“更新”的数据;如果另一事务最终回滚了数据,那么所读取到的数据就是无效的。

②不可重复读,一个事务中可以读取到另一个事务中已经提交的数据,一个事务多次读取同一数据,结果不一样。

③幻读,一个事务读取了其他事务还没有提交的数据,当另一个事务在表中插入了一些新数据时再次读取表时会多出几行,如同出现了幻觉。

Q13:简述事务的ACID属性

image-20220218174954303

①Atomicity表示原子性,事务中的所有操作都是不可分割的原子单位,要么全部成功,要么全部失败。

②Consistency表示一致性,无论正常执行还是异常退出,事务执行前后数据的完整性必须保持一致,比如转账前后双方的总金额是不变的。

③Isolation表示隔离性,并发操作中不同事务是互相隔离的,之间不会互相影响。

④Durability表示持久性,事务操作完成后数据就会被持久化修改到永久存储中。

Q14:简述主从复制的基本原理

答:①主从复制是指一台服务器充当主数据库服务器,另外一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器中。对于多级复制,数据库服务器既可充当主机也可充当从机。②MySQL主从复制的基础是主服务器对数据库修改二进制记录,从服务器通过主服务器的二进制日志自动执行更新。

Q15:MySQL有哪些锁?

①按操作类型可以分为:读锁(共享锁S)、写锁(排它锁X)。

读锁:对同一份数据,多个读操作可以同时进行而不会互相影响。写锁:当前操作没有完成之前,会阻塞其他读锁和写锁。

②按操作粒度分为:表锁、行锁、页锁。表锁指对当前操作的整张表加锁,实现简单,资源消耗较少。行锁指对某行数据加锁,是一种排它锁。③页锁的锁定粒度介于行锁和表锁之间,一次锁定相邻的一组记录。

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

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

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

Q16:视图是什么?和普通的表有什么区别?

答:①视图(View)是一种虚拟存在的表。视图并不在数据库中实际存在,它的数据来自于表,通过执行时动态生成。通俗的讲,视图就是一条SELECT语句执行后返回的结果集。所以我们在创建视图的时候,主要的工作就落在创建这条SQL查询语句上。

1、简化sql语句
2、提高了sql的重用性
3、保护基表的数据,提高了安全性

Q17:存储过程和函数是什么?有什么区别?

说明:都类似于java中的方法,将一组完成特定功能的逻辑语句包装起来,对外暴露名字
好处:
1、sql语句简单
2、提高重用性
3、减少了和数据库服务器连接的次数,提高了效率

②两者的区别在于存储过程没有返回值,函数必须有返回值

Q18:触发器是什么?

答:①触发器是与表有关的数据库对象,指在 insert/update/delete 之前或之后,触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性 , 日志记录 , 数据校验等操作 。②使用别名 OLD 和 NEW 来引用触发器中发生变化的记录内容。现在触发器还只支持行级触发,不支持语句级触发。

Q19:了解MySQL的体系结构吗?

答:① 连接层:最上层是一些客户端和链接服务,包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于 TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。②服务层:第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如 过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询的顺序,是否利用索引等, 最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。③ 引擎层:存储引擎层, 存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。④存储层:数据存储层, 主要是将数据存储在文件系统之上,并完成与存储引擎的交互。

Q20:存储引擎应当怎样进行选择?

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

①在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。以下是几种常用的存储引擎的使用环境。

②InnoDB : 是Mysql的默认存储引擎,用于事务处理应用程序,支持行级锁和外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询以外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。InnoDB存储引擎除了有效的降低由于删除和更新导致的锁定, 还可以确保事务的完整提交和回滚,对于类似于计费系统或者财务系统等对数据准确性要求比较高的系统,InnoDB是最合适的选择。

③MyISAM : 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。

④MEMORY:将所有数据保存在RAM中,在需要快速定位记录和其他类似数据环境下,可以提供几块的访问。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,其次是要确保表的数据可以恢复,数据库异常终止后表中的数据是可以恢复的。MEMORY表通常用于更新不太频繁的小表,用以快速得到访问结果。⑤MERGE:用于将一系列等同的MyISAM表以逻辑方式组合在一起,并作为一个对象引用他们。MERGE表的优点在于可以突破对单个MyISAM表的大小限制,并且通过将不同的表分布在多个磁盘上,可以有效的改善MERGE表的访问效率。这对于存储诸如数据仓储等VLDB环境十分合适。

Q21:优化SQL的步骤了解吗?

答:①查看SQL执行频率。②定位低效率执行SQL。可以通过以下两种方式:慢查询日志 : 通过慢查询日志定位那些执行效率较低的 SQL 语句。show processlist : 慢查询日志在查询结束以后才记录,所以在应用反映执行效率出现问题的时候查询慢查询日志并不能定位问题,可以使用show processlist命令查看当前MySQL在进行的线程,包括线程的状态、是否锁表等,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化。③通过以上步骤查询到效率低的 SQL 语句后,可以通过 EXPLAIN或者 DESC命令获取 MySQL如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。④Mysql从5.0.37版本开始增加了对 show profilesshow profile 语句的支持。show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里了。⑤MySQL5.6提供了对SQL的跟踪trace, 通过trace文件能够进一步了解为什么优化器选择A计划, 而不是选择B计划。打开trace , 设置格式为 JSON,并设置trace最大能够使用的内存大小,避免解析过程中因为默认内存过小而不能够完整展示。

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

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

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

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

  • ①查看SQL执行频率。②定位低效率执行SQL。
  • 开启慢查询日志,查看慢查询的 SQL。
  • 使用 show processlist 命令查看当前所有连接信息。在进行的线程,包括线程的状态、是否锁表,可以实时地查看 SQL 的执行情况,同时对一些锁表操作进行优化。
  • 使用 explain 命令查询 SQL 语句执行计划。

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

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

B树和B+树的区别

B-树,这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树(B树是一颗多路平衡查找树
它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。

B-树有如下特点:

  1. 所有键值分布在整颗树中(索引值和具体data都在每个节点里);
  2. 任何一个关键字出现且只出现在一个结点中;
  3. 搜索有可能在非叶子结点结束(最好情况O(1)就能找到数据);
  4. 在关键字全集内做一次查找,性能逼近二分查找;

B+树是B-树的变体,也是一种多路搜索树, 它与 B- 树的不同之处在于:

  1. 所有关键字存储在叶子节点出现,内部节点(非叶子节点并不存储真正的 data),内部节点不存储数据,只存储索引,
  2. 为所有叶子结点增加了一个链指针

B-树和B+树的区别

1.B+树内节点不存储数据,所有 data 存储在叶节点导致查询时间复杂度固定为 log n。而B-树查询时间复杂度不固定,与 key 在树中的位置有关,最好为O(1)。

2. B+树叶节点两两相连可大大增加区间访问性,可使用在范围查询等,而B-树每个节点 key 和 data 在一起,则无法区间查找。

3.B+树更适合外部存储。由于内节点无 data 域,每个节点能索引的范围更大更精确

B树和B+树都是多叉树,是改变二叉树结构的高度较大进行优化的(树的高度较大不适合存储海量数据);但两者之间也有不同,Mysql选择B+树作为存储的数据结构。

  1. B+树只有叶子节点存放数据,根节点无数据(只有)指向指针(占用6 byte),这样的好处是在高度相等的树结构中,可以存放更多的数据。
  2. B+树在叶子节点之间增加了双向链表指针,对于范围查询,有更高的效率。

为什么mysql索引要用b+树原因如下:

B+树能显著减少IO次数,提高效率
B+树的查询效率更加稳定,因为数据放在叶子节点
B+树能提高范围查询的效率,因为叶子节点指向下一个叶子节点

JVM

在这里插入图片描述

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

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

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

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

不同虚拟机的运行时数据区可能略微有所不同,但都会遵从 Java 虚拟机规范, Java 虚拟机规范规定的区域分为以下 5 个部分:

  • 方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
  • Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;
  • Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
  • 本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;
  • 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;

什么是堆?

堆是用来存放对象实例的,是垃圾收集器管理的主要区域,因此也被称为GC堆。

堆可以细分为:新生代(Eden、From Survivor、To Survivor)和老年代

196. 说一下堆栈的区别?

  • 推的物理地址分配是不连续的,性能比较慢;栈的物理地址分配是连续的,性能相对比较快。
  • 功能方面:堆是用来存放对象的实例和数组;栈是用来执行程序的,存放的是局部变量,操作数栈,返回结果。
  • 共享性:堆是线程共享的,栈是线程私有的。
  • 空间大小:堆大小远远大于栈。

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

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

队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。

栈和队列很相似,但它运行对元素进行后进先出进行检索。

198. 有哪些类加载器,类加载器的加载模型是什么,有什么好处?

1、启动类加载器,负责加载JAVA_HOME/lib中的类库;

2、扩展类加载器,负责加载JAVA_HOME/lib/ext中的类库;

3、应用程序类加载器,也称系统类加载器,负责加载用户类路径上指定的类库;

4、其他类加载器

②类加载器之间的层次关系叫做双亲委派模型,要求除了顶层的启动类加载器外其余的类加载器都应当有自己的父类加载器。一个类收到类加载请求后会层层找父类加载器去尝试加载,因此所有的加载请求最终都会被传送到顶层的启动类加载器,只有当父类加载器反馈自己无法完成加载时子加载器才会尝试自己去加载。③双亲委派模型的好处是保障类加载的唯一性和安全性,例如加载rt.jar包中的java.lang.Object,无论哪一个类加载最终都会委托给启动类加载器,这样就保证了类加载的唯一性。如果存在包名和类名都相同的两个类,那么该类就无法被加载。

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

类装载分为以下 5 个步骤:

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

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

一般有两种方法来判断:

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

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

  • 强引用:发生 gc 的时候不会被回收。
  • 软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
  • 弱引用:不是必须的对象,在下一次GC时会被回收。
  • 虚引用(幽灵引用/幻影引用):目的就是在这个对象被收集器回收时收到一个系统通知,被虚引用关联的对象,和其生存时间完全没关系。
    在这里插入图片描述

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

  • 标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
  • 标记-整理算法:标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
  • 复制算法:按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
  • 分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。

203. 说一下 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 选项。

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

CMS 是英文 Concurrent Mark-Sweep 的简称,是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,这种垃圾回收器非常适合。在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器。

CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。

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

  • 新生代回收器:Serial(单线程)、ParNew(Serial收集器的多线程版本)、Parallel Scavenge(复制算法的并行多线程收集器,追求最大吞吐量)
  • 老年代回收器:Serial Old(Serial的老年代版本,基于标记-整理算法)、Parallel Old(使用的是标记-整理的内存回收算法)、CMS(老年代并行收集器,基于标记清除算法,追求最小停顿时间)
  • 整堆回收器:G1(一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项。)

新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

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

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

新生代使用的是复制算法,新生代里有 3 个分区:Eden、From Survivor、To Survivor,它们的默认占比是 8:1:1,它的执行流程如下:

  • 把 Eden + From Survivor 存活的对象放入 To Survivor 区;
  • 清空 Eden 和 From Survivor 分区;
  • From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。

每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。

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

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

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

  • jconsole:用于对 JVM 中的内存、线程和类等进行监控;
  • jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁,监控内存的变化、gc 变化等。
  • 第三方:MAT,一个基于Eclipse的内存分析工具
  • GChisto,一款专业分析gc日志的工具。

208. 常用的 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 详细信息。

Spring

为什么要使用 spring?

  • spring 提供 ioc 技术,容器会帮你管理依赖的对象,从而不需要自己创建和管理依赖对象了,更轻松的实现了程序的解耦。
  • spring 提供了aop面向切片编程,这样可以更方便的处理某一类的问题。
  • spring 提供了事务支持,使得事务操作变的更加方便。
  • 更方便的框架集成,spring 可以很方便的集成其他框架,比如 MyBatis、hibernate 等。

Spring的IOC和DI是什么?AOP是什么?

①IOC即控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理,作用是降低代码的耦合度。IOC底层原理:XML解析,工厂模式,反射。

②DI即依赖注入,也就是注入属性,是IOC的一种具体实现方式。假设一个Car类需要Engine的对象,那么一般需要new一个Engine,利用IOC就是只需要定义一个私有的Engine引用变量,容器会在运行时创建一个Engine的实例对象并将引用自动注入给变量。

③AOP面向切面,不通过修改源代码方式,在主干功能里面添加新功能。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得
业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

Q2:简述Spring中bean对象的生命周期

(1)Spring对bean进行实例化
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(4)bean 可以使用了(对象获取到了)
(5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

当有bean 的后置处理器,bean 生命周期有七步

(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

93. spring 有哪些主要模块?

  • spring core:框架的最基础部分,提供 ioc 和依赖注入特性。
  • spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
  • spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的对象访问方法。
  • spring dao:Data Access Object 提供了JDBC的抽象层。
  • spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。
  • spring Web mvc:spring 中的 mvc 封装包提供了 Web 应用的 Model-View-Controller(MVC)的实现。

94. 可以注入哪些数据类型?spring 常用的注入方式有哪些?

可以注入的数据类型有基本数据类型、String、Bean、以及集合等复杂数据类型

  • 构造方法注入,通过constructor-arg标签实现

  • set 属性注入,通过property标签实现

  • 注解方式注入,@Autowired根据类型注入,@Qualifier根据名称进行注入,

    ​ @Resource可以根据类型注入,可以根据名称注入,@Value注入普通类型属性

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

spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。

实际上大部分时候 spring bean 无状态的(比如 dao 类),所以某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean()了,所以就可以保证线程安全了。

  • 无状态就是不会保存数据。
  • 有状态就是有数据存储功能。

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

spring 支持 5 种作用域,如下:

  • singleton:spring ioc 容器中只存在一个 bean 实例,bean 以单例模式存在,是系统默认值;
  • prototype:每次从容器调用 bean 时都会创建一个新的示例,既每次 getBean()相当于执行 new Bean()操作;

Web 环境下的作用域:

  • request:仅作用于HttpRequest,每次Http请求都会创建一个新的bean
  • session:仅作用于HttpSession,不同的Session使用不同的实例,相同的Session使用同一个实例
  • global-session:仅作用于HttpSession,所有的Session使用同一个实例。globalsession 提供一个全局性的 http session

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

  • no:默认值,表示没有自动装配,应使用显式 bean 引用进行装配。
  • byName:它根据 bean 的名称注入对象依赖项。
  • byType:它根据类型注入对象依赖项。
  • 构造函数:通过构造函数来注入依赖项,需要设置大量的参数。
  • autodetect:容器首先通过构造函数使用 autowire 装配,如果不能,则通过 byType 自动装配。

Q4:BeanFactory和FactoryBean,ApplicationContext的区别?

1、Spring 提供 IOC 容器实现两种方式:(两个接口)
(1)BeanFactory:IOC 容器基本实现,是一个Factory接口,是 Spring 内部的使用接口,不提供开发人员进行使用
加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
加载配置文件时候就会把在配置文件对象进行创建,一般推荐使用ApplicationContext。

2、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
普通 bean:在配置文件中定义 bean 类型就是返回类型
工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
就是你定义一个工厂 然后就可以加工东西了 不用频繁的去xml文件中定义
FactoryBean是一个Bean接口,是一个可以生产或者装饰对象的工厂Bean,可以通过实现该接口自定义的实例化Bean的逻辑

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

  • 声明式事务:在 Spring 进行声明式事务管理,底层使用 AOP 原理,声明式事务也有两种实现方式,基于 xml 配置文件的方式和注解方式(在类上添加 @Transactional 注解)。
  • 编码方式:提供编码的形式管理和维护事务。

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

spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:

ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;

ISOLATIONREADUNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);

ISOLATIONREADCOMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;

ISOLATIONREPEATABLEREAD:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;

ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。

脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。

不可重复读 :是指在一个事务内,多次读同一数据。

幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。

SpringMVC

Q1:SpringMVC的作用是什么?MVC的含义分别是什么?

①springMVC是一种基于Java实现的mvc设计模型的轻量级Web层框架,作用包括:获取请求参数、调用业务层 、进行请求响应。

②mvc全名是model view controller模型视图控制器,model指数据模型,JavaBean的类,用来封装数据;view指jsp,html等用来展示数据给用户的界面;controller是整个流程的控制器,用来接收用户请求以及进行数据校验等功能。

Q2:要搭建一个最基础的SpringMVC环境,你是如何配置的?

①在pom.xml中导入以下jar包:org.springframework下的spring-context)、spring-web、spring-webmvc、javax.servlet下的servlet-api、javax.servlet.jsp下的jsp-api以及测试用的junit包。②创建一个springmvc.xml的springconfig配置文件,开启包扫描,注册视图解析器,配置视图的前缀和后缀。③在web.xml中配置核心控制器,servlet和servlet-mapping的映射等。

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

  • 拦截级别:struts2 是类级别的拦截;spring mvc 是方法级别的拦截。
  • 数据独立性:spring mvc 的方法之间基本上独立的,独享 request 和 response 数据,请求数据通过参数获取,处理结果通过 ModelMap 交回给框架,方法之间不共享变量;而 struts2 虽然方法之间也是独立的,但其所有 action 变量是共享的,这不会影响程序运行,却给我们编码和读程序时带来了一定的麻烦。
  • 拦截机制:struts2 有以自己的 interceptor 机制,spring mvc 用的是独立的 aop 方式,这样导致struts2 的配置文件量比 spring mvc 大。
  • 对 ajax 的支持:spring mvc 集成了ajax,所有 ajax 使用很方便,只需要一个注解 @ResponseBody 就可以实现了;而 struts2 一般需要安装插件或者自己写代码才行。

SpringMVC常用组件

  • DispatcherServlet:前端控制器,不需要工程师开发,由框架提供

作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求

  • HandlerMapping:处理器映射器,不需要工程师开发,由框架提供

作用:根据请求的url、method等信息查找Handler(将请求和控制器方法映射),即控制器方法

  • Handler/Controller:处理器,需要工程师开发

作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理

  • HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供

作用:通过HandlerAdapter对处理器(控制器方法)进行执行

  • ViewResolver:视图解析器,不需要工程师开发,由框架提供

作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView

  • View:视图

作用:将模型数据通过页面展示给用户

Q3:说一下 spring mvc 运行流程?

(1)用户发送请求到前端控制器。
(2)中央调度器将请求转给处理器映射器。
(3)处理器映射器获取能处理该请求的处理器对象,并将其放到处理器执行链中,返回给中央调度器。
(4)中央调度器根据处理器执行链中的处理器对象找到能够处理该请求的处理器适配器。
(5)处理器映射器根据URL找到需要调用的处理器(Controller)。
(6)Controller 进行业务逻辑处理后,得到一个ModelAndView,将其返回给处理器适配器。
(7)处理器适配器再将这个ModelAndView返回给中央调度器。
(8)中央调度器将ModelAndView传给视图解析器。
(9)视图解析器获取ModelAndView中的数据、解析ModelAndView中的视图、通过前缀和后缀拼接视图的完整路径,再将数据渲染到 这个视图上,封装为一个视图对象,并将其返回给中央调度器。
(10)中央调度器根据这个视图对象调用具体的视图,形成响应对象。中央调度器响应浏览器。

102. @RequestMapping 的作用是什么?

将 http 请求映射到相应的类/方法上。

103. @Autowired 的作用是什么?

@Autowired 它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作,通过@Autowired 的使用来消除 set/get 方法。

Q9:SpringMVC的常用注解有哪些?

@RequestParam是将请求参数和控制器方法的形参创建映射关系

@RequestHeader是将请求头信息和控制器方法的形参创建映射关系

@CookieValue是将cookie数据和控制器方法的形参创建映射关系,就一个值JSESSIONID

@RequestBody:用于标识控制器方法形参,标识的形参就可以获取请求体。

@ResponseBody:用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器

@RestController:@Controller+@ResponseBody,当前控制器所有的控制器方法,所有的返回值都是作为响应浏览器的响应体存在

104、拦截器的三个抽象方法

preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法

postHandle:控制器方法执行之后执行postHandle()

afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()

MyBatis

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

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

126. MyBatis 有几种分页方式?

分页方式:逻辑分页和物理分页。

逻辑分页: 使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。

物理分页: 自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。

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

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

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

MyBatis 支持延迟加载,设置 lazyLoadingEnabled=true 即可。

延迟加载的原理的是调用的时候触发加载,而不是在初始化的时候就加载信息。延迟加载就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载。

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

Mybatis的缓存分为:

①一级缓存:指的是Mybatis中SqlSession对象的缓存,当我们执行完查询结果后,查询的结果会同时存入到SqlSession为我们提供的一块区域中,该区域的结构是一个Map,当我们再次查询同样数据时,Mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。当SqlSession对象消失时,Mybatis的一级缓存也就消失了。一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

②二级缓存:二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库。

缓存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

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

  • 灵活性:MyBatis 更加灵活,自己可以写 SQL 语句,使用起来比较方便。
  • 可移植性:MyBatis 有很多自己写的 SQL,因为每个数据库的 SQL 可以不相同,所以可移植性比较差。
  • 学习和使用门槛:MyBatis 入门比较简单,使用门槛也更低。
  • 二级缓存:hibernate 拥有更好的二级缓存,它的二级缓存可以自行更换为第三方的二级缓存。

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

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 批处理相同。

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

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

SpringBoot

104. 什么是 spring boot?

spring boot 是为 spring 服务的,是用来简化新 spring 应用的初始搭建以及开发过程的。

105. 为什么要用 spring boot?

  • 配置简单
  • 独立运行
  • 自动装配
  • 无代码生成和 xml 配置
  • 提供应用监控
  • 易上手
  • 提升开发效率

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

spring boot 核心的两个配置文件:

  • bootstrap (. yml 或者 . properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,且 boostrap 里面的属性不能被覆盖;
  • application (. yml 或者 . properties):用于 spring boot 项目的自动化配置。

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

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

. properties 配置如下:

spring. RabbitMQ. port=5672

. yml 配置如下:

spring:
    RabbitMQ:
        port: 5672

. yml 格式不支持 @PropertySource 注解导入。

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

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

Springboot的原理:

Springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean,

而它是通过启动类的SpringBootApplication注解进行开始解析的,标注这个类是一个springboot的应用:启动类下的所有资源被导入,SpringBootApplication里面有三个注解@Springbootconfiguration说明主函数他也是一个配置类(核心配置类);@ComponentScan指定扫描哪些包,扫描当前主启动类同级的包;@EnableAutoConfiguration根据EnableAutoConfiguration开启自动化配置,里面又有两个注解,@AutoConfigurationPackage自动配置包,给容器中导入一系列组件,只有在主函数所在的包下才导入,这就解释了为什么默认的包路径是Main所在的包下;@importSelect自动导入选择器选择性的导入,根据loadFactoryNames根据classpash路径以MATA-INF/spring.factorces(自动配置核心文件)所在的位置来加载一个文件,就是spring-boot-autoconfigure所有的自动配置类都在里面。下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解(@Conditional),最终会按需配置。先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器,只有满足条件装配规则的才会被配置。

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值