“is-a” 关系是继承的一个明显特征。

注释: 我们使用员工和经理的例子,不过这个例子要有所保留。在真实的世界里,员工也可能会成为经理,所以你建模时可能希望经理也是员工,而不是员工的一个子类。不过,在例子中,假设公司只有两类人:一些人永远是员工,另一些人一直是经理。

Employee 类:

import java.time.LocalDate;
import java.util.Objects;public class Employee {private String name;private double salary;private LocalDate hireDay;public Employee(String name, double salary, int year, int month, int day) {this.name = name;this.salary = salary;hireDay = LocalDate.of(year, month, day);}public String getName() {return name;}public double getSalary() {return salary;}public LocalDate getHireDay() {return hireDay;}public void raiseSalary(double byPercent) {double raise = salary * byPercent / 100;salary += raise;}@Overridepublic String toString() {String str = "%s[name=%s, salary=%.2f, hireDay=%s]";return String.format(str, getClass(), name, this.getSalary(), hireDay);}
}

Manager 类:

public class Manager extends Employee {private double bonus;public Manager(String name, double salary, int year, int month, int day) {super(name, salary, year, month, day);this.bonus = 0;}public void setBonus(double bonus) {this.bonus = bonus;}@Overridepublic double getSalary() {double baseSalary = super.getSalary(); return baseSalary + bonus;}
}

1. 定义子类

继承 Employee 类来定义 Manager 类,使用关键字 extends 表示继承。

public class Manager extends Employee {// added methods and fields
}

在 Java 中,所有的继承都是公共继承。

关键字 extends 表明正在构造的新类派生于一个已存在的类。已存在的类称为超类(superclass)、基类(base class)或父类(parent class);新类称为子类(subclass)、派生类(derived class)或孩子类(child class)。超类和子类是 Java 程序员最常用的两个术语,而了解其他语言的程序员可能更加偏爱使用父类和孩子类,这可能很贴切地体现了“继承”。

尽管 Employee 类是一个超类,但并不是因为它优于子类或者拥有比子类更多的功能。实际上恰恰相反,子类比超类拥有的功能更多。例如,读过 Manager 类的源代码之后就会发现,Manager 类比超类 Employee 封装了更多的数据,拥有更多的功能。

注释: 前缀 “超”(super) 和 “子”(sub) 来源于计算机科学与数学理论中集合语言的术语。所有员工组成的集合包含所有经理组成的集合。可以这样说,员工集合是经理集合的超集,也可以说,经理集合是员工集合的子集。

在 Manager 类中,增加了一个用于存储奖金信息的字段,以及一个用于设置这个字段的新方法 :

public class Manager extends Employee
{// 奖金private double bonus;...public void setBonus(double bonus){this.bonus = bonus;}
}

这里定义的方法和字段并没有什么特别之处。如果有一个 Manager 对象,就可以使用 setBonus 方法。

Manager boss = ...;
boss.setBonus(5000);

由于 setBonus 方法不是在 Employee 类中定义的,所有属于 Employee 类的对象不能使用它。

然而,尽管在 Manager 类中没有显式地定义 getName 和 getHireDay 等方法,但是可以对 Manager 对象使用这些方法,这是因为 Manager 类自动地继承了超类 Employee 中的这些方法。

类似地,从超类中还继承了 name、salary 和 hireDay 这 3 个字段。这样一来,每个 Manager 对象就包含了 4 个字段:name、salary、hireDay 和 bonus。

通过扩招超类定义子类的时候,只需要指出子类与超类的不同之处。因此在设计类的时候,应当将最一般的方法放在超类中,而将更特殊的方法放在子类中,这种将通用功能抽取到超类的做法在面向对象设计中十分普遍。

2. 覆盖方法

超类中的有些方法对子类 Manager 并不一定适用。具体来说,Manager 类中的 getSalary 方法应该返回薪水和奖金的总和。为此,需要提供一个新的方法覆盖(override)超类中的这个方法:

public class Manager extends Employee {...public dobule getSalary() {...}
}

应该如何实现这个方法呢?乍看起来似乎很简单,只要返回 salary 和 bonus 字段的总和就可以了:

public double getSalary() {return salary + bonus; // 不能运行
}

不过,这样做是不行的。回想一下,只有 Employee 方法能直接访问 Employee 类的私有字段。这意味着,Manager 类的 getSalary 方法不能直接访问 salary 字段。如果 Manager 类的方法想要访问那些私有字段,就要像所有其他方法一样使用公共接口,在这里就是要使用 Employee 类中的公共方法 getSalary。

现在,再试一下。你需要调用 getSalary 方法而不是直接访问 salary 字段:

public double getSalary() {double baseSalary = getSalary(); // 仍然不能运行return baseSalary + bonus;
}

