Java 中的接口以及常见的 Cloneable 接口

目录

1. 概念

2. 语法规则

3. 接口的命名规则

4. 实现多个接口

5. 接口实现示例

 6. Cloneable 接口和深、浅拷贝

6.1 Cloneable 接口

6.2 浅拷贝

6.3 深拷贝


1. 概念

接口在 Java 中是一个抽象类型,是抽象方法的集合,是抽象类的更进一步。接口通常以 Interface 来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

2. 语法规则

在打印图形的示例中(可以看上一篇),父类中没有 Shape 没有包含别的非抽象方法,所以也可以将它设计成一个接口。

interface IShape {
    void draw();
}
class Cycle implements IShape {
    @Override
    public void draw() {
        System.out.println("○");
    }
}

public class Test {
    public static void main(String[] args) {
        IShape shape = new Rect();
        shape.draw();
    }
}

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。

接口中只能包含抽象方法。对于字段来说, 接口中只能包含静态常量(final static)。

interface IShape {
    void draw();
    public static final int num = 10;
}

小结:

  • 使用 interface 定义一个接口.
  • 子类使用 implements 实现接口,子类可以同时实现多个接口 implements 多个接口。
  • 接口中的方法一定是抽象方法, 因此可以省略 abstract.
  • 接口中的方法一定是 public, 因此可以省略 public.
  • Cycle 使用 implements 继承接口,此时表达的含义不再是 "扩展", 而是 "实现",扩展指的是当前已经有一定的功能了, 进一步扩充功能。实现指的是当前啥都没有, 需要从头构造出来。
  • 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。
  • 一个类如果同时继承抽象类,实现接口,请先 extends 一个类,而后 implements 多个接口。
  • Java 中允许同时实现多个接口,不允许多继承。

3. 接口的命名规则

  • 我们创建接口的时候, 接口的命名一般以大写字母 I 开头。
  • 接口的命名一般使用 "形容词" 词性的单词。
  • 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性。

4. 实现多个接口

有时候我们需要一个类继承多个父类,但是在 Java 中只支持单继承,所以这时候就可以用多接口实现这种多继承的效果。

例子:

先通过类表示一组动物:

public class Animal {
    protected String name;

    // 有参构造方法
    public Animal(String name) {
        this.name = name;
    }
}

动物都有不同的技能,比如说会飞,会游泳,会跑,要想让不同的动物具有各自不同的特点,可以将各种不同的技能设置成接口。这样也方便多种技能的动物“实现”多个技能。

public interface IFlying {
    void fly();
}
public interface IRunning {
    void running();
}
public interface ISwimming {
    void swimming();
}

接下来就是设计几种不同的动物:

首先是,猫会跑,可以实现跑的接口方法。

public class Cat extends Animal implements IRunning {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void running() {
        System.out.println(this.name + "正在跑");
    }
}

还有鱼,鱼可以游泳。

青蛙,青蛙既可以在陆地上跑跳,也可以在水中游泳,所以这个类可以实现两个接口。

public class Frog extends Animal implements ISwimming, IRunning {
    public Frog(String name) {
        super(name);
    }

    @Override
    public void running() {
        System.out.println(this.name + "正在跳");
    }

    @Override
    public void swimming() {
        System.out.println(this.name + "正在游泳");
    }
}

有了接口后,类的调用者就不必关注具体类型,而只关注某个类具备的能力。

在 walk 方法内部,不必关注到底是哪种动物,只要关注他是否具有跑这个功能就行。其他方法也是如此。

// 多接口
public class TestAniaml {
    public static void main(String[] args) {
        Cat cat = new Cat("猫猫");
        Frog frog = new Frog("青蛙");
        Fish fish = new Fish("小鱼");
        walk(frog);
        walk(cat);
        swim(frog);
        swim(fish);
    }
    public static void walk(IRunning running) {
        running.running();
    }
    public static void swim(ISwimming swimming) {
        swimming.swimming();
    }
}

5. 接口实现示例

给对象数组排序

在Arrays工具类中,sort 函数可以对普通数组进行排序,但是在如下代码中,如果使用 sort 方法进行排序,就会运行出错,抛出异常。

import java.util.Arrays;

/**
 * 接口示例,对学生对象数组进行排序
 */
public class Student implements Comparable {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                this.name + '\'' +
                ", score=" + this.score +
                '}';
    }

    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("小王", 87),
                new Student("小赵", 90),
                new Student("小敏", 89),
        };
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

错误:

Java 中的接口以及常见的 Cloneable 接口_第1张图片

Java 中的接口以及常见的 Cloneable 接口_第2张图片

 从报错信息以及源码上来看,可以发现所有使用 sort (Object[ ] a)方法进行排序的对象都必须实现Comparable接口。而源码中的比较类型是 int 类型,所以如果我们想要使用 sort 方法,就要先复写Comparable接口中的 compareTo 方法,传入想要比较的对象类型,也就是 Student 类。

Java 中的接口以及常见的 Cloneable 接口_第3张图片

然后就可以实现 sort 方法,运行结果成为按照 score 的大小排序的数组:

Java 中的接口以及常见的 Cloneable 接口_第4张图片

 6. Cloneable 接口和深、浅拷贝

6.1 Cloneable 接口

Cloneable 是 Java 中内置的接口之一。使用场景:Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 "拷贝"。但是要想合法调用 clone 方法, 必须要先实现 Cloneable 接口, 否则就会抛出 CloneNotSupportedException 异常。

/**
 * Cloneable 接口
 */
public class B implements Cloneable{
    @Override
    protected B clone() throws CloneNotSupportedException {
        return (B)super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        B b = new B();
        B b1 = b.clone();
        B b2 = b;
        System.out.println(b == b1);
        System.out.println(b == b2);
    }
}

 运行结果:

从结果可以看出 B 类已经具有了拷贝能力,== 看得是两个对象引用的地址是否是同一个,b 和 b2 还是一个对象只是有两个名字所以结果是 true,而 b 经过拷贝创新创建了一个新对象 b1,引用指向的地址自然不是一个,结果就是 false。

拷贝分为浅拷贝和深拷贝,要注意它们的区别。

6.2 浅拷贝

Java 中的接口以及常见的 Cloneable 接口_第5张图片

可以从运行结果看出,改变了拷贝的值,随之原对象的值也发生了变化;同样的改变原对象的值,拷贝后的对象的值也发生了变化。 这是因为虽然 b1 和 b 是两个不同的对象,但他们内部包含的 a 对象却是相同的。开始时 a 的默认值都是0,将100赋给 b1 中的 a 时,b 中的 a 也就改变了,因为这两个对象中的 a 是同一个引用。

小结:浅拷贝就是当一个对象是通过另一个对象 clone 出来的,此时这两个对象虽然是独立的两个对象,但是这两个对象的内部包含的其他引用是相同的。

6.3 深拷贝

深拷贝就是当一个对象是通过另一个对象 clone 出来的,此时这两个对象是独立的两个对象,这两个对象所包含的所有其他引用也是独立的。

深拷贝的实现方式:

1. 嵌套实现 clone 方法

Java 中的接口以及常见的 Cloneable 接口_第6张图片

2. 序列化实现 clone 方法

现在开发中常见的序列化就是将一个对象转化成字符串( json),后续学习 Java web 时会用到。

你可能感兴趣的:(JavaSE,java,开发语言)