8.接口与抽象类 让我们通过接口使用多态 深入多态!——《Head First Java》读书笔记 第八章 & 菜鸟教程内容 总结

1.引入

首先强化一下“多态”的概念
菜鸟教程上给出例子——
在这里插入图片描述

超棒的一个例子!!!看它!!

多态:同一个接口 不同的子类引用了这个接口(例如打印机)后
在这里插入图片描述
可以执行不同的操作(子类(例如彩色打印机和黑白打印机)进行不同的覆盖)
在这里插入图片描述

另外 多态的实现方式有下面这几种 我们这一章重点来看一下方法二:接口
在这里插入图片描述

之后来看看Head First Java对本章的引入~

继承只是个开始

  • 要使用多态 我们还需要“接口”(不是GUI中的I——interface所代表的接口哦!)!

  • 我们需要超越简单的继承并前进到更高的适应性和扩展性!——这需要我们设计和编写接口规格

  • 很多Java功能如果没有接口就无法工作 因此就算我们不会去设计接口 也还是会使用到接口~

  • 那么接口是什么呢?
    ——是一种100%纯抽象的类

  • 那什么是抽象类呢?
    ——就是无法初始化的类

  • 那抽象类又有什么作用啊?
    ——诶问题真多 下面慢慢讲解给你
    在这里插入图片描述

上一章让Vet(兽医)的方法能运用Animal的所有子类只是多态的基本招式~接口才是多态和Java的重点!

2.设计继承

先来回顾一下最开始的Animal类

在这里插入图片描述
通过这个类 我们可以这样初始化类——

  • 初始化引用变量的类型和新建对象的类型相同
Wolf aWolf = new Wolf();

在这里插入图片描述

  • 初始化引用变量的类型和新建对象的类型不同
Animal aHippo = new Hippo();

在这里插入图片描述
好了 上面两种指令都是可以的 但是这样呢?——

Animal anim = new Animal();

在这里插入图片描述

如何处理这个问题?
我们要有Animal这个类来继承和产生多态 但是要限制只有Animal的子类才能被初始化——我们要的是Lion、Hippo对象 不是Animal对象!

当然有方法来解决这个问题了!
这个方法可以防止类被初始化!(防止类被new出来)

标记类为抽象类即可!!
这样编译器就知道不管在哪里 这个被抽象的类都是不能创建任何类型的实例了!

综上所述 我们设计好继承结构之后 必须要决定哪些类是抽象的 哪些是具体的!
具体的类是实际可以被初始化为对象的!(例如Hippo Lion这些)

如何设计抽象的类?

很简单~
在类的声明前面加上抽象类的关键词abstract 即可

abstract class Canine extends Animal {
	public void main()
}

好!抽象类设计完成!!
从现在开始 编译器就不会让你初始化抽象类了!!
(当然了 我们还是可以使用抽象类来 声明为引用类型给多态使用 只不过不用担心这个抽象类被初始化了~)
在这里插入图片描述
c = new Canine()直接报错hhh
在这里插入图片描述

3.抽象类与具体类

还是先来举个例子
在这里插入图片描述

我们创建出来的Animal Feline Canine就是抽象类~
其他的就是具体类

查阅Java API我们就会发现其中有很多的抽象类 特别是GUI的函数库中更多
举个例子 GUI的组件类是 按钮、滚动条与GUI有关的类的父类
而我们只会对组件类下的具体子类做初始化动作!

抽象的方法

不光有抽象的类 我们还可以有抽象的方法!
在这里插入图片描述
做个对比~

抽象的类代表这个类一定要继承过父类~
在这里插入图片描述

抽象的方法代表这个方法一定要被覆盖过

抽象的方法是没有实体的!

在这里插入图片描述
编写出抽象方法的程序代码是没有意义的!
如果我们声明出一个抽象的方法 就必须将标记为抽象的

就算只有一个抽象的方法 这个类也必须标记为抽象的!

多态的使用

接下来举一个例子来深入了解下多态的使用!

假设我们不知道有ArrayList这种类
然而我们需要自行 编写&维护 list的类 来保存Dog对象
来看看我们一点点完善这个类的过程吧

【1】创建Dog对象专用的List

在这里插入图片描述

public class MyDogList {
	private Dog [] dogs = new Dog[5];//实际上我们新建的是数组对象
	private int nextIndex = 0;

	public void add(Dog d) {
		//先只写出add方法
		//因为我们使用的大小为5的简单Dog数组来保存新加入的对象
		//Dog对象超过五个时 我们调用add()什么都不会发生!
		if (nextIndex < dogs.length) {
			//可用索引nextIndex小于数组长度 就可以进行添加!
			dogs[nextIndex] = d;
			System.out.println("Dog added at" + nextIndex);
			nextIndex ++;//递增计数
		}
	}
}

【2】Cat要用list咋办?

