06 Java 的继承

简介: 继承Java 继承的实现(只支持类的单继承,不支持类多继承,但是支持接口的多实现)。多个类中存在相同属性和行为时,将这些内容抽取到单独一个类. 定义类时直接通过 extends 关键字指明要继承的父类。

继承


Java 继承的实现(只支持类的单继承,不支持类多继承,但是支持接口的多实现)。

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类. 定义类时直接通过 extends 关键字指明要继承的父类。


子类对象除了可以访问子类中直接定义的成员外,可直接访问父类的所有非私有成员。


继承的作用


  • 继承提高了代码的复用性。


  • 继承的出现让类与类之间产生了关系,提供了多态的前提。


  • 不要仅为了获取其他类中某个功能而去强行使用继承,类与类之间要有所属( " is a " )关系)。


如何使用一个继承体系中的功能


  • 查阅父类功能(定义了共性的功能)


  • 创建子类对象使用功能(因为父类可能不能创建对象, 而且子类提供了更丰富的功能)


  • 继承中自子类变量的特点:如果子类出现非私有的同名变量时, 子类访问本类变量用 this, 子类访问父类中的同名变量用 super。


成员变量的隐藏


子类成员变量与父类一样,会屏蔽父类中的成员变量,称为“成员变量隐藏”。


方法的覆盖(Override)


如果子类方法完全与父类方法相同,即:相同的方法名、相同的参数列表和相同的返回值,只是方法体不同,这称为子类覆盖(Override)父类方法。


在声明方法时最后添加@Override注解,@Override注解不是方法覆盖必须的,但添加 @Override 注解有两个好处:


  • 提高程序的可读性。


  • 编译器检查 @Override 注解的方法在父类中已定义的方法是否匹配,如果不匹配则会报错。


方法覆盖时应遵循的原则


  1. 覆盖后的方法不能比原方法有更严格的访问控制(可以相同)。例如将代码访问控制 public 修改 private,那么会发生编译错误。


  1. 覆盖后的方法不能比原方法产生更多的异常。


  1. 父类中的私有方法不可以被覆盖。


  1. 子类的返回类型可以是父类的子类。


覆盖的应用


  • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样也沿袭了父类的功能


  • 构造方法在类继承中的作用构造方法不能继承.由于子类对象要对来自父类的成员进行初始化,因此,在创建子类对象时除了执行子类的构造方法外,还需要调用父类的构造方法.具体遵循如下原则:


  1. 当子类未定义构造方法时,创建对象时将无条件地调用父类的空构造方法,会默认在第一条添加super();


  1. 对于父类的含参数构造方法,子类可以在自己构造方法中使用关键字 super 来调用它, 但 super 调用语句必须是子类构造方法中的第一个可执行语句;


  1. 子类在自己定义构造方法中如果没有用 super 明确调用父类的构造方法,则在创建对象时,将自动先执行父类的无参构造方法,然后再执行自己定义的构造方法。
    所以在一个类的设计时如果有构造方法,最好提供一个无参构造方法。例如系统类库中的类大多提供了无参构造方法,用户编程时最好也要养成此习惯。


【注意】使用 this 查找匹配的方法时首先在本类查找,找不到时再到其父类和祖先类查找;使用 super 查找匹配方法时,首先到直接父类查找,如果不存在,则继续到其祖先类逐级往高层查找。


继承的更多细节


❑ 构造方法;


在父类构造方法中调用可被子类重写的方法,是一种不好的实践,容易引起混淆,应该只调用private的方法。


❑ 重名与静态绑定;


静态绑定在程序编译阶段即可决定。实例变量、静态变量、静态方法、private 方法,都是静态绑定的。


而动态绑定则要等到程序运行时。子类可以重写父类非private的方法,当调用的时候,会动态绑定,执行子类的方法。


❑ 重载和重写;


重载是指方法名称相同但参数签名不同(参数个数、类型或顺序不同),重写是指子类重写与父类相同参数签名的方法。


对一个函数调用而言,可能有多个匹配的方法,有时候选择哪一个并不是那么明显。当有多个重名函数的时候,在决定要调用哪个函数的过程中,首先是按照参数类型进行匹配的,换句话说,寻找在所有重载版本中最匹配的,然后才看变量的动态类型,进行动态绑定。


❑ 父子类型转换;


类型转换有两个方向:


  • 将父类引用类型变量转换为子类类型,这种转换称为向下转型(downcast);


  • 将子类引用类型变量转换为父类类型,这种转换称为向上转型(upcast)。向下转型需要强制转换,而向上转型是自动的


