- 本节学习目标
- 1️⃣ 继承性
-
- 1.1 继承的限制
- 2️⃣ 覆写
-
- 2.1 方法的覆写
- 2.2 属性的覆盖
- 2.3 关键字 this与 super的区别
- 3️⃣ 继承案例
-
- 3.1 开发数组的父类
- 3.2 开发排序类
- 3.3 开发反转类
- * 总结
- 掌握继承性的主要作用、实现、使用限制;
- 掌握方法覆写的操作;
继承是面向对象编程的第二个主要特性,它解决了代码重用的问题。通过继承,我们可以从现有类派生出新的子类,并且还可以在子类中添加额外的操作功能。
在我们详细讲解继承这一特性之前,通过按照之前所学知识对类和 类的开发,我们可以进一步分析一个问题。
通过以上两段代码的比较,相信大家可以清楚地发现,如果按照之前学习的概念进行开发,程序中会出现大量重复代码。通过分析发现,从现实生活角度来讲,学生本来就属于人,但是学生所表示的范围要比人表示的范围更小,也更加具体。所以要想解决类似问题,只能依靠面向对象中的继承概念来完成。
继承性严格来讲就是指扩充一个类已有的功能。在Java 中,要实现继承关系可以使用如下语法:
对于继承的格式有以下说明:
- 对于extends 而言,应该翻译为扩充,但是为了理解方便,统一将其称为继承;
- 子类又被称为派生类;
- 父类又被称为超类 (Super Class)。
程序运行结果:
通过上边范例代码可以发现,子类() 并没有定义任何操作,而在主类中使用的全部操作都是由 类定义的,因此可以证明,子类即使不扩充父类,也属于维持功能的状态。
程序运行结果:
在此程序代码中,子类对父类的功能进行了扩充(扩充了一个属性和两个方法,如下图所示)。子类从外表上看是扩充了父类的功能,但是对于此程序的代码,子类还有一个特点,即子类实际上是将父类定义得更加具体化的一种手段。 父类表示的范围大,而子类表示的范围小。
虽然继承可以进行类功能的扩充,但是其在定义的时候也会存在若干种操作的限制。
限制一:Java 不允许多继承,但是允许多层继承。
在C++ 语言中具备一种概念——多继承,即一个子类可以同时继承多个父类。但是在Java 中是不允许存在多继承的。
此程序编写的目的是希望 类可以同时继承 和 两个类的操作,但是在Java 中是不允许存在多继承的,所以这样的代码在编译时不能通过。而之所以会存在多继承的概念实际上是因为希望一个子类可以同时继承多个父类的操作,而Java中存在接口、内部类等多种语法形式来支持,内部类前面已经介绍过,接口将在后面文章中介绍。
不允许多继承实际上也就代表着单继承的局限,这一操作是与 语言彼此相反的部分, 允许多重继承。但是从面向对象、从现实的角度讲,一个人只能有一个父亲,这应该属于单继承的概念,而不能说一个人有多个亲生父亲,否则就违背逻辑了。
实际上属于(孙)子类,这样一来就相当于 类继承了 类的全部方法,而 类又继承了 和 类的方法,这种操作称为多层继承。所以 Java 中只允许多层继承,不允许多重继承, 也即Java 存在单继承局限。
需要注意的是,虽然 Java语言从自身讲并没有继承层数的限定,但从实际的开发角度讲,类之间的继承关系最好不要超过三层。也就是说开发人员所编写的代码如果出现了继承关系,三层就够了,如果太多层的继承关系会比较复杂。
限制二: 子类在继承父类时,严格来讲会继承父类中的全部操作,但是对于所有的私有操作属于隐式继承,而所有的非私有操作属于显式继承。
程序执行结果:
限制三: 在子类对象构造前一定会默认调用父类的构造(默认使用无参构造),以保证父类的对象先实例化,子类对象后实例化。
程序执行结果:
本程度虽然实例化的是子类对象,但是发现它会默认先执行父类构造,调用父类构造的方法体执行,再实例化子类对象并且调用子类的构造方法。而这时,对于子类的构造而言,就相当于隐含了 的语句调用,由于 “” 主要是调用父类的构造方法,所以必须放在子类构造方法的首行。
从此程序中可以发现,当父类中提供有无参构造方法时,是否编写 “” 没有区别。但是如果父类中没有无参构造方法,则必须明确地使用 调用父类指定参数的构造方法。
程序执行结果:
此程序在父类中由于没有提供无参构造方法,所以在子类中就必须明确地使用 调用指定参数的构造方法,否则将出现语法错误。
下面我们思考一个问题,既然子类默认会自动调用父类构造方法,那么有没有可能性,不让子类去调用父类构造呢?
既然 和 都是调用构造方法,而且都要放在构造方法的首行,那么如果 ""出现了,则默认的""应该就不会出现,于是编写如下案例程序。
运行发现此程序有编译错误。
在本程序中,子类 的每一个构造方法,都使用了 调用本类构造方法,这样就表示子类无法调用父类构造。而在之前讲解 关键字时强调过:如果一个类中有多个构造方法之间使用 互相调用,那么至少要保留一个构造方法作为出口,而这个出口一定会去调用父类构造 。
从逻辑上说,我们每一个人都一定会有自己的父母,在正常情况下,父母一定都要比我们先出生。在程序中,实例化就表示对象的出生,所以子类出生前(实例化前)父类对象一定要先出生(默认调用父类构造,实例化父类对象)。也就是说,子类会默认调用父类的无参构造函数。如果父类没有无参构造函数,子类会无法正常实例化。
此时在某种程度上讲,有一个问题解释了一半—— 一个简单Java类为何一定要保留一个无参构造方法。而关于此问题的另外一部分解释在讲解完反射机制之后就可以得到答案。
继承性的主要特征是子类可以根据父类已有的功能进行功能的扩展,但是在子类定义属性或方法时,有可能出现定义的属性或方法与父类同名的情况,这样的操作就称为覆写。
当子类定义了和父类的方法名称、返回值类型、参数类型及个数完全相同的方法时,就称为方法的覆写。为了更好地帮助大家理解方法覆写的意义,下面编写两个案例程序进行说明。
程序执行结果:
此程序在定义子类 时没有定义任何方法,所以在主方法中利用 类的实例化对象调用的 方法是通过 类继承而来的。
程序执行结果:
此程序在 类中定义了一个与 类完全一样的 方法,所以当实例化 子类对象,调用 方法时,将不再执行父类的方法,而是直接调用已经被子类覆写过的方法。
一个类可能会产生多个子类,每个子类都可能会覆写父类中的方法,这样一个方法就会根据不同的子类有不同的实现效果。
程序执行结果:
此程序为 类定义了两个子类: 类 和 类,并且都覆写了 类中的 方法,当实例化各自类对象并且调用 方法时,调用的一定是被覆写过的方法。
对于方法的覆写操作,记住一个原则:如果子类覆写了方法(如类中的 方法,或者可能有更多的子类也覆写了方法),并且实例化了子类对象()时,调用的一定是被覆写过的方法。
简单地讲,就是要注意以下覆写代码执行结果的分析要素。
(1)观察实例化的是哪个类;
(2)观察这个实例化的类里面调用的方法是否已经被覆写过,如果没被覆写过则调用父类的方法。
这一原则也与后续的对象多态**息相关,所以大家应该认真领悟。
需要注意的是,在覆写的过程中必须考虑到权限问题, 即:被子类所覆写的方法不能拥有比父类更严格的访问控制权限。
对于访问控制权限实际上我们已经接触了解过3种了,这3种权限由大到小的顺序是: > (默认,什么都不写) > , 也就是说 的访问权限是最严格的(只能被本类访问)。即如果父类的方法使用的是 声明,那么子类覆写此方法时只能是 ; 如果父类的方法是 (默认),那么子类覆写方法时候只能使用 或 。
Java中一共分为4种访问权限,对于这些访问权限,后面文章中还会详细介绍。而且在实际的开发中,绝大多数情况下都使用 定义方法。
程序执行结果:
此程序的子类奉行着方法覆写的严格标准,父类中的 方法使用 访问权限,而子类中的 方法并没有将权限变得更加严格,依然使用 ( 这也符合绝大多数方法都使用 定义的原则)。
此程序 类中的 方法使用了 权限声明,所以子类如果要覆写 方法只能使用 或 权限,在本程序中子类使用了 权限进行覆写。
此程序子类中的 方法使用了 权限,相对于父类中的 权限更加严格,所以无法进行覆写。
需要注意的是,如果父类中方法使用了声明,则因 表示是私有的,既然是私有的就无法被外部看见,所以子类是不能覆写的。下面抛开 权限不看,先编写一个正常的覆写操作。
程序执行结果:
此程序子类成功覆写了父类中的 方法,所以当利用子类对象调用 方法 时,里面调用的 为子类覆写过的方法。下面将以上代码修改,使用 声明父类中的 方法。
程序执行结果:
从概念上来讲,本程序子类符合覆写父类方法的要求。但是从本质上讲,由于父类 方法使用的是 权限,所以此方法不能够被子类覆写,即对子类而言,就相当于定义了一个新的 方法,而这一方法与父类方法无关。
大部分情况下开发者并不需要过多地关注 的限制,因为在实际的开发中,主要用于声明属性,而主要用于声明方法。
一旦有了覆写后会发现,默认情况下子类所能调用的一定是被覆写过的方法。为了能够明确地由子类调用父类中已经被覆写的方法,可以使用 来进行访问。
程序执行结果:
此程序在子类的 方法里编写了"" 语句,表示在执行子类 方法时会先调用父类中的 方法,同时利用“”的形式主要是子类调用父类指定方法的操作, 可以放在子类方法的任意位置。
如果子类定义了和父类完全相同的属性名称时,就称为属性的覆盖。
程序执行结果:
此程序在子类中定义了一个与父类同名的 属性,这样就发生了属性的覆盖,所以在子类中直接访问 属性时 ()会自动找到被覆盖的属性内容,也可以使用 “”的形式调用父类中的指定属性 ()。
需要注意的是,属性覆盖的在实际开发中没有意义。因为在任何开发中,类中的属性必须使用 封装,那么一旦封装后属性覆盖是没有任何意义的,因为父类定义的私有属性子类根本就看不见,更不会互相影响了。
通过前面文章以及本文里的一系列介绍可以发现,与 在使用形式上很相似,下面进行两个关键字的操作对比。
- 功能上,this 调用本类构造、本类方法、本类属性,super用于子类调用父类构造、父类方法、父类属性;
- 形式上,this 先查找本类中是否存在有指定的调用结构,如果有则直接调用,如果没有则调用父类定义。super则不查找子类,直接调用父类操作;
- 特殊的是,this 还直接表示了本类的当前对象。
在开发中,对于本类或父类中的操作,强烈建议加上"“或者是"” 的标记,这样的代码更加清晰、便于维护。
现在要求定义一个整型数组的操作类,数组的大小由外部决定,用户可以向数组中增加数据,以及 取得数组中的全部数据。随后在原本的数组上扩充指定的容量,另外,在此类上派生以下两个子类。
(1)排序类:取得的数组内容是经过排序出来的结果;
(2)反转类:取得的数组内容是反转出来的结果。
分析:本程序要求数组实现动态的内存分配,也就是说里面的数组的大小是由程序外部决定的,在本类的构造方法中应该为类中的数组进行初始化操作,之后每次增加数据时都应该判断数组的内容是否已经是满的,如果不满则可以向里面增加,如果满则不能增加。另外如果要增加数据时肯定需要有一个指向可以插入的下标,用于记录插入的位置,如下图所示。
在进行此程序开发的过程中,首先要完成的是定义父类,此时不需要考虑子类的实现问题。
要求定义一个数组操作类 ( 类),在这个类里面可以进行整型数组的操作,由外部传入类可以操作数组的大小,并且要求实现数据的保存以及数据的输出操作。
程序执行结果:
此程序首先定义了一个专门操作数组的 类,然后在这个类对象实例化时必须传入要设置的数组大小,如果设置的数组大小小于等于, 则数组默认开辟一个空间的大小,即保证数组至少可以保存一个数据。最后使用 方法向数组中保存数据,如果此时数组有空余空间,则可以进行保存,返回 , 如果没有空间则不能够保存,返回 。如果需要取出数组中全部保存的数据时,可以调用 方法取得。
范例21的程序实际上只能算是一个最为基础的实现,因为本程序编写的目的并不是让大家去使用,而是要通过程序的设计帮助大家更好的理解继承。此程序的缺陷在于数据删除时的复杂处理,可以试想一下,如果数组有个元素,但是现在要将索引为、、的数据删除,就表示数组有空余空间,这个时候应该可以继续增加数据才对,但事实上这样的操作是非常复杂的。所以通过程序,也再一次提醒大家,数组的缺陷就在于长度固定,而这个问题可以利用链表或者后面文章将会介绍到的类集框架来解决。
对于上边范例 21的代码大家需要注意一个问题,主方法中进行数组操作时只使用了 和 两个方法,而为了保持一致,即使 类定义了其他子类(排序子类、反转子类), 也应该使用这两个方法增加、取得数据。类结构如下图所示。
排序的数组类只需要在进行数据取得时返回排序好的数据即可,而数据取得的操作方法是 , 也就是说如果要定义排序子类,只需要覆写父类中的 方法即可。
程序执行结果:
此程序实例化的是 子类,这样在使用 方法 (方法名称为父类定义,子类扩充) 时返回的数据就是排序后的数组,而整个程序的操作过程中,除了替换一个类名称外,与正常使用 类没有区别。
反转类指的是在进行数组数据取得时,可以实现数据的首尾交换。在本类中依然需要针对父类的 方法进行覆写。
程序执行结果:
此程序在定义 类时依然保存了 类的 方法,并且在 类中的 方法里进行了数组数据的反转操作。
在本文中,我们深入探讨了Java中继承的概念以及与之相关的一些重要概念。我们首先了解了继承的基本原理,并学习了如何通过创建子类来实现继承。然后,我们详细介绍了继承的限制条件,包括单继承、构造方法和私有成员的访问限制等。
接下来,我们研究了覆写的概念,在子类中重新定义父类的方法或属性,从而改变其行为或值。我们了解到,方法覆写和属性覆盖是实现多态性的重要手段,能够提供灵活和可扩展的代码结构。
我们还特别介绍了关键字的使用,它使得在子类中能够访问父类的成员,尤其是当子类成员与父类成员同名时。通过关键字,我们可以调用父类的构造方法、访问父类的成员变量和方法,实现更加灵活的继承结构。
最后,我们通过一个继承案例进一步巩固了所学知识。通过这个案例,我们具体了解了如何创建继承关系,在子类中进行方法覆写和属性覆盖,以及如何利用关键字来访问父类成员。
继承是面向对象编程中非常重要的概念,它提供了代码重用、可扩展性和多态性等好处。但是,在应用继承时,我们需要注意其限制条件,并正确地使用覆写和关键字。通过深入学习和实践,我们可以充分发挥继承的优势,构建更加强大和灵活的程序结构。
[ ]nbsp_nbsp 4
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/java-jiao-cheng/9297.html