文章目录

1. Java 语言特点

Java语言特点

  • Java 是一门面向对象的编程语言(面向对象三大特性:封装,继承,多态)。
  • Java 是跨平台的。有了 JVM, Java 程序可以一次编写,到处运行。不同的操作系统只要安装相应的 JVM,就能运行 Java 程序。
  • Java 是支持多线程的。C++ 11 以前没有多线程机制,只能调用操作系统提供的多线程 API。
  • Java 是 编译与解释并存的。Java 程序运行 需要经过 先编译,后解释 两个步骤。
    • .java 文件 → javac 编译 → .class 文件 → 解释器 & JIT → 机器可理解的机器码 → 计算机
      在这里插入图片描述

2. JVM vs JDK vs JRE

  • JVM (Java 虚拟机)是二进制字节码的运行环境。有了 JVM, Java 程序可以一次编写,到处运行。不同的操作系统只要安装相应的 JVM,就能运行 Java 程序,这就是 JVM 的跨平台性。而且 JVM 提供了垃圾回收功能,可以自动管理内存。
  • 在 JVM 的基础之上 结合基础类库就构成了 JRE(Java 运行时环境),基础类库就是开发 Java 代码用到的所有的类,比如 java.lang.*, 集合类,线程类,日期类,IO 类等。
  • 在 JRE 的基础之上 结合编译工具,比如 javac(编译),javap(反编译),还有一些内存监测工具,就构成了 JDK (Java 开发工具包)
  • 简单来说,JDK 包含 JRE,JRE 包含 JVM。
    在这里插入图片描述

3. 什么是字节码? 采用字节码的好处是什么?

  • JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的文件)。

  • 好处:字节码能被 JVM 识别,可以实现跨平台性,Java 程序可在不同操作系统上运行。

4. 标识符和关键字的区别是什么?

在我们写代码的时候,需要为一些变量、方法啥的取名字,这些名字就叫 标识符 。简单来说, 标识符就是一个名字

有一些特殊含义的标识符,比如 int,private,public 这种,就是 关键字

5. Java 有哪些数据类型?

Java 语言数据类型分为两种:基本数据类型和引用数据类型。

5.1 基本数据类型

  • 6 种数字类型:
    • 4 种整数型:byteshortintlong
    • 2 种浮点型:floatdouble
  • 1 种布尔型:boolean
  • 1 种字符类型:char

这 8 种基本数据类型的默认值以及所占空间的大小如下:

基本类型位数字节默认值取值范围
byte810-128 ~ 127
short1620-32768 ~ 32767
int3240-2147483648 ~ 2147483647
long6480L-9223372036854775808 ~ 9223372036854775807
float3240f1.4E-45 ~ 3.4028235E38
double6480d4.9E-324 ~ 1.7976931348623157E308
boolean1falsetrue、false
char162‘u0000’0 ~ 65535

5.2 引用数据类型

  • 类(class)
  • 接口(interface)
  • 数组 ( [] )
    在这里插入图片描述

6. 自动类型转换、强制类型转换

  • 表示范围小的数值赋给表示范围大的变量时,进行自动类型转换;反之,需要强制转换。

  • 这就好像,小杯里的水倒进大杯没问题,但大杯的水倒进小杯就不行了,可能会溢出。
    在这里插入图片描述

float f = 3.4,对吗?

不正确。3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于向下转型(down-casting)会造成精度损失,因此需要强制类型转换 float f = (float) 3.4; 或者写成 float f = 3.4F

short s1 = 1; s1 = s1 + 1;对吗?short s1 = 1; s1 += 1;对吗?

对于 short s1 = 1; s1 = s1 + 1; 编译出错,由于 1 是 int 类型,因此 s1+1 运算结果也是 int 型,需要强制转换类型才能赋值给 short 型。