将父类引用赋值给子类变量时要进行强制转换,强制转换在编译时总是认可的,但运行时的情况取决于对象的值.如果父类对象引用指向的就是该子类的一个对象,则转换是成功的。否则会抛出ClassCastException。如果不能确定实例是哪一种类型,可以在转型之前使用 instanceof 运算符进行判断。


因此并不是所有的引用类型都能互相转换,只有属于同一棵继承层次树中的引用类型才可以转换。


❑ 可见性重写;


重写方法时,一般并不会修改方法的可见性。但我们还是要说明一点,重写时,子类方法不能降低父类方法的可见性。


为什么要这样规定呢?继承反映的是“is-a”的关系,即子类对象也属于父类,子类必须支持父类所有对外的行为,将可见性降低就会减少子类对外的行为,从而破坏“is-a”的关系,但子类可以增加父类的行为,所以提升可见性是没有问题的。


举例:

// 父类
Object lalala(Integer x) throws IllegalArgumentException {
// 子类
@Override
Integer lalala(Integer x);


子类只能抛出范围内或者更小的异常,返回类型也只能更小,但是方法的可见性可以更大。虽然重写方法时,一般并不会修改方法的签名。


❑ 防止继承(final)


final关键字可以修饰变量,也可修饰final 使成为最终类。


继承是把双刃剑


继承破坏封装什么是封装呢?封装就是隐藏实现细节,提供简化接口。使用者只需要关注怎么用,而不需要关注内部是怎么实现的。实现细节可以随时修改,而不影响使用者。


继承可能破坏封装是因为子类和父类之间可能存在着实现细节的依赖。子类在继承父类的时候,往往不得不关注父类的实现细节,而父类在修改其内部实现的时候,如果不考虑子类,也往往会影响到子类。


继承既强大又有破坏性,那怎么办呢?1)避免使用继承;2)正确使用继承。


怎么避免继承的有三种方法:


❑ 使用final关键字;


❑ 优先使用组合而非继承;


❑ 使用接口。


使用组合,子类就不需要关注基类是如何实现的了,基类修改实现细节,增加公开方法,也不会影响到子类了。


多态性


父类或者接口的引用指向或者接收自己的子类对象叫多态 。实际执行调用的是子类实现,这叫动态绑定。


作用:多态的存在提高了程序的扩展性和后期可维护性.


发生多态要有三个前提条件:


  1. 继承。多态发生一定要子类和父类之间。


  1. 覆盖。子类覆盖了父类的方法。


  1. 声明的变量类型是父类类型,但实例则指向子类实例。


UML图简介



UML是 Unified Modeling Language 的缩写,即统一标准建模语言。它集成了各种优秀的建模方法学发展而来的。UML 图常用的有例图、协作图、活动图、序列图、部署图、构件图、类图、状态图。


面向对象分析与设计(OOAD)时,会用到 UML 图,其中类图非常重要,用来描述系统静态结构。Student 继承 Person的类图如图所示。类图中的各个元素说明如图所示,类用矩形表示,一般分为上、中、下三个部分,上部分是类名,中部分是成员变量,下部分是成员方法。实线+空心箭头表示继承关系,箭头指向父类,箭头末端是子类。UML 类图中还有很多关系,如图所示,如图虚线+空心箭头表示实线关系,箭头指向接口,箭头末端是实线类。


image.png


类图中的元素


参考


  • 丁振凡编著,《Java 语言程序设计(第2版)》华东交大版,2014.9





目录
相关文章
|
3月前
|
Java 程序员
Java中的继承和多态:理解面向对象编程的核心概念
【8月更文挑战第22天】在Java的世界中,继承和多态不仅仅是编程技巧,它们是构建可维护、可扩展软件架构的基石。通过本文,我们将深入探讨这两个概念,并揭示它们如何共同作用于面向对象编程(OOP)的实践之中。你将了解继承如何简化代码重用,以及多态如何为程序提供灵活性和扩展性。让我们启程,探索Java语言中这些强大特性的秘密。
|
22天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
16 3
|
22天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
29 2
|
22天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
28 2
|
22天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
28 1
|
2月前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
1月前
|
Java 测试技术 编译器
Java零基础-继承详解!
【10月更文挑战第4天】Java零基础教学篇,手把手实践教学!
34 2
|
1月前
|
Java 编译器
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
21 5
|
1月前
|
Java
java继承和多态详解
java继承和多态详解
42 5
|
1月前
|
Java 编译器
【一步一步了解Java系列】:子类继承以及代码块的初始化
【一步一步了解Java系列】:子类继承以及代码块的初始化
23 3