继承(inheritance)是面向对象的重要概念。继承是除组合(composition)之外,提高代码重复可用性(reusibility)的另一种重要方式。我们在组合(composition)中看到,组合是重复调用对象的功能接口。我们将看到,继承可以重复利用已有的类的定义。

类的继承

我们之前定义类的时候,都是从头开始,详细的定义该类的每一个成员。比如下面的Human类:

class Human
{   /*** accessor*/ public int getHeight() { return this.height; } /** * mutator */ public void growHeight(int h) { this.height = this.height + h; }  /** * breath */ public void breath() { System.out.println("hu...hu..."); } private int height; }

从上面的类定义,我们可以了解该类的所有细节: 该类的数据成员,该类的方法,该类的接口。

现在要定义一个新的类,比如Woman类,并假设Woman与Human类相当类似:

Human & Woman

我们可以像以前一样,从头开始,完整的定义Woman类:

class Woman
{/*** accessor*/ public int getHeight() { return this.height; } /** * mutator */ public void growHeight(int h) { this.height = this.height + h; } 
    /**     * breath     */ public void breath() { System.out.println("hu...hu..."); } /** * new method */ public Human giveBirth() { System.out.println("Give birth"); return (new Human(20)); } private int height; }

一个程序员在写上面程序的时候,会有很大的烦恼。许多定义都曾在Human类中写过,但我们还要重新敲一遍。Woman类只新增了一个giveBirth()方法 (该方法创建并返回一个新的Human对象)。

利用继承,我们可以避免上面的重复。让Woman类继承自Human类,Woman类就自动拥有了Human类中所有public成员的功能。

我们用extends关键字表示继承:

class Woman extends Human
{    /** * new method */ public Human giveBirth() { System.out.println("Give birth"); return (new Human(20)); } }

这样,我们就省去了大量的输入。通过继承,我们创建了一个新类,叫做衍生类(derived class)。被继承的类(Human)称为基类(base class)。衍生类以基类作为自己定义的基础,并补充基类中没有定义的giveBirth()方法。继承关系可以表示为:

继承: 箭头指向基类

可以用以下Test类测试:

public class Test
{public static void main(String[] args) { Woman aWoman = new Woman(); aWoman.growHeight(120); System.out.println(aWoman.getHeight()); } }

衍生层

通过继承,我们创建了Woman类。整个过程可以分为三个层次: 基类定义,衍生类定义,外部使用。

基类定义的层次就是正常的定义一个类,比如上面的Human类定义。

在外部使用者看来(比如Test类中创建Woman类对象),衍生类有一个统一的外部接口:

对于外部使用者来说,上述接口就已经足够了。仅从接口看,衍生类也没有什么特别之处。

然而,当程序员在衍生类定义的层次时,就必须要小心:

首先,接口是混合的: getHeight()方法和growHeight()方法来自基类,giveBirth()方法则是在衍生类内部定义的。

还有更加复杂的地方。我们之前在类的内部可以自由访问类的成员(利用this指代对象)。然而,当我们在Woman类的定义范围内,我们无法访问基类Human的private成员。我们记得private的含义: private的成员仅供该类内部使用。Woman类是一个不同于Human类的新类,所以位于Human类的外部。在衍生类中,不能访问基类的private成员。

但有趣的是,我们的growHeight()和getHeight()方法依然可以运行。这说明基类的private成员存在,我们只是不能直接访问。

为了清晰概念,我们需要了解衍生类对象的生成机制。当我们创建一个衍生类的对象时,Java实际上先创建了一个基类对象(subobject),并在基类对象的外部(注意,这里是基类对象的外部,衍生类对象的内部),增加衍生类定义的其他成员,构成一个衍生类对象。外部使用者能看到的,就是基类和衍生类的public成员。如下图:

基类对象与衍生类对象

图中黄色为基类对象。基层的成员之间可以互相访问 (利用Human类定义中的this指代基类对象)。

蓝色部分为衍生对象新增的内容,我将这部分称为衍生层。蓝色和黄色部分共同构成衍生对象。衍生层的成员可以相互访问(Woman定义中的this)。更进一步,我们还可以访问基层中public的成员。为此,我们用super关键字来指代基类对象,使用super.member的方式来表示基层的(public)成员。

