常见面试题

目录

在这里插入图片描述

一.Java 基础

1.JDK JRE JVM 认识 (包含问题“JDK 和JRE有什么区别?”)
  • JDK (Java Development Kit 的简称) : Java开发工具包
    JDK提供了 Java 的开发环境和运行环境。如果要写 Java 程序,就要装JDK。
    具体来讲它包含了JRE,以及一堆Java常用工具(javac/java/javadoc/jdb等)和Java基础类库(即Java 提供给我们使用的常用方法)。
  • JRE (Java Runtime Enviroment 的简称) : Java运行时环境
    JRE为Java的运行提供了所需环境,所有的Java程序都要在JRE下才能运行。
    它包括了JVM和JAVA核心类库和支持文件。
    如果你只需要运行 Java 程序,只需安装 JRE 就可以了
  • JVM (Java Virtual Machine) :
    Java虚拟机,Java程序运行在其中. Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行. JVM 对上层的 Java 源文件是不关心的,它关注的只是由源文件生成的类文件(class file)。
    在这里插入图片描述
2. == 和 equals 的区别是什么?

== 解读
对于基本类型和引用类型 ==的作用效果是不同的,如下所示:

  • 基本类型:比较的是值是否相同;
  • 引用类型:比较的是引用地址是否相同;
    代码示例:
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

代码解读:因为 x 和 y 指向的是同一个引用,所以 "= ="也是 true,而 new String()方法则重写开辟了内存空间,所以 "= = "结果为 false,而 equals 比较的一直是值,所以结果都为 true。
equals 解读
equals 本质上就是 ==,只不过 String 和 Integer等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。
首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:

class Cat {
    public Cat(String name) {
        this.name = name;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false

输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:

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

原来 equals 本质上就是 = =。
那问题来了,两个相同值的 String 对象,为什么返回的是 true?代码如下:

String s1 = new String("老王");String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true

同样的,当我们进入 String 的 equals 方法,找到了答案,代码如下:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

原来是 String 重写了 Object 的 equals 方法,把引用比较改成了值比较。
总结 := = 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 本质上就是==,只不过很多类重写了equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

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

不对,两个对象的 hashCode() 相同,equals() 不一定 true。
代码示例:

String str1 = "通话";String str2 = "重地";
System. out. println(String. format("str1:%d | str2:%d",  str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));

执行的结果:

str1:1179395 | str2:1179395
false

代码解读:很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则为 false,因为在散列表中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等。
哈希码并不是完全唯一的,它是一种算法,让同一个类的对象按照自己不同的特征尽量的有不同的哈希码,但不表示不同的对象哈希码完全不同。也有相同的情况,看程序员如何写哈希码的算法。

4. final 在 Java 中有什么作用?
  • final 修饰的类叫最终类,该类不能被继承。
  • final 修饰的方法不能被重写。
  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
0. Java中的String类能否被继承?为什么?

不能被继承,因为String类有final修饰符,而final修饰的类是不能被继承的。

5. Java 中的 Math. round(-1. 5) 等于多少?

等于-1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负0.5是直接舍弃。

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

基础类型有 8 种:byte、boolean、char、short、int、long、float、double,
String不属于基础类型,String属于对象/引用数据类型。

7. Java中操作字符串都有哪些类?它们之间有什么区别?
  • 操作字符串的类有:String、StringBuffer、StringBuilder。
  • String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
  • StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,
    所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
8. String str="i"与 String str=new String(“i”)一样吗?

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

9. 如何将字符串反转?

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。
示例代码:

// StringBuffer reverse
StringBuffer stringBuffer = new StringBuffer();
stringBuffer. append("abcdefg");
System. out. println(stringBuffer. reverse()); // gfedcba// StringBuilder reverse

StringBuilder stringBuilder = new StringBuilder();
stringBuilder. append("abcdefg");
System. out. println(stringBuilder. reverse()); // gfedcba
10. String 类的常用方法都有那些?
  • indexOf():返回指定字符的索引。
  • charAt():返回指定索引处的字符。
  • replace():字符串替换。
  • trim():去除字符串两端空白。
  • split():分割字符串,返回一个分割后的字符串数组。
  • substring():截取字符串。
  • equals():字符串比较。
  • length():返回字符串长度。
  • toLowerCase():将字符串转成小写字母。
  • toUpperCase():将字符串转成大写字符。
  • getBytes():返回字符串的 byte 类型数组。
11. 抽象类必须要有抽象方法吗?

不需要,抽象类不一定非要有抽象方法。
示例代码:

abstract class Cat {
    public static void sayHi() {
        System. out. println("hi~");
    }
}

上面代码,抽象类并没有抽象方法但完全可以正常运行。
抽象类可以没有抽象方法
不过如果一个类中有了抽象方法,那么这个类必须声明为抽象类,否则编译通不过。

12. 普通类和抽象类有哪些区别?
  • 区别:
    1、抽象类的存在是为了被继承,不能直接实例化,而普通类可以直接实例化
    2、抽象类的子类必须重写抽象类中的抽象方法,而普通类可以选择重写父类的方法,也可以直接调用父类的方法
    3、抽象类必须用abstract来修饰,普通类则不用
    4、抽象类可以包含抽象方法,普通类不能包含抽象方法
  • 相同点:
    1、普通类和抽象类都可以含有普通成员属性和普通方法
    2、普通类和抽象类都可以继承别的类或者被别的类继承
    3、普通类和抽象类的属性和方法都可以通过子类对象来调用
13. 抽象类能使用 final 修饰吗?

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

14. 接口和抽象类有什么区别?
  • A:体现的理念不同
    抽象类是用来捕捉子类的通用特性的,而接口则是抽象方法的集合
    抽象类里面定义的都是一个继承体系中的共性内容。接口是功能的集合,是一个体系额外的功能,是暴露出来的规则。
    抽象类是对根源的抽象,表示的是这个对象是什么。接口是对动作的抽象,表示的是这个对象能做什么。
  • B:成员的区别
    构造方法:抽象类有构造方法,用于子类实例化使用。接口不能有构造方法
    成员变量:抽象类的成员变量可以是变量,也可以是常量。接口的成员变量只能是常量,默认修饰符:public static final
    成员方法:抽象类的方法可以是抽象的,也可以是非抽象的,接口中所有方法都是抽象的。
    访问修饰符:抽象类中的方法可以是任意访问修饰符;接口中的方法默认使用public修饰。
    实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
    多重继承:抽象类不支持多重继承;接口支持多重继承,用逗号分隔开来就可以。
  • C:类和接口的关系区别
    类与类:继承关系,不支持多重继承,只能单继承。可以多层继承。
    类与接口:实现关系,可以单实现,也可以多实现。类还可以在继承一个类的同时实现多个接口。
    接口与接口:继承关系,支持多重继承,用逗号分隔开来就可以。
15. Java 中 IO 流分为几种?

按照流的流向分,可以分为输入流和输出流;
按照操作单元划分,可以划分为字节流和字符流;
按照流的角色划分,可以划分为节点流和处理流。
Java Io 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

16. BIO、NIO、AIO 有什么区别?查阅
  • BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
  • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
  • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
17. Files的常用方法都有哪些?
  • Files. createFile():创建文件。
  • Files. createDirectory():创建文件夹。
  • Files. delete():删除一个文件或目录。
  • Files. copy():复制文件。
  • Files. move():移动文件。
  • Files. size():查看文件个数。
  • Files. read():读取文件。
  • Files. write():写入文件。
  • Files. exists():检测文件路径是否存在。

二.异常

74. throw 和 throws 的区别?
  • 1.throw 是真实抛出一个异常,执行throw一定出现了某种异常;throws:表示有出现异常的可能性,并不一定出现这些异常。
  • 2.throw 是方法体内语句抛出一个异常,由语句来处理,后面跟的是异常类对象名 ;throws 是方法抛出一个异常,跟在方法声明后面,后面跟的是异常类名,由该方法的调用者来处理。
  • 3.throw不能单独使用,要么和try-catch-finally语句配套使用,要么与throws配套使用;而throws可以单独使用,由处理异常的方法捕获。
  • 4.throw:只能抛出一个异常对象名;throws:可以跟多个异常类名,用逗号隔开。
75. final、finally、finalize 有什么区别?
  • final:是修饰符,如果修饰类,此类不能被继承;如果修饰方法和变量,则表示此方法和此变量不能再被改变,只能使用。
  • finally:是 try{} catch{} finally{} 最后一部分,表示不论发生任何情况都会执行,finally 部分可以省略,但如果 finally 部分存在,则一定会执行 finally 里面的代码。
  • finalize: 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。是SUN公司为java程序员准备的一个垃圾销毁时机。如果希望在对象销毁时机执行一段代码的话,这段代码要写在finalize()方法的重写当中。
76. try-catch-finally 中哪个部分可以省略?

try-catch-finally 其中 catch 和 finally 都可以被省略,但是不能同时省略,也就是说有 try 的时候,必须后面跟一个 catch 或者 finally。

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

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

78. 常见的异常类有哪些?
  • NullPointerException 空指针异常
  • ClassNotFoundException 类不存在异常
  • FileNotFoundException 文件未找到异常
  • NoSuchMethodException 方法不存在异常
  • NumberFormatException 字符串转换为数字异常
  • ClassCastException 数据类型转换异常
  • IndexOutOfBoundsException 数组下标越界异常
  • IOException IO 异常
  • SocketException Socket 异常
00. Exception和Error的区别

Exception是程序正常运行中,可以预料的意外情况,可以并且应该被捕获,进行相应的处理。
Error是指正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序(比如JVM自身)处于非正常状态,不可恢复状态。既然是非正常情况,所以不便于也不需要捕获。

三.多线程

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

并行:多个处理器或多核处理器同时执行多个任务。
并发:多个任务在同一个CPU核上,按细分的时间片交替执行,从感觉上来看那些任务是一起执行的

36. 线程和进程的区别?
  • 进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,
  • 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
  • 一个程序下至少有一个进程,一个进程下可以有若干个线程。
37. 守护线程是什么?

java里线程分2种,
1、用户线程,就是应用程序里自定义的线程。
2、守护线程(即daemon thread),是运行在后台的一种特殊线程,它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。是个服务其他线程的线程,最典型的就是垃圾回收线程

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

创建线程有三种方式:

  1. 继承 Thread 重写 run 方法;
  2. 实现 Runnable 接口;
  3. 实现 Callable 接口。
39. 说一下 runnable 和 callable 有什么区别?

Java多线程有两个重要的接口,Runnable和Callable,分别提供一个run方法和call方法,二者是有较大差异的。
1)Runnable提供run方法,无法通过throws抛出异常,所有CheckedException必须在run方法内部处理。Callable提供call方法,直接抛出Exception异常。
2)Runnable的run方法无返回值,Callable的call方法提供返回值用来表示任务运行的结果
3)Runnable可以作为Thread构造器的参数,通过开启新的线程来执行,也可以通过线程池来执行。而Callable只能通过线程池执行。