而 short s1 = 1; s1 += 1; 可以正确编译,因为 s1+= 1;相当于 s1 = (short(s1 + 1); 其中有隐含的强制类型转换

7. 自动装箱与拆箱了解吗?原理是什么?

  • 基本类型与对应的包装类型之间的赋值使用自动装箱与拆箱完成。
Integer x = 2;     // 装箱
int y = x;         // 拆箱

装箱调用了 包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。

因此,

  • Integer i = 10 等价于 Integer i = Integer.valueOf(10)
  • int n = i 等价于 int n = i.intValue();

8. 基本类型和包装类型的区别?

  • 为什么要有包装类 : 方便编程。Java 是一种面向对象的语言,有时候需要对象操作才比较方便。比如说一个方法的参数是个集合,集合只能存对象,不能存基本数据类型。包装类型可用于泛型,而基本类型不可以。

  • 比较方式 :对于基本数据类型来说,== 比较的是值。对于包装数据类型来说,== 比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用 equals() 方法。

9. 包装类型的缓存机制了解么?

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False

10. 为什么浮点数运算的时候会有精度丢失的风险?

在这里插入图片描述
计算机里的数据是二进制的,在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。
在这里插入图片描述

11. 如何解决浮点数运算的精度丢失问题?

BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。

12. 超过 long 整型的数据应该如何表示?

基本数值类型都有一个表示范围,如果超过这个范围就会有数值溢出的风险。在 Java 中,64 位 long 整型是最大的整数类型。
可以用 BigInteger 表示。 BigInteger 内部使用 int[] 数组来存储任意大小的整型数据。

13. & 和 && 有什么区别?

我们一般都是用短路与运算符(&&),而不是逻辑与运算符(&)。

&& 之所以称为短路运算是因为,如果 && 左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。

例如在验证用户登录时判定用户名不是 null 而且不是空字符串,应当写为username != null && !username.equals(""),二者的顺序不能交换,更不能用 & 运算符,因为第一个条件如果不成立,根本不能进行字符串的 equals 比较,否则会产生 NullPointerException 异常。

注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

14. 面向过程和面向对象的区别?

  • 面向过程:面向过程就是分析出解决问题所需要的步骤,即先干啥,后干啥。然后用函数把这些步骤一步一步实现,使用的时候再一个个调用。
  • 面向对象:看名字它是注重对象的。当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决。

例子:问题: 洗衣机里面放有脏衣服,怎么洗干净?

面向过程的解决方法:

1、执行加洗衣粉方法;

2、执行加水方法;

3、执行洗衣服方法;

4、执行清洗方法;

5、 执行烘干方法;

以上就是将解决这个问题的过程拆成一个个方法(是没有对象去调用的),通过一个个方法的执行来解决问题。

面向对象的解决方法:

1、我先弄出两个对象:“洗衣机”对象和“人”对象

2、针对对象“洗衣机”加入一些属性和方法:“洗衣服方法”“清洗方法”、“烘干方法”

3、针对对象“人”加入属性和方法:“加洗衣粉方法”、“加水方法”

4、然后执行

人.加洗衣粉

人.加水

洗衣机.洗衣服

洗衣机.清洗

洗衣机.烘干

解决同一个问题 ,面向对象编程就是先抽象出对象,然后用对象执行方法的方式解决问题。

15. 面向对象三大特征

封装、继承、多态
有了封装,才有继承,有了继承,才能说多态。

  • 封装:

    • 第一步:属性私有化(使用 private 关键字进行修饰。)
    • 第二步:对外提供简单的操作入口。1个属性对外提供 set 和 get 方法。外部程序只能通过 set 方法修改,只能通过 get 方法读取。
  • 继承:

    • 基本作用:子类继承父类,代码可以得到复用。
    • 主要作用:因为有了继承关系,才有方法重写和多态机制。
    • 继承相关特性:java 中的继承只支持单继承,不支持多继承。java 中的类没有显式的继承任何类,则默认继承 Object 类。
  • 多态:

    • 多态就是对象的多种形态,多种状态,编译和运行有两个不同的状态。

      编译期叫做静态绑定

      运行期叫做动态绑定

      比如 Animal a = new Cat();

      ​ a.move();

      编译的时候编译器发现 a 的类型是 Animal,所以编译器会去 Animal 类中找对应的 move() 方法,找到了,绑定,编译通过。

      但是运行的时候和堆中的实际对象有关,真正执行的时候会自动调用“堆内存中真实对象”的相关方法。

16. 方法重载和方法重写有什么区别?

  • 方法重载发生在同一个类中,方法名相同,参数列表不同。
  • 方法重写发生在具有继承关系的父子类中,重写之后的方法必须和之前的方法一致:方法名一致、参数列表一致、返回值类型一致。

17. 接口和抽象类有什么共同点和区别?

共同点

  • 都不能被实例化。
  • 都可以包含抽象方法。
  • 都可以有默认实现的方法(Java 8 可以用 default 关键字在接口中定义默认方法)。

区别

  • 接口主要是对行为的抽象,实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。
  • 一个类只能继承一个抽象类,但是可以实现多个接口。
  • 接口中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值,而抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值。

18. 成员变量与局部变量的区别?

语法形式 :成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数

存储方式

  • 如果是 static 修饰的,那这个成员变量是静态变量,在方法区。

  • 如果没有 static 修饰,这个成员变量就是实例变量,在堆中。

  • 局部变量在栈中。

线程安全

  • 局部变量:永远都不会存在线程安全问题。因为局部变量不共享。(一个线程一个栈。)
  • 成员变量:可能会有线程安全问题。
public class VariableExample {
    // 成员变量
    private String name;
    private int age;
    
    // 方法中的局部变量
    public void method() {
        int num1 = 10; // 栈中分配的局部变量
        String str = "Hello, world!"; // 栈中分配的局部变量
        System.out.println(num1);
        System.out.println(str);
    }
    
    // 带参数的方法中的局部变量
    public void method2(int num2) {
        int sum = num2 + 10; // 栈中分配的局部变量
        System.out.println(sum);
    }
    
    // 构造方法中的局部变量
    public VariableExample(String name, int age) {
        this.name = name; // 对成员变量进行赋值
        this.age = age; // 对成员变量进行赋值
        int num3 = 20; // 栈中分配的局部变量
        String str2 = "Hello, " + this.name + "!"; // 栈中分配的局部变量
        System.out.println(num3);
        System.out.println(str2);
    }
}

19. 静态变量有什么作用?

静态变量是被 static 关键字修饰的变量。

  • 可以节省内存:无论一个类创建了多少个对象,它们都共享同一份静态变量,静态变量只会被分配一次内存。

  • 静态变量可以直接通过类名来访问。

  • 常量 用 public static final 表示。

public class ConstantVariableExample {
    // 常量
    public static final int constantVar = 0;
}

20. 静态方法为什么不能调用非静态成员?

静态方法是属于类的,在类加载的时候就会分配内存,通过 类名. 访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过 对象. 去访问。

21. 创建一个对象用什么运算符? 对象实例与对象引用有何不同?

new 运算符,new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。

22. 比较两个字符串 ==equals

String str1 = "hello";
String str2 = new String("hello");
String str3 = "hello";
// 使用 == 比较字符串的引用相等
System.out.println(str1 == str2);
System.out.println(str1 == str3);
// 使用 equals 方法比较字符串的相等
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));