当我们位于衍生层时(也就是在定义Woman类时),不能访问红色的基层private成员。当我们位于外部时,既不能访问紫色的衍生层private成员,也不能访问红色的基层private成员。

(衍生层的private成员有访问禁忌,所以标为斜线。基层的private成员访问禁忌最多,所以标为交叉斜线)

super和this类似,也是隐式参数。我们在类定义的不同层次时,this会有不同的含义。要小心的使用this和super关键字。

(Java并不强制使用this和super。Java在许多情况下可以自动识别成员的归属。但我觉得这是个好习惯。)

protected

我们之前介绍了两个访问权限相关的关键字,private和public,它们控制了成员的外部可见性。现在,我们介绍一个新的访问权限关键字: protected。

标为protected的成员在该类及其衍生类中可见。这个概念很容易理解,就是说,基类的protected成员可以被衍生层访问,但不能被外部访问,如下图:

方法覆盖

衍生类对象的外部接口最终由基类对象的public成员和衍生层的public成员共同构成。如果基类public成员和衍生层的public成员同名,Java接口中呈现的究竟是哪一个呢?

我们在构造方法与方法重载中已经提到,Java是同时通过方法名和参数列表来判断所要调用的方法的。方法是由方法名和参数列表共同决定的。上述问题中,如果只是方法名相同,而参数列表不同,那么两个方法会同时呈现到接口,不会给我们造成困扰。外部调用时,Java会根据提供的参数,来决定使用哪个方法 (方法重载)。

如果方法名和参数列表都相同呢? 在衍生层时,我们还可以通过super和this来确定是哪一个方法。而在外部时,我们呈现的只是统一接口,所以无法同时提供两个方法。这种情况下,Java会呈现衍生层的方法,而不是基层的方法。

这种机制叫做方法覆盖(method overriding)。方法覆盖可以被很好的利用,用于修改基类成员的方法。比如,在衍生层,也就是定义Woman时,可以修改基类提供的breath()方法:

class Woman extends Human
{/** * new method */ public Human giveBirth() { System.out.println("Give birth"); return (new Human(20)); } /** * override Human.breath() */ public void breath() { super.breath(); System.out.println("su..."); } }

注意,此时我们位于衍生层,依然可以通过super来调用基类对象的breath()方法。当我们外部调用Woman类时,由于方法覆盖,就无法再调用基类对象的该方法了。

方法覆盖保持了基类对象的接口,而采用了衍生层的实现。

构造方法

在了解了基类对象和衍生层的概念之后,衍生类的构造方法也比较容易理解。

我们要在衍生类的定义中定义与类同名的构造方法。在该构造方法中:

  • 由于在创建衍生对象的时候,基类对象先被创建和初始化,所以,基类的构造方法应该先被调用。我们可以使用super(argument list)的语句,来调用基类的构造方法。
  • 基类对象创建之后,开始构建衍生层 (初始化衍生层成员)。这和一般的构建方法相同,参考构造方法与方法重载

比如下面的程序中,Human类有一个构造方法:

class Human
{   
    /*** constructor*/public Human(int h) {this.height = h; }/** * accessor */ public int getHeight() { return this.height; } /** * mutator */ public void growHeight(int h) { this.height = this.height + h; }  /** * breath */ public void breath() { System.out.println("hu...hu..."); } private int height; }

衍生类Woman类的定义及其构造方法:

class Woman extends Human
{/** * constructor */ public Woman(int h) { super(h); // base class constructor System.out.println("Hello, Pandora!"); } /** * new method */ public Human giveBirth() { System.out.println("Give birth"); return (new Human(20)); } /** * override Human.breath() */ public void breath() { super.breath(); System.out.println("su..."); } }

总结

extends

method overriding

protected

super.member, super()

转载于:https://www.cnblogs.com/ssjie/p/4736713.html

Java基础08 继承相关推荐

  1. 第二十八节:Java基础-进阶继承,抽象类,接口

    前言 Java基础 - 进阶继承,抽象类,接口 进阶继承 class Stu {int age = 1; } class Stuo extends Stu {int agee = 2; } class ...

  2. Java基础篇--继承(inherit),多态(Polymorphism)

    Java基础篇--继承(inherit),多态(Polymorphism) 1. 继承概述 1.1 什么是继承 1.2 为什么要使用继承 1.3 继承的特点 1.4 继承的优点 2. 组合设计模式 2 ...

  3. Java基础:继承、多态、抽象、接口

    第一讲    继承 一.继承概述 1.多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可. 2.通过extends关键字可以实现类与类的 ...

  4. Java基础(08) 面向对象

    Java基础(八)-- 面向对象 ​ 面向对象(Object Oriented Programming, 简称OOP )是Java基础学习的重点,必须掌握每个细节,接下来对面向对象的学习主要围绕以下三 ...

  5. Java 基础(继承)

    继承 基础 1. 继承的特点 2. super关键字 3. 继承中变量访问特点(就近原则) 4. 继承中成员方法访问特点 5. 继承中构造访问特点 为什么子类中所有构造方法默认都会访问父类无参构造方法 ...

  6. java基础:继承的使用

    一.继承的使用 1.什么是继承 2.继承的设计规范 3.继承的内存原理 test.java package Test;public class test {public static void mai ...

  7. 11. Java基础之继承

    在<Think in java>中有这样一句话:复用代码是Java众多引人注目的功能之一.但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情.在这句 ...

  8. java基础 通过继承Thread类和实现Runnable接口创建线程

    java 创建线程 Java中,线程也是一种对象,但不是任何对象都可以成为线程. 只有实现了Runnable接口或继承了Thread类的对象才能成为线程. 继承Thread类 //格式: class ...

  9. Java基础复习——继承机制

    一.写在前面 1.基本作用 代码复用,更重要的就是有了继承机制后才会有后面的"方法重写"和"多态机制". 2.语法格式 //Student类继承了Person类 ...

最新文章

  1. python数据结构简单总结
  2. gateway sentinel 熔断 不起作用_微服务Gateway新一代网关
  3. 2.Docker技术入门与实战 --- 核心概念与安装配置
  4. 学计算机专业选择设计为类需要艺考,想学设计必须参加艺考吗?设计学类专业,新高考选科怎么选?...
  5. 超级终端连接华为交换机_win8系统使用超级终端连接华为交换机的操作方法
  6. CRM 实施计划和准备的8个步骤!
  7. qa 芯片测试_关于半导体设备测试,看这一篇就够了
  8. 苹果6显示连接id服务器出错,appleid:appleid连接失败该如何解决
  9. 【数据分析】2种常见的反爬虫策略,信息验证和动态反爬虫
  10. 图像小波去噪matlab程序,小波去噪程序(用matlab实现)
  11. 如何区分静态网页与动态网页
  12. ffmpeg生成缩略图
  13. 加州房价篇 (一) : 了解数据
  14. CSS3初级学习(二)背景图片叠加
  15. 空间直角坐标系、大地坐标系、平面坐标系、高斯平面直角坐标系
  16. IEEE trans模板格式中的分栏及左下角作者信息脚注的添加方法(Word 2010)
  17. 维特智能六轴姿态传感器JY61P_stm32f1xx驱动代码解析
  18. 语音信号a率压缩算法c语言,基于OMAP5912平台的语音压缩算法实现
  19. 链接h5代码_H5到底是什么?看完你就明白了!
  20. mac mysql版本_mac mysql安装哪个版本

热门文章

  1. java出现no XXX in java.library.path的解决办法及eclipse配置
  2. ×××生成算法的分析
  3. 我国有线电视信源编码现有体制和对高清的制约
  4. 第九次会议(5.14)
  5. 嵌入式软硬件开发中遇到的坑
  6. systemback-----做你折腾的后盾
  7. 我的C#文章模块代码
  8. ubuntu---php脚本中执行换行
  9. 黑马lavarel教程---3、数据库和视图注意点
  10. python错误 ImportError: No module named setuptools 解决方法[转]