40. 线程有哪些状态?

线程的状态:

1	NEW 				尚未启动
2	RUNNABLE 			执行中
3	BLOCKED 			阻塞的(被同步锁或者IO锁阻塞)
4	WAITING 			等待
5	TIMED_WAITING 		等待到指定的时间被唤醒
6	TERMINATED 			结束
41. sleep() 和 wait() 有什么区别?
  • 类的不同:sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。
  • 释放锁:sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
  • 用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()去唤醒。
  • 使用范围: sleep可以在任何地方使用,而wait只能在同步控制方法或者同步控制块里面使用。
42. notifyAll() 和 notify() 有什么区别?
  • notifyAll()会唤醒所有的线程,notify()之后唤醒一个线程。
  • notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而 notify()只会唤醒一个线程,没有竞争,具体唤醒哪一个线程由虚拟机控制。
43. 线程的 run() 和 start() 有什么区别?

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

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

线程池创建有七种方式,最核心的是最后一种:

  • new SingleThreadExecutor ():创建一个单线程化的线程池,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行
  • new CachedThreadPool ():创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。
  • new FixedThreadPool (int nThreads):创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程
  • new SingleThreadScheduledExecutor ():创建单线程池,返回 ScheduledExecutorService,可以进行定时或周期性的工作调度;
  • new ScheduledThreadPool (int corePoolSize):创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。
  • new WorkStealingPool (int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序;
  • new ThreadPoolExecutor ():是最原始的线程池创建,上面1-3创建方式都是对ThreadPoolExecutor的封装。
45. 线程池都有哪些状态?
  • RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。
  • SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。
  • STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。
  • TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时, 会 执行钩子方法 terminated()。
  • TERMINATED:terminated()方法结束后,线程池的状态就会变成这个。
46. 线程池中 submit() 和 execute() 方法有什么区别?
  • execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
  • submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果
  • shutdown()和shutdownNow()是用来关闭线程池的。
47. 在Java程序中怎么保证多线程的运行安全?
  • 方法一:使用安全类,比如 Java. util. concurrent 下的类。
  • 方法二:使用自动锁 synchronized。
  • 方法三:使用手动锁 Lock。
    手动锁 Java 示例代码如下:
	Lock lock = new ReentrantLock();
	lock. lock();
	try {
	    System. out. println("获得锁");
	} catch (Exception e) {
	    // TODO: handle exception
	} finally {
	    System. out. println("释放锁");
	    lock. unlock();
	}
48. 多线程中 synchronized 锁升级的原理是什么?

synchronized 锁升级原理:
在锁对象的对象头里面有一个 threadid 字段,

  • 在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,
  • 再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,
  • 轻量级锁通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。
  • 锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。
49. 什么是死锁?

在某个时刻,线程A持有独占锁a,并尝试去获取独占锁b,线程B持有独占锁b,并尝试获取独占锁a,那么在这种情况下,AB 两个线程就会发生阻塞现象,我们称为死锁。

50. 怎么防止死锁?
  • 尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
  • 尽量使用 Java. util. concurrent并发类代替自己手写锁。
  • 尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
  • 尽量减少同步的代码块。
51. ThreadLocal 是什么?有哪些使用场景?
  • ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
  • ThreadLocal 的经典使用场景是数据库连接和 session管理等。
52.说一下 synchronized 底层实现原理?

synchronized是由一对monitorenter/monitorexit指令实现的,monitor对象是同步的基本实现单元。

在Java 6之前,monitor的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。

但在Java 6的时候,Java虚拟机对此进行了大刀阔斧地改进,提供了三种不同的monitor实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。

另:
执行同步代码块后首先要先执行monitorenter指令,退出的时候monitorexit指令。通过分析之后可以看出,使用Synchronized进行同步,其关键就是必须要对对象的监视器monitor进行获取,当线程获取monitor后才能继续往下执行,否则就只能等待。而这个获取的过程是互斥的,即同一时刻只有一个线程能够获取到monitor。上面的demo中在执行完同步代码块之后紧接着再会去执行一个静态同步方法,而这个方法锁的对象依然就这个类对象,那么这个正在执行的线程还需要获取该锁吗?答案是不必的,从上图中就可以看出来,执行静态同步方法的时候就只有一条monitorexit指令,并没有monitorenter获取锁的指令。这就是锁的重入性,即在同一锁程中,线程不需要再次获取同一把锁。Synchronized先天具有重入性。每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加一,释放锁后就会将计数器减一。
任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取该对象的监视器才能进入同步块和同步方法,如果没有获取到监视器的线程将会被阻塞在同步块和同步方法的入口处,进入到BLOCKED状态
该图可以看出,任意线程对Object的访问,首先要获得Object的监视器,如果获取失败,该线程就进入同步状态,线程状态变为BLOCKED,当Object的监视器占有者释放后,在同步队列中得线程就会有机会重新获取该监视器。

53. synchronized 和 volatile 的区别是什么?
  • synchronized 是修饰类、方法、代码段;volatile 是修饰的是变量。
  • synchronized 可以保证变量的修改可见性和原子性;而volatile 仅能实现变量的修改可见性, 不能保证原子性。
  • synchronized 可能会造成线程的阻塞;volatile不会造成线程的阻塞。
54. synchronized 和 Lock 有什么区别?
  • synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
  • synchronized 可以自动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己手动加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
  • synchronized 无法知道有没有成功获取锁,而通过Lock可以知道。
55. synchronized 和 ReentrantLock 区别是什么?

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

  • ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
  • ReentrantLock 必须手动获取与释放锁,而 synchronized 可以自动释放和开启锁;
  • ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
56. 说一下 atomic 的原理?

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

四.容器

18. Java 容器都有哪些?

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

  • Collection
      List
        ArrayList  LinkedList  Vector
      Set
        HashSet  TreeSet  LinkedHashSet
  • Map
      HashMap   Hashtable  ConcurrentHashMap   LinkedHashMap  TreeMap
19. Collection 和 Collections 有什么区别?
  • Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。
  • Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法: Collections. sort(list)。
20. List、Set、Map 之间的区别是什么?

List、Set、Map 的区别主要体现在两个方面:元素是否有序、是否允许元素重复。
三者之间的区别,如下表:
在这里插入图片描述

21. HashMap 和 Hashtable 有什么区别? ? ?
  • 存储:HashMap 可以存储null键null值,Hashtable 不可以存储null键null值。
  • 线程安全:HashMap 是非线程安全的。 Hashtable 是线程安全的,
    推荐使用:单线程环境下推荐使用 HashMap ,多线程环境下则推荐用 ConcurrentHashMap。 Hashtable 是保留类不建议使用,
22. 如何决定使用 HashMap 还是 TreeMap?

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

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

HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。
当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。
当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当hash冲突个数少时使用链表,当hash冲突个数多时使用红黑树。

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

HashSet 是基于HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。

25. ArrayList 和 LinkedList 的区别是什么?
  • 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
  • 随机访问效率:ArrayList 的随机查询效率要高,因为有下标;LinkedList 低 因为是线性的数据存储方式,需要移动指针从前往后依次查找。
  • 增加和删除效率:在非首尾的增加和删除操作,ArrayList效率低, 因为 ArrayList 增删操作要影响数组内的其他数据的下标。LinkedList 增加和删除效率要高.
  • 综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。
26. 如何实现数组和 List 之间的转换?
  • 数组转 List:使用 Arrays. asList(array) 进行转换。
  • List 转数组:使用 List 自带的 toArray() 方法。
    代码示例:
// list to arrayList<String> list = new ArrayList<String>();list. add("王磊");list. add("的博客");list. toArray();// array to list
String[] array = new String[]{"王磊","的博客"};
Arrays. asList(array);
27. ArrayList 和 Vector 的区别是什么?
  • 线程安全:ArrayList是非线程安全的。Vector是线程安全的,使用了 - Synchronized 来实现线程同步
  • 性能:ArrayList 在性能方面要优于 Vector。
  • 扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过ArrayList 扩容每次只会增 加50%。而Vector 扩容每次会增加 1 倍。
28. Array(数组)和 ArrayList(动态数组)有何区别?
  • Array只能存储同类型的对象,而ArrayList可以存储不同类型的数据。
  • Array 创建后大小固定,而 ArrayList 大小可以动态变化。
  • Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
29. 在 Queue 中 poll()和 remove()有什么区别?

poll()和remove()都是移除并返回第一个元素,但是在poll()在队列为空时返回null,而remove()会抛出NoSuchElementException异常。
代码示例:

Queue<String> queue = new LinkedList<String>();queue. offer("string"); // add
System. out. println(queue. poll());
System. out. println(queue. remove());
System. out. println(queue. size());
30. 哪些集合类是线程安全的?

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

31. 迭代器 Iterator 是什么?

Iterator是一个可以遍历任何Collection的接口。可以用来遍历Collection ;
在 Collection 中使用迭代器方法来获取迭代器实例。然后遍历,在遍历过程中还可以同时移除元素。

32. Iterator 怎么使用?有什么特点?

Iterator 使用代码如下:

List<String> list = new ArrayList<>();
Iterator<String> it = list. iterator();
while(it. hasNext()){
  String obj = it. next();
  System. out. println(obj);
}

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

33. Iterator 和 ListIterator 有什么区别?
  • Iterator 可以遍历List和Set 集合,而 ListIterator 只能遍历 List。
  • Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
  • ListIterator 从 Iterator 接口继承,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。
34. 怎么确保一个集合不能被修改?

可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。
示例代码如下:

List<String> list = new ArrayList<>();
list. add("x");
Collection<String> clist = Collections. unmodifiableCollection(list);
clist. add("y"); // 运行时此行报错
System. out. println(list. size());

五.设计模式

88. 说一下你熟悉的设计模式?
单例模式:写方法保证只返回同一个对象,即只创建一次对象,后面关于这个类都用同一个对象。
		饿汉式:写获取实例方法返回静态代码块已创建的对象
				类加载时自动创建,线程安全
		懒汉式:写获取实例方法,对象不存在就创建返回,存在就直接返回
				用的时候才创建对象,线程不安全,要加锁
工厂模式(简单工厂、抽象工厂):解耦代码。
		简单工厂模式:1.写抽象父类提取子类共有特性,2.写工厂类,类中写获取对象方法根据入参返回返回值类型是抽象父类的子类对象
		抽象工厂模式:用抽象扩大产品大类,是简单工厂模式的扩展
装饰者模式:1.写子类继承某类,2.构造方法传入父类,3.增强方法用传入父类执行父类方法并拓展新逻辑
						在不改变原有类基础上,对该类进行增强或功能拓展。
适配器模式:1.写适配器增强苹果充电为type-c充电,2.小米手机构造方法传入适配器 写方法执行适配器的type-c充电方法
			苹果充电方法,写一个适配器-用装饰者增强苹果充电为type-c充电,在小米充电方法调用适配器充电	方法,给小米手机传入适配器,完成充电
策略模式:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。可用来消除代码中的if else。
			操作接口-运算方法,4个实现类-加减乘除运算方法,计算器类传入操作接口执行运算,
观察者模式:定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。
外观模式:提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层的接口,让子系统更容易使用。
模版方法模式:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。
状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
89. 简单工厂和抽象工厂有什么区别?
  • 简单工厂模式
    是由一个工厂对象创建产品实例,简单工厂模式的工厂类一般是使用静态方法,通过不同的参数的创建不同的对象实例
    可以生产结构中的任意产品,不能增加新的产品
  • 抽象工厂模式
    提供一个创建一系列相关或相互依赖对象的接口,而无需制定他们具体的类,生产多个系列产品
    生产不同产品族的全部产品,不能新增产品,可以新增产品族

六.反射

57. 什么是反射?

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

58. 什么是 Java 序列化?什么情况下需要序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
  以下情况需要使用 Java 序列化:
想把的内存中的对象状态保存到一个文件中或者数据库中时候;
想用套接字在网络上传送对象的时候;
想通过RMI(远程方法调用)传输对象的时候。

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

动态代理是运行时动态生成代理类。
动态代理的应用有 spring aop、hibernate 数据查询、测试框架的后端 mock、rpc,Java注解对象获取等。
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和
CGLIB之间转换
如何强制使用CGLIB实现AOP?
(1)添加CGLIB库,SPRING_HOME/cglib/*.jar
(2)在spring配置文件中加入<aop:aspectj­autoproxy proxy­target­class=“true”/>
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final

60. 怎么实现动态代理?

JDK 原生动态代理和 cglib 动态代理。JDK 原生动态代理是基于接口实现的,而 cglib 是基于继承当前类的子类实现的。

七.对象拷贝

61. 为什么要使用克隆?

克隆的对象可能包含一些已经修改过的属性,而 new 出来的对象的属性都还是初始化时候的值,
所以需要用克隆方法来把当前对象的“状态”保存到一个新的对象中。

62. 如何实现对象克隆?
  • 实现 Cloneable 接口并重写 Object 类中的 clone() 方法。
  • 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
63. 深拷贝和浅拷贝区别是什么?
  • 浅克隆:当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象 不会复制。
  • 深克隆:除了对象本身被复制外,对象所包含的值类型的和引用类型的成员变量也都将复制。

八.MySQL

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

1.第一范式(确保每列保持原子性),不可拆分
2.第二范式(确保表中的非主键列都和主键列完全相关,而不是部分相关), 消除部分依赖
3.第三范式(确保表中的非主键列都和主键列直接相关,而不是间接相关),消除间接依赖

1.列的原子性,每列的属性都是最小的单位,不可再分拆
2.主键唯一,每条记录是惟一的
3.不要有冗余数据,不同类型的数据分开做表

https://blog.csdn.net/ffhgjgj6576567/article/details/118657917
https://www.cnblogs.com/linjiqin/archive/2012/04/01/2428695.html

165. 一张自增表里面总共有 7 条数据,删除了最后 2 条数据,重启 MySQL 数据库,又插入了一条数据,此时 id 是几?
  • 表类型如果是 MyISAM ,那 id 就是 8。
  • 表类型如果是 InnoDB,那 id 就是 6。
  • InnoDB 表只会把自增主键的最大 id 记录在内存中,所以重启之后会导致最大 id 丢失。
166. 如何获取当前数据库版本?

使用 select version() 获取当前 MySQL 数据库版本。

167. 说一下 ACID 是什么?
  • Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
  • Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
168. char 和 varchar 的区别是什么?
  • char(n) :固定长度类型,比如订阅 char(10),当你输入"abc"三个字符的时候,它们占的空间 还是 10 个字节,其他 7 个是空字节。
    chat 优点:效率高;缺点:占用空间;适用场景:存储密码的 md5 值,固定长度的,使用 char 非常合适。
  • varchar(n) :可变长度,存储的值是每个值占用的字节再加上一个用来记录其长度的字节的长度。
    所以,从空间上考虑 varcahr 比较合适;从效率上考虑 char 比较合适,二者使用需要权衡。
169. float 和 double 的区别是什么?
  • float 最多可以存储 8 位的十进制数,并在内存中占 4 字节。
  • double 最可可以存储 16 位的十进制数,并在内存中占 8 字节。
170. MySQL 的内连接、左连接、右连接有什么区别?

内连接关键字:inner join;左连接:left join;右连接:right join。
内连接是把匹配的关联数据显示出来;左连接是左边的表全部显示出来,右边的表显示出符合条件的数据;右连接正好相反。

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

索引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。
具体来说MySQL中的索引,不同的数据引擎实现有所不同,但目前主流的数据库引擎的索引都是B+树实现的,B+ 树的搜索效率,可以到达二分法的性能,找到数据区域之后就找到了完整的数据结构了,所有索引的性能也是更好的。

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

使用 explain 查看 SQL 是如何执行查询语句的,从而分析你的索引是否满足需求。
explain 语法:explain select * from table where type=1。

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

MySQL 的事务隔离是在 MySQL. ini 配置文件里添加的,在文件的最后添加:
transaction-isolation = REPEATABLE-READ
可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。

  • READ-UNCOMMITTED:读未提交,最低隔离级别,事务未提交前,就可被其他事务读取(会出现脏读、不可重复读、幻读)。
  • READ-COMMITTED:读已提交,一个事务提交后才能被其他事务读取到(会出现不可重复读、幻读)。
  • REPEATABLE-READ:可重复读,默认级别,保证同一事务的多个实例在并发读取数据时,会看到同样的数据行。即在一个事务里读取数据,怎么读都不会变。(会出现幻读)。
  • SERIALIZABLE:序列化/串行化读,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读,解决了所有的问题,但效率低。需要事务排队。
  1. 脏读 :表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录 A,此时该事务还未提交,然后另一个事务尝试读取到了记录 A。
  2. 不可重复读 :一个事务范围内两个相同的查询却返回了不同数据。
  3. 幻读 :指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录,但是第二次同等条件下查询却有 n+1 条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。
    幻读 :在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。
    没有读到的记录,以为不存在,但其实是可以更新成功的,并且,更新成功后,再次读取,就出现了。
    https://blog.csdn.net/unbelievevc/article/details/128112110
174. 说一下 MySQL 常用的引擎?
  • InnoDB 引擎:mysql 5.1 后默认的数据库引擎,提供了对数据库 acid 事务的支持,并且还提供了行级锁和外键的约束,它的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候,InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎是不支持全文搜索,同时启动也比较的慢,它是不会保存表的行数的,所以当进行 select count(*) from table 指令的时候,需要进行扫描全表。由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。
  • MyISAM 引擎:不提供事务的支持,也不支持行级锁和外键。因此当执行插入和更新语句时,即执行写操作的时候需要锁定这个表,所以会导致效率会降低。不过和 InnoDB 不同的是,MyISAM 引擎是保存了表的行数,于是当进行 select count(*) from table 语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的,可以将 MyISAM 作为数据库引擎的首选。
    在这里插入图片描述
175. 说一下 MySQL 的行锁和表锁?

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

  • 表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低。
  • 行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高。
176. 说一下乐观锁和悲观锁?
  • 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
  • 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。
    数据库的乐观锁需要自己实现,在表里面添加一个 version 字段,每次修改成功值加 1,这样每次修改的时候先对比一下,自己拥有的 version 和数据库现在的 version 是否一致,如果不一致就不修改,这样就实现了乐观锁。
177. MySQL问题排查都有哪些手段?
  • 使用show processlist命令查看当前所有连接信息。
  • 使用explain命令查询SQL语句执行计划。
  • 开启慢查询日志,查看慢查询的SQL。
178. 数据库优化和SQL优化?
  • 一、架构优化
    1.分布式缓存
    当接收到查询请求后,我们先查询缓存,判断缓存中是否有数据,有数据就直接返回给应用,如若没有再查询数据库,并加载到缓存中,这样就大大减少了对数据库的访问次数,自然而然也提高了数据库性能。更适用于高并发、大数据量大场景。
    2.读写分离
    一主多从,读写分离,主动同步,是一种常见的数据库架构优化手段。
    一般来说当你的应用是读多写少,数据库扛不住读压力的时候,采用读写分离,通过增加从库数量可以线性提升系统读性能。
    主库,提供数据库写服务;从库,提供数据库读能力;主从之间,通过binlog同步数据。
    主要是用于解决 “数据库读性能问题”
    3.水平切分
    当你的应用业务数据量很大,单库容量成为性能瓶颈后,采用水平切分,可以降低数据库单库容量,提升数据库写性能。
    主要是用于解决“数据库数据量大的问题”

  • 二、硬件优化
    我们使用数据库,不管是读操作还是写操作,最终都是要访问磁盘,所以说磁盘的性能决定了数据库的性能。一块PCIE固态硬盘的性能是普通机械硬盘的几十倍不止。我们可以从吞吐率、IOPS两个维度看一下机械硬盘、普通固态硬盘、PCIE固态硬盘之间的性能指标。通过这个指标数据可以很直观的看到不同规格的硬盘之间的性能差距非常大,当然性能更好的硬盘价格会更贵,在资金充足并且迫切需要提升数据库性能时,尝试更换一下数据库的硬盘不失为一个非常好的举措,你之前遇到SQL执行缓慢问题在你更换硬盘后很可能将不再是问题。

  • 三、DB优化
    要让一台数据库实例完全发挥其性能,首先我们就得先优化数据库的实例参数。
    数据库实例参数优化遵循三句口诀:日志不能小、缓存足够大、连接要够用。
    数据库事务提交后需要将事务对数据页的修改刷( fsync)到磁盘上,才能保证数据的持久性。这个刷盘,是一个随机写,性能较低,如果每次事务提交都要刷盘,会极大影响数据库的 性能。数据库在架构设计中都会采用如下两个优化手法:
    先将事务写到日志文件RedoLog(WAL),将随机写优化成顺序写
    加一层缓存结构Buffer,将单次写优化成顺序写
    所以日志跟缓存对数据库实例尤其重要。而连接如果不够用,数据库会直接抛出异常,系统无法访问。

  • 四、表优化
    1.建表时选择合适的存储引擎。Innodb:支持事务和外键,擅长更新,删除。myisam:不支持事务和外键,但是擅长插入和查询。
    2.创建索引
    对于查询占主要的应用来说,索引显得尤为重要。很多时候性能问题很简单的就是因为我们忘了添加索引而造成的,或者说没有添加更为有效的索引导致。如果不加索引的话,那么查找任何哪怕只是一条特定的数据都会进行一次全表扫描,如果一张表的数据量很大而符合条件的结果又很少,那么不加索引会引起致命的性能下降。但是也不是什么情况都非得建索引不可,比如性别可能就只有两个值,建索引不仅没什么优势,还会影响到更新速度,这被称为过度索引
    对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
    3.水平分表。
    4.垂直分表。
    5.三范式和逆三范式应用(减少关联查询(多表联查)(部分展示表可加冗余字段))

  • 五、SQL优化
    1.避免select * 写法,列出需要查询的字段
    执行SQL时优化器需要将 * 转成具体的列;每次查询都要回表,不能走覆盖索引。
    2.避免复杂SQL语句
    提升可阅读性;避免慢查询的概率;可以转换成多个短查询,用业务端处理
    3.关于UNION和UNION ALL
    可以使用联合(UNION)来代替手动创建的临时表,但是使用 union 前,尽量保证 union 之前的结果集已是最小,也就是说,要先用 where 过滤出相应的数据,再做 union ;另外 union 和 union all 是有性能差异的,如果确定前后两段的数据没有重复,尽量用 union all 来替代 union ;因为 union all 的执行效率比 union 高,union 执行时需要排重。
    4.like语句操作。
    一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。
    5.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
    6.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描(id<>3则可使用id>3 or id<3来代替)。
    7.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描(可换union all)
    8.应尽量避免 in 和 not in,否则会导致全表扫描,用 exists 代替 in,not in 可以用 not exists 代替。
    9.应尽量避免在 where 子句中对字段进行表达式/运算操作,这将导致引擎放弃使用索引而进行全表扫描。
    10.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。
    11.应尽量避免where 1=1写法,被迫进行全表扫描,执行效率不高
    12.应尽量避免order by rand()类似写法,这种随机排序需要 Using temporary 和 Using filesort,查询的执行代价往往比较大。

  • 执行计划
    要想优化SQL必须要会看执行计划,执行计划会告诉你哪些地方效率低,哪里可以需要优化。通过explain sql 可以查看执行计划

https://blog.csdn.net/qq_42446961/article/details/121283651 数据库优化的八种方式
https://blog.csdn.net/ThinPikachu/article/details/122152689 数据库优化的四大方法
https://blog.csdn.net/jie_liang/article/details/77340905 sql优化的几种方法

000. mysql 索引使用教程
  • 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的位置信息。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度.
  • 除了词典,生活中随处可见索引的例子,如火车站的车次表、图书的目录等。它们的原理都是一样的,通过不断的缩小想要获得数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是我们总是通过同一种查找方式来锁定数据。
  • 查看一个表中是否已经创建索引 show index from 表名;
  • 创建索引(如果指定字段是字符串,需要指定长度,建议长度与定义字段时的长度一致,字段类型如果不是字符串,可以不填写长度部分) create index 索引名 on 数据表(字段名称长度)
  • 删除索引 drop index 索引名称 on 数据表

注意

  • 1.索引可以明显提高某些字段的查询效率,但不能加快插入,更新,删除数据的效率,相反由于每次数据表数据的更改都会让索引重新排列,会降低效率
  • 2.要注意的是,建立太多的索引将会影响更新和插入的速度,因为它需要同样更新每个索引文件。对于一个经常需要更新和插入的表格,就没有必要为一个很少使用的where字句单独建立索引了,对于比较小的表,排序的开销不会很大,也没有必要建立另外的索引。建立索引会占用磁盘空间。
  • 3.建立索引要创建在常用的查询字段上,并且根据自己的需求建立说因数量
    https://www.cnblogs.com/xuchuankun/p/9451495.html
000. mysql常用索引

https://www.cnblogs.com/houss/p/10587274.html

九.网络

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

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

80. forward 和 redirect 的区别?
  • forward 是转发 ; redirect 是重定向:
  • 地址栏url显示:foward url 不会发生改变,redirect url 会发生改变;
  • 数据共享/携带:forward可以共享/携带request里的数据;redirect不能共享/携带request里的数据;
  • 效率:forward 效率 比 redirect 高。
81. 简述 tcp 和 udp的区别?

tcp 和 udp 是 OSI 模型中的运输层中的协议。
TCP (Transmission Control ProtocoI) 传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议,
而 UDP (User Datagram Protocol) 用户数据报协议 是一种无连接、不可靠、基于数据报的传输层通信协议
两者的区别大致如下:

  • TCP面向连接(如打电话要先拨号建立连接),UDP是无连接的,即发送数据前不需要建立连接。
  • TCP提供可靠的服务,传送的数据无差错,不丢失,不重复,按序到达;UDP 无法保证可靠交付;
  • TCP面向字节流,UDP面向报文;
  • TCP数据传输慢,UDP数据传输快;
  • TCP连接只能是点到点、一对一的。UDP支持一对一,一对多,多对一和多对多的交互通信。
82. tcp 为什么要三次握手,两次不行吗?为什么?

如果采用两次握手,那么只要服务器发出确认数据包就会建立连接,但由于客户端此时并未响应服务器端的请求,那此时服务器端就会一直在等待客户端,这样服务器端就白白浪费了一定的资源。若采用三次握手,服务器端没有收到来自客户端的再此确认,则就会知道客户端并没有要求建立请求,就不会浪费服务器的资源。
https://baike.baidu.com/item/%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B/5111559?fr=aladdin

00. 四次挥手流程

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
(1) TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送。
(2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
(3) 服务器关闭客户端的连接,发送一个FIN给客户端。
(4) 客户端发回ACK报文确认,并将确认序号设置为收到序号加1。

https://blog.csdn.net/w8y56f/article/details/120653921?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-120653921-blog-81090081.pc_relevant_3mothn_strategy_and_data_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-120653921-blog-81090081.pc_relevant_3mothn_strategy_and_data_recovery&utm_relevant_index=2
83. 说一下 TCP 粘包是怎么产生的?

TCP 粘包可能发生在发送端或者接收端,分别来看两端各种产生粘包的原因:

  • 发送端粘包:发送端需要等缓冲区满才发送出去,造成粘包;
  • 接收方粘包:接收方不及时接收缓冲区的包,造成多个包接收。
84. OSI的七层模型都有哪些?

在这里插入图片描述

  • 7.应用层: 软件
  • 6.表示层: 帮我们解决不同系统之间的通信语法问题。
  • 5.会话层: (处理用户的会话,控制用户间逻辑连接的)
  • 4.传输层: tcp udp
  • 3.网络层: ip寻址 (计算很多 确定我把计算机发送给谁)
  • 2.数据链路层: 网卡 网桥 (提供错误检测和纠正,以确保数据的可靠传输)
  • 1.物理层: 网线 光纤 (传输这些二进制数据)
85. get 和 post 请求有哪些区别?
  • get 请求能被浏览器主动缓存,而 post 请求不会被浏览器主动缓存。
  • get 传递参数有长度限制,而 post 传递参数没有长度限制。
  • get 的参数会明文显示在 url 上,而post 不会明文显示,参数传输更安全。
86. 如何实现跨域?

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

  • 服务器端运行跨域 设置 CORS 等于 *;
  • 在单个接口使用注解 @CrossOrigin 运行跨域;
  • 使用 jsonp 跨域;
87. 说一下 JSONP (JSON with Padding) 实现原理?

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

十.Java Web

64. JSP 和 servlet 有什么区别?

JSP(全称JavaServer Pages)是由Sun Microsystems公司主导创建的一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。
jsp和servlet的区别有以下几点:
JSP 是 servlet 技术的扩展,本质上就是 servlet 的简易方式。使用Jsp只需要完成程序员需要输出到客户端的内容,Jsp中的Java脚本如度何镶嵌到一个类中,由Jsp容器完成。
1、jsp经编译后就变成了Servlet。
2、jsp更擅长表现于页面显示,侧重于视图;servlet类似于一个Controller,更擅长于控制逻辑。
3、Jsp中的内置对象都是必须通过HttpServletResponse对象以及HttpServlet对象得到;而Servlet则没有内置对象,是个完整的Java类,这个类的Service方法用于生成对客户端的响应
5、Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。
https://blog.csdn.net/lzh_99999/article/details/105929127

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

JSP 有 9 大内置对象:

  • application:封装服务器运行环境的对象;ServletContext,全局对象
  • session:封装用户会话的对象;HttpSession;
  • request:封装客户端的请求,其中包含来自 get 或 post 请求的参数;HttpServletRequest
  • pageContext:通过该对象可以获取其他对象;当前页对象;PageContext
  • response:封装服务器对客户端的响应;
  • out:输出服务器响应的输出流对象;
  • page:JSP 页面本身(相当于 Java 程序中的 this);
  • config:Web 应用的配置对象;
  • exception:封装页面抛出异常的对象。
66. 说一下 JSP 的 4 种作用域?
  • page:代表与一个页面相关的对象和属性。
  • request:代表与客户端发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多 个 Web 组件;需要在页面显示的临时数据可以置于此作用域。
  • session:代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应 该放在用户自己的 session 中。
  • application:代表与整个 Web 应用程序相关的对象和属性,它实质上是跨越整个 Web 应用 程序,包括多个页面、请求和会话的一个全局作用域。
67. session 和 cookie 有什么区别?
  • 存储位置不同:session 存储在服务器端;cookie 存储在浏览器端。
  • 安全性不同:session 安全性好;cookie 安全性一般,在浏览器存储,可以被修改和伪造。
  • 容量和个数限制:session没有限制;cookie 有容量限制,每个站点下的 cookie 也有个数限制。
  • 存储的多样性:session 可以存储在 Redis 、数据库、应用程序中;而 cookie 只能存储在浏览器中。
68. 说一下 session 的工作原理?

在这里插入图片描述

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

69. 如果客户端禁止 cookie , 那么 session 还能用吗?

一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。
如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。
但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session。

  • 通过url重写,把 sessionid 作为参数追加到原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
  • 服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
  • 通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。
70. spring mvc 和 struts 的区别是什么?
  • 拦截级别:spring mvc 是方法级别的拦截。struts2 是类级别的拦截
  • 数据独立性:spring mvc 的方法之间基本上独立的,独享 request 和 response 数据,请求数据通过参数获取,处理结果通过 ModelMap 交回给框架,方法之间不共享变量;而 struts2 虽然方法之间也是独立的,但其所有 action 变量是共享的,这不会影响程序运行,却给我们编码和读程序时带来了一定的麻烦。
  • 拦截机制:spring mvc 用的是独立的 aop 方式,而struts2 有自己的 interceptor 机制,这样导致struts2 的配置文件量比 spring mvc 大。
  • 对ajax的支持:spring mvc 集成了ajax,所有 ajax 使用很方便,只需要一个注解 @ResponseBody 就可以实现了;而 struts2 一般需要安装插件或者自己写代码才行。
71. 如何避免 SQL 注入?
  • 使用预处理 PreparedStatement。
  • 使用正则表达式过滤掉字符中的特殊字符。
72. 什么是 XSS 攻击,如何避免?
  • XSS攻击:即(Cross Site Scripting)跨站脚本攻击,它是 Web 程序中常见的漏洞。原理是攻 击者往 Web 页面里插入恶意的脚本代码(css代码、Javascript代码等),当用户浏览该页面 时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的,如盗取用户cookie、破坏页 面结构、重定向到其他网站等。
  • 预防 XSS 的核心是必须对输入的数据做过滤处理。
73. 什么是 CSRF 攻击,如何避免?
  • CSRF:Cross-Site Request Forgery(跨站请求伪造),可以理解为攻击者盗用了你的身份,以 你的名义发送恶意请求,比如:以你名义发送邮件、发消息、购买商品,虚拟货币转账等。
  • 防御手段:
    验证请求来源地址;
    关键操作添加验证码;
    在请求地址添加 token 并验证。

十一.MyBatis

125. MyBatis 中 #{}和 ${}的区别是什么?
  1. #{}是预编译处理,是占位符,${}是字符串替换,是拼接符
  2. Mybatis在处理#{}的时候会将sql中的#{}替换成?号,调用PreparedStatement来赋值
    Mybatis在处理${}的时候就是把${}替换成变量的值,调用Statement来赋值
  3. #{}的变量替换是在DBMS中、变量替换后,#{}对应的变量自动加上单引号
    ${}的变量替换是在DBMS外、变量替换后,${}对应的变量不会加上单引号
  4. 使用#{}可以有效的防止sql注入,提高系统的安全性
126. MyBatis 有几种分页方式?

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

  • 逻辑分页: 使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。
  • 物理分页: 自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。
127. RowBounds 是一次性查询全部结果吗?为什么?

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

128. MyBatis 逻辑分页和物理分页的区别是什么?
  • 逻辑分页是一次性查询很多数据,然后再在结果中检索分页的数据。这样做弊端是需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。
  • 物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点,比如需要大量的内存,对数据库查询压力较大等问题。
129. MyBatis是否支持延迟加载?延迟加载的原理是什么?
  • MyBatis 支持延迟加载,设置 lazyLoadingEnabled=true 即可。
  • 延迟加载的原理的是调用的时候触发加载,而不是在初始化的时候就加载信息。比如调用 a. getB(). getName(),这个时候发现 a. getB() 的值为 null,此时会单独触发事先保存好的关联 B 对象的 SQL,先查询出来 B,然后再调用 a. setB(b),而这时候再调用 a. getB(). getName() 就有值了,这就是延迟加载的基本原理。
130. 说一下 MyBatis 的一级缓存和二级缓存?

一级缓存(mybatis默认支持);SqlSession层,线程级别的
二级缓存;SqlSessionFactory层,进程级别的,xml里面加一个标签

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 方言,添加对应的物理分页语句和物理分页参数。

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

自定义插件实现原理
MyBatis 自定义插件针对 MyBatis 四大对象(Executor、StatementHandler、ParameterHandler、ResultSetHandler)进行拦截:
Executor:拦截内部执行器,它负责调用 StatementHandler 操作数据库,并把结果集通过 ResultSetHandler 进行自动映射,另外它还处理了二级缓存的操作;
StatementHandler:拦截 SQL 语法构建的处理,它是 MyBatis 直接和数据库执行 SQL 脚本的对象,另外它也实现了 MyBatis 的一级缓存;
ParameterHandler:拦截参数的处理;
ResultSetHandler:拦截结果集的处理。
自定义插件实现关键
MyBatis 插件要实现 Interceptor 接口,接口包含的方法,如下:

public interface Interceptor {   
   Object intercept(Invocation invocation) throws Throwable;       
   Object plugin(Object target);    
   void setProperties(Properties properties);
}

setProperties 方法是在 MyBatis 进行配置插件的时候可以配置自定义相关属性,即:接口实现对象的参数配置;
plugin 方法是插件用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理,可以决定是否要进行拦截进而决定要返回一个什么样的目标对象,官方提供了示例:return Plugin. wrap(target, this);
intercept 方法就是要进行拦截的时候要执行的方法。
自定义插件实现示例
官方插件实现:

@Intercepts({@Signature(type = Executor. class, method = "query",
        args = {MappedStatement. class, Object. class, RowBounds. class, ResultHandler. class})})public class TestInterceptor implements Interceptor {
   public Object intercept(Invocation invocation) throws Throwable {
     Object target = invocation. getTarget(); //被代理对象
     Method method = invocation. getMethod(); //代理方法
     Object[] args = invocation. getArgs(); //方法参数
     // do something . . . . . .  方法拦截前执行代码块
     Object result = invocation. proceed();
     // do something . . . . . . . 方法拦截后执行代码块
     return result;
   }
   public Object plugin(Object target) {
     return Plugin. wrap(target, this);
   }
}

十二.Spring/Spring MVC

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

aop 是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
简单来说就是统一处理某一“切面”(类)的问题的编程思想,比如统一处理日志、异常等。

92. 解释一下什么是 ioc?

ioc:Inversion of Control(中文:控制反转)是 spring 的核心,对于 spring 框架来说,就是由 spring 来负责控制对象的生命周期和对象间的关系。
简单来说,控制指的是当前对象对内部成员的控制权;控制反转指的是,这种控制权不由当前对象管理了,由其他(类,第三方容器)来管理。

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

spring中的bean默认是单例模式,但是spring框架并没有对单例bean进行多线程的封装处理。所有线程都共享一个单例实例Bean,因此存在资源的竞争,不能保证绝对安全。
不过实际上大部分时候spring bean是无状态的,所有某种程度上来说bean也是安全的,但如果bean 有状态的话,那就要去解决线程安全问题了。最简单的就是改变bean的作用域,把“singleton”变更为“prototype”,这样请求bean相当于new Bean( ),线程之间就不会再存在Bean共享,就不会有线程安全的问题了

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

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

  • singleton:单例,默认作用域,spring ioc 容器中只存在一个 bean 实例;
  • prototype:原型,每次从容器调用 bean 时都会创建一个新的实例;
    Web环境下的作用域:
  • request:请求,每次 http请求都会创建一个新bean/对象/实例;
  • session:会话,同一个会话共享一个实例,不同会话使用不用的实例。
  • global-session:全局会话,所有会话共享一个实例。
    注意: 使用 prototype 作用域需要慎重,因为频繁创建和销毁 bean 会带来很大的性能开销。
97. spring 自动装配 bean 有哪些方式?
  • no:默认值,表示没有自动装配,应使用显式 bean 引用进行装配。
  • byName:它根据 bean 的名称注入对象依赖项。
  • byType:它根据类型注入对象依赖项。
  • 构造函数:通过构造函数来注入依赖项,需要设置大量的参数。
  • autodetect:容器首先通过构造函数使用 autowire 装配,如果不能,则通过 byType 自动装配。
98. spring 事务实现方式有哪些?

实现方式共有两种:声明式事务管理方式;编码方式
声明式事务管理又有两种实现方式:基于XML配置文件的方式和注解方式
编码方式:提供编码的形式管理和维护事务。

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

当多个事务同时访问相同数据时,如果没有采取必要的隔离机制,就可能发生以下问题:

  • 脏读:所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在 未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。
    也就是说,当前事务读到的数据是别的事务想要修改成为的但是没有修改成功的数据。
  • 幻读:事务A首先根据条件索引得到N条数据,然后事务B改变了这N条数据之外的M条或者增添了 M条符合事务A搜索条件的数据,导致事务A再次搜索发现有N+M条数据了,就产生了幻读。
    也就是说,当前事务读第一次取到的数据比后来读取到数据条目少。
  • 不可重复读:事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务 A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
    也就是说,当前事务先进行了一次数据读取,然后再次读取到的数据是别的事务修改成功的数据,导 致两次读取到的数据不匹配,也就照应了不可重复读的语义。

不可重复读和幻读比较:
两者有些相似,但是前者针对的是update或delete,后者针对的insert。

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

  • Default:使用数据库本身使用的隔离级别,MySQL(可重复读)ORACLE(读已提交)
  • Read Uncommitted:未提交读,最低隔离级别、事务中的修改,即使没有提交也可被其他事务读取(可能幻读、脏读、不可重复读);
  • Read-Committed:提交读,一个事务提交后才能被其他事务读取到(可能幻读、不可重复读),
  • Repeatable-Read:可重复读,保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。保证多次读取同一个数据时,其值都和事务开始时候的内容是一致的,不会读取到别的事务未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,(可能幻读),MySQL 的默认级别;
  • Serializable:序列化,事务串行执行,最可靠资源消耗也最高的隔离级别,能防止脏读、不可重复读、幻读。
000.什么是Spring MVC ?简单介绍下你对springMVC的理解?

Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

100.说一下 spring mvc 运行流程?

https://blog.csdn.net/m0_53067943/article/details/123926050

在这里插入图片描述

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求调用处理器映射器HandlerMapping。
    处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。
  3. DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter,执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等
    执行处理器Handler(Controller 也叫页面控制器),执行完成返回ModelAndView
    HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
  4. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
    ViewReslover解析后返回具体View
  5. DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。然后响应用户。
101. spring mvc 有哪些组件?
  • 前端控制器(DispatcherServlet)
  • 处理器映射器(HandlerMapping)
  • 处理器适配器(HandlerAdapter)
  • 拦截器(HandlerInterceptor)
  • 视图解析器(ViewResolver)
  • 文件上传处理器(MultipartResolver)
  • 异常处理器(HandlerExceptionResolver)
  • 数据转换(DataBinder)
  • 消息转换器(HttpMessageConverter)
  • 请求转视图翻译器(RequestToViewNameTranslator)
  • 页面跳转参数管理器(FlashMapManager)
  • 处理程序执行链(HandlerExecutionChain)
102. @RequestMapping 的作用是什么?

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

103. @Autowired 的作用是什么?

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

十三.Spring Boot

104. 什么是 spring boot?
  1. springboot是为 spring服务的,为简化Spring项目配置而生
  2. 它使用maven的方式对Spring应用开发进行进一步封装和简化
  3. 是用来简化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 有哪些方式可以实现热部署?
  • 使用 devtools 启动热部署,添加 devtools 库,在配置文件中把 spring. devtools. restart. enabled 设置为 true;
  • 使用 Intellij Idea 编辑器,勾上自动编译或手动重新编译。
109. jpa 和 hibernate 有什么区别?

jpa 全称 Java Persistence API,是 Java 持久化接口规范,hibernate 属于 jpa 的具体实现。

十四.Redis

179. Redis 是什么?都有哪些使用场景?
  • Redis 是一个使用 C 语言开发的高速缓存数据库。
  • Redis 使用场景:
    记录帖子点赞数、点击数、评论数;
    缓存近期热帖;
    缓存文章详情信息;
    记录用户会话信息。
180. Redis 有哪些功能?

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

181. Redis 和 memcache 有什么区别?

1)redis支持持久化
2)redis支持存储类型更多

  • 存储方式不同:memcache 把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小;Redis 有部份存在硬盘上,这样能保证数据的持久性。
  • 数据支持类型:memcache 对数据类型支持相对简单;Redis 有复杂的数据类型。
  • 使用底层模型不同:它们之间底层实现方式,以及与客户端之间通信的应用协议不一样,Redis 自己构建了 vm 机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
  • value 值大小不同:Redis 最大可以达到 512mb;memcache 只有 1mb。
182. Redis 为什么是单线程的?

因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。
关于 Redis 的性能,官方网站也有,普通笔记本轻松处理每秒几十万的请求。
而且单线程并不代表就慢 nginx 和 nodejs 也都是高性能单线程的代表。

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

缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

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

string(字符串)、list(列表)、set(集合)、zset(有序集合)、hash(字典)

185. Redis 支持的 Java 客户端都有哪些?

支持的 Java 客户端有 Redisson、jedis、lettuce 等。

186. jedis 和 Redisson 有哪些区别?
  • jedis:提供了比较全面的 Redis 命令的支持。
  • Redisson:实现了分布式和可扩展的 Java 数据结构,与 jedis 相比 Redisson 的功能相对简单,不支持排序、事务、管道、分区等 Redis 特性。
187. 怎么保证缓存和数据库数据的一致性?
  • 合理设置缓存的过期时间。
  • 新增、更改、删除数据库操作时同步更新 Redis,可以使用事务机制来保证数据的一致性。
188. Redis 持久化有几种方式?

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

  • RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。
  • AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。
189. Redis 怎么实现分布式锁?

Redis 分布式锁其实就是在系统里面占一个“坑”,其他程序也要占“坑”的时候,占用成功了就可以继续执行,失败了就只能放弃或稍后重试。
占坑一般使用 setnx(set if not exists)指令,只允许被一个程序占有,使用完调用 del 释放锁。

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

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

191. Redis 如何做内存优化?

尽量使用 Redis 的散列表,把相关的信息放到散列表里面存储,而不是把每个字段单独存储,这样可以有效的减少内存使用。比如将 Web 系统的用户对象,应该放到散列表里面再整体存储到 Redis,而不是把用户的姓名、年龄、密码、邮箱等字段分别设置 key 进行存储。

192. 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(驱逐):禁止驱逐数据。
193. Redis 常见的性能问题有哪些?该如何解决?
  • 主服务器写内存快照,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以主服务器最好不要写内存快照。
  • Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,主从库最好在同一个局域网内。

十五.RabbitMQ

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

异步、削峰、解耦

  • 抢购活动,削峰填谷,防止系统崩塌。
  • 延迟信息处理,比如 10 分钟之后给下单未付款的用户发送邮件提醒。
  • 解耦系统,对于新增的功能可以单独写模块扩展,比如用户确认评价之后,新增了给用户返积分的功能,这个时候不用在业务代码里添加新增积分的功能,只需要把新增积分的接口订阅确认评价的消息队列即可,后面再添加任何功能只需要订阅对应的消息队列即可。
    https://www.zhihu.com/question/34243607/answer/1023686807
136. RabbitMQ 有哪些重要的角色?

RabbitMQ 中重要的角色有:生产者、消费者和代理:

  • 生产者:消息的创建者,负责创建和推送数据到消息服务器;
  • 消费者:消息的接收方,用于处理数据和确认消息;
  • 代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
137. RabbitMQ 有哪些重要的组件?
  • ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用。
  • Channel(信道):消息推送使用的通道。
  • Exchange(交换器):用于接受、分配消息。
  • Queue(队列):用于存储生产者的消息。
  • RoutingKey(路由键):用于把生成者的数据分配到交换器上。
  • BindingKey(绑定键):用于把交换器的消息绑定到队列上。
138. RabbitMQ 中 vhost 的作用是什么?

vhost:每个 RabbitMQ 都能创建很多 vhost,我们称之为虚拟主机,每个虚拟主机其实都是 mini 版的RabbitMQ,它拥有自己的队列,交换器和绑定,拥有自己的权限机制。

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

首先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间会创建一个 tcp 连接,一旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的用户名和密码),你的客户端和 RabbitMQ 就创建了一条 amqp 信道(channel),信道是创建在“真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有一个唯一的 id,不论是发布消息,订阅队列都是通过这个信道完成的。

140. RabbitMQ 怎么保证消息的稳定性?
  • 提供了事务的功能。
  • 通过将 channel 设置为 confirm(确认)模式。
141. RabbitMQ 怎么避免消息丢失?
  • 把消息持久化磁盘,保证服务器重启消息不丢失。
  • 每个集群中至少有一个物理磁盘,保证消息落入磁盘。
142. 要保证消息持久化成功的条件有哪些?

声明队列必须设置持久化 durable 设置为 true.
消息推送投递模式必须设置持久化,deliveryMode 设置为 2(持久)。
消息已经到达持久化交换器。
消息已经到达持久化队列。
以上四个条件都满足才能保证消息持久化成功。

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

持久化的缺地就是降低了服务器的吞吐量,因为使用的是磁盘而非内存存储,从而降低了吞吐量。可尽量使用 ssd 硬盘来缓解吞吐量的问题。

144. RabbitMQ 有几种广播类型?

direct(默认方式):最基础最简单的模式,发送方把消息发送给订阅方,如果有多个订阅者,默认采取轮询的方式进行消息发送。
fanout:分发模式,把消费分发给所有订阅者。
topic:匹配订阅模式,使用正则匹配到消息队列,能匹配到的都能接收到。

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

延迟队列的实现有两种方式:
通过消息过期后进入死信交换器,再由交换器转发到延迟消费队列,实现延迟功能;
使用 RabbitMQ-delayed-message-exchange 插件实现延迟功能。

146. RabbitMQ 集群有什么用?

集群主要有以下两个用途:
高可用:某个服务器出现问题,整个 RabbitMQ 还可以继续使用;
高容量:集群可以承载更多的消息量。

147. RabbitMQ 节点的类型有哪些?

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

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

各节点之间使用“–link”连接,此属性不能忽略。
各节点使用的 erlang cookie 值必须相同,此值相当于“秘钥”的功能,用于各节点的认证。
整个集群中必须包含一个磁盘节点。

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

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

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

如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:
不能创建队列
不能创建交换器
不能创建绑定
不能添加用户
不能更改权限
不能添加和删除集群节点
唯一磁盘节点崩溃了,集群是可以保持运行的,但你不能更改任何东西。

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

RabbitMQ 对集群的停止的顺序是有要求的,应该先关闭内存节点,最后再关闭磁盘节点。如果顺序恰好相反的话,可能会造成消息的丢失。

十六.ElasticSearch

000.什么是ElasticSearch?

Elasticsearch是一个基于Lucene的搜索引擎。它提供了具有HTTP Web界面和无架构JSON文档的分布式,多租户能力的全文搜索引擎。
Elasticsearch是用Java开发的,根据Apache许可条款作为开源发布。

000.Elasticsearch中的倒排索引是什么?
  • 倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。倒排索引是一种像数据结构一样的散列图,可将用户从单词导向文档或网页。它是搜索引擎的核心。其主要目标是快速搜索从数百万文件中查找数据。
  • 倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
  • 也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构。通过倒排索引,可以根据单词快速获取包含这个单词的文档列表。倒排索引主要由两个部分组成:“单词词典”和“倒排文件”。
000.ElasticSearch是否有架构?

ElasticSearch可以有一个架构。架构是描述文档类型以及如何处理文档的不同字段的一个或多个字段的描述。Elasticsearch中的架构是一种映射,它描述了JSON文档中的字段及其数据类型,以及它们应该如何在Lucene索引中进行索引。因此,在Elasticsearch术语中,我们通常将此模式称为“映射”。
Elasticsearch具有架构灵活的能力,这意味着可以在不明确提供架构的情况下索引文档。如果未指定映射,则默认情况下,Elasticsearch会在索引期间检测文档中的新字段时动态生成一个映射。

000.ElasticSearch中的副本是什么?

一个索引被分解成碎片以便于分发和扩展。副本是分片的副本。一个节点是一个属于一个集群的ElasticSearch的运行实例。一个集群由一个或多个共享相同集群名称的节点组成。

000.ElasticSearch中的分析器是什么?

在ElasticSearch中索引数据时,数据由为索引定义的Analyzer在内部进行转换。 分析器由一个Tokenizer和零个或多个TokenFilter组成。编译器可以在一个或多个CharFilter之前。分析模块允许您在逻辑名称下注册分析器,然后可以在映射定义或某些API中引用它们。
Elasticsearch附带了许多可以随时使用的预建分析器。或者,您可以组合内置的字符过滤器,编译器和过滤器器来创建自定义分析器。

000.什么是ElasticSearch中的编译器?

编译器用于将字符串分解为术语或标记流。一个简单的编译器可能会将字符串拆分为任何遇到空格或标点的地方。Elasticsearch有许多内置标记器,可用于构建自定义分析器。

000.启用属性,索引和存储的用途是什么?

enabled属性适用于各类ElasticSearch特定/创建领域,如index和size。用户提供的字段没有“已启用”属性。 存储意味着数据由Lucene存储,如果询问,将返回这些数据。
存储字段不一定是可搜索的。默认情况下,字段不存储,但源文件是完整的。因为您希望使用默认值(这是有意义的),所以不要设置store属性 该指数属性用于搜索。
索引属性只能用于搜索。只有索引域可以进行搜索。差异的原因是在分析期间对索引字段进行了转换,因此如果需要的话,您不能检索原始数据。

000.为什么要使用Elasticsearch?

因为在我们商城中的数据,将来会非常多,所以采用以往的模糊查询,模糊查询前置配置,会放弃索引,导致商品查询是全表扫面,在百万级别的数据库中,效率非常低下,而我们使用ES做一个全文索引,我们将经常查询的商品的某些字段,比如说商品名,描述、价格还有id这些字段我们放入我们索引库里,可以提高查询速度。

000.ElasticSearch中的集群、节点、索引、文档、类型是什么?
  • 群集是一个或多个节点(服务器)的集合,它们共同保存您的整个数据,并提供跨所有节点的联合索引和搜索功能。群集由唯一名称标识,默认情况下为“elasticsearch”。此名称很重要,因为如果节点设置为按名称加入群集,则该节点只能是群集的一部分。
  • 节点是属于集群一部分的单个服务器。它存储数据并参与群集索引和搜索功能。
  • 索引就像关系数据库中的“数据库”。它有一个定义多种类型的映射。索引是逻辑名称空间,映射到一个或多个主分片,并且可以有零个或多个副本分片。
    MySQL =>数据库    ElasticSearch =>索引
  • 文档类似于关系数据库中的一行。不同之处在于索引中的每个文档可以具有不同的结构(字段),但是对于通用字段应该具有相同的数据类型。
    MySQL => Databases =>   Tables => Columns / Rows ElasticSearch => Indices => Types =>具有属性的文档
  • 类型是索引的逻辑类别/分区,其语义完全取决于用户。
000.ElasticSearch中的分片是什么?

在大多数环境中,每个节点都在单独的盒子或虚拟机上运行。
  索引 - 在Elasticsearch中,索引是文档的集合。
  分片 -因为Elasticsearch是一个分布式搜索引擎,所以索引通常被分割成分布在多个节点上的被称为分片的元素。

十七.Spring Cloud

110. 什么是 spring cloud?

spring cloud 是一系列框架的有序集合。它利用 spring boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 spring boot 的开发风格做到一键启动和部署。

111. spring cloud 断路器的作用是什么?

在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。

112. spring cloud 的核心组件有哪些?
  • Eureka:服务注册发现(注册中心);帮我们管理服务的通信地址(ip,端口)
  • Ribbon\Feign : 客户端负载均衡;解决服务负载均衡调用的
    Feign:基于动态代理机制,根据注解和选择的机器,拼接请求 url 地址,发起请求。
  • Hystrix:断路器;提供线程池,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。
  • Zuul:网关,统一访问入口;微服务的大门(安保部门);由 Zuul 网关转发请求给对应的服务。
  • Config :分布式配置;统一管理微服务的配置

十八.JVM

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

在这里插入图片描述
类加载器(ClassLoader)
运行时数据区(Runtime Data Area)
执行引擎(Execution Engine)
本地库接口(Native Interface)

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

https://blog.csdn.net/drose29/article/details/125296697
https://blog.csdn.net/wjgcl/article/details/124805628

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

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

196. 说一下堆栈的区别?

功能方面:堆是用来存放对象的,栈是用来执行程序的。
共享性:堆是线程共享的,栈是线程私有的。
空间大小:堆大小远远大于栈。

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

队列和栈都是被用来预存储数据的。
队列允许先进先出检索元素,但也有例外的情况,Deque 接口允许从两端检索元素。
栈和队列很相似,但它运行对元素进行后进先出进行检索。

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

在介绍双亲委派模型之前先说下类加载器。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。

类加载器分类:
启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库;
其他类加载器:
扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录或Java. ext. dirs系统变量指定的路径中的所有类库;
应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。

双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。

loadClass方法的主要职责就是实现双亲委派机制:首先检查这个类是不是已经被加载过了,如果加载过了直接返回,否则委派给父加载器加载,这是一个递归调用,一层一层向上委派,最顶层的类加载器(启动类加载器)无法加载该类(它的搜索范围中没找到所需的类)时,再一层一层向下委派给子类加载器加载。

为什么要双亲委派?
双亲委派保证类加载器,自下而上的委派,又自上而下的加载,保证每一个类在各个类加载器中都是同一个类。
一个非常明显的目的就是保证java官方的类库\lib和扩展类库\lib\ext的加载安全性,不会被开发者覆盖。

https://blog.csdn.net/wzwjm123/article/details/124803527?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-124803527-blog-125683873.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-124803527-blog-125683873.pc_relevant_aa&utm_relevant_index=2
199. 说一下类装载的执行过程?

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

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

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

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

强引用:发生 gc 的时候不会被回收。
软引用:有用但不是必须的对象,在发生内存溢出之前会被回收。
弱引用:有用但不是必须的对象,在下一次GC时会被回收。
虚引用(幽灵引用/幻影引用):无法通过虚引用获得对象,用 PhantomReference 实现虚引用,虚引用的用途是在 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、Parallel Scavenge
老年代回收器:Serial Old、Parallel Old、CMS
整堆回收器:G1
新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收。

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

分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 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)时,升级为老生代。大对象也会直接进入老生代。
老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。

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

JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是 jconsole 和 jvisualvm 这两款视图监控工具。
jconsole:用于对 JVM 中的内存、线程和类等进行监控;
jvisualvm:JDK 自带的全能分析工具,可以分析:内存快照、线程快照、程序死锁、监控内存的变化、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 详细信息。

结尾

凡事预则立,不预则废。能读到这里的人,我相信都是这个世界上的“有心人”,星光不问赶路人,时光不负有心人!我相信你的每一步努力,都会收获意想不到的回报。

其他

000.数据库为什么要分库?

答案很简单:数据库出现性能瓶颈。用大白话来说就是数据库快扛不住了。
数据库出现性能瓶颈,对外表现有几个方面:
大量请求阻塞在高并发场景下,大量请求都需要操作数据库,导致连接数不够了,请求处于阻塞状态。
SQL 操作变慢如果数据库中存在一张上亿数据量的表,一条 SQL 没有命中索引会全表扫描,这个查询耗时会非常久。
存储出现问题业务量剧增,单库数据量越来越大,给存储造成巨大压力。
从机器的角度看,性能瓶颈无非就是CPU、内存、磁盘、网络这些,要解决性能瓶颈最简单粗暴的办法就是提升机器性能,但是通过这种方法成本和收益投入比往往又太高了,不划算,所以重点还是要从软件角度入手。

Kafka

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

kafka 不能脱离 zookeeper 单独使用,因为 kafka 使用 zookeeper 管理和协调 kafka 的节点服务器。

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

kafka 有两种数据保存策略:按照过期时间保留和按照存储的消息大小保留。

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

这个时候 kafka 会执行数据清除工作,时间和大小不论那个满足条件,都会清空数据。

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

cpu 性能瓶颈
磁盘读写瓶颈
网络瓶颈

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

集群的数量不是越多越好,最好不要超过 7 个,因为节点越多,消息复制需要的时间就越长,整个群组的吞吐量就越低。
集群数量最好是单数,因为超过一半故障集群就不能用了,设置为单数容错率更高。

Zookeeper

157. zookeeper 是什么?

zookeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 google chubby 的开源实现,是 hadoop 和 hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

158. zookeeper 都有哪些功能?

集群管理:监控节点存活状态、运行请求等。
主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用 zookeeper 可以协助完成这个过程。
分布式锁:zookeeper 提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。zookeeper可以对分布式锁进行控制。
命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。

159. zookeeper 有几种部署模式?

zookeeper 有三种部署模式:
单机部署:一台集群上运行;
集群部署:多台集群运行;
伪集群部署:一台集群启动多个 zookeeper 实例运行。

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

zookeeper 的核心是原子广播,这个机制保证了各个 server 之间的同步。实现这个机制的协议叫做 zab 协议。 zab 协议有两种模式,分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,zab 就进入了恢复模式,当领导者被选举出来,且大多数 server 完成了和 leader 的状态同步以后,恢复模式就结束了。状态同步保证了 leader 和 server 具有相同的系统状态。

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

在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,所以就需要主节点。

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

可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。

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

客户端端会对某个 znode 建立一个 watcher 事件,当该 znode 发生变化时,这些客户端会收到 zookeeper 的通知,然后客户端可以根据 znode 变化来做出业务上的改变。

Dubbo

SpringCloud与Dubbo的区别?

https://blog.csdn.net/weixin_51291483/article/details/109212137/

Hibernate

113. 为什么要使用 hibernate?

hibernate 是对 jdbc 的封装,大大简化了数据访问层的繁琐的重复性代码。
hibernate 是一个优秀的 ORM 实现,很多程度上简化了 DAO 层的编码功能。
可以很方便的进行数据库的移植工作。
提供了缓存机制,是程序执行更改的高效。

114. 什么是 ORM 框架?

ORM(Object Relation Mapping)对象关系映射,是把数据库中的关系数据映射成为程序中的对象。
使用 ORM 的优点:提高了开发效率降低了开发成本、开发更简单更对象化、可移植更强。

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

在 Config 里面把 hibernate. show_SQL 设置为 true 就可以。但不建议开启,开启之后会降低程序的运行效率。

116. hibernate 有几种查询方式?

三种:hql、原生 SQL、条件查询 Criteria。

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

实体类可以定义为 final 类,但这样的话就不能使用 hibernate 代理模式下的延迟关联提供性能了,所以不建议定义实体类为 final。

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

Integer 类型为对象,它的值允许为 null,而 int 属于基础数据类型,值不能为 null。

119. hibernate 是如何工作的?

读取并解析配置文件。
读取并解析映射文件,创建 SessionFactory。
打开 Session。
创建事务。
进行持久化操作。
提交事务。
关闭 Session。
关闭 SessionFactory。

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

数据查询时,没有 OID 指定的对象,get() 返回 null;load() 返回一个代理对象。
load()支持延迟加载;get() 不支持延迟加载。

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

hibernate 常用的缓存有一级缓存和二级缓存:
一级缓存:也叫 Session 缓存,只在 Session 作用范围内有效,不需要用户干涉,由 hibernate 自身维护,可以通过:evict(object)清除 object 的缓存;clear()清除一级缓存中的所有缓存;flush()刷出缓存;
二级缓存:应用级别的缓存,在所有 Session 中都有效,支持配置第三方的缓存,如:EhCache。

122. hibernate 对象有哪些状态?

临时/瞬时状态:直接 new 出来的对象,该对象还没被持久化(没保存在数据库中),不受 Session 管理。
持久化状态:当调用 Session 的 save/saveOrupdate/get/load/list 等方法的时候,对象就是持久化状态。
游离状态:Session 关闭之后对象就是游离状态。

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

getCurrentSession 会绑定当前线程,而 openSession 则不会。
getCurrentSession 事务是 Spring 控制的,并且不需要手动关闭,而 openSession 需要我们自己手动开启和提交事务。

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

hibernate 中每个实体类必须提供一个无参构造函数,因为 hibernate 框架要使用 reflection api,通过调用 ClassnewInstance() 来创建实体类的实例,如果没有无参的构造函数就会抛出异常。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值