抽象类和接口的定义以及区别
抽象类
定义
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
解释
比如一个food
类,我们知道他是一个食物,但是不知道它的形状、大小、味道等等,所以它是抽象的,需要一个具体的饼干,面条来给它特定的描述。
特点
- 1) 不能被实例化
这个我考虑很久,查阅了一些资料,个人理解有几点:
- 空间分配,内存垃圾问题。在实例化对象的时候(
new food()
)会开辟一个堆内存空间,而它没有实现的方法,是个东西,但没用。那就是垃圾。 - 安全。如果调用未实现的类就会报异常。比如
public abstract class Food{
public void print(){
System.out.println("un_abstract method");
}
public abstract void abstractmethod();
}
假设我们new
一个对象出来,Food food = new Food()
,那么执行food.abstractmethod
呢?
3. 面向对象思想不允许。可能也是最重要的吧。面向对象领域的一切都是对象,抽象在问题领域是没有概念的。在映射到现实社会的模拟,抽象是没有意义的。简单的来说,你实例化一个抽象的东西能干吗,没有实际的意义,违背了面向对象思想。
关于这三点,属于个人观点,希望得到探讨和批评,不保证正确性。
- 2) 抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
public abstract class Person{
private String name; // 变量
private String address;
public Employee(String name, String address){ // 构造方法
this.name = name;
this.address = address;
}
public abstract void cry(); // 抽象方法
public void jump(){ // 普通方法
System.out.println(this.name + "跳起来了")
}
// getter/setter 略
}
- 3)类中有抽象方法就必须定义为抽象类,抽象类中不一定包含抽象方法;
关于抽象类中不一定包含抽象方法,这个也有点钻牛角尖问题。如果一个抽象类不包含任何抽象方法,为何还要设计为抽象类?所以暂且记住这个概念吧,不必去深究为什么。
- 4)抽象类必须被继承使用。子类必须重写父类所有抽象方法或者声明自己为抽象类(实现部分抽象方法);
public class Employee extends Person{
private int number;
public Employee(String name, String address, int number){
super(name,address);
this.number = number;
}
@Override
public void cry(){ //
System.out.println(this.number + "在叫")
}
// public abstract void *();
// 子类也可以继续声明自己的抽象方法,继续被继承实现
}
- 5)abstract不能与final并列修饰同一个类。
- 6)abstract 不能与private、static、final或native并列修饰同一个方法。
- 7)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
为什么需要抽象类?
抽象方法和抽象类看上去是多余的,对于抽象方法,不知道如何实现,定义一个空方法体不就行了吗,而抽象类不让创建对象,看上去只是增加了一个不必要的限制。
引入抽象方法和抽象类,是Java提供的一种语法工具,对于一些类和方法,引导使用者正确使用它们,减少被误用。
使用抽象方法,而非空方法体,子类就知道他必须要实现该方法,而不可能忽略。
使用抽象类,类的使用者创建对象的时候,就知道他必须要使用某个具体子类,而不可能误用不完整的父类。
无论是写程序,还是平时做任何别的事情的时候,每个人都可能会犯错,减少错误不能只依赖人的优秀素质,还需要一些机制,使得一个普通人都容易把事情做对,而难以把事情做错。抽象类就是Java提供的这样一种机制。
接口
在软件工程中,接口泛指供别人调用的方法或者函数。从这里,我们可以体会到 Java 语言设计者的初衷,它是对行为的抽象。在 Java 中,定一个接口的形式如下:
[public] interface InterfaceName {
}
特征
- 1)接口中可以含有变量和方法
但是要注意,接口中变量只能是 public static final变量,用其他报错。
方法且只能是 public abstract 方法,用其他关键字,比如 private、protected、static、 final 等修饰会报编译错误
- 2)接口中的方法必须都是抽象方法
- 3)允许一个类遵循多个特定的接口
class ClassName implements Interface1,Interface2,[....]{
}
如果一个非抽象类遵循了某个接口,就必须实现该接口中的所有方法。对于遵循某个接口的抽象类,可以不实现该接口中的抽象方法。
抽象类和接口的区别
1. 语法层面上
-
抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
-
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
-
接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
-
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
说明抽象类只是类的另一种形式。而接口是一个公共的方法集合,不能有别的,一旦用了一个方法还得全部都得用,很纯粹,很牛XX
2. 设计层面上
要真正理解和区分,就要在设计层面上下功夫了,这样才能知道在什么场景下用它。
- 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。
举个栗子,飞机和鸟是不同的事物,但有一个共同的特征就是会飞。因此我们可以设计的时候,设计Airplan
和Bird
两个类,但飞是一个动作,一种行为,不是一类事物的抽象描述。所以就可以设计一个接口Fly
,包含方法fly()
,然后Airplane和Bird分别根据自己的需要实现Fly这个接口。
然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。
从这里可以看出,类继承属于‘是不是’的问题,你属不属于飞机类,鸟类。接口实现是‘有没有’的问题,你有没有飞这个功能。
- 抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
我认为这就话就是精髓所在。直接再举起来一个栗子:门和报警
门都有open()
和close()
两个动作,但现在要给门加上alarm()
,这就要分析一下了。
Door
的 open()
、close()
和 alarm()
属于两个不同范畴内的行为,open() 和 close() 属于门本身固有的行为特性,而 alarm() 属于延伸的附加行为。门也分很多种,玻璃门、防盗门、推拉门等等。不是所有的都具有报警功能。以后添加报警装置,也只是增加了一个报警功能。
因此最好的解决办法是单独将报警设计为一个接口,包含 alarm()
行为,Door
设计为单独的一个抽象类,包含 open
和 close
两种行为。再设计一个报警门继承 Door
类和实现 Alarm
接口。
即:
public abstract class Door{
public abstract void open();
public abstract void close();
}
interface Alarm{
void alarm();
}
class AlarmDoor extends Door implements Alarm{
@Override
void oepn() {
//....
}
@Override
void close() {
//....
}
@Override
void alarm() {
//....
}
}
参考资料:
https://www.runoob.com/w3cnote/java-abstract-interface-different.html