![名师讲坛:Java开发实战经典(第2版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/824/26793824/b_26793824.jpg)
6.2 继承的进一步研究
掌握了继承的基本概念及实现之后,下面将针对继承操作中的一些注意点做进一步的研究。
6.2.1 子类对象的实例化过程
在继承的操作中,对于子类对象的实例化也是有要求的,即子类对象在实例化之前必须首先调用父类中的构造方法之后再调用子类自己的构造方法,子类对象实现化过程如图6-7所示。
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P165_21762.jpg?sign=1738828911-gNXs89S8OdeMLSHHzbD5aYpdqmO0ridC-0-f54317e7a76d2b382c324b944b058584)
图6-7 子类对象实例化过程
提示
子类对象的实例化过程与生活是很类似的。
在实际生活中肯定是要先有父母之后才能有孩子,孩子不可能凭空“蹦”出来,那么对于程序也是一样的,之所以会调用父类中的构造方法,就是要用父类的构造方法为父类中的属性初始化,就表示先有父类实例,然后才能产生子类实例。
【例6.7】观察子类对象的实例化过程
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P165_124321.jpg?sign=1738828911-Mmi0rzfZlSkax9Pmc6d0XwXJaQhh6AF1-0-2a680d156ed0c8ee594ded5be6a3f02b)
程序执行结果:
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P166_124323.jpg?sign=1738828911-ZmGQ4ygN6Gib59oo0DbQAvvke0f5QMwl-0-3bc3e0e84f7dcbb692a0fdad54416d9a)
从结果中可以清楚地发现,子类对象在实例化之前会先默认调用父类中的构造方法。就好像如果没有父亲就没有孩子,所以在实例化子类对象之前需要先将父类中的属性进行初始化。当然,对于以上的代码实际在子类的构造方法中隐含了一个super()的语法,代码如下所示。
【例6.8】子类的构造方法中的super()
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P166_124324.jpg?sign=1738828911-VS5UbgV1VPINRrxGIaqsGbOkECsRly8H-0-f74fce8cb95363cac1e1019b876d9384)
以上程序的运行结果与之前一样。super表示超级的意思,在一些书中也喜欢把父类称为超类,而上面的语法就是表示子类可以直接使用super()调用父类中的无参构造。
6.2.2 方法的覆写
在继承的关系中也存在着方法覆写的概念,所谓的方法覆写就是指子类定义了与父类中同名的方法,但是在方法覆写时必须考虑到权限,即被子类覆写的方法不能拥有比父类方法更加严格的访问权限。
提示
已学习过的3种访问权限。
关于访问权限,实际上前几章读者已经见过3种访问权限了,即private、default、public,这3种访问权限的具体作用本书后面会有介绍,读者现在只需要记住大小关系即可,即private<default<public。
所以,如果在父类中使用public定义的方法,子类的访问权限必须是public,否则程序会无法编译。
【例6.9】方法的覆写
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P167_124328.jpg?sign=1738828911-IehXiD25zIoSKxi1aLF6Vmk08lL5MOKy-0-064cacf68ed1c699d2847236f8e4b53d)
程序执行结果:
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P167_124329.jpg?sign=1738828911-qziBl6g06WMnoexdI4GQClDREQ7XF4ZO-0-cf36ffbf476c37bfdfdebb3013ef6f37)
在以上的程序中,Student子类定义了与Person父类中同名的方法,但是在子类中此方法的访问权限被扩大了,符合覆写的概念,当方法被覆写之后,子类对象调用的将是被覆写后的方法。
另外,要提醒读者的是,如果现在被子类覆写的方法权限缩小的话,则在编译时将出现错误提示。
【例6.10】错误的方法覆写
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P168_124330.jpg?sign=1738828911-CEv3M7bjwrbBwyGRctYD6Nttdi1yCntw-0-540bce00f87c873f268a3b2e20993425)
程序编译结果:
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P168_124331.jpg?sign=1738828911-zL35l8oBPdXNZUpkB5jZwrXoiI7LZdBM-0-0837cf25c5120ed9b0a2cc801f2534be)
以上的错误提示指的就是正在指定更小的访问权限,所以编译是无法通过的。
如果现在要在子类的方法中访问父类的方法,则使用super关键字即可,代码如下所示。
【例6.11】调用父类中被子类覆写过的方法
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P168_124332.jpg?sign=1738828911-KdCyjc5oAQEelEKpMXM0o5UPirDVlYWP-0-2eec68f91d391738cd338d4f73869928)
程序执行结果:
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P168_124333.jpg?sign=1738828911-P2NHA2Ja6xc8qF0jCH26XBaKzXXDdFkF-0-2eebd627f45098e3f6a366d0166fb05a)
从程序中可以清楚地发现,在子类中直接通过“super.方法()”的形式就可以访问父类中的相关方法。
说明
提问:方法覆写时从private变为default是方法覆写吗?
如果现在将父类的一个方法定义成private访问权限,在子类中将此方法声明为default访问权限,那么这样还是覆写吗?
回答:不算是方法覆写。
在解释这个问题之前,请先看一下以下的程序代码。
实例:方法覆写
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P169_124337.jpg?sign=1738828911-DP3O4sR0PF63TXCu3CH3nObcEAu3CNbR-0-ece87f12b9d579e0ee369318675b4062)
程序执行结果:
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P169_124339.jpg?sign=1738828911-XCPiHkazg1hPhNKFBD3kgdqkuzIzybnJ-0-a440cbf995285bb9896a7c773923d445)
从程序运行结果可以清楚的发现,现在调用的方法是父类中的方法,也就是说此时子类并没有覆写父类中的方法,而是在子类中重新定义了一个新的方法,所以此时方法没有被覆写。
实际上与方法覆写概念相同的还有另外一个称为属性的覆盖,这一点在开发中使用较少,下面为读者简单介绍一下属性的覆盖操作。
【例6.12】属性的覆盖
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P169_124340.jpg?sign=1738828911-z3EW7QHWfZlfMeo0hT8VwQthuRriYuu0-0-fd09995e091d78b6a0dbcee09ee8abbe)
程序执行结果:
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P170_124342.jpg?sign=1738828911-F3LKWvTMf77MSBZqtnF4z8VBgabP4qks-0-6a75336d28041beba23c4474959e6aee)
以上的程序只作为参考使用,读者只需要了解:子类如果和父类声明了相同名称的属性,则在子类中直接访问的时候肯定是采用“就近访问原则”,即先找到本类中的属性,如果此时要调用父类中的属性,直接使用“super.属性”格式即可。
实际上,方法的重载与覆写是非常类似的,表6-1逐步剖析了两者的区别。
表6-1 方法的重载与覆写的区别
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-T170_124344.jpg?sign=1738828911-6aLRNjemCzN3ilnXS0hHa9fheRsvskiT-0-250e50c7e72b7b2b59d5fd74852b9114)
6.2.3 super关键字的作用
在以上的程序中一直都出现了super关键字,使用super可以从子类中调用父类中的构造方法、普通方法、属性。之前已经为读者演示过了调用普通方法和属性的基本操作,下面再来看一下使用super调用父类中指定构造方法的操作,与this调用构造方法的要求一样,语句必须放在子类构造方法的首行。
【例6.13】使用super调用父类中的指定构造方法
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P170_124345.jpg?sign=1738828911-y2wO8TX4MqPzfbn4AlYfPDbHwX0IBTdO-0-85766dc5dfae4a6e7d1ceb82891bb26e)
程序执行结果:
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-P171_124347.jpg?sign=1738828911-eX4KpRu3LvZDMwyKvqrb7ybi3FRQ19WQ-0-9d2d8d754cf9b0055fb9c81bae2707f6)
在上面程序的子类中使用super()的形式调用了父类中有两个参数的构造方法,然后在子类中又覆写了父类中的getInfo()方法,所以输出的内容是被子类覆写过的内容。
可见super与this的作用非常相似,都是可以调用构造、普通方法、属性的,那么两者之间到底有哪些区别呢?下面通过表6-2进行说明。
表6-2 this与super的区别
![](https://epubservercos.yuewen.com/DE8B7B/15253389304122106/epubprivate/OEBPS/Images/Figure-T171_124349.jpg?sign=1738828911-qGwSfkE2dy4d9xGZom69i8q4vVgJUCqI-0-37ad4b5b5c4c9512bc25e85ef62dfe46)
表6-2中详细地列出了两者的区别,但是在这里还有一个问题需要读者注意:既然this和super都可以调用构造方法,那么两者是不可以同时出现的,因为两者调用构造的时候都必须放在构造方法的首行,另外,需要注意的是,无论子类怎样操作,最终都是要先调父类中的构造方法。