1. 🤖 前情提要

随着接口的到来,JavaSE的学习笔记大结局也即将来临,最近的几篇博客写到了封装,继承,多态,抽象类等等,都循序渐进得介绍了这类的知识,大家如果接口这一块理解的很困难的话,建议去完善一下前面的知识哦 👉Java封装 👉静态成员 👉代码块 👉内部类 👉继承 👉多态 👉抽象类

码字不易,本篇博客干货较多,建议收藏食用哦
那么我们就正式进入对接口的介绍

2. 🍚 接口

一图流
image.png

2.1 🍟 接口的概念以及一些知识点汇总

接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。 除非实现接口的类是抽象类,否则该类要定义接口中的所有方法接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。

2.1.1 🍣 接口与类的相同处

  • 一个接口中可以有多个
  • 接口文件保存在.Java结尾的文件中,文件名使用接口名
  • 接口的字节码文件保存在.class结尾的文件中
  • 接口相应的字节码文件必须在与包名称相匹配的目录结构中

2.1.2 🍜 接口与类的不同处

  • 接口不能用于实例化对象
  • 接口没有构造方法
  • 接口中所有的方法必须是抽象方法,在Java8之后接口中可以使用default关键字修饰的非抽象方法
  • 接口不能包含成员变量,除了static和final变量
  • 接口被类继承这个概念不准确,准确来说应该是要被类实现
  • 接口可以实现我们所说的多继承

2.1.3 🍔 接口的一些特点

  • 接口中每一个方法也是隐式抽象的,所以接口中的方法会被隐式得指定为public abstract (只可以是public abstract,其他修饰符都会报错)
  • 接口中含有变量,但是接口中得变量会被隐式的指定为public static final 变量(并且只能是public,用private修饰会报编译错误)
  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法

2.1.4 🍛 抽象类和接口的区别