对鸭 Dog对象可以用MyDogList
那Cat用啥?

  • 再写一个MyCatList类?
    大可不必

  • 创建一个单独的DogAndCatList类?
    这样就要用 addCat(Cat c) addDog(Dog d)分别处理两个不同的数组实例了 也不咋地!

正解是——

编写一个AnimalList类让它处理Animal的所有子类

在这里插入图片描述

public class MyAnimalList {
	private Animal [] animals = new Animal[5];
	//这可不是在创建Animal对象哦!只是一个保存Animal的数组对象而已!
	private int nextIndex = 0;

	public void add(Animal a) {
		if (nextIndex < animals.length){
			animals[nextIndex] = a;
			System.out.println("Animal added at " + nextIndex);
			nextIndex ++;
		}
	}
}

AnimalList类写完了!
来试着用add方法添加下Dog和Cat~

public class AnimalTestDrive {
	public static void main(String[] args) {
		MyAnimalList list = new MyAnimalList();
		Dog d = new Dog();
		Cat c = new Cat();
		list.add(d);
		list.add(c);
	}
}

在这里插入图片描述

【3】如果添加进去的不是Animal呢?

对啊 写个万用类一劳永逸不好嘛?

在java中的所有类都是从Object(“对象之母”)这个类继承出来的!

可以看到ArrayList中的很多方法都用到了Object这个终极类型
因为每个类都是对象的子类,所以ArrayList可以处理任何类~
在这里插入图片描述
Object这个类是所有类的源头(是所有类的父类)
那你可能会问了 要这么个“对象之母”干啥子哦

如果Java中没有共同的父类 那将无法让Java的开发人员创建出可以处理自定义类型的类
eg:无法写出像ArrayList这样可以处理各种类的类

其实吧 所有的类都是从Object继承出来的
比如咱们写一个public class Dog其实这个就等同于public class Dog extends Object()

你可能又问了 Dog类不是从Canine(犬科)类extends出来的么?
害 不成问题 编译器会知道如何改成让Canine去继承对象的!(事实上是Animal这个更大的类去进行继承)

4.宠物特性的设置

来举个很有意思的例子
我们想要在目前的Dog类 中 加入一些宠物特性
(即为修改合约)
在这里插入图片描述
但是这样还是有一些问题
在这里插入图片描述
我们想设置一个总的Pet类(宠物有太多太多啦)
但是怎么去做呢?
来看几种方法

【1】宠物方法加进Animal类中

来分别看看优缺点~
在这里插入图片描述

【2】把宠物方法加入到Animal类中 但是把宠物方法设定成抽象的 强迫每个动物子类覆盖它们

在这里插入图片描述

【3】把方法加到需要的地方

在这里插入图片描述

多重继承?

我们真正需要的方法是:

  • 一种可以让宠物行为只应用在宠物身上的方法
  • 一种确保所有宠物的类都有相同的方法定义的方法
  • 一种可以运用到多态的方法

看起来我们需要搞两个上层的父类?
在这里插入图片描述
可恶 这个还是有个问题!

多继承的问题

多重继承 OK

多继承 不行!!

首先 Java就不支持这种方式

因为多继承会有称为“致命方块”的问题

在这里插入图片描述
总之就是不行就是了!!
在这里插入图片描述

接口是我们的救星!

Java对于我们上面说的“解决多继承”的问题
有一个解决方案——

使用接口

这个接口

  • 不是GUI的接口
  • 不是“沟通管道”的接口
  • 不是“存取途径”的接口

我们说的这个“救星接口” 是Java的interface关键词
这个接口可以用来解决多重继承问题且不会产生致命方块这种问题!

接口解决致命方块的办法很简单:
把全部的方法设置为抽象的!
这样子类就得要实现此方法,因此Java虚拟机在执行期间就不会搞不清楚要用哪一个继承版本

在这里插入图片描述

5.接口

接口的定义和实现

定义:

public interface Pet {...}

接口的实现:

public class Dog extends Canine implements Pet {...}
//使用implements这个关键词 
//注意到实现interface时 还是必须在某个类的继承下

【承接上个part】设计与实现Pet接口

在这里插入图片描述
设计一个简单的Pet接口
【1】先进行一个接口的定义
【2】再进行接口的实现

public interface Pet {
//接口的定义

	public abstract void beFriendly();
	public abstract void play();
	//接口的方法一定是抽象的!它们是没有内容的!!!
	}

public class Dog extends Canine implements Pet {
//接口的实现
	public void beFriendly() {...}
	public void play() {...}
	//上面的两个方法是Pet的方法 一定要在大括号中实现!
	//这个是合约的规定

	public void roam() {...}
	public void eat() {...}
	//上面的两个是一般的覆盖方法
	}

不同继承树的类也可以实现相同的接口

在这里插入图片描述
可以看到 我们如果想要定义一个动物&宠物 只需要让它继承Animal类和Pet接口就可以
同理 想要定义一个机器宠物 让它继承Robot类和Pet接口即可!

