接口
基本语法
//定义接口类型 interface IAnimal{ //抽象方法 // public static final 字段 }
使用interface 定义一个接口
接口中的方法一定是抽象方法, 因此可以省略 abstract
接口中的方法一定是public, 因此可以省略public
接口中只能包含抽象方法,对于字段来说, 接口中只能包含静态常量(final static)
interface IAnimal{ // public static final 字段 可以省略public static final String name = "animal"; //因为是final 修饰,需要赋初值! int age = 18; //抽象方法 public abstract void speak(); void eat(); //省略public abstract }
子类 使用 implements继承接口。 此时表达的含义不再是 “扩展”, 而是 “实现”
在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例。
接口不能单独被实例化。
扩展(extends)vs 实现(implements)
扩展指的是当前已经有一定的功能了, 进一步扩充功能。
实现指的是当前啥都没有, 需要从头构造出来。
接口不能用限定符修饰
实现多个接口
接口弥补了java无法多继承的缺陷!
我们可以实现多个接口
基本语法
有时候我们需要让一个类同时继承自多个父类. 这件事情在有些编程语言通过 多继承 的方式来实现的。然而 Java 中只支持单继承, 一个类只extends 一个父类。 但是可以同时实现多个接口, 也能达到多继承类似的效果。
class 类名 implement interfa1,interface2,....interfaceN{ //实现所有接口中的方法! }
实例一
下面我们通过类来表示张三
//实现多个接口 interface IAnimal{ String name = "animal"; int age = 18; void speak(); void eat(); } interface IPeople{ String iQ = "140"; void study(); } class Zhansan implements IAnimal,IPeople{ @Override public void speak() { System.out.println("Speak Chinese!"); } @Override public void eat() { System.out.println("Eat food!"); } @Override public void study() { System.out.println("Study java!"); } } public class Test_6 { public static void main(String[] args) { Zhansan zhansan = new Zhansan(); zhansan.eat(); zhansan.speak(); zhansan.study(); } }
实例二
现在我们通过一个类来表示一组动物。
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 + "正在蹬腿游泳"); } }
提示,IDEA中使用 ctrl + i 快速实现接口
还有一种神奇的动物, 水陆空三栖, 叫做 “鸭子”
class Duck extends Animal implements IRunning, ISwimming, IFlying { public Duck(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 特性 。
猫是一种动物, 具有会跑的特性。
青蛙也是一种动物, 既能跑, 也能游泳
鸭子也是一种动物, 既能跑, 也能游, 还能飞
这样设计有什么好处呢?
时刻牢记多态的好处, 让程序猿忘记类型. 有了接口之后, 类的使用者就不必关注具体类型, 而只关注某个类是否具备某种能力!
例如, 现在实现一个方法, 叫 “散步”
public static void walk(IRunning running) { System.out.println("我带着伙伴去散步"); running.run(); }
在这个walk方法内部, 我们并不关注到底是哪种动物, 只要参数是会跑的, 就行
Cat cat = new Cat("小猫"); walk(cat); Frog frog = new Frog("小青蛙"); walk(frog);
甚至参数可以不是 “动物”, 只要会跑
class Robot implements IRunning { private String name; public Robot(String name) { this.name = name; } @Override public void run() { System.out.println(this.name + "正在用轮子跑"); } } Robot robot = new Robot("机器人"); walk(robot);
接口的使用实例
我们java系统包中的很多类都实现了很多接口,使得该类具有某种属性
给对象排序!
//创建student类 class Student{ private String name; private double score; public Student(String name,double score){ this.name = name; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", score=" + score + '}'; } }
public static void main(String[] args) { //学生对象数组 Student[] students = new Student[]{ new Student("张三",88.7), new Student("李四",67), new Student("王五",98), }; Arrays.sort(students); //排序 System.out.println(students); }
显然,我们无法排序对象类型的数据,
报异常,说我们Student类没有实现Comparable接口!
Comparable接口中的抽象方法 compareTo
方法详细信息
int compareTo(T o)将此对象与指定的对象进行比较以进行排序。 返回一个负整数,零或正整数,因为该对象小于,等于或大于指定对象。
实现程序必须确保sgn(x.compareTo(y)) == -sgn(y.compareTo(x))所有x和y。 (这意味着x.compareTo(y)必须抛出异常if y.compareTo(x)引发异常。)
实施者还必须确保关系是可传递的: (x.compareTo(y)>0 && y.compareTo(z)>0)表示x.compareTo(z)>0。
最后,实施者必须确保x.compareTo(y)==0意味着sgn(x.compareTo(z)) == sgn(y.compareTo(z)) ,对于所有z 。
强烈建议,但不要严格要求(x.compareTo(y)==0) == (x.equals(y)) 。 一般来说,任何实现Comparable接口并违反这种情况的类应清楚地表明这一点。 推荐的语言是“注意:此类具有与equals不一致的自然排序”。
在前面的描述中,符号sgn( ) 表达式表示数学符号函数,其定义根据表达式的值是否为负,零或正返回的-1一个,0,或1。
参数
o -要比较的对象。
结果
负整数,零或正整数,因为该对象小于,等于或大于指定对象。
异常
NullPointerException- 如果指定的对象为空
ClassCastException- 如果指定的对象的类型阻止它与该对象进行比较。
看到这么多文字,是不是头都大了!
没有关系,bug郭也头大,不过我知道咋用
//创建student类并且实现Comparable接口 class Student implements Comparable{ private String name; private double score; public Student(String name,double score){ this.name = name; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", score=" + score + '}'; } @Override //实现compareTo方法 public int compareTo(Object o) { Student s = (Student)o; //对o强转, o指的是待比较对象 if (this.score > s.score) { //当前对象的score值大 return -1; } else if (this.score< s.score) { //对象o的score值大 return 1; } else { return 0; } } }
我们可以看到,这时排的升序,如果要降序就只需将大于号变成小于号即可!
在 sort 方法中会自动调用compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student类型的对象.
然后比较当前对象和参数对象的大小关系(按分数来算).
如果当前对象应排在参数对象之前, 返回小于0 的数字;
如果当前对象应排在参数对象之后, 返回大于 0 的数字;
如果当前对象和参数对象不分先后, 返回0;
注意事项: 对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备compareTo这样的能力. 通过重写compareTo 方法的方式, 就可以定义比较规则。
为了进一步加深对接口的理解, 我们可以尝试自己实现一个sort方法来完成刚才的排序过程(使用冒泡排序)!
public static void sort(Comparable[] array) { for (int bound = 0; bound < array.length; bound++) { for (int cur = array.length - 1; cur > bound; cur--) { if (array[cur - 1].compareTo(array[cur]) > 0) { // 说明顺序不符合要求, 交换两个变量的位置 Comparable tmp = array[cur - 1]; array[cur - 1] = array[cur]; array[cur] = tmp; } } } }
接口间的继承
接口可以继承一个接口, 达到复用的效果. 使用extends关键字!
interface IAmphibious extends IRunning, ISwimming { } class Frog implements IAmphibious { @Override public void run() { System.out.println("Frog run!"); } @Override public void swim() { System.out.println("Frog swim!"); } } public class Test_1 { public static void main(String[] args) { Frog frog = new Frog(); frog.run(); frog.swim(); } }
通过接口继承创建一个新的接口 IAmphibious 表示 “两栖的”. 此时实现接口创建的 Frog类, 就继续要实现 run 方法,也需要实现 swim方法!
接口间的继承相当于将多个接口合并在一起!