前言:菜鸟一枚,如有问题欢迎大家在评论区指出,我们一起学习。
1.10.1:抽象类与抽象方法
当某些事物有共同的特征,但是又不能在父类中给出具体的体现,只能在子类中得到体现。那么父类在声明这些方法时,可以只有方法签名,没有方法体,我们将这种方法成为抽象方法 。在Java中,抽象方法必须包含在抽象类中。我们需要借助 abstract 关键字实现抽象。
示例代码:
public abstract class Animal {
public abstract void eat();
}
public class Cat extends Animal {
public void run (){
System.out.println("小猫吃鱼和猫粮");
}
}
public class CatTest {
public static void main(String[] args) {
// 创建子类对象
Cat c = new Cat();
// 调用eat方法
c.eat();
}
}
在前一篇文章中,我们学习到了多态,并使用了普通父类和普通子类来实现多态。实现多态其中最重要的是父类定义方法,子类重写方法。现在我们学习了抽象方法后,父类方法可以不用些方法体。至此,我们学习了第二种实现多态的方法,接下来我们学习如何使用接口实现多态。
1.10.2:接口
接口的定义,它与定义类方式相似,但是使用 interface
关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。接口定义后,类可以通过 implements 关键字来实现该接口,这个类也被称为实现类。
示例代码:
//接口也不能实例化
public interface Person {
// 灰色的是可以不用写的,接口中声明的属性默认修饰符是 public static final,写和不写都是一样的,所以只需要写String name = "Tom"
String NAME = "Tom";
//我们发现接口中声明的属性是常量,一般都不声明
//接口中方声明的方法默认修饰符是public abstract
void sayHello();
//jdk1.8开始其他修饰的方法
//default修饰的方法有方法体,不强制要求实现类必须实现
//default修饰的方法由实现类对象调用
default void sayHi() {
System.out.println("Hi");
}
//static修饰的方法也由方法体,由接口调用,不允许实现类调用(不同于类中静态方法)
static void sayHello2() {
System.out.println("Hello2");
}
}
public class Teacher implements Person{
//实现和重写一样
@Override
public void sayHello() {
System.out.println("我是老师");
}
}
public class Doctor implements Person{
@Override
public void sayHello() {
System.out.println("我是医生");
}
}
public class Master {
public void WhoAmI(Person person){
person.sayHello();
}
}
public class TestMaster {
public static void main(String[] args) {
Person person = new Teacher();
Doctor doctor = new Doctor();
Master master = new Master();
master.WhoAmI(person);
master.WhoAmI(doctor);
}
}
注意: 一个实现类如果实现一个接口,则必须实现接口里所有的抽象方法,并且静态方法不能被实现类调用。
运行后发现,WhoAmI()方法传入不同的对象,它们所调用的方法是自己重写后的方法 ,至此,第三种实现多态的方法——利用接口。
实现多态的三种方法:
1.普通父类和普通子类
2.抽象父类和普通子类
3.接口和实现类
在现在看来,接口跟抽象父类 好像是一样的,无非就是省略了修饰符,不能有其他成员,没有构造器,没有初始化块(没有成员变量,只有常量),那为什么要设计一个接口这种引用数据类型了?因为Java中一个类只能继承一个父类,但是类可以实现多个接口(单继承,多实现),接口的好处是接口不会影响父类。接口也可以继承,并且可以同时继承多个接口。
示例代码:
public interface Father {
void sayHi();
}
public interface GrandFather {
void sayHello();
}
public interface Son extends Father,GrandFather{
void sayGoodbye();
}
public class TestSon implements Son{
@Override
public void sayGoodbye() {
System.out.println("再见");
}
@Override
public void sayHi() {
System.out.println("你好");
}
@Override
public void sayHello() {
System.out.println("你好!");
}
}
注意:虽然实现类 TestSon 只是实现Son接口,但是由于Son接口继承了两个接口,因此在实现类里,我们需要重写三个抽象方法。
1.10.3:接口命名冲突
我们已经知道类可以同时继承父类和实现接口,或者可以实现多个接口,那么当它们之中有同样命名的方法时,我们到底访问的是哪一个了?
亲爹优先原则:当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的抽象方法重名,子类就近选择执行父类的成员方法。代码如下:
public class Father {
public void sayHello (){
System.out.println("你好");
}
}
public interface Brother {
default void sayHello(){
System.out.println("Hello");
}
}
public class Son extends Father implements Brother{
@Override
public void sayHello() {
Brother.super.sayHello();
}
}
public class TestSon {
public static void main(String[] args) {
Son son = new Son();
son.sayHello();
}
}
运行结果: Hello 调用的 Father 接口的 sayHello 方法
当接口和父类方法重名时,不重写,父类优先。重写时,使用 接口名.super.方法名 来调用接口,方法或者直接自己完全重写一个。
左右为难:当一个类实现多个接口,我们需要做出抉择。代码如下:
public interface Brother {
default void sayHello(){
System.out.println("Hello");
}
}
public interface Sister {
default void sayHello(){
System.out.println("幸会");
}
}
public class Me implements Brother,Sister{
@Override
public void sayHello() {
Sister.super.sayHello();
}
}
运行结果:幸会 调用的 Sister 接口的 sayHello 方法
实现两个接口时,若两个接口中有同名方法,则必须重写 重写时,使用 接口名.super.方法名 来指定一个方法进行调用
1.10.4:Comparable接口
Comparable接口用于比较两个对象的大小,但是我们需要对compareTo()方法进行重写。
public class Worker implements Comparable{
private String name;
private int age;
private String gender;
public Worker() {
}
public Worker(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Worker{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
@Override
public int compareTo(Object o) {
Worker worker = (Worker) o;
return this.age - worker.getAge();
}
}
public class TestWorker {
public static void main(String[] args) {
Worker worker1 = new Worker("小白",13,"男");
Worker worker2 = new Worker("小蓝",14,"女");
System.out.println(worker1.compareTo(worker2));
}
}
运行结果: -1
//返回负数时,调用对象小,参数对象大 //返回正数时,调用对象大,参数对象小 //返回0时,一样大
通过重写方法后,让其比较两个对象的 age 属性值大小。