在JDK1.8以前,它们有如下区别👇

  • 抽象类中的方法可以有具体可执行的语句,即方法体,就是能实现方法的具体功能,但是接口中的方法就不行(比如:System.out.println(“I’m super corn!!”);
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的
  • 接口中不能含有静态代码块以及静态方法的使用(用static修饰的方法),而抽象类可以有静态代码块和静态方法
  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口

那么这里要注意的是:
JDK1.8以后,接口中允许含有静态方法和方法体,允许包含具体实现的方法,该方法我们称之为“默认方法”,这种方法使用default关键字来修饰
JDK1.9以后,允许将方法定义为private,使某些复用的代码不会将方法暴露出去
抽象类存在的意义是为了让编译器更好地校验,一般抽象类我们不会直接使用,而是使用它的子类,如果不小心通过抽象类创建了对象,编译器就会及时提醒我们。

注意🍙:以上内容大致浏览一遍即可,看不懂没关系,下面我将为你一一讲解,然后回头再来看这些知识点将会有一种大梦初醒的感觉


那么在现实生活中,接口是什么呢?它可以是笔记本上的USB口,电源插座等
image.png
image.png
那么这些接口在实现意义上以及使用标准上也有所不同

  • 电脑的USB口上,可以插:U盘、鼠标、键盘…所有符合USB协议的设备
  • 电源插座插孔上,可以插:电脑、电视机、电饭煲…所有符合规范的设备

通过上述的例子我们就可以看出:接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型

2.2 🍱 语法规则

接口的定义格式与定义类的格式基本上相同,将class关键字换成interface关键字就定义了一个接口。

  1. public interface 接口名称{
  2. //抽象方法
  3. public abstract void method1();
  4. //public abstract是固定搭配,可以不写
  5. public void method2();
  6. abstract void method3();
  7. void method4();
  8. //注意:在接口中上述的写法都是抽象方法,所以method4这样写代码更整洁
  9. }

提示🍙 :

  1. 创建接口时,接口的命名一般以大写字母I(读ai)开头
  2. 接口的命名一般使用形容词词性的单词
  3. 阿里编码规范中约定,接口中的方法和属性不要加任何修饰符,保持代码的整洁性

2.3 🍕 接口的使用

接口不能直接实例化使用,必须要有一个类去实现它,实现接口中所有的抽象方法

  1. public class 类名称 implements 接口名称{
  2. //...
  3. }

注意🍙 :子类和父类之间是extends继承关系,类与接口之间是implements实现关系。

笔记本电脑中使用USB鼠标,USB键盘的类和接口实现功能

  1. USB接口:包含打开设备、关闭设备的功能
  2. 笔记本类:包含开关机功能、使用USB设备功能
  3. 鼠标类:实现USB接口,并具备点击功能
  4. 键盘类:实现USB接口,并具备输入功能
  1. //USB接口
  2. public interface USB{
  3. void openDevice();
  4. void closeDevice();
  5. }
  6. //鼠标类,实现USB接口
  7. public class Mouse implements USB{
  8. @Override
  9. public void openDevice(){
  10. System.out.println("打开鼠标");
  11. }
  12. @Override
  13. public void closeDevice(){
  14. System.out.println("关闭鼠标");
  15. }
  16. public void click(){
  17. System.out.println("鼠标点击");
  18. }
  19. }
  20. //键盘类,实现USB接口
  21. public class KeyBoard implements USB {
  22. @Override
  23. public void openDevice(){
  24. System.out.println("打开键盘");
  25. }
  26. @Override
  27. public void closeDevice(){
  28. System.out.println("关闭键盘");
  29. }
  30. public void inPut(){
  31. System.out.println("键盘输入");
  32. }
  33. }
  34. //笔记本类:使用USB设备
  35. public class Computer {
  36. public void powerOn(){
  37. System.out.println("打开笔记本电脑");
  38. }
  39. public void powerOff(){
  40. System.out.println("关闭笔记本电脑");
  41. }
  42. public void useDevice(USB usb){
  43. usb.openDevice();
  44. if(USB instanceof Mouse){
  45. Mouse mouse = (Mouse)usb;
  46. mouse.click();
  47. }else if(usb instanceof KeyBoard){
  48. KeyBoard keyBoard = (KeyBoard)usb;
  49. keyBoard.inPut();
  50. }
  51. usb.closeDevice();
  52. }
  53. }
  54. //测试类:
  55. public class TestUSB{
  56. public static void main(String[] args){
  57. Computer computer = new Computer();
  58. computer.powerOn();
  59. //使用鼠标设备
  60. computer.useDevice(new Mouse());
  61. //使用键盘设备
  62. computer.useDevice(new KeyBoard());
  63. computer.powerOff();
  64. }
  65. }

输出:👇
image.png

2.4 🌭接口的特性

  1. 接口类型是一种引用类型,但是不能直接new接口的对象 ```java public class TestUSB { public static void main(String[] args){
    1. USB usb = new USB();
    } }

//编译会出错:USB是抽象的,无法实例化

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25529268/1649747861834-5955e3df-ff48-4653-95bd-2d944db9b9da.png#clientId=u1c9f5073-9144-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=113&id=u3c8915ce&margin=%5Bobject%20Object%5D&name=image.png&originHeight=141&originWidth=368&originalType=binary&ratio=1&rotation=0&showTitle=false&size=11668&status=done&style=none&taskId=u6c154116-73a8-4dd7-8f15-6d1b1847c79&title=&width=294.4)
  2. 2. 接口中每一个方法都是public的抽象方法,即接口中的方法会被隐式地指定为public abstract(只能是public abstract,其他修饰符都会报错)
  3. ```java
  4. public interface USB {
  5. //编译出错:此处不允许使用修饰符private
  6. //或者是java: 缺少方法主体, 或声明抽象
  7. private void openDevice();
  8. void closeDevice();
  9. //不同JDK版本编译器的标准是不一样的,报错也是不一样的
  10. }
  1. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

    1. public interface USB {
    2. void openDevice();
    3. //编译失败:因为接口中的方法默认为抽象方法
    4. //Error:接口抽象方法不能带有主体
    5. }

    image.png
    但这里如果我们加上一个default,那么就可以实现方法体了。
    image.png

  2. 重写接口中的方法时,不能使用default作为访问权限修饰

    1. public interface USB {
    2. void openDevice();//默认为public
    3. void closeDevice();//默认为public
    4. }
    5. public class Mouse implements USB {
    6. @Override
    7. void openDevice(){
    8. System.out.println("打开鼠标");
    9. }
    10. //...
    11. }
    12. //这里编译会报错,重写USB中的openDevice方法时,不能使用默认修饰符

    image.png
    实现这个接口,重写这个接口的方法的访问限定修饰符范围要比接口中的更大

  3. 接口中可以含有变量,但是接口中的变量会被编译器自动隐式指定为public static final变量 ```java public interface USB { double brand = 3.0;//默认为:final public static修饰 void openDevice(); void closeDevice(); }

public class TestUSB { public static void main(String[] args){ System.out.println(USB.brand); //可以直接通过接口名访问,说明变量时静态的

  1. //下面写法会报错 Java:无法为最终变量brand分配值
  2. USB.brand = 2.0;
  3. //说明brand具有final属性
  4. }

}

  1. ![image.png](https://cdn.nlark.com/yuque/0/2022/png/25529268/1649749781675-cff04ec3-91e6-45fc-8d57-b3188e136553.png#clientId=u1c9f5073-9144-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=150&id=u51b8d87d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=187&originWidth=393&originalType=binary&ratio=1&rotation=0&showTitle=false&size=14445&status=done&style=none&taskId=u30a1f165-00b7-493d-8dff-c4cfbf4479f&title=&width=314.4)
  2. 6. 接口中不能有静态代码块和构造方法
  3. ```java
  4. public interface USB {
  5. public USB(){
  6. }//编译失败
  7. {
  8. }//编译失败
  9. void openDevice();
  10. void closeDevice();
  11. }

image.png

  1. 接口虽然不是类,但是接口编译完成之后的字节码文件的后缀格式也是.class
  2. 如果类没有实现接口中的所有抽象方法,则类必须设置为抽象类
  3. JDK8中规定了接口中可以包含上面所说的default方法

    2.5 🍤 实现多个接口

    在Java中,类和类之间是单继承的,一个类只能由一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。下面用代码来演示

    1. public class Animal {
    2. protected String name;
    3. public Animal(String name){
    4. this.name = name;
    5. }
    6. }

    然后我们再写一组接口,分别来表示“会飞的”“会跑的”“会游泳的”.

  1. public interface IFlying {
  2. void fly();
  3. }
  4. public interface IRunning {
  5. void run();
  6. }
  7. public interface ISwimming {
  8. void swim();
  9. }

image.png
那么接下来我们创建几个具体的动物类来接受并实现这些接口
比如,猫会跑👇

  1. public class Cat extends Animal implements IRunning{
  2. public Cat(String name) {
  3. super(name);
  4. }
  5. @Override
  6. public void run() {
  7. System.out.println("小猫"+this.name+"正在跑");
  8. }
  9. }

鱼会游泳👇

  1. public class Fish extends Animal implements ISwimming{
  2. public Fish(String name){
  3. super(name);
  4. }
  5. @Override
  6. public void swim() {
  7. System.out.println("小鱼"+this.name+"正在游泳");
  8. }
  9. }

而青蛙即会跑又会游泳

  1. public class Frog extends Animal implements IRunning,ISwimming{
  2. public Frog(String name){
  3. super(name);
  4. }
  5. @Override
  6. public void run() {
  7. System.out.println("青蛙"+this.name+"正在跑");
  8. }
  9. @Override
  10. public void swim() {
  11. System.out.println("青蛙"+this.name+"正在游泳");
  12. }
  13. }

🍙 注意:一个类实现多个接口的时候,每个接口中的抽象方法都要去实现,除非类用abstract修饰,为抽象类

提示👉IDEA中使用ctrl + i 可以快速实现接口

还有一种动物水陆空三栖,它是大白鹅

  1. public class Goose extends Animal implements IRunning,ISwimming,IFlying{
  2. public Goose(String name) {
  3. super(name);
  4. }
  5. @Override
  6. public void fly() {
  7. System.out.println(this.name+"正在飞");
  8. }
  9. @Override
  10. public void run() {
  11. System.out.println(this.name+"正在跑");
  12. }
  13. @Override
  14. public void swim() {
  15. System.out.println(this.name+"正在漂在水上");
  16. }
  17. }

这段代码展现了Java面向对象编程中最常见的用法:一个类继承了一个父类,然后同时实现多个接口
继承表达的含义是is-a,而接口表达的含义是具有xxx的特性

猫是一种动物,具有会跑的特性 青蛙是一种动物,即能跑也能有用 大白鹅也是一种动物,技能跑,也能游,还能飞

有了接口之后,类的使用者就不需要去关注具体的类的属性是否符合,而只需要关心某个类是否具有某个特性/功能,如果有,就可以实现对应的接口
那么我们现在实现一个走路的方法

  1. public class TestDemo1 {
  2. public static void walk(IRunning iRunning){
  3. System.out.println("我带着小伙伴去散步");
  4. iRunning.run();
  5. }
  6. public static void main(String[] args) {
  7. Cat cat = new Cat("小猫");
  8. walk(cat);
  9. Frog frog = new Frog("小青蛙");
  10. walk(frog);
  11. }
  12. }

输出结果👇
image.png
只要是会跑的,带有跑这个属性特征的,都可以接受相应的对象

  1. public class Robot implements IRunning{
  2. private String name;
  3. public Robot(String name){
  4. this.name = name;
  5. }
  6. @Override
  7. public void run() {
  8. System.out.println(this.name+"正在用轮子跑");
  9. }
  10. public static void main(String[] args) {
  11. Robot robot = new Robot("机器人");
  12. walk(robot);
  13. }
  14. }

image.png
image.png
故输出结果为👇
image.png

2.6 🧋 接口之间的继承

在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。
即:用接口可以达到多继承的目的
接口可以继承一个接口,达到复用的效果。这里使用extends关键字

  1. interface IRunning {
  2. void run();
  3. }
  4. interface ISwimming {
  5. void swim();
  6. }
  7. //两栖的动物,即能跑,也能游泳
  8. interface IAmphibious extends IRunning ISwimming {
  9. }
  10. class Frog implements IAmphibious {
  11. ...
  12. }

通过接口继承创建一个新的接口IAmphibious表示“两栖的”。
创建的Frog类就实现了这个两栖的接口

接口之间的继承就相当于把多个接口合并到了一起

2.7 🥘 接口使用的例子

我们在之前的数组中讲解过给数组排序,那么我们该如何给对象数组排序呢?
首先我们定义一个Student的类,然后重写一下String方法

  1. public class Student {
  2. private String name;
  3. private int score;
  4. public Student(String name,int score){
  5. this.name = name;
  6. this.score = score;
  7. }
  8. @Override
  9. public String toString() {
  10. return "Student{" +
  11. "name='" + name + '\'' +
  12. ", score=" + score +
  13. '}';
  14. }
  15. }

我们再给定一个学生对象数组,根据这个对象数组中的元素进行排序
这里我们按照分数降序排序

  1. public class Student {
  2. private String name;
  3. private int score;
  4. public Student(String name,int score){
  5. this.name = name;
  6. this.score = score;
  7. }
  8. @Override
  9. public String toString() {
  10. return "Student{" +
  11. "name='" + name + '\'' +
  12. ", score=" + score +
  13. '}';
  14. }
  15. public static void main(String[] args) {
  16. Student[] students = new Student[]{
  17. new Student("A",95),
  18. new Student("B",96),
  19. new Student("C",97),
  20. new Student("D",98),
  21. };
  22. }
  23. }

那么按照我们之前的理解,数组中有一个可以供我们使用的sort方法,我们能否直接使用呢?

  1. Arrays.sort(students);
  2. System.out.println(students);
  3. //运行结果:
  4. Exception in thread "main" java.lang.ClassCastException: class ClassArray.Student cannot be cast to class java.lang.Comparable (ClassArray.Student is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
  5. at java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
  6. at java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
  7. at java.base/java.util.Arrays.sort(Arrays.java:1041)
  8. at ClassArray.Student.main(Student.java:36)

image.png
我们可以看到这里程序报错了,这里的意思是Student并没有实现Comparable的接口
那么这里的sort是进行普通数字的比较,大小关系明确,而我们指定的是两个学生对象的引用变量,这样的大小关系的指定是错误的,我们需要额外去人为规定对象中的比较元素
那么怎么实现呢?

我们可以用Student类实现Comparable接口,并实现其中的compareTo方法

  1. public class Student implements Comparable<Student>{
  2. private String name;
  3. private int score;
  4. public Student(String name,int score){
  5. this.name = name;
  6. this.score = score;
  7. }
  8. @Override
  9. public String toString() {
  10. return "Student{" +
  11. "name='" + name + '\'' +
  12. ", score=" + score +
  13. '}';
  14. }
  15. @Override
  16. public int compareTo(Student o) {
  17. if (this.score>o.score){
  18. return -1;
  19. // 如果当前对象应排在参数对象之前,则返回小于0的数字
  20. } else if(this.score<o.score){
  21. return 1;
  22. // 如果当前对象应排在参数对象之后,则返回大于0的数字
  23. } else{
  24. return 0;
  25. // 如果当前对象和参数对象不分先后,则返回0
  26. }
  27. }
  28. }

那么我们在这里重写了compareTo的方法,自己定义了比较的规则,我们就自己再去写一个sort的方法,去调用这个compareTo方法,真正意义上实现对 对象数组的排序
我们使用冒泡排序法

  1. public static void sort(Comparable[] array){
  2. // 这里要注意,虽然接口不能实例化对象,
  3. // 但是接口类型的引用变量可以指向它的实现类对象
  4. // 这里的实现类对象就是实现了这个接口的对象
  5. // 例如Comparable[] comparable = new Student[3];
  6. // 所以这里的参数就可以用Comparable[] array来接收
  7. for (int bound = 0;bound<array.length;bound++){
  8. for (int cur = array.length-1;cur>bound;cur--){
  9. if (array[cur-1].compareTo(array[cur])>0){
  10. //这里就说明顺序不符合要求,交换两个变量的位置
  11. Comparable tmp = array[cur-1];
  12. array[cur-1] = array[cur];
  13. array[cur] = tmp;
  14. }
  15. }
  16. }
  17. }

sort方法写好了,我们写一个main函数来测试一下

  1. public static void main(String[] args) {
  2. Student[] students = new Student[]{
  3. new Student("A",95),
  4. new Student("B",91),
  5. new Student("C",97),
  6. new Student("D",95),
  7. };
  8. System.out.println("sort前:"+Arrays.toString(students));
  9. sort(students);
  10. System.out.println("sort后:"+Arrays.toString(students));
  11. }

运行结果👇

  1. E:\develop\Java\jdk-11\bin\java.exe "-javaagent:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\lib\idea_rt.jar=65257:E:\IDEA\IntelliJ IDEA Community Edition 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath E:\JAVAcode\gyljava\Interface\out\production\Interface ClassArray.Student
  2. sort前:[Student{name='A', score=95}, Student{name='B', score=91}, Student{name='C', score=97}, Student{name='D', score=95}]
  3. sort后:[Student{name='C', score=97}, Student{name='A', score=95}, Student{name='D', score=95}, Student{name='B', score=91}]

那么我们如果想要按照名字排序呢?也是可以的

  1. import java.util.Arrays;
  2. import java.util.Comparator;
  3. /**
  4. * Created with IntelliJ IDEA.
  5. * Description: Hello,I would appreciate your comments~
  6. * User:Gremmie
  7. * Date: -04-13
  8. * Destination:利用Comparable的接口实现对 对象数组 选择性排序的功能
  9. */
  10. class Student implements Comparable<Student>{
  11. public String name;
  12. public int age;
  13. public Student(String name, int age) {
  14. this.name = name;
  15. this.age = age;
  16. }
  17. @Override
  18. public String toString() {
  19. return "Student{" +
  20. "name='" + name + '\'' +
  21. ", age=" + age +
  22. '}';
  23. }
  24. @Override
  25. public int compareTo(Student o) {
  26. return this.name.compareTo(o.name);
  27. }
  28. }
  29. class AgeComparator implements Comparator<Student> {
  30. @Override
  31. public int compare(Student o1, Student o2) {
  32. return o1.age-o2.age;
  33. }
  34. }
  35. class NameComparator implements Comparator<Student> {
  36. @Override
  37. public int compare(Student o1, Student o2) {
  38. return o1.name.compareTo(o2.name);
  39. }
  40. }
  41. public class TestDemo {
  42. public static void main(String[] args) {
  43. Student[] students = new Student[3];
  44. students[0] = new Student("zhangsan",19);
  45. students[1] = new Student("lisi",8);
  46. students[2] = new Student("abc",78);
  47. AgeComparator ageComparator = new AgeComparator();
  48. NameComparator nameComparator = new NameComparator();
  49. //这里的方法sort是Array里面自带的,非常方便,
  50. //只需将我们写好的比较器传过去就好了
  51. System.out.println("排序前:"+Arrays.toString(students));
  52. Arrays.sort(students,nameComparator);
  53. System.out.println("排序后:"+Arrays.toString(students));
  54. Comparable<Student>[] studentComparable =students;
  55. }
  56. public static void main2(String[] args) {
  57. /*Student students1 = new Student("zhangsan",19);
  58. Student students2 = new Student("abc",78);
  59. if(students2.compareTo(students1) > 0) {
  60. System.out.println("fafaa");
  61. }*/
  62. }
  63. public static void main1(String[] args) {
  64. Student[] students = new Student[3];
  65. students[0] = new Student("zhangsan",19);
  66. students[1] = new Student("lisi",8);
  67. students[2] = new Student("abc",78);
  68. System.out.println("排序前:"+Arrays.toString(students));
  69. Arrays.sort(students);
  70. System.out.println("排序后:"+Arrays.toString(students));
  71. }
  72. }

2.8 🤯 Clonable接口以及深拷贝

其作用如其名,是用来进行克隆的,Clonable是个很有用的接口。
Object类中存在一个clone方法,调用这个方法可以创建出一个对象,实现“拷贝”。
但是我们想要合法调用clone方法,就要先实现Clonable接口,
否则就会抛出CloneNotSupportedException异常

  1. /**
  2. * Created with IntelliJ IDEA.
  3. * Description: Hello,I would appreciate your comments~
  4. * User:Gremmie
  5. * Date: -04-13
  6. * Destination:利用Clonable的接口实现clone方法,克隆含对象的对象
  7. */
  8. class Money implements Cloneable{
  9. public double money = 19.9;
  10. @Override
  11. protected Object clone() throws CloneNotSupportedException {
  12. return super.clone();
  13. }
  14. }
  15. class Person implements Cloneable{
  16. public int id = 1234;
  17. public Money m = new Money();
  18. @Override
  19. public String toString() {
  20. return "Person{" +
  21. "id='" + id + '\'' +
  22. '}';
  23. }
  24. @Override
  25. protected Object clone() throws CloneNotSupportedException {
  26. Person tmp = (Person) super.clone();
  27. tmp.m = (Money) this.m.clone();
  28. return tmp;
  29. //return super.clone();
  30. }
  31. }
  32. public class TestDemo {
  33. public static void main(String[] args) {
  34. Object o = new Person();
  35. Object o2 = new Money();
  36. }
  37. public static void main1(String[] args) throws CloneNotSupportedException {
  38. Person person1 = new Person();
  39. Person person2 = (Person)person1.clone();
  40. System.out.println(person1.m.money);
  41. System.out.println(person2.m.money);
  42. System.out.println("=========================");
  43. person2.m.money = 99.99;
  44. System.out.println(person1.m.money);
  45. System.out.println(person2.m.money);
  46. }
  47. }

我们如果只是通过clone,那么就只是拷贝了Person的对象,但是Person中的money对象我们并没有拷贝下来,只是单纯拷贝下来一个地址,那么我们在这里就要进行深拷贝,讲Money类也接受Clonable接口,这样在调用clone方法的时候,money也会进行克隆

接口就到这里了,如果文章中哪里还不够完善请各位大佬在评论区指出😊
希望能帮到你
感谢阅读~