继承

继承的起源

在面向对象编程中,可以通过扩展一个已有的类,并继承该类的属性和行为,来创建一个新的类,这种方式称为继承(Inheritance)。

已有的类称为父类,而新类称为子类。父类也可以称为基类、超类,子类也可以称为派生类。这些术语可以互换,但是意思是相同的。

使用"is a"关系判断继承

“is a”关系是一个简单而强大的规则,用于判断父类和子类的继承关系是否正确。当使用继承时,我们必须能够说子类“ is a(是一个)” 父类。如果这个语句是真的,那么继承关系就是对的。

Java中继承的实现

我们已经知道为什么在面向对象编程中要使用继承。现在,让我们来看看Java中是如何实现继承的。

在Java中,一个类使用关键字extends继承其它类。关键字extends出现在类声明时的类名后,extends后面跟着的是要继承的类的名称。

单继承与多继承

某些OOP语言(例如C++)允许子类有多个父类。但是,在Java中这是不允许的。在Java中,一个类只能有一个父类。例如,Salary类不能同时继承Employee类和Manager类。多继承在Java中是不允许的。Java语言的目标之一是创建一个易于使用和理解的面向对象编程语言。而多继承只能给编程语言带来混淆。

一个Java类只能有一个父类。但是这并不意味着一个类没有祖父、曾祖父等等。一个Java类可以有父类、父类也可以父类,依此类推。

所有类的根类Object

Java语言API中包含了一个名为Object的特殊类,它是整个Java类层次中的根类。Object类在java.lang包中,是每个Java类的父类,要么是直接的父类,要么就是间接父类。

方法重写

子类可以重写从父类继承的方法,从而允许子类添加或者改变父类中方法的行为。这称为方法重写,是OOP的特征之一。

当子类重写父类的方法时,必须遵循如下的规则:

  • 子类的方法的返回值的类型、方法名和形式参数列表,必须和父类中的是相同的。如果子类中的方法与父类中的方法同名,但是子类的方法改变了形式参数列表,那么这是方法重载,而不是方法重写。不要混淆这两个概念,二者的用法是完全不同的。
  • 访问修饰符必须不小于父类中的访问修饰符。例如,如果父类的方法是public,那么子类的必须是public。如果父类的方法是protected,那么子类必须是protected或public(public比protected访问权更大)
  • 子类中重写的异常不能抛出比父类更多的异常

equals()方法

Object类有一个用于判断两个对象是否相等的equals()方法。我们编写的每一个类应该覆盖equals()方法,以让类的用户判断什么时候类的实例是相等的。

super关键字

在前面,我们演示了子类如何通过方法重写,完全改变继承自父类的方法。有时,子类想要给继承的方法添加行为,但是并不是完全替换父类的方法。在这种情况下,可以使用关键字super来调用父类中重写的方法。我们已经知道,每个对象都有一个对自身的引用,称为this引用。当在一个类中,引用类本身的成员变量或方法时,可以显式地使用this引用。类似地,一个类可以使用关键字super,来显式地引用从父类继承的成员变量或方法。我们可以把super看作是子对象对父对象的引用。

final关键字

在前面,我们已经学习了创建符号常量时使用final关键字。符号常量在被赋值后就不能被改变。

现在我们已经学习了继承和方法重写,我们可以学习final关键字的另外两种用法了。

  • final类。一个类可以声明为最终类的。最终类不能被有子类。
  • final方法。一个方法可以声明为最终的。最终方法不能被重写。

final类

有时候,我们希望一个类成为最终类,不能被其它类继承,也就是不能有子类。在这种情况下,我们就需要在声明类时,在关键字class前加上关键字final,让该类成为一个最终类。

final方法

当子类重写父类的一个方法时,父类中被重写的方法实质上被隐藏了。在子类中,要调用父类中的方法,唯一的方法是显式地调用。如果我们编写的方法有中很重要的行为,我们不想子类重写,可以将该方法声明为最终方法。最终方法不能被子类重写。

实例化过程

子类对象是对父类的扩展。当子类被实例化时,父类对象需要先被构造。更特别地,父类的构造器必须在子类的构造器执行之前执行。此外,如果子类有祖父类,那么祖父对象需要先构造。这个过程一直向继承树继续传递。

当一个对象被实例化时,发生下列事件序列:

  1. new运算符调用子类的构造器。
  2. 子类会用this关键字调用子类的其它构造器。最后,子类中的第一行代码中不是this()的构造器将被调用。
  3. 在子类的构造器中的任何语句执行前,父类的构造器必须用super关键字调用。如果没有显式地使用super调用,那么编译器会用super()调用无参数的父类构造器。
  4. 如果父类也是另一个类的子类,那么在父类的构造器中的任何语句执行前,父类构造器必须使用super调用更高一层父类的构造器。
  5. 本过程继续执行,直到到达类层次树的顶部,即Object类。
  6. Object类中的构造器执行,然后控制流程转到继承层次树中Object下面的类的构造器中。
  7. 构造器沿着继承层次树向下执行。最后一个要执行的构造器,实际上是第一步中先调用的那个构造器。

调用父类的构造器

关键字super用于调用父类的构造器。如果构造器没有显式地使用super关键字,那么编译器会自动添加一个无参数的super()来调用父类构造器。但是,如果父类没有无参数的构造器会发生什么呢?在这种情况下,调用空括号的super()不会通过编译。因此,子类构造器需要显式地调用父类构造器,并将恰当的实际参数传递给父类的构造器。

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值