【Java】面向对象之——接口的理解
🎄语法规则
🎄实现多个接口
🎄接口使用实例
🎄接口之间的继承
🎄Cloneable 接口和深浅拷贝
🎄总结
接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量.
有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
接口(interface)是抽象方法和常量值的定义的集合。
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
🎄语法规则
在打印图形的示例中, 我们的父类 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 Cycle(); shape.draw(); } }
代码解释:
使用 interface 定义一个接口
接口中的方法一定是抽象方法, 因此可以省略 abstract
接口中的方法一定是 public, 因此可以省略 public
Cycle 使用 implements 继承接口. 此时表达的含义不再是 “扩展”, 而是 “实现”
在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例.
接口不能单独被实例化.
扩展(extends) vs 实现(implements)
扩展指的是当前已经有一定的功能了, 进一步扩充功能.
实现指的是当前啥都没有, 需要从头构造出来.
接口中只能包含抽象方法. 对于字段来说, 接口中只能包含静态常量(final static)
interface IShape { void draw(); public static final int num = 10; }
其中的 public, static, final 的关键字都可以省略.省略后的 num 仍然表示 public 的静态常量.
提示:
1.我们创建接口的时候, 接口的命名一般以大写字母 I 开头.
2.接口的命名一般使用 “形容词” 词性的单词.
3.阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
一个错误的代码
interface IShape { abstract void draw() ; // 即便不写public,也是public } class Rect implements IShape { void draw() { System.out.println("□") ; //权限更加严格了,所以无法覆写。 } }
技巧:接口中的 public, static, final 的关键字都可以省略.
接口中的方法一定是抽象方法, 因此可以省略 abstract
接口中的方法一定是 public, 因此可以省略 public
接口中只能包含抽象方法. 对于字段来说, 接口中只能包含静态常量(final static).
🎄实现多个接口
有的时候我们需要让一个类同时继承自多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的.
然而 Java 中只支持单继承, 一个类只能 extends 一个父类. 但是可以同时实现多个接口, 也能达到多继承类似的效果.
现在我们通过类来表示一组动物.
class Animal { protected String name; public Animal(String name) { this.name = name; } }
另外我们再提供一组接口, 分别表示 “会飞的”, “会跑的”, “会游泳的”.
interface IFlying { void fly(); } interface IRunning { void run(); } interface ISwimming { void swim(); }
接下来我们创建几个具体的动物
猫, 是会跑的.
class Cat extends Animal implements IRunning { public Cat(String name) { super(name); } @Override public void run() { System.out.println(this.name + "会跑"); } }
鱼, 是会游的.
class Fish extends Animal implements ISwimming { public Fish(String name) { super(name); } @Override public void swim() { System.out.println(this.name + "会游泳"); } }
青蛙, 既能跑, 又能游(两栖动物)
class Frog extends Animal implements IRunning, ISwimming { public Frog(String name) { super(name); } @Override public void run() { System.out.println(this.name + "会跑"); } @Override public void swim() { System.out.println(this.name + "会游泳"); } }
天鹅,既能飞,又能跑,还会游泳(三栖动物)
class Swan extends Animal implements IRunning, ISwimming, IFlying { public Swan(String name) { super(name); } @Override public void fly() { System.out.println(this.name + "会飞"); } @Override public void run() { System.out.println(this.name + "会跑"); } @Override public void swim() { System.out.println(this.name + "会游泳"); } }
运行结果如下:
上面的代码展示了 Java 面向对象编程中最常见的用法: 一个类继承一个父类, 同时实现多种接口(会跑,会游,会飞).
继承表达的含义是 is - a 语义, 而接口表达的含义是 具有 xxx 特性 .
猫是一种动物, 具有会跑的特性.
青蛙也是一种动物, 既能跑, 也能游泳
天鹅也是一种动物, 既能跑, 也能游, 还能飞
【这样设计有什么好处呢?】
时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型, 而只关注某个类是否具备某种能力.
🎄接口使用实例
实例:给对象数组排序
先给定一个学生类
class Student { private String name; private int score; public Student(String name, int score) { this.name = name; this.score = score; } @Override public String toString() { return "[" + this.name + ":" + this.score + "]"; } }
再给定一个学生对象数组, 对这个对象数组中的元素进行排序(按分数降序).
Student[] students = new Student[] { new Student("张三", 95), new Student("李四", 96), new Student("王五", 97), new Student("赵六", 92), };
按照我们之前的理解, 数组我们有一个现成的 sort 方法, 能否直接使用这个方法呢?
Arrays.sort(students); System.out.println(Arrays.toString(students)); // 运行出错, 抛出异常. Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
仔细思考, 不难发现, 和普通的整数不一样, 两个整数是可以直接比较的, 大小关系明确. 而两个学生对象的大小关系怎么确定?
这时候就需要我们额外指定.
让我们的 Student 类实现 Comparable 接口, 并实现其中的 compareTo 方法
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 "[" + this.name + ":" + this.score + "]"; } @Override//重写Comparable接口里的compare To 方法 public int compareTo(Object o) { Student s = (Student)o; if (this.score > s.score) { return -1; } else if (this.score < s.score) { return 1; } else { return 0; } } }
在 sort 方法中会自动调用 compareTo 方法(具体原因可以通过ctrl+左键点击sort方法查看源码,此处不多赘叙). compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.
然后自定义 比较当前对象和参数对象的大小关系 的规则(按分数来算).
如果当前对象应排在参数对象之前, 返回小于 0 的数字;
如果当前对象应排在参数对象之后, 返回大于 0 的数字;
如果当前对象和参数对象不分先后, 返回 0;
再次执行程序, 结果就符合预期了.
注意事项:
对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备 compareTo 这样的能力. 通过重写 compareTo 方法的方式, 就可以定义比较规则.
接口就相当于是具备某种能力,在这里Comparable接口就表示具有 “可比较能力“,Student类加上(实现)了这个接口,就拥有了可比较能力,就能使用sort方法进行排序了