类可以实现多个接口。

更棒的是 我们的接口还可以让子类实现“多继承”
也就是让子类继承多个接口!
举个栗子

通过继承结构 Dog可以是个Canine 可以是个 Animal 也是一个(超类)Object
然而 Dog Is-A Pet 是通过接口实现的机制达成的
为啥用接口?接口可以实现多继承呐~

public class Dog extends Animal implements Pet, Saveable, paintable{...}
可以看到这里就是一个Dog子类实现了多个接口

来个小口诀
在这里插入图片描述

如何判断应该是设计类/子类/抽象类/接口

  • 如果新的类无法对其他的类通过IS-A测试时,就设计不继承其他类的类
  • 只有在需要其类的特殊化版本时,以覆盖或增加新的方法来继承现有的类
  • 当你需要定义一群子类的模板 又不想让程序员初始化此模板时 设计出抽象的类给它们用
  • 如果想要定义出类可以扮演的角色 使用接口

6.super的使用

调用父类的方法

来看看我们的需求——

  • 创建出一个具体的子类
  • 我们必须覆盖父类的某个方法A
  • 我们同时需要执行父类封装的这个方法A

简单来说 我们不打算完全覆盖掉原来的方法 但是我们要加入额外的动作

使用super关键词
可以让我们在子类中调用父类的方法~
来举个栗子

先来看看父类的定义

abstract class Report {
	void runReport() {
	//设置报告方法~
	}
	void printReport() {
	//输出报告的方法
	}
}

之后我们的子类想要调用父类中的方法&自己加入新的方法

class BuzzwordsReport extends Report {
	void runReport() {
		super.runReport();//调用父类的方法
		buzzwordCompliance();//新加入的方法
		printReport();//调用父类的方法
		//(但是与新增方法不名~所以不用加super)
	}
	void buzzwordCompliance() {...}
	//子类新增的方法
}

仔细康康上面的代码
子类中我们指定super.runReport这个命令 父类的方法就会被执行
在这里插入图片描述
从上面的关系图中我们可以看出来 子类的方法包含了父类的这些方法
虽然我们对子类的对象进行调用会执行子类覆盖过的方法

BuzzwordReport 子类对象 = new BuzzwordReport();

子类对象.runReport();//这样调用 会执行子类覆盖过的方法

但是!我们用了super.runReport()所以在子类中可以调用父类的方法

7.本章小结!

标星号“*”的内容是有待进一步理解的!
【1】如果不想让某个类被初始化 就用abstract这个关键词将它标记为抽象的

比如我们不想创建一个Animal类型的animal
在这里插入图片描述

【2】抽象的类可以带有抽象&非抽象的方法

记住一点 抽象方法所在的类一定是抽象类!
也就是说这个类必须这么标识——
abstract class Animal() {...}Animal即为类名

【3】抽象的方法没有内容 它的声明是以分号结束滴~

abstract class Pet() {
	public abstract void beFriendly();
	public abstract void play();
	//这些方法是木有内容滴~ 
}

【4】“抽象”的方法必须在“具体”的类中运行
【5】Java所有的类都是Object(java.lang.Object)直接或间接的子类

定义类的时候可以省去 但是其实是默认存在 extends Object的~

【6】方法可以声明Object的参数或返回类型
*【7】不管实际上引用的对象是什么类型 只有在引用变量的类型是带有方法A时才可以调用这个方法A
*【8】Object引用变量在没有类型转换的情况下不能赋值给其他的类型,若堆上的对象类型与所要转换的类型不兼容 则这个转换会在执行期产生异常

这个真的理解不了哇 回头多学一些再来看这个

*【9】从ArrayList<Object>取出的对象只能被Object引用 不然就要用类型转换来改变

取出的对象?
emmmm
这个要再去找找例子!

【10】Java不允许多重继承 因为那样会有致命方块的问题

所以需要接口嘛~
在这里插入图片描述

【11】接口就好像是100%纯天然的抽象类!
【12】以interface这个关键词取代class来声明接口

public interface Pet() {
	public abstract beFriedly();
	public abstract play();
}

【13】实现接口时要使用implements这个关键词

public class Dog extends Canine implements Pet {
	public void beFriedly() {......};
	public void play() {......};
	//这两个方法一定要在大括号中实现
}

【14】class可以实现多个接口

public class Dog extneds Animal implements Pet, Sveable, paintable {
...
}

【15】实现某个接口的类必须实现它所有的方法 因为这些方法都是 public 与 abstract 的
【16】要在子类中调用到父类的方法(当子类中要覆盖的方法恰好与父类是同一个 但是同时又要用到父类方法的时候)时,可以使用super这个关键词来引用

//这是子类的一个方法(与父类同名~)
void runReport() {
		super.runReport();//调用父类的方法
		printReport();//调用父类的方法
		//(但是与新增方法不名~所以不用加super)
	}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值