Java面向对象_05_接口


面向对象引入了类的概念,即将相似的事物抽象出来形成模板,用于后续事物的创建。那么有没有相似的类,将相似的类进行抽象呢?这样就可以定义相似的类,实现更高层次的代码复用,Java提供了抽象类接口的概念。

1. 抽象类

1.1 抽象类的基本介绍

类和类之间具有共同特征,将这些共同特征提取出来,形成的类就是抽象类。注意,抽象级别:对象 < 类 < 抽象类。因为对象是实际存在的,所以类可以实例化;但是因为类本身是不存在的,仅仅是抽象出的一个概念,所以抽象类无法实例化(无法创建对象)成类,抽象类也属于引用数据类型

抽象类是在类的基础上进一步提取共有特征而抽象出来的,对对象进行抽象形成类,对类进行抽象则形成抽象类。换句话说,对抽象类也可进一步抽象。注意,类和抽象类在现实世界中是不存在的,对象则是现实世界中实际存在的。

oop_01.png (1400×690) (gitee.io)

抽象类的定义语法:

1
2
3
[修饰符列表] abstract class 类名{
类体;
}
  1. 抽象类是无法实例化的,主要用来被子类继承。抽象类可以继承抽象类,仍然是抽象类。
  2. 因为抽象类是用来继承的,所以abstract不能和final关键字一起使用,会报错:非法的修饰符组合
  3. 抽象类虽然无法实例化,但是有构造方法,这个构造方法是供子类使用的。因为继承,子类创建对象的时候,会默认有super()方法,就是默认调用父类的构造方法。
  4. 抽象类中的一个概念:抽象方法。抽象类中可以没有抽象方法,但抽象方法必须出现在抽象类中。所以,非抽象子类必须实现父类的抽象方法,否则,因为子类也会继承父类的抽象方法,如果抽象方法没有实现(覆盖、重写)的话,这就是普通类中有抽象方法了。
  5. 抽象类也可以实现多态,抽象父类指向非抽象子类对象。

1.2 抽象方法

抽象方法只能存在抽象类中,表示没有实现的方法。该方法没有方法体(即没有大括号),修饰符列表中有abstract关键字,语法结构如下:

1
2
3
4
[修饰符列表] abstract 返回值类型 方法名();

// 例如
public abstract void doSome();

抽象类多态代码案例如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 抽象类
abstract class AnimalPlus{
// 抽象方法
public abstract void move();
}

class BirdPlus extends AnimalPlus{
// 实现抽象方法,注意要去掉abstract关键字。
public void move(){
System.out.println("鸟儿在飞翔!");
}
}

public class AbstractTest{
public static void main(String[] args){

// 抽象,父类型引用指向子类型对象。
// 注意,此处虽然表面上是创建了抽象类对象,但实际上底层不是。
AnimalPlus ap = new BirdPlus();
ap.move();
}
}

注意,问题:Java语言中凡是没有方法体的方法都是抽象方法?

不对,是错误的,Object类中就有很多方法都没有方法体,都是以”;”结尾的,但他们都不是抽象方法,例如public native int hashCode();,这个方法就是底层调用了c++写的动态链接库程序。前面修饰符列表中没有abstract,有一个native。表示调用 JVM本地程序。

2. 接口

抽象类中既有抽象方法也有非抽象方法,并且存在构造方法,并不是完全抽象,那么此时结构上可能就不是很清晰。可不可以将其完全抽象呢?Java提供了接口的概念。

2.1 接口的基本介绍

接口是完全抽象的,而抽象类则是半抽象的,或者也可以说接口是特殊的抽象类。接口也是一种引用数据类型,和抽象类一样,编译后生成.class文件。接口的基本语法如下:

