面向对象的三个基本特征_面向对象的程序设计员是

(39) 2024-06-15 15:01:03

文章目录

  • 前言
  • 一.了解什么是继承
  • 二.我们针对继承来设计一个动物继承树
  • 前言
    • 2.1 第一步 找出共同属性和行为的对象
    • 2.2 设计代表共同状态行为的类
    • 2.3 决定子类是否让某项行为有不同的运作方式。
    • 2.4我们仔细去观察一下子类的特征,争取更多的抽象化的机会。
  • 三.继承的相关语法
    • 3.1 父类的成员的访问
    • 3.2 子类访问父类的成员方法
    • 3.3 super关键字
  • 四.怎么去检验继承关系
  • 五.总结

前言

自从上节课我们开始进入面向对象的封装已经有一些日子了,接下来我们会进入对象村非常有意思的环节,就是继承与多态。为什么我们要去学习这些特性呢?因为这是面向对象非常的重要,还记得我们在第一次进入面向对象村举的第一个例子吗?你的老板要你和你的朋友几个不同的形状,来满足需求,这个时候我们就可以给你的朋友解释一下什么是继承了。


一.了解什么是继承

在设计类的时候,你会把共同的带啊放在某个类中,然后告诉其他的类说此类是它们的父类。当某一个类继承另一个类的时候,也就是子类继承父类的时候。
以java的方式来说,这是“子类继承父类“。继承关系意味着子类继承了父类的方法,当我们提及“类的成员”时,成员的意思就是实例变量和方法。


说了这么多,我举一个简单的例子,让你们大致的了解一下,我上面说的一堆话,到底是什么。
这是一个简单的继承的一个范例:

public class Doctor { 
    boolean worksAtHopsptial; void treatPatient(){ 
    //执行检查 } } public class FamilyDoctor extends Doctor{ 
    boolean makesHouseCalls; void giveAdvice(){ 
    //提出诊断 } } public class Surgeon extends Doctor{ 
    void treatPatient(){ 
    //执行检查 } void makeIncision(){ 
    //截肢 } } 

面向对象的三个基本特征_面向对象的程序设计员是 (https://mushiming.com/)  第1张
这里面具体的例子就是,我们子类在继承父类的时候可以覆盖父类的方法,也可以增加子类自己独有的实例变量和方法,这这是一个简单例子,让大家继承的了解更清楚一些。


二.我们针对继承来设计一个动物继承树

在开始设计动物继承树之前,我们先来了解一下java继承的语法,在Java中如果要表示类之间的继承关系,需要extends关键字去来实现继承关系。具体怎么操作,跟着我去完成照这个动物继承树的例子之后,你就明白了。


前言

1.假设你要设计一个仿真系统程序,可以让用户设定将一群动物丢到某种环境中以观察会发生什么事情。现在不需要写出程序,我们只在乎设计。
2.我们已经被告知一部分会用到的动物,但是并不知道还有多少种动物会加进来。每个动物都会用一个对象来表示,且动物会在环境中活动,执行任何被设计出的行为。
3.这个程序必须能够在任何时间加入新类型的动物

2.1 第一步 找出共同属性和行为的对象

我这里列举六个动物
面向对象的三个基本特征_面向对象的程序设计员是 (https://mushiming.com/)  第2张大家想一想着六种动物有什么共同点在里面?
当然我已经给大家列出来了。我的答案并不是标准的。
我么可以设五个实例变量
picture:动物图像的名称
food:动物所吃得食物。现在持有meat和grass俩种值
hunger:代表饥饿程度的值,根据动物吃了多少而改变。
boundaries:动物活动的区域的长宽
location:动物活动区域的X与Y坐标。
还有四个方法:
makeNoise():动物发出的声音
eat():动物吃的行为
sleep():不同的动物的睡觉行为
roam():其他行为


2.2 设计代表共同状态行为的类

因为对象都是动物的原因,我们可以用Animal作为共同父类的名称。
并且构造图如下。
面向对象的三个基本特征_面向对象的程序设计员是 (https://mushiming.com/)  第3张


2.3 决定子类是否让某项行为有不同的运作方式。

这里就非常有趣了。我们想一想我们应该让子类覆盖哪些方法呢?其实按照常识来说,狮子的叫声绝对不会跟河马的叫声一样,如果你说一样,那我也没办法了,所以我们要根据不同的类型去设计不同的行为程序。
观察animal的这个类以后,我们认为eat()与makeNosie()应该由各个子类自行覆盖。


2.4我们仔细去观察一下子类的特征,争取更多的抽象化的机会。

类的继承结构已经大致成型。我们让每个子类都去覆盖掉makeNoise()与eat()这两个方法,因此狗不会瞄瞄叫、河马也不会抢狮子的食物。
但或许我们还能做更多的设计。我们必须观察Animal的子类找寻是否有可以组织归纳使用共同程序代码的部分。看起来小红帽的好朋友大野狼跟狗有共同的部分。猫、狮子与老虎也有共同的部分。
1.我们观察到Wolf与Dog可能有某些共同的行为,在Lion、Tiger、Cat之间也是。
2.相信你应该知道我们说的是什么,因为动物本来就有组织化的层次(界、门、纲、目、科、属、种),我们可以用这些层次来制作有意义的类设计。我们使用犬科和猫科动物的分类来作出Feline与Caninc这两个类。
3.我们决定Canine使用共同的roam()方法,因为它们都以相同的方式移动。Feline之间也是差不多。而Hippo则持续使用继承下来的roam()方法。

于是我们得出了下面的继承图
面向对象的三个基本特征_面向对象的程序设计员是 (https://mushiming.com/)  第4张

上述我们对动物继承类的构造已经完成了。大家应该明白的差不多了吧。


三.继承的相关语法

前言

3.1 父类的成员的访问

1.子类和父类不存在相同的成员变量

public class Base { 
    int a; int b; } public class Derived extends Base{ 
    int c; public void method(){ 
    a = 10; // 访问从父类中继承下来的a b = 20; // 访问从父类中继承下来的b c = 30; // 访问子类自己的c } } 

2.子类和父类的成员变量相同

public class Base { 
    int a; int b; int c; } / public class Derived extends Base{ 
    int a; // 与父类中成员a同名,且类型相同 char b; // 与父类中成员b同名,但类型不同 public void method(){ 
    a = 100; // 访问父类继承的a,还是子类自己新增的a? b = 101; // 访问父类继承的b,还是子类自己新增的b? c = 102; // 子类没有c,访问的肯定是从父类继承下来的c // d = 103; // 编译失败,因为父类和子类都没有定义成员变量b } } 

总结:
如果访问的成员变量子类中有,优先访问自己的成员变量。
如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
如果访问的成员变量与父类中成员变量同名,则优先访问自己的。


3.2 子类访问父类的成员方法

1.成员方法名字不同

public class Base { 
    public void methodA(){ 
    System.out.println("Base中的methodA()"); } } public class Derived extends Base{ 
    public void methodB(){ 
    System.out.println("Derived中的methodB()方法"); } public void methodC(){ 
    methodB(); // 访问子类自己的methodB() methodA(); // 访问父类继承的methodA() // methodD(); // 编译失败,在整个继承体系中没有发现方法methodD() } } 
  1. 成员方法名字相同
public class Base { 
    public void methodA(){ 
    System.out.println("Base中的methodA()"); } public void methodB(){ 
    System.out.println("Base中的methodB()"); } } public class Derived extends Base{ 
    public void methodA(int a) { 
    System.out.println("Derived中的method(int)方法"); } public void methodB(){ 
    System.out.println("Derived中的methodB()方法"); } public void methodC(){ 
    methodA(); // 没有传参,访问父类中的methodA() methodA(20); // 传递int参数,访问子类中的methodA(int) methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到 } } 

总结:
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到
则访问,否则编译报错。
通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用
方法适传递的参数选择合适的方法访问,如果没有则报错;


3.3 super关键字

由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成
员时,该如何操作?直接访问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父
类的成员。

public class Base { 
    int a; int b; public void methodA(){ 
    System.out.println("Base中的methodA()"); } public void methodB(){ 
    System.out.println("Base中的methodB()"); } } public class Derived extends Base{ 
    int a; // 与父类中成员变量同名且类型相同 char b; // 与父类中成员变量同名但类型不同 // 与父类中methodA()构成重载 public void methodA(int a) { 
    System.out.println("Derived中的method()方法"); } // 与基类中methodB()构成重写(即原型一致,重写后序详细介绍) public void methodB(){ 
    System.out.println("Derived中的methodB()方法"); } public void methodC(){ 
    // 对于同名的成员变量,直接访问时,访问的都是子类的 a = 100; // 等价于: this.a = 100; b = 101; // 等价于: this.b = 101; // 注意:this是当前对象的引用 // 访问父类的成员变量时,需要借助super关键字 // super是获取到子类对象中从基类继承下来的部分 super.a = 200; super.b = 201; // 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法 methodA(); // 没有传参,访问父类中的methodA() methodA(20); // 传递int参数,访问子类中的methodA(int) // 如果在子类中要访问重写的基类方法,则需要借助super关键字 methodB(); // 直接访问,则永远访问到的都是子类中的methodA(),基类的无法访问到 super.methodB(); // 访问基类的methodB() } } 

四.怎么去检验继承关系

这里其实我们当用一个类去继承另外一个类的时候,我们会去说这是子类应该去继承父类,若你想知道某物是否会去继承父类的时候,则可以用IS-A去验证。
三角形是一个多边形?
外科医生是一个医生?
狮子是一种动物?
这些都是继承关系。
要确认你的设计是否正确,使用这样的测验来加以检验。如果不合理,则表示你的设计有问题。
就比如我们洗澡的时候,浴室和澡盆有关联吧,确实是有关联,但不是继承上的关系。澡盆和浴室发生的是HAS-A的关系,如果表示浴室有澡盆的话,只能说明澡盆是浴室的实例变量,浴室里面会有一个澡盆的引用,但他们其实没有继承关系。

这一个环节到这里就结束了,哈哈,朋友,简单的对我们的继承做个总结吧。


五.总结

我们通过一个问题来总结,我们对继承的意义,这里你先别说,听我列举,然后你再去思考

1.避免了重复的代码
在单一的位置定义共同程序代码,然后让子类继承父类的程序代码。当你想要改变这个行为程序时,只需修改这个地方,而子类就会发生同样的改变。
2.定出了共同的协议
当你在父类中定义方法时,它们会被子类继承,这样你就是在对其他程序代码声明:“我所有的子类(例如说subclass)都能用这些方法来执行这几项工作……”。也就是说你拟出了一份“合约”。
我们提出动物继承关系的Animal这个类拟出所有动物子型的共同协议。

朋友们,今天到这里就结束了,我们还没有讨论最精彩的部分,就是多态,这里留到下一部分,我们再讨论。

THE END

发表回复