输出结果:

false
true
true
true

str1str2 不相等,而 str1str3 相等。这是因为 == 运算符比较的是字符串的引用是否相等。

str1str2str3 三者的内容都相等。这是因为 equals 方法比较的是字符串的内容,即使这些字符串的对象引用不同,只要它们的内容相等,就认为它们是相等的。

23. 什么条件满足的时候构成方法重写 @override?

  • 有继承关系的两个类
  • 具有相同方法名、返回值类型、形式参数列表
  • 访问权限不能更低。
  • 抛出异常不能更多。

24. 为什么重写 equals() 时必须重写 hashCode() 方法?

  • 因为两个相等的对象的 hashCode 值必须是相等的。

Object 类中的 equals() 方法,比较的是对象的内存地址。比如 new 了两个对象
Student s1 = new Student(“zhangsan”,“18”);
Student s2 = new Student(“zhangsan”,“18”);
没有重写 equals() 的时候比较的是对象的内存地址,它们就不相等。但是我们认为内容相等的就是同一对象,所以要重写 equals。
但是 s1 和 s2 两个对象的 hashcode 值是不同的。所以要重写 hashcode。

hashCode() 有什么用?

hashCode() 的作用是获取哈希码(int 整数)。作用是确定该对象在哈希表中的索引位置。

