从设计层面上谈谈抽象类和接口的区别

为什么想谈谈抽象类和接口呢?

当我百度抽象类和接口的区别时,网上会有一堆千篇一律的回答,就像下面:

  1. 接口的方法默认是public,所有的方法在接口中不能有实现(JDK8开始方法可以实现,不过要增加static或者default修饰符);而抽象类可以有非抽象的方法,也就是可以实现方法,也就是方法可以有方法体。
  2. 一个类能实现多个接口,但是一个类只能继承一个抽象类。
  3. 从方法上来说,接口中的方法默认以public abttact 修饰,而抽象类中的抽象方法可以有 public、protected 和 default 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰)。
  4. 从属性上来说,抽象类中的属性可以用各种各样的修饰符去修饰,而接口中的属性默认是public static final。
  5. 抽象类可以含有构造方法,而接口不可以有构造方法。

JDK7~JDK9期间,接口发生的变化:

  1. 在 jdk7或更早版本中,接口里面只能有常量变量和抽象方法。这些接口方法必须由选择实现接口的类实现。
  2. jdk8的时候接口可以有默认方法和静态方法功能。

image-20201006221453185

  1. Jdk9在接口中引入了私有方法和私有静态方法。

以上回答都是基于接口和抽象类使用上的区别来说明的,当看到这么多区别的时候,我又觉得这两者是那么的相似,为什么有了接口还会有抽象类呢?什么时候会用到抽象类,什么时候又会用到接口呢?

于是有了接口和抽象类在设计层面上的区别。

先来举一个简单的例子:

学生和老师都有吃饭、写字的本领吧,就下面这样:

class Student {
    public void eat() { }

    public void sleep() { }
}

class Teacher {
    public void eat() { }

    public void sleep() { }
}

这样Student和Teacher都有重复的代码,那么我们有两个办法:

  1. 可以定义一个接口,接口里面包含了eat()和sleep()方法,然后让Student和Teacher都实现这个方法就好了。
  2. 可以定义一个抽象类,抽象类里面包含了eat()和sleep()方法,然后让Student和Teacher都继承这个抽象类就好了。

这样一看,抽象类和接口似乎没什么区别。

但是如果给Teacher增加一个教书的方法teach(),而Student是不能教书的,也就没有teach()方法,那要怎么解决呢?

  1. 如果在接口中增加teach(),那学生实现了这个接口的话,也就具备了teach(),不可行!
  2. 如果在抽象类中增加teach(),那学生继承了这个抽象类的话,也就具备了teach(),不可行!

试想一下eat()和sleep()都是学生和老师共有的方法,那我们可以将eat()和sleep()都写进一个抽象类Person,然后让老师和学生都继承Person,teach()是老师独有的方法,也就是对Person的一种拓展和延伸,那我们可以再定义一个teach接口,然后让老师去实现这个teach接口,总体解决方案如下:

abstract class Person {
    public abstract void eat();

    public abstract void write();
}

interface teach {
    void teach();
}

class Student extends Person {

    @Override
    public void eat() {
    }

    @Override
    public void write() {
    }
}

class Teacher extends Person implements teach {
    @Override
    public void eat() {
    }

    @Override
    public void write() {
    }

    @Override
    public void teach() {
    }
}

写到这,应该大部分人能够明白接口和抽象类的区别了,总的来说:

继承是一个 "是不是"的关系,而 接口实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如一个人是否能教书,能则可以实现这个接口,不能就不实现这个接口。

你以为这样就完了吗?

细心的人会说,那为什么有抽象类,我用一个普通类Person也可以呀,然后让Student和Teacher继承这个Person就好了呀,是的, 没错,就像这样:

class Person {
    public void eat() {
    }

    public void write() {
    }
}

interface teach {
    void teach();
}

class Student extends Person {

}

class Teacher extends Person implements teach {

    @Override
    public void teach() {
    }
}

不知道你有没有忘,抽象类中既可以定义未实现的方法,也可以定义实现了的方法。

当不同的类具有某些相同的行为时,且实现的方法一致时,比如说see()都是用眼睛看,hear()都是用耳朵听,那么see()和hear()都可以在抽象类中实现,其他的类都派生于这个抽象类,避免了see()和hear()的重复实现,反正大家see()和hear()都是一样的,这就达到了代码复用的目的。

当不同的类具有某些相同的行为时,且实现的方法不一致时,比如说eat(),老师可能在教师餐厅吃,而学生可能在学生食堂吃,这就使得eat()的实现方式是不同的,所以eat()可以作为抽象类中的抽象方法,所有的派生类自己去实现这个eat()方法!

这些,普通的类能做到吗?如果是普通的类,它的方法必须有方法体,那么所有派生于普通类的类的方法实现都是集成自父类的(除非方法重写)!

abstract class Person {
    public void see() {
        System.out.println("see by eyes");
    }

    public abstract void eat();
}

interface teach {
    void teach();
}

class Student extends Person {

    @Override
    public void eat() {
        System.out.println("eat in student's restaurant");
    }
}

class Teacher extends Person implements teach {

    @Override
    public void teach() {
    }

    @Override
    public void eat() {
        System.out.println("eat in teacher's restaurant");
    }
}

接口的设计目的,是对类的行为进行约束(更准确的说是一种“有”约束,因为接口不能规定类不可以有什么行为),也就是提供一种机制,可以强制要求不同的类具有相同的行为。它只约束了行为的有无,但不对如何实现行为进行限制。

抽象类的实现目的,是代码复用,可以让这些类都派生于一个抽象类。在这个抽象类中实现了共有的方法,避免让所有的子类来实现这个共有的方法,这就达到了代码复用的目的。


以上就是我对抽象类和接口的一些理解,如果有错误之处欢迎大家指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值