这段代码有问题。问题出现在调用 getSalary 的语句上,它只是在调用自身,这是因为 Manager 类也有一个 getSalary 方法,所以这条语句将会导致无限次调用自己,直到整个程序最终崩溃。

这里需要指出:我们希望调用超类 Employee 中的 getSalary 方法,而不是当前类的这个方法。为此,可以使用特殊的关键字 super 解决这个问题:
super.getSalary()
这个语句调用的是 Employee 类中的 getSalary 方法。下面是 Manager 类中 getSalary 方法的正确版本:

public double getSalary() {double baseSalary = super.getSalary();return baseSalary + bonus;
}

注释: 有些人认为 super 与 this 引用是类似的概念,实际上,这样比较并不太恰当。这是因为 super 不是一个对象的引用,例如,不能将值 super 赋给另一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字。

在子类中可以增加字段、增加方法或覆盖超类的方法,不过,继承绝对不会删除父类任何字段或方法。

3. 子类构造器

public Manager(String name, double salary, int year, int month, int day)
{super(name, salary, year, month, day);bonus = 0;
}

这里的关键字 super 具有不同的含义。语句
super(name, salary, year, month, day);
是 “调用超类中含有 name、 salary、year、month 和 day 参数的构造器” 的简写形式。

由于 Manager 类的构造器不能访问 Employee 类的私有字段,所以必须通过一个构造器来初始化这些私有字段。可以利用特殊的 super 语法调用这个构造器。使用 super 调用构造器的语句必须是子类构造器的第一条语句。

如果子类的构造器没有显式地调用超类的构造器,将自动地调用超类的无参数构造器。如果超类没有无参数构造器,并且在子类的构造器中又没有显式地调用超类的其他构造器,则 Java 编译器将报告错误。

this 关键字有两个含义:

  • 指示隐式参数的引用。
  • 调用该类的其他构造器。

super 关键字有两个含义:

  • 调用超类的方法。
  • 调用超类的构造器。

在调用构造器的时候,this 和 super 这两个关键字紧密相关。调用构造器的语句只能作为另一个构造器的第一条语句出现。构造器参数可以传递给当前类(this)的另一个构造器,也可以传递给超类(super)的构造器。

重新定义 Manager 对象的 getSalary 方法之后,奖金将会自动添加到经理的薪水中。
下面给出一个例子说明这个类的使用。我们要创建一个新经理,并设置他的奖金:

Manager boss = new Manager("Carl Cracker", 75000, 1987, 12, 15);
boss.setBonus(500000);

下面定义一个包含 3 个员工的数组:
Employee[] staff = new Employee[3];
在数组中混合填入经理和员工:

staff[0] = boss;
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);

输出每个人的信息:

for (Employee e : staff) {System.out.println(e);
}

运行这条循环语句将会输出下列数据:

Carl Cracker 575000.0
Harry Hacker 50000.0
Tommy Tester 40000.0

这里的 staff[1] 和 staff[2] 仅输出了基本薪水,这是因为它们是 Employee 对象,而 staff[0] 是一个 Manager 对象,它的 getSalary 方法会将奖金和基本薪水相加。
需要提醒大家的是,以下调用
e.getSalay()
能够选出应该执行的正确 getSalary 方法。请注意,尽管这里将 e 声明为 Employee 类型,但实际上 e 即可以引用 Employee 类型的对象,也可以引用 Manager 类型的对象。

当 e 应用 Employee 对象时,e.getSalary() 调用的是 Employee 类中的 getSalary 方法;当 e 引用 Manager 对象时,e.getSalary() 调用的是 Manager 类中的 getSalary 方法。虚拟机知道 e 实际引用的对象类型,因此能够正确地调用相应的方法。

一个对象变量可以指示多种实际类型的现象被称为多态(polymorphism)。在运行时能够自动地选择调用哪个方法的现象称为动态绑定(dynamic binding)

在 Java 中,动态绑定是默认的行为。如果不希望让一个方法虚拟的,可以将它标记为 final。

4. 继承层次

继承并不仅限于一个层次。例如,可以由 Manager 类派生 Executive 类。

由一个公共超类派生出来的所有类的集合被称为***继承层次(inheritance hierarchy)***,在继承层次中,从某个特定的类到其祖先的路径称为该类的***继承链(inheritance chain)***。

通常,一个祖先类可以拥有多个子孙继承链。例如,可以由 Employee 类派生出子类 Programmer 和 Secretary,它们与 Manager 类没有任何关系(它们彼此之间也没有任何关系)。必要的话,可以将这个过程一直延续下去。

Java 不支持多重继承,但提供了一些类似多重继承的功能——接口。