1
2
3
[修饰符列表] interface 接口名{
接口体;
}
  1. 接口可以继承,支持多继承。一个接口可以继承多个接口,一个类可以实现多个接口,弥补了单继承带来的缺陷。

  2. 接口中只包含两部分内容:常量和抽象方法。不包含其他的普通方法。

  3. 接口中所有的元素都是public修饰的,即都是公开的。既然接口中都是抽象方法,并且都有public,所以实际上public和abstract是可以省略的,系统会自动加上

  4. 同上,变量都是常量,所以public、static、final也是可以省略的,系统会自动加上。

  5. 类和类之间叫做继承extends,类和接口之间叫做实现implements

  6. 抽象类也可以实现接口,但是非抽象类实现接口,必须实现接口里的所有方法。(因为接口里的方法都是抽象的,如果不实现的话,那这就是抽象方法存在于非抽象类中了)

  7. 注意,类在实现接口方法的时候,一定要注意修饰符列表,因为在接口中是默认加public和abstract的,而在类中,是不会加上的,所以要手动加上public。

  8. 接口也是一种类,可以使用多态。

  9. 继承和实现都存在的话?要先继承,再实现。就是extends要在implements的前面

    1
    class Cat extends Animal implements Flyable{}
  10. 注意,之前提到过,每个类都是默认继承Object的,那么如果我们只是实现了某个接口,底层实际上是先继承Object类的,如下所示:

    1
    2
    3
    class Fish implements Flyable{}
    // 底层实际上是这样的
    class Fish extends Object implements Flyable{}

2.2 接口在开发中的作用

接口在开发中的作用,类似于多态在开发中的作用。多态的作用:面向抽象编程,不要面向具体编程。降低程序的耦合度,提高程序的扩展力。

简单案例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Master{
public void feed(Dog d){

}

public void feed(Cat c){

}
}

// 这样写是不合理的,扩展力太差了,如果再养一个其他宠物,那么就得再写一个方法。那么如果用多态的话,可以直接写一个方法。Dog和Cat都继承Animal,直接传入Animal参数即可。父类型引用指向子类型对象。
public class Master{
public void feed(Animal a){

}
}

接口:接口是纯抽象的,面向抽象编程,可以认为是面向接口编程。可以降低程序的耦合度,提高程序的扩展力。符合OCP开发原则。另外,接口是将两侧给解耦合了,一侧是调用接口的方法,另一侧是实现接口。即其中一侧只负责调用接口的方法,另一侧只要按照要求实现接口的方法即可。根据多态,接口类型引用指向实现接口类的对象。所以接口相当于解耦合了。

所以,接口的作用就是解耦合。当然,除了解耦合,另一个作用就是代码复用了。

2.3 抽象类和接口有什么区别?

只说一下二者在语法上的区别。

  1. 抽象类是半抽象的,接口是完全抽象的。
  2. 抽象类中有构造方法,接口中没有构造方法
  3. 接口和接口之间支持多继承。类和类之间只能单继承。
  4. 一个类可以同时实现多个接口,一个抽象类只能继承一个类。
  5. 接口中只允许出现常量和抽象方法。接口使用的比抽象类多。
  6. 接口一般都是对行为的抽象
  7. 接口的祖先也是Object类。

3. 类型之间的关系

目前介绍到的类型有方法/属性对象接口(抽象类)。四者的关系如下:

  1. is a

    is a表示的是“继承关系”,即类之间的继承关系。

  2. has a

    has a表示的是“关联关系”,即类有某些属性/方法。

  3. like a

    like a表示的是“实现关系”,类实现接口。

4. 简单案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
interface Eating{
public abstract void eating();
}

class CatItf implements Eating{
public void eating(){
System.out.println("猫在吃鱼!");
}

public void doSome(){
System.out.println("猫在走猫步!");
}
}

class DogItf implements Eating{
public void eating(){
System.out.println("狗在吃骨头!");
}

public void doSome(){
System.out.println("狗在奔跑!");
}
}

public class TestOOP_05 {

public static void someMethod(Eating e){
e.eating();

if(e instanceof CatItf){
((CatItf) e).doSome();
}

if(e instanceof DogItf){
((DogItf) e).doSome();
}
}

public static void main(String[] args) {
Eating eC = new CatItf();
Eating eD = new DogItf();

someMethod(eC);

System.out.println("--------------");

someMethod(eD);
}
}

5. 备注

参考B站《动力节点》。


文章作者: 浮云
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 浮云 !
  目录