当前课程知识点:“做中学”Java程序设计 > 第4章使用面向对象高级语法 > 4.4使用抽象类 > Video
在讨论今天的主题时我们先来看一个在设计Java类的时候经常会碰到的一个现实问题
了解了类和对象以后
在讨论今天的主题时我们先来看一个在设计Java类的时候经常会碰到的一个现实问题
今天我们来一起学习一下
今天我们来一起学习一下
如何在JAVA当中创建一个自定义的类
比如要描述所有人都会讲话
以及如何和像类当中添加相关的属性
以及如何和像类当中添加相关的属性
但是美国人讲的是美式英语
首先我们一起来看看类是如何定义的
首先我们一起来看看类是如何定义的
而中国人讲的是汉语
这里给出 了JAVA类的标准定义语法
这里给出 了JAVA类的标准定义语法
先简要的介绍一下他们的基本含义
你可能会这样设计
先简要的介绍一下他们的基本含义
父类person当中包含两个子类American和Chinese
父类person当中包含两个子类American和Chinese
类的声明主要有Class关键字
类名
类体部分和修饰符部分构成
类体部分和修饰符部分构成
而 Person都有speak()行为
而 Person都有speak()行为
作为父类Person它是无法确知这个方法的具体实现的
包含关键字Class的这一行
我们称之为类的声明
我们称之为类的声明
只有具体的子类 American他才知道自己讲是美式英语
称之为类体
只有具体的子类 American他才知道自己讲是美式英语
类的声明使用关键字Class
类的声明使用关键字Class
同样Chinese也知道自己应该讲汉语
同样Chinese也知道自己应该讲汉语
后面紧跟一个类名
同样Chinese也知道自己应该讲汉语
这里的类名必须是合法的标志符
这里的类名必须是合法的标志符
JAVA当中允许你将父类当中
这里的类名必须是合法的标志符
这个无法实现的特殊方法打上一个abstract标签
这个无法实现的特殊方法打上一个abstract标签
方括号括起来的关键字
是类的可选关键字
我们将这样的方法称之为抽象方法
我们将这样的方法称之为抽象方法
我们将这样的方法称之为抽象方法
这些关键字我们称之为类的修饰符
而将这个方法的具体实现
而将这个方法的具体实现
这些关键字我们称之为类的修饰符
交给继承这个类的子类来完成
他们具有一些特定含义
交给继承这个类的子类来完成
我们可以先了解一下
我们可以先了解一下
随着学习的深入
例如你要将类Person定义为抽象类
你会对这些关键字
你会对这些关键字
只需要在Class的标识符前面
有更深刻的认识
只需要在Class的标识符前面
加上abstract关键字就可以了
Public
Private
加上abstract关键字就可以了
Protect和Default
Protect和Default
现在我们来总结一下抽象类的应用场景
现在我们来总结一下抽象类的应用场景
是JAVA当中
是JAVA当中
访问权限的相关关键字
正如前面所示例的
访问权限的相关关键字
当某个类知道其子类应该具有某个方法
这里的Public关键字就描述了该类的访问权限是公开的共有的
这里的Public关键字就描述了该类的访问权限是公开的共有的
但是无需具体了解这些子类的方法实现的时候
共有的
但是无需具体了解这些子类的方法实现的时候
就目前而言你需要记住的一点是
就目前而言你需要记住的一点是
我们就可以使用抽象方法
凡是声明为Pulic的类
凡是声明为Pulic的类
必须保存在与它名字相同的源文件当中
你也可以从多个具有相同特性的类当中来抽象出一个抽象类
你也可以从多个具有相同特性的类当中来抽象出一个抽象类
更具体一点的来讲
更具体一点的来讲
以这个抽象类作为子类的模板
以这个抽象类作为子类的模板
在一个JAVA源代码文件当中
在一个JAVA源代码文件当中
从而避免子类设计的随意性
你可以包含多个类的定义
你可以包含多个类的定义
就刚才的例子来讲
但是只能有且只有一个
但是只能有且只有一个
我们如果要增加一个新的Japanese日本人
可以声明为Public的
我们如果要增加一个新的Japanese日本人
因为它的名字必须要和
因为它的名字必须要和
那么同样他只要继承我们的Person类
那么同样他只要继承我们的Person类
这个源文件的文件名保持一致
这个源文件的文件名保持一致
凡是继承Person类
凡是继承Person类
可选的修饰符还包括abstract
可选的修饰符还包括abstract
那么他这个类那就具有speak()功能
那么他这个类那就具有speak()功能
它是一个抽象修饰符
它是一个抽象修饰符
从而规范了所有继承自Person类的子类行为
声明为抽象的类
将不能被实例化
将不能被实例化
在使用抽象类的时候我们还需要特别注意它的使用规则
在使用抽象类的时候我们还需要特别注意它的使用规则
final
这是一个最终的修饰符
这是一个最终的修饰符
表明这个类不能被继承
除了抽象类在定义时必须使用abstract关键字进行修饰之外
除了抽象类在定义时必须使用abstract关键字进行修饰之外
无法对它进行扩展
无法对它进行扩展
抽象方法也必须使用abstract关键字进行修饰
extends
抽象方法也必须使用abstract关键字进行修饰
它是用来声明类的继承关系
它是用来声明类的继承关系
那么这里 大家需要特别注意在定义抽象方法的时候我们不需要给出方法体
那么这里 大家需要特别注意在定义抽象方法的时候我们不需要给出方法体
关键字implements
那么这里 大家需要特别注意在定义抽象方法的时候我们不需要给出方法体
表明该类是现在相关的接口
表明该类是现在相关的接口
只需要保留方法的签名就可以了
只需要保留方法的签名就可以了
那么各个接口之间
那么各个接口之间
要记住 凡是具有抽象方法的类这个类必须定义成一个抽象类
使用逗号进行分隔
要记住 凡是具有抽象方法的类这个类必须定义成一个抽象类
了解了这些知识以后呢
要记住 凡是具有抽象方法的类这个类必须定义成一个抽象类
下面我们要实现如图所示的时间应用程序项目
如果你只添加了抽象方法
下面我们要实现如图所示的时间应用程序项目
而忘了给类添加的abstract关键字的话
而忘了给类添加的abstract关键字的话
在这个项目当中
我们可以根据用户设定的时
我们可以根据用户设定的时
那么将产生一个编译错误
分秒的时间信息
分秒的时间信息
作为抽象类你无法使用它进行对象的实例化
作为抽象类你无法使用它进行对象的实例化
来设定一个时间对象
作为抽象类你无法使用它进行对象的实例化
并且以均用格式和标准格式
并且以均用格式和标准格式
所以直接new Person是一个常见的语法错误
两种不同的显示方式来显示这个时间对象
两种不同的显示方式来显示这个时间对象
但是抽象类可以作为一种数据类型
但是抽象类可以作为一种数据类型
这里就让我们从定义时间类来开始
这里就让我们从定义时间类来开始
你可以用它来定义一个引用变量
时间类是对时间对象的一个共同抽象
时间类是对时间对象的一个共同抽象
比如你可以这样写Person p=new Chinese();
比如你可以这样写Person p=new Chinese();
那么我们有哪些时间对象呢
你可以找出很多
你可以找出很多
这样你就可以用它来引用一个子类的实际对象
这样你就可以用它来引用一个子类的实际对象
比如
你早上上课的时间是八点十五分
这样你就可以用它来引用一个子类的实际对象
早操的时间是九点钟
早操的时间是九点钟
刚才我们提到
早操的时间是九点钟
如果一个方法是抽象方法
如果一个方法是抽象方法
晚上熄灯的时间是二十二点
晚上熄灯的时间是二十二点
那么他所在的类 必然是一个抽象类
那么他所在的类 必然是一个抽象类
观察一下这些时间对象
它们有哪些共同的属性呢
它们有哪些共同的属性呢
但是反过来
它们有哪些共同的属性呢
抽象类并不一定必须要有抽象方法的
我们在学习当中对每个对象
在内存当中持有的数据
在内存当中持有的数据
你可以在一个抽象类当中仅仅只包含 普通方法而没有抽象方法
称之为类的属性
它们在类的定义当中
对应的就是我们类的实例变量
对应的就是我们类的实例变量
有了这些基本概念以后呢我们就可以引入面向对象的第三大特性
在每个创建的对象当中
都存在一套这样的实例变量
多态性
都存在一套这样的实例变量
多态和继承有时候密不可分
针对我们现在的时钟应用程序
针对我们现在的时钟应用程序
它是指同一个操作
首先我们要确定它的类名
首先我们要确定它的类名
作用于不同的对象
然后是它的实例变量
然后是它的实例变量
具有不同的显示从而产生不同的执行结果
具有不同的显示从而产生不同的执行结果
同时还要考虑这些实例变量的类型又是什么
同时还要考虑这些实例变量的类型又是什么
在继承关系当中我们曾经讲过你可以借助is-a来判断两个类是否具有有继承关系
在继承关系当中我们曾经讲过你可以借助is-a来判断两个类是否具有有继承关系
最后呢我们还要注意到
他们有各自不同的取值范围
在继承关系当中我们曾经讲过你可以借助is-a来判断两个类是否具有有继承关系
根据见名知意的原则
根据见名知意的原则
在我们的学生信息管理系统当中
我们可以又使用Time作为我们的类名
我们可以又使用Time作为我们的类名
学生是其中的一个用户
学生是其中的一个用户
它准确的表达了我们这个类的用途以及它的含义
它准确的表达了我们这个类的用途以及它的含义
所以学生是用户是符合逻辑的
它准确的表达了我们这个类的用途以及它的含义
教师也是我们的一类用户
同时
教师也是我们的一类用户
我们可以使用hour,mimute
我们可以使用hour,mimute
所以教师是用户也是符合逻辑的
second
分别表示它的属性
分别表示它的属性
因此将用户作为父类
对应的就是我们的时、分、秒
对应的就是我们的时、分、秒
将学生和教师作为它的子类
三个数据
将学生和教师作为它的子类
至于这些属性
至于这些属性
是符合继承关系的
至于这些属性
但是这种关系的倒过来就不合逻辑了比如用户是教师 用户是学生
它的类型
但是这种关系的倒过来就不合逻辑了比如用户是教师 用户是学生
我想大家一定会选择
int作为它的数据类型
int作为它的数据类型
明显是不符合逻辑的
因为这些值都是一个整型数的表达
因为这些值都是一个整型数的表达
也正是这种逻辑关系
也正是这种逻辑关系
我们推而广之
好了
我们推而广之
接下去我们就开始动手写出这个类吧
著名的里氏替换原则就讲到
接下去我们就开始动手写出这个类吧
任何基类对象出现的地方
我们可以通过类的新建向导
创建一个类
创建一个类
继承于他的子类也一定可以出现
继承于他的子类也一定可以出现
取名为time
继承于他的子类也一定可以出现
eclipse已经为我们搭建好了time类的框架
eclipse已经为我们搭建好了time类的框架
你可以这样理解
eclipse已经为我们搭建好了time类的框架
所有引用父类的地方
所有引用父类的地方
接下去我们在类当中
接下去我们在类当中
我们都可以透明的把它替换成子类的对象
定义三个实例变量
分别取名为hour、minute、second
分别取名为hour、minute、second
就我们的学生信息管理系统当中
它们的数据类型都是整型
它们的数据类型都是整型
所有的用户对象
所有的用户对象
他都能够登录和退出系统
好了
这样三个属性就添加完成了
这样三个属性就添加完成了
那么作为他的子类 学生和教师
要在外界引用这些属性,需要通过对象成员的访问方式
要在外界引用这些属性,需要通过对象成员的访问方式
也同样就应该具有 登录和退出系统的功能
来完成
也同样就应该具有 登录和退出系统的功能
我们先来新建一个测试类
我们先来新建一个测试类
这是符合逻辑的
取名为App
取名为App
并勾选main方法
自动生成可运行的程序入口main的框架
这里的代码的第9行
这里的代码的第9行
在这个方法当中
在这个方法当中
我们声明了一个user类型的引用变量
我们来测试下time类的使用
我们来测试下time类的使用
原则上他应该指向一个user对象
原则上他应该指向一个user对象
这里我们就要需要补充两个重要的语法
这里我们就要需要补充两个重要的语法
但这里的我们将一个子类student赋值给他
但这里的我们将一个子类student赋值给他
创建实例的语法
但这里的我们将一个子类student赋值给他
和属性引用的语法
和属性引用的语法
因为父类的引用变量可以保持任何继承他的子类对象
因为父类的引用变量可以保持任何继承他的子类对象
要创建一个实例
需要通过new这个关键字
这是符合里氏替换原则的
这是符合里氏替换原则的
并且调用相应的构造方法
并且调用相应的构造方法
但是种功能也会带来负面的作用
比如
你这里要创建一个time实例
你这里要创建一个time实例
有时候你无法确定
有时候你无法确定
你就可以new time 来完成
你就可以new time 来完成
这父类引用变量当中保持了你要使用的哪一个特定的类型对象呢
这父类引用变量当中保持了你要使用的哪一个特定的类型对象呢
Time
这父类引用变量当中保持了你要使用的哪一个特定的类型对象呢
圆括号是一个默认的构造方法
圆括号是一个默认的构造方法
这时候你就可以使用了
这时候你就可以使用了
我们用它来创建一个时间对象t当中
我们用它来创建一个时间对象t当中
instance of语法来进行类型的检查
instance of语法来进行类型的检查
并保存在我们的引用变量
并保存在我们的引用变量
从而判断某个对象是不是某个特定的类型的子类
从而判断某个对象是不是某个特定的类型的子类
有了这个对象
我们就可以通过对象引用的方法
我们就可以通过对象引用的方法
比如代码的第14号 这里的t 保持的是代码第8行中的教师对象
比如代码的第14号 这里的t 保持的是代码第8行中的教师对象
也称之为原点访问法
比如代码的第14号 这里的t 保持的是代码第8行中的教师对象
来获取对象当中的属性
比如代码的第14号 这里的t 保持的是代码第8行中的教师对象
虽然不是user对象 但instance of 判断是基于里氏替换原则的 它将返回的是一个Ture
比如我们要获取小时就可以用t.hour
虽然不是user对象 但instance of 判断是基于里氏替换原则的 它将返回的是一个Ture
要获取分钟
虽然不是user对象 但instance of 判断是基于里氏替换原则的 它将返回的是一个Ture
就可以用t.minutes
就可以用t.minutes
同样的代码的第15行第16行返回了Ture
要获取秒钟
同样的代码的第15行第16行返回了Ture
只需要用t.second就可以了
只需要用t.second就可以了
代码第17行 引用变量u当中
来运行下我们的程序
你可以看到,在控制台当中
你可以看到,在控制台当中
实际保持的是代码第九行的学生对象
它们的输出的结果都是零
它们的输出的结果都是零
学生对象不是这Teacher类型的实例
学生对象不是这Teacher类型的实例
接下去我们用
接下去我们用
因此他返回的是一个false
原点访问法去修改这些属性
原点访问法去修改这些属性
这些设计 使得我们的多态的实现成为了可能
这些设计 使得我们的多态的实现成为了可能
再次打印你将会发现这些t的值发生了相应的变化
下面我们一起来完成下面的任务
再次打印你将会发现这些t的值发生了相应的变化
体会多态的奥秘
再次打印你将会发现这些t的值发生了相应的变化
用类似的方式
你可以试一试在你的程序当中
在这个任务当中 学生信息管理系统当中需要我们实现user和teacher-student三者的继承关系
创建一个早操的时间
在这个任务当中 学生信息管理系统当中需要我们实现user和teacher-student三者的继承关系
和晚上熄灯的时间
在这个任务当中 学生信息管理系统当中需要我们实现user和teacher-student三者的继承关系
今天的课就到这里
今天的课就到这里
其中所有用户都具有登录
其中所有用户都具有登录
好了
其中所有用户都具有登录
总结一下今天的内容
总结一下今天的内容
login和退出logout方法
login和退出logout方法
在今天的课程当中
在今天的课程当中
但父类user无法对具体的登录和退出进行实现
你学习了如何使用JAVA程序
编写一个自定义的类
但父类user无法对具体的登录和退出进行实现
以及如何使用类
所以我们可以将它们定义为抽象方法
所以我们可以将它们定义为抽象方法
声明自己的属性
声明自己的属性
作为子类Teacher在登陆时
在代码当中
作为子类Teacher在登陆时
创建类的实例变量
创建类的实例变量
将显示相应的登录信息并切换到教师的操作视图
将显示相应的登录信息并切换到教师的操作视图
通过原点的访问方式
将显示相应的登录信息并切换到教师的操作视图
来引用对象的相应属性
来引用对象的相应属性
学生在登陆以后 除了同样显示登陆信息之外 将切换到学生的操作视图
学生在登陆以后 除了同样显示登陆信息之外 将切换到学生的操作视图
这些知识呢非常重要
学生在登陆以后 除了同样显示登陆信息之外 将切换到学生的操作视图
学会他们
学生在登陆以后 除了同样显示登陆信息之外 将切换到学生的操作视图
你就可以像上帝一样
你就可以像上帝一样
创造计算机世界当中的一切事物了
我们可以在 proj03的包当中找到我们前面设计好的student类 并在其中添加我们的Login和Logout方法
我们可以在 proj03的包当中找到我们前面设计好的student类 并在其中添加我们的Login和Logout方法
根据前面说明 学生作为具体子类 他知道如何 登录和退出系统
这里那我们就用一个输出语句来作为一个登陆和退出的示例
接下去我们创建一个新的类user
要在eclipse当中定义抽象类是非常方便的
你只需要在创建类的时候那打上这个abstract复选框即可了
单击完成
我们可以看到 eclipse自动为我们生成了抽象类的框架代码
接着 要让 Student 继承 User类
只需要在Student类当中修改类的声明部分添加extends继承关系
将它指定为User
我们可以将子类当中的共同的方法抽取到父类当中去
这里介绍一个小技巧
Eclipse提供了pull-up重构功能
单击右键
我们选择Refactor菜单
在子菜单当中我们可以找到Pull up功能
他可以将我们的子类当中的相关变量和方法提取到我们的父类当中来
在打开的Pull up对话框当中
确保你的目标类型是我们的父类user
选择要抽象的共同的属性 id name 和共同的方法 login和logout
单击finish完成
然后我们打开user类
子类的相应变量和方法已经提取到了父类当中
那么我们 接下去修改这些 变量的访问权限为protected
由于父类无法确定我们的login和logout 具体行为
因此这两个方法的我们将它定义为abstract抽象方法
一旦你声明了抽象方法以后呢 这个方法体你就可以去掉了
我们只要保留这个方法的签名就可以了
保存所有的文件以后你会发现学生类开始报错
将鼠标移动到类名当中
显示子类的Student必须实现父类User自定义的抽象方法
单击这个链接
系统将为你自动添加尚未实现的那些方法框架
我们只需要在这个方法框架里面
填写具体的方法实现即可
好保存一下
没有任何的错误
下面我们来创建一个代码测试
名字的取名为SMSApp
在这里的我们勾选main方法让它自动生成主方法
在main方法当中我们添加一个User类型数组
并使用初始化列表的方式
添加一个学生对象
这里出现的波浪线告诉我们尚未定义带有两个参数的构造方法
单击快速修改方案的这个链接 可以帮助我们快速地创建相应的构造方法
将方法的参数名字调整为id 和name
由于此类Student已经将id name的实例变量提取到了父类
所以我们可以 调用super方法
将创建对象的工作哪 交给父类的相应的方法去完成
那么这时候的super下波浪线
告诉我们 父类还需要提供带两个参数的构造方法
所以我们继续单击这个链接
创建一个带两个参数的构造方法的框架
切换到user 修改相应的参数名
这里呢 我们可以轻松地将用户传入的参数
保存到我们实例变量当中去了
使用this.id==id这样的形式将右侧的参数id保存到左侧实例变量id当中
对name也是如此操作
在Student类当中
我们以前写的26到37行的相关测试代码
我们先把它注释掉
回到SMSApp的测试程序当中
如果你想向数组当中添加教师对象
这时候我们就需要定义教师类
在创建教师类型的时候
你可以直接指定他的父类 为我们的user
这时候呢代码就自动识别出来
user这里面定义两个抽象方法
作为子类 你必须实现这两个抽象方法
相应的代码框架已经给你写好 你只要对他进行相应的实现就可以了
接下去我们还需要为这个Teacher的添加带两个参数的构造方法
同样我们可以使用 super 交给我们的父类去保存相应的id和name
现在我们已经可以在 数组当中添加任意的学生对象和教师对象了
利用前面学过的for each语法
我们可以用一条语句让数组的容器当中的每个user对象来登录系统
代码如此简单
简单到有点像变一个小魔术
这背后的奥秘这是我们前面讲过的里氏替换原则
user数组保持的都是User类型的引用
由于父类出现的地方都可以用Teacher对象来替代
这也就为我们的第7行第8行代码实现提供了相关的理论依据
此外父类的设计当中定义了抽象方法login
这就意味着第11行代码调用的是合法
这些代码及符合逻辑也符合语法规则
现在我们来运行下看看实际的运行效果
当第1次循环时 循环变量user实际引用的是一个学生对象 这将调用学生对象的login方法
而在第2次循环时 循环变量user实际引用的是一个教师对象 这将调用教室对象的login方法
这一切系统都会借助Java的动态绑定机制
自动的在后台替我们完成所有的工作而不需要我们的程序员手动的进行类型的转换
这个示例就是多态的一个典型示例
对照我们前面多态的定义
这里的共同行为就是我们的登录行为Login
对于这一个同一个操作它
作用于学生对象 和作用于教师对象
会呈现出不同的执行结果
通过这个例子 相信你应该明白了 多态的用法
在今天的课程当中
我们学习了Java多态的特性 以及他的代码实现
多态作为面向对象的主要特性
可以帮我们简化程序的设计和实现
这一切需要取决于你对他是否有一个全面而正确的理解
-1.1Java简介
--01Java简介
-1.2搭建Java开发环境
--Video
-1.3使用记事本编写Java程序
--Video
-1.4使用Eclipse开发Java程序
--Video
-1.5导入/导出Java项目
--Video
-1.6管理组织代码
--Video
-第1章初始Java--测试题
-2.1认识标识符
--Video
-2.2变量和变量类型
--Video
-2.3数据类型和类型转换
--Video
-2.4使用常量
--Video
-2.5使用注释
--Video
-2.6使用运算符进行算术运算
--Video
-第2章使用Java基本语法(1)--测试题
-2.7使用运算符进行逻辑处理
--Video
-2.8使用if进行流程控制
--Video
-2.9使用switch进行流程控制
-2.10使用循环
--Video
-2.11循环的跳出和继续
--Video
-2.12多重循环
--Video
-第2章使用Java基本语法(2)--测试题
-3.1认识类和对象
--Video
-3.2为类添加属性
--Video
-3.3为类添加方法
--Video
-3.4为类添加静态方法
--Video
-3.5递归方法
--递归方法
-3.6构造方法
--Video
-3.7重载方法
--Video
-第3章使用面向对象基础语法--测试题
-4.1使用继承语法
--Video
-4.2使用数组
--Video
-4.3使用数组类操作数组
--Video
-4.4使用抽象类
--Video
-4.5使用接口
--Video
-第4章使用面向对象高级语法--测试题
-5.1异常处理机制
--Video
-5.2捕获异常
--Video
-5.3创建自定义异常
--Video
-第5章捕获并处理异常--测试题
-6.1SWT图形界面
--Video
-6.2使用SWT布局管理-FillLayout
-6.3使用SWT布局管理-RowLayout
-6.4SWT中的事件模型
--Video
-6.5常见事件处理写法
--Video
-第6章使用SWT设计界面--测试题
-7.1微波炉模拟程序的界面制作
--Video
-7.2微波炉模拟程序的代码重构
--Video
-7.3微波炉模拟程序的事件监听
--Video
-7.4微波炉模拟程序的数字键盘
--Video
-7.6微波炉模拟程序CookTimer
--Video
-7.7微波炉模拟程序添加图像
--Video
-7.8微波炉模拟程序添加声音
--Video
-7.9微波炉模拟程序安装包制作
--Video