目录
一、抽象类
本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。
1、为啥需要抽象类?
抽象方法:
1、为了实现多态,如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法;
2、把一个方法声明为abstract
,表示它是一个抽象方法,本身没有实现任何方法语句。因为这个抽象方法本身是无法执行的,所以,定义类抽象方法的类也无法被实例化。编译器会告诉我们,无法编译该
类,因为它包含抽象方法。
2、抽象类有什么特征?
抽象类:
1、如果一个class
定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract
修饰。
2、因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
3、使用abstract
修饰的类就是抽象类。抽象类无法实例化;
4、子类继承抽象类,必须重写抽象方法,但是不一定对所有抽象方法完全重写;
凡是实现类必须要完整的重写所有的方法。(实现类!)部分不重写的话子类只能也是抽象类。。直至。。。。所有抽象方法被全部重写为止。
3、面向抽象类编程
1、面向抽象编程的本质就是:
-
上层代码只定义规范(例如:
abstract class Person
); -
不需要子类就可以实现业务逻辑(正常编译);
-
具体的业务逻辑由不同的子类实现,调用者并不关心。
2、实例:
当我们定义了抽象类Person
,以及具体的Student
、Teacher
子类的时候,我们可以通过抽象类Person
类型去引用具体的子类的实例:
Person s = new Student();
Person t = new Teacher();
这种引用抽象类的好处在于,我们对其进行方法调用,并不关心Person
类型变量的具体子类型:
// 不关心Person变量的具体子类型:
s.run();
t.run();
同样的代码,如果引用的是一个新的子类,我们仍然不关心具体类型:
// 同样不关心新的子类是如何实现run()方法的:
Person e = new Employee();
e.run();
这种尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象编程。
二、接口
1、为啥需要接口?
还是为了实现多态,和抽象类的主要区别是:接口中连字段都没有,所有方法都是抽象方法,并且声明的类型默认为 public abstract。
2、实现接口
当一个具体的class
去实现一个interface
时,需要使用implements
关键字。举个例子:
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(this.name + " run");
}
@Override
public String getName() {
return this.name;
}
}
我们知道,在Java中,一个类只能继承自另一个类,不能从多个类继承(单继承)。但是,一个类可以实现多个interface(多实现)
,例如:
class Student implements Person, Hello { // 实现了两个interface
...
}
3、接口可以继承接口
一个interface
可以继承自另一个interface
。interface
继承自interface
使用extends
,它相当于扩展了接口的方法。例如:
interface Hello {
void hello();
}
interface Person extends Hello {
void run();
String getName();
}
此时,Person
接口继承自Hello
接口,因此,Person
接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello
接口。
4、继承关系
合理设计interface
和abstract class
的继承关系,可以充分复用代码。一般来说,公共逻辑适合放在abstract class
中,具体逻辑放到各个子类,而接口层次代表抽象程度。可以参考Java的集合类定义的一组接口、抽象类以及具体子类的继承关系:
┌───────────────┐
│ Iterable │
└───────────────┘
▲ ┌───────────────────┐
│ │ Object │
┌───────────────┐ └───────────────────┘
│ Collection │ ▲
└───────────────┘ │
▲ ▲ ┌───────────────────┐
│ └──────────│AbstractCollection │
┌───────────────┐ └───────────────────┘
│ List │ ▲
└───────────────┘ │
▲ ┌───────────────────┐
└──────────│ AbstractList │
└───────────────────┘
▲ ▲
│ │
│ │
┌────────────┐ ┌────────────┐
│ ArrayList │ │ LinkedList │
└────────────┘ └────────────┘
在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象:
List list = new ArrayList(); // 用List接口引用具体子类的实例
Collection coll = list; // 向上转型为Collection接口
Iterable it = coll; // 向上转型为Iterable接口
5、接口中的默认方法 - default
在接口中,可以定义default
方法。例如,把Person
接口的run()
方法改为default
方法:
public class Main {
public static void main(String[] args) {
Person p = new Student("Xiao Ming");
p.run();
}
}
interface Person {
String getName();
default void run() {
System.out.println(getName() + " run");
}
}
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
Run
抽象类和接口的对比
抽象类 abstract class | 接口 interface(比抽象类更抽象) | |
---|---|---|
继承 | 只能extends一个class,单继承 | 可以implements多个interface,多实现 |
字段 | 可以定义实例字段 | 不能定义实例字段 |
抽象方法 | 可以定义 | 可以定义 |
非抽象方法 | 可以定义非抽象方法 | 可以定义default方法 + java8可以在接口中定义静态方法:参考文章 |
实例化 | 不可 | 不可 |
构造函数 | 抽象类可以有构造器 | 无 |
成员数据 | final static | |
成员方法 | public abstract | public abstract |