为什么两个对象有相同的 hashcode 值,它们也不⼀定是相等的?

因为哈希冲突, hashCode() 所使用的散列函数也许刚好会让多个对象传回相同的散列值。

25. 浅拷贝和深拷贝区别?

  • 浅拷贝:浅拷贝只拷贝对象的成员变量的值,而引用类型的对象不会拷贝。
  • 深拷贝 :深拷贝会完全复制整个对象,包括这个对象所包含的引用类型的对象。

例如现在有一个 order 对象,里面有一个 products 列表,它的浅拷贝和深拷贝的示意图:
在这里插入图片描述
因此深拷贝是安全的,浅拷贝的话如果有引用类型,那么拷贝后对象,引用类型变量修改,会影响原对象。

浅拷贝如何实现呢?

Object 类提供的 clone() 方法可以非常简单地实现对象的浅拷贝。

深拷贝如何实现呢?

  • 重写克隆方法:重写克隆方法,引用类型变量单独克隆,这里可能会涉及多层递归。
  • 序列化:可以先将原对象序列化,再反序列化成拷贝对象。

26. final、finally、finalize 的区别?

  • final 表示最终的,不可变的。可以修饰变量、方法和类:
    • final 修饰的变量不可变。
    • final 修饰的方法不可被重写;
    • final 修饰的类不可被继承;
  • finally 是一个关键字,和 try/catch 一起用于异常的处理。
    • 无论在 try 中的语句块是否发生异常,finally 中的语句块一定会被执行。
    • 一般会在 finally 在代码执行完毕后用于释放资源,如断开连接,关闭流等操作。
    • finally 语句块是一定会被执行的(除非把虚拟机干掉 System.exit (0))。
  • finalize() 是 java.lang.Object 里定义的方法,也就是说每一个对象都有这个方法,这个方法在 gc 启动,该对象被垃圾回收的时候被调用。
    • 尽量不使用 finalize() 方法,让垃圾回收器自己去释放对象的内存就好了。一个对象的 finalize() 方法只会被调用一次,finalize() 被调用不一定会立即回收该对象,所以有可能调用 finalize() 后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会再次调用 finalize() 了,进而产生问题。

27. == 和 equals 的区别?

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

  • 对于基本数据类型来说,== 比较的是值。
  • 对于引用数据类型来说,== 比较的是对象的内存地址。

equals() 作用也是判断两个对象是否相等。但是这个“相等”一般也分两种情况:

  • 默认情况:一个类没有重写 equals() 方法,则 equals() 比较的是两个对象的内存地址。
  • 自定义情况:一个类重写了 equals() 方法。我们一般认为两个对象的值如果相等,两个对象就是相等的。

举个例子,Person,我们认为两个人的编号和姓名相同,就是一个人:

public class Person {
    private String no;
    private String name;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return Objects.equals(no, person.no) &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(no, name);
    }
}

