2.5 实现多个接口
在Java语言中,类和类之间是单继承关系,一个类只可以有一个父类,即Java中不支持多继承关系,但是一个类可以实现多个接口,下面通过Animal类来具体说明
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 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,即一个类属于什么大类,接口则表示的是can xxx,即一个类具有什么样的特性或能力
 这样设计的好处在哪里呢?时刻牢记多态的好处,它可以让程序员忘记类型,有了接口之后,就不必关心具体的类型,而是关注某个类是否具有某种能力
面试题:什么时候会发生多态
在不同类继承一个父类,并且重写了父类中的方法,并且通过父类引用创建的子类对象调用了重写的方法,此时会发生动态绑定并发生向上转型,此时就会发生多态
只要这个类的东西具有某种能力,就可以实现一个接口,比如实现上述接口的类不一定必须是动物类,还可以是其他类,比如机器人也会跑
class Robot implements IRunning {
	private String name;
	public Robot(String name) {
		this.name = name;
	}
	@Override
	public void run() {
		System.out.println(this.name + "正在用轮子跑");
	}
}
 
2.6 接口之间的继承
在Java中,类和类之间是单继承关系,一个类可以实现多个接口,接口与接口之间可以多继承,即接口可以达到多继承的目的
 接口可以继承一个接口,达到复用的效果,使用extends关键字
interface IRunning {
	void run();
}
interface ISwimming {
	void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
}
class Frog implements IAmphibious {
}
 
通过一个创建一个新接口IAmphibious,并继承了IRunning, ISwimming,来达到合并两个接口的目的,在Frog类中,还需要继续重写run方法和swim方法
2.7 接口的经典使用案例
- 给对象数组排序
如果我们有一个数组,这个数组中的数据类型是学生类 
public 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 "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}
 
public class Main {
    public static void main(String[] args) {
        Student[] student = new Student[]{
                new Student("zhangsan",95),
                new Student("lisi",89),
                new Student("wangwu",88),
                new Student("zhaoliu",98),
        };
        }
    }
 
比如我们在这里要对这个数组中的学生按照成绩进行排序,数组中我们有sort方法,能否直接使用这个方法呢?
 
 在这里我们可以看出,编译器在这个地方抛出了异常,为什么呢,是因为一个数组中的对象有两个成员,一个是姓名,一个是成绩,编译器并不知道你要通过哪个成员来比较对象,所以我们这里引入了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 "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
    @Override
    public int compareTo(Object o) {//重写compareTo方法
        Student s = (Student) o;//object类强转为Student类
        if (this.score > s.score){
            return -1;
        }else if (this.score<s.score){
            return 1;
        }else {
            return 0; 
        }
    }
}
 
在sort方法中,会自动调用重写的compareTo方法
 然后比较当前对象和参数对象的大小关系(按分数来算).
 如果当前对象应排在参数对象之前, 返回小于 0 的数字;
 如果当前对象应排在参数对象之后, 返回大于 0 的数字;
 如果当前对象和参数对象不分先后, 返回 0;
 之后的运行就符合预期了
 
 注意事项: 对于 sort 方法来说, 需要传入的数组的每个对象都是 “可比较” 的, 需要具备 compareTo 这样的能力. 通过重写 compareTo 方法的方式, 就可以定义比较规则.
在这里我们给出几种更加灵活的方法,利用比较器,即实现Compartor接口
public class nameCompare implements Comparator<Student> {//比较什么,尖括号里就写什么
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}
public class sorceCompare implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        if (o1.score > o2.score){
            return -1;
        }else if (o1.score<o2.score){
            return 1;
        }else {
            return 0;
        }
    }
}
public class Main {
    public static void main(String[] args) {
        Student[] student = new Student[]{
                new Student("zhangsan",95),
                new Student("lisi",89),
                new Student("wangwu",88),
                new Student("zhaoliu",98),
        };
        Arrays.sort(student);
        System.out.println(Arrays.toString(student));
        nameCompare comparator1 = new nameCompare();
        sorceCompare comparator2 = new sorceCompare();
        System.out.println(comparator1.compare(student[0],student[1]));
        System.out.println(comparator2.compare(student[0],student[1]));
        }
}
 

 在这里我们可以看出,与Comparable接口不同的是,它不是在想要比较的类上直接添加,而是单独创建了一个类并创建对象,可以根据成绩比较,可以根据名字比较,返回的是一个整数,若我们想利用比较器对数组进行排序,可不可以实现呢,当然可以,我们在上述代码的基础上稍作改动
public class Main {
    public static void main(String[] args) {
        Student[] student = new Student[]{
                new Student("zhangsan",95),
                new Student("lisi",89),
                new Student("wangwu",88),
                new Student("zhaoliu",98),
        };
        Arrays.sort(student);
        System.out.println(Arrays.toString(student));
        nameCompare comparator1 = new nameCompare();
        sorceCompare comparator2 = new sorceCompare();
        System.out.println(comparator1.compare(student[0],student[1]));
        System.out.println(comparator2.compare(student[0],student[1]));
        Arrays.sort(student,comparator1)//传入比较器
        System.out.println(Arrays.toString(student));
        }
    }
 
在数组类的sort方法中,我们传入了一个关于名字的比较器,是sort的一个重载方法,那么运行结果就会根据名字字母的先后顺序进行排序,运行结果如下
 
 在这里我们给出重载sort方法的源码,方便大家理解
 public static <T> void sort(T[] a, Comparator<? super T> c) {//在这里我们可以看到该方法有比较器的形式参数
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }
 
2.8抽象类与接口的区别
抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别(重要!!! 常见面试题).
 核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.
 如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口.
class Animal {
	protected String name;
	public Animal(String name) {
		this.name = name;
	}
}
                

