Java 超类和子类(学习 Java 编程语言 042)相关推荐

  1. java培训分享:学习java开发的优势是什么

    想要进入到互联网行业的小伙伴,经常比较纠结学那个学科比较好,目前java.web前端.Python等都是非常热门的行业,前景也是比较好的,选择java学科的人比较多,那么学习java开发的优势是什么呢 ...

  2. java培训分享:学习Java需要什么软件

    在参加java培训过程中学习java技术,需要用到很多辅助工具,这些辅助工具是具有多功能性和实用性的,从代码构建到bug压缩.学习这些工具可以帮助您提高代码的质量,并成为一个更高效的Java开发人员. ...

  3. java 类的子类对象_使用超类初始化子类对象Java

    本问题已经有最佳答案,请猛点这里访问. SuperClass object = new SubClass(); 为什么要使用一个超类来像上面那样实例化一个子类对象?因为我学会实例化对象的唯一方法是: ...

  4. java图形界面_学习Java有什么用?Java的应用领域有哪些?

    很多人可能会问,学习Java有用吗?Java是世界第一编程语言!由于它开源.免费.跨平台,这些特性都让他富有生命力.国内最具有知名度三家IT企业BAT-(百度,阿里,腾讯)和其他众多企业,都在广泛使用 ...

  5. java大致了解_学习Java第一天,大致了解

    第一章: java核心 1 了解 java的产生背景 2 了解java的体系结构和组成 3 了解java程序的编写 编译 运行 4 掌握java的 api文档的使用 5 了解 jdk的组成 1. ja ...

  6. groovy 使用java类_深入学习java中的Groovy 和 Scala 类

    前言 Java 传承的是平台,而不是语言.有超过 200 种语言可以在 JVM 上运行,它们之中不可避免地会有一种语言最终将取代 Java 语言,成为编写 JVM 程序的最佳方式.本系列将探讨三种下一 ...

  7. java程序设计心得_学习Java编程的学习方法总结

    现在越来越多的人学习Java,参加高强度的Java培训,希望毕业后高薪就业,但是学习Java并非是短期内就可以速成的,不可能一口气吃成一个大胖子,4-5个月就能吧全部的技能学完,这需要一个寻寻渐进的过 ...

  8. java序列化_深入学习Java序列化

    前言 对于Java的序列化,一直只知道只需要实现Serializbale这个接口就可以了,具体内部实现一直不是很了解,正好这次在重复造RPC的轮子的时候涉及到序列化问题,就抽时间看了下 Java序列化 ...

  9. java安装_在线学习Java编程的最佳方法

    java安装 1.简介 Java是使用最广泛的编程语言之一. 根据Github的最新报告,Java被列为仅次于JavaScript的第二大最常用的编程语言. 掌握Java的人有很多话题. 好消息是,您 ...

最新文章

  1. @Profile-根据不同环境注入bean
  2. Windows 技术篇-设置dns提升网速,刷新dns缓存
  3. 似然函数代码c语言,从似然函数到EM算法(附代码实现)
  4. delphi 怎么获取工程版本号
  5. 办公出口ip多个地址_如何正确分配与高效管理IP地址
  6. linux 配置思科路由器,将配置文件导入cisco2611路由器步骤
  7. 工程代码_Egret开发笔记(二)基础工程代码阅读
  8. Python 第三方模块之 PDFMiner(pdf信息提取)
  9. 【干货】微信小程序如何让view标签中内容居中
  10. Windows下安装Elasticsearch
  11. 10款屏幕取色器/颜色拾取工具软件介绍及下载地址(附截图)
  12. Javascript上传图片转base64并预览
  13. svn分支合并到主干
  14. win10在哪里找到计算机,win10计算器在哪里?win10怎么调出计算器?
  15. 飞图FLYTOUAV垂起固定翼无人机采用交叉环绕飞行搭载单镜头相机做高精度,高效率,长航时,大比例尺地籍测量中应用
  16. 大学计算机基础线下作业,【计算机基础论文】大学计算机基础教学新形式探究(共4665字)...
  17. 大工2021年11月份《电气制图与CAD》课程设计离线作业
  18. 使用机器学习预测大盘
  19. LFtoolBox0.4工具包解码Lytro光场图像及子孔径图像获取
  20. 《团队协作的五大障碍》读后感

热门文章

  1. 汽车驾驶技术图文详解
  2. Python+OpenCV+dlib汽车驾驶员疲劳驾驶检测
  3. 油墨、版纸(速印机)
  4. 电脑爱好者2006下半年配套光盘][1.22G][ISO]:BT下载
  5. GeekPwn2015胸卡ESP8266 12E串口调试
  6. AMD Duron安装redhat linux 6.2成功后无法进入linux的解决办法
  7. C++ primer 第五版习题答案, Stanley B. Lippman( 斯坦利 李普曼)(持续更新中)
  8. 女程序员要为了男友放弃华为offer吗?网友:别想了,去华为!
  9. 微信PC最新测试版3.3.0.60 朋友圈采集 HOOK技术
  10. matlab 生理药动学模型,生理药物代谢动力学模型及其应用.PDF