28. String、StringBuffer 与 StringBuilder 的区别

  • 第一点: 可变和适用范围。String 是不可变的,而 StringBuffer 和 StringBuilder 是可变的。每次对 String 的操作相当于生成一个新的 String 对象,而对 StringBuffer 和 StringBuilder 的操作是对对象本身的操作,而不会生成新的对象,所以对于频繁改变内容的字符串避免使用 String,因为频繁的生成对象将会对系统性能产生影响。
  • 第二点: 线程安全。String 由于有 final 修饰,是不可变的,是线程安全的。StringBuilder 和StringBuffer 的区别在于 StringBuilder 不是线程安全的,StringBuffer 有 synchronized 修饰,是线程安全的。
  • 如果使用局部变量的话,建议使用:StringBuilder,因为局部变量不存在线程安全问题。

29. Java 中三大变量

实例变量:在堆中。

静态变量:在方法区。

局部变量:在栈中。

以上三大变量中:
局部变量永远都不会存在线程安全问题。
因为局部变量不共享。(一个线程一个栈。)
局部变量在栈中。所以局部变量永远都不会共享。

实例变量在堆中,堆只有1个。
静态变量在方法区中,方法区只有1个。
堆和方法区都是多线程共享的,所以可能存在线程安全问题。

局部变量+常量:不会有线程安全问题。
成员变量:可能会有线程安全问题。

30. Java 创建对象有哪几种方式?

Java 中有以下四种创建对象的方式:

  • new 创建新对象
  • 通过反射机制
  • 采用 clone 机制
  • 通过序列化机制

前两者都需要显式地调用构造方法。对于 clone 机制,需要注意浅拷贝和深拷贝的区别,对于序列化机制需要明确其实现原理,在 Java 中序列化可以通过 Serializable 来实现。
在这里插入图片描述

31. Java 中异常处理体系

Throwable 是 所有错误和异常的父类。 Throwable 有 2 个子类: Error 和 Exception。

其中 Error 是程序无法处理的错误,比如内存溢出,虚拟机异常。

Exception 是程序问题导致的异常,又分为两种:Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。

  • 在编译过程中,如果受检查异常没有被处理的话,就没办法通过编译。常见的受检查异常有:IO 相关的异常、ClassNotFoundException、SQLException 等。
  • 在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。常见的不受检查异常有:空指针、数组下标越界等。

在这里插入图片描述

31. 异常的处理方式

异常的处理主要有两种方式:用 try catch finally 捕获异常 或者 抛出异常。

其中 throws 是向上抛异常,throw 是手动建一个处理异常的类并抛出它。
在这里插入图片描述

32. Java 中 IO 流分为几种?

IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。

既然有了字节流,为什么还要有字符流?

字符流是由 Java 虚拟机将字节转换得到的,这个过程还算是比较耗时;
如果我们不知道编码类型的话,使用字节流的过程中很容易出现乱码问题。

33. BIO、NIO?

BIO 是传统的 IO,是阻塞的,NIO 是非阻塞的。

Java 中的 NIO 使我们可以进行非阻塞 IO 操作。比如说,单线程中从通道读取数据到缓冲区,同时可以继续做别的事情,当数据读取到缓冲区中后,线程再继续处理数据。写数据也是一样的,一个线程请求写入一些数据到通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。

34. 什么是 IO 多路复用?

IO 多路复用:一个进程可以同时处理多个 IO 请求。

35. 什么是反射?反射的原理?反射在开发中的应用

什么是反射?

我们平时通过 new 来创建对象,这种方式是一种“正射”,这种方式在编译的时候就确定了对象的类型。

而如果我们想要动态地获取类里面的信息、创建类的实例、调用类里面的方法,这时候就需要用到反射。

反射的原理?

Java 程序的执行分为编译和运行两步,编译之后会生成字节码(.class)文件,JVM 进行类加载的时候,会加载字节码文件,将类的相关信息加载进方法区,反射就是去获取这些信息,然后进行各种操作。

反射的应用

正是因为反射,我们轻松地使用各种框架。

像 Spring 里的很多 注解 ,它的功能实现就是利用反射。

  • @Component 注解就声明了一个类为 Spring Bean,
  • @Value 注解可以读取到配置文件中的值。

这些都是基于反射操作类,然后获取到注解。注解本质上是一个标记,有了这个标记之后,我们就可以在编译或者运行阶段去识别这些标记,然后搞一些事情,这就是注解的用处。

  • 比如 Spring 里面的 AOP,使用注解作为切点,做一些功能增强,这就是反射在程序运行时的应用。
  • 比如使用 lombok,我们就不需要再写 getter、setter 这些方法。这就是注解在程序编译时的应用。

36. 什么时候数据在多线程并发的环境下会存在安全问题?

三个条件:
条件1:多线程并发。
条件2:有共享数据。
条件3:共享数据有修改的行为。

满足以上3个条件之后,就会存在线程安全问题。

37. 怎么解决线程安全问题?

专业术语叫做:线程同步 synchronized,让线程不能并发,线程必须排队执行。

38. 什么是线程池?

线程池是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。

39. 为什么要用线程池?

如果没有线程池,用户每发起一个请求,就要新创建一个线程处理,创建新线程开销是很大的。比如有1000万个请求,那我们要创建1000万个线程对象,每个对象都要占内存,那电脑的系统很快就卡死了。我们又需要很多线程,又不能创建太多线程,需要使用线程池,它能够复用线程。

40. 如何创建线程池?

  • 通过 ThreadPoolExecutor 构造函数来创建(推荐)。
  • 通过 Executors 工具类 来创建。

41. 为什么需要泛型?

指定集合中元素的类型,避免取出集合元素时的类型转换异常。
在这里插入图片描述

在这里插入图片描述

42. Spring 框架中的单例 bean 是线程安全的吗?

  • 不是线程安全的。Spring 中有一个 @Scope 注解,默认的值就是 singleton,单例的。
  • 一般在 bean 中注入的对象都是 Service 和 DAO 这样的无状态的对象,没有线程安全问题。
  • 一般在开发中最好不要定义可修改的成员变量,因为要考虑线程安全问题。解决办法就是将 bean 的作用域 @Scope 由 “singleton(单例)”改为 “prototype(多例)”。
    在这里插入图片描述
    在这里插入图片描述

43. Spring 两大特性 IOC 和 AOP

什么是 IOC? 如何实现的?

  • IOC(控制反转)是一种设计思想,将原本手动创建对象的控制权,交给 Spring 容器来管理,由 Spring 容器帮我们创建对象。当我们需要创建一个对象的时候,只需要配置好相应的配置文件即可(Bean 就是那些被 IOC 容器所管理的对象),不用考虑对象是如何被创建出来的。
  • Spring中的IOC的实现原理就是工厂模式加反射机制。
    在这里插入图片描述

什么是 AOP?

AOP(面向切面编程)是基于动态代理的。将公共的代码抽出来作为一个切面类,在这个切面类里做功能增强。主要是用来降低耦合,公共的代码不要和业务的代码写在一起。可以使用AOP用于后台管理系统中记录系统的操作日志,事务处理等。

AOP用于记录系统操作日志

AOP用于后台管理系统中记录系统操作日志:
主要思路是这样的,使用AOP中的 环绕通知 和 切点表达式,这个表达式就是要找到要记录日志的方法,然后通过环绕通知进行功能增强,比如获取请求方法的参数,请求方式,登录的IP,操作时间等,将这些信息保存到数据库,记录操作日志就完成咯

切点表达式:
在这里插入图片描述
某个类加了Log注解,就会进入环绕通知,进行功能增强

在这里插入图片描述

Spring中的事务是如何实现的?

Spring中的事务是AOP完成的,对方法前后进行拦截,在执行目标方法之前开启事务,执行完目标方法之后根据执行情况提交或者回滚事务,具体就是在方法或者类上加上@Transactional注解。
在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