Java开发笔记(四十九)关键字super的用法
前面介绍了如何从Bird类继承而来Swallow类,按道理子类应当继承父类的所有要素,但是对于构造方法来说,Swallow类仅仅继承了Bird类的默认构造方法,并未自动继承带参数的构造方法。如果子类想继续使用父类的其它构造方法,就得自己重写心仪的构造方法。例如老鹰属于鸟类,那么可以编写继承自Bird类的Eagle类,同时要在Eagle类内部重新定义拥有多个输入参数的构造方法,由此得到如下所示的Eagle类代码:
//定义了一个继承自鸟类的老鹰类
public class Eagle extends Bird { // 老鹰类重写了带三个参数的构造方法,则不使用没有输入参数的构造方法
public Eagle(String name, int sexType, String voice) {
// 利用super指代父类的构造方法名称
super(name, sexType, voice);
}
}
注意到如上代码用到了关键字super,它的字面意思是“超级的”,但并非说它是超人,而是用super指代父类的名称,所以这里“super(name, sexType, voice)”实际表达的是“Bird(name, sexType, voice)”,也就是依然利用了Bird类的同名且同参数的构造方法。外部若想创建Eagle类的实例,就要调用新定义的带三个参数的构造方法,此时创建实例的代码如下所示:
// 通过构造方法设置属性值
private static void setConstruct() {
// 调用Bird类带三个参数的构造方法
Bird cuckoo = new Bird("杜鹃", 1, "布谷");
System.out.println(cuckoo.toString());
// Eagle类重写了带三个参数的构造方法
Eagle eagle = new Eagle("鹰" , 0, "啁啁");
System.out.println(eagle.toString());
}
在类继承的场合,关键字super表示父类,对应的this表示本类。如同this的用法一般,super不但可用于构造方法,还可作为成员属性和成员方法的前缀,例如“super.属性名称”代表父类的属性,“super.方法名称”代表父类的方法。
在中文世界里,性别名称的“雄”和“雌”专用于野生动物,而家畜、家禽的性别应当采用“公”和“母”,比如公鸡、公牛、母鸭、母猪等等。前述的Bird类,默认的性别名称为“雄”和“雌”,显然并不适用于家禽。为此几种家禽从Bird类派生而来时,需要重新定义它们的性别名称属性,也就是重写setSexType方法,在该方法内部另行对sexName字段赋值。以鸭子类为例,重写方法后的类定义代码如下:
//定义了一个继承自鸟类的鸭子类
public class Duck extends Bird { // 定义一个家禽类的性别名称
private String sexName; public Duck(String name, int sex) {
// 利用super指代父类的构造方法名称
super(name, sex, "嘎嘎");
} public void setSexType(int sexType) {
// 方法内部再调用自身方法,会变成递归调用,如果没有退出机制就变成死循环了
//setSexType(sexType);
// 在方法前面添加前缀“super.”,表示这里调用的是父类的方法
super.setSexType(sexType);
// 修改家禽类的性别名称,此时父类和子类都有同名属性sexName,不加前缀的话默认为子类的属性
sexName = (sexType==0) ? "公" : "母";
//this.sexName = (sexType==0) ? "公" : "母";
} // 父类的getSexName方法需要重写,否则父类的方法会使用父类的属性
public String getSexName() {
return this.sexName;
} // 父类的toString方法需要重写,否则父类的方法会使用父类的属性
public String toString() {
String desc = String.format("这是一只%s%s,它会%3$s、%3$s地叫。",
this.sexName, getName(), getVoice());
return desc;
}
}
以上的Duck类代码,看起来颇有些奇特之处,且待下面细细道来:
1、由于Bird类的sexName属性为private类型,表示其为私有属性,不可被子类访问,因此Duck类另外定义自己的sexName属性,好让狸猫换太子。
2、重写后的setSexType方法,只有sexName属性才需额外设置,而sexType属性仍遵循父类的处理方式,故此时要调用父类的setSexType方法,即给该方法添加前缀“super.”。
3、因为Duck类重新定义了sexName属性,所以与sexName有关的方法都要重写,改为读写当前类的属性,否则父类的方法依旧操作父类的属性。
再来看一个以super修饰成员属性的例子,倘若Bird类的sexName属性为public类型,就意味着子类也可访问它,那么Duck类便能通过“super.sexName”操作该属性了。此时新定义的DuckPublic类代码就变成下面这样:
//演示同名的父类属性、子类属性、输入参数三者的优先级顺序
public class DuckPublic extends Bird { public DuckPublic(String name, int sex) {
super(name, sex, "嘎嘎");
} public void setSexType(int sexType) {
super.setSexType(sexType);
// 若想对父类的属性直接赋值,则考虑把父类的属性从private改为public
super.sexName = (sexType==0) ? "公" : "母";
// 父类和子类拥有同名属性,则不带前缀的属性字段默认为子类属性
//sexName = (sexType==0) ? "公" : "母";
//this.sexName = (sexType==0) ? "公" : "母";
} private String sexName; public void setSexName(String sexName) {
// 输入参数与类的属性同名,则不带前缀的参数字段默认为输入参数
this.sexName = sexName;
}
}
假设DuckPublic类也定义了同名属性,并且另外实现了setSexName方法,于是该类里面将会出现三个sexName,分别是:super.sexName表示父类的属性,this.sexName表示本类的属性,而setSexName内部的sexName表示输入参数。要是三者同时出现两个,必定有一个需要添加“super”或者“this”的前缀,不然编译器哪知同名字段是啥含义?或者说,假如有一个sexName未加任何前缀,那么编译器应该优先认定它是父类属性,还是优先认定它是本类属性,还是优先认定它是输入参数?对于这些可能产生字段名称混淆的场合,Java制定了下列的优先级判断规则:
1、方法内部存在同名的输入参数,则该字段名称默认代表输入参数;
2、方法内部不存在同名的输入参数,则该字段名称默认代表本类的成员属性;
3、方法内部不存在同名的输入参数,且本类也未重新定义同名的成员属性,则该字段名称只能代表父类的成员属性;
概括地说,对于同名的字段名称而言,其所表达含义的优先级顺序为:输入参数>本类属性>父类属性。
更多Java技术文章参见《Java开发笔记(序)章节目录》
Java开发笔记(四十九)关键字super的用法的更多相关文章
- 【Java学习笔记之十九】super在Java继承中的用法小结
1)有人写了个很好的初始化属性的构造函数,而你仅仅想要在其中添加另一些自己新建属性的初始化,这样在一个构造函数中调用另外一个构造函数,可以避免重复的代码量,减少工作量: 2)在一个构造函数中调用另外一 ...
- Java开发笔记(十九)规律变化的for循环
前面介绍while循环时,有个名叫year的整型变量频繁出现,并且它是控制循环进出的关键要素.不管哪一种while写法,都存在三处与year有关的操作,分别是“year = 0”.“year<l ...
- Java开发笔记(九十九)定时器与定时任务
前面介绍了线程的几种运行方式,不管哪种方式,一旦调用了线程实例的start方法,都会立即启动线程的事务处理.然而某些业务场景在事务执行时间方面有特殊需求,例如期望延迟若干时间之后才开始事务运行,又如期 ...
- Java开发笔记(十四)几种运算符的优先级顺序
到目前为止,我们已经学习了Java语言的好几种运算符,包括算术运算符.赋值运算符.逻辑运算符.关系运算符等基础运算符,并且在书写赋值语句时都没添加圆括号,显然是默认了先完成算术.逻辑.关系等运算,最后 ...
- Java学习笔记(十九)——Java 日志记录 AND log4j
[前面的话] 学习的进度应该稍微在快一点. Java日志到了必须学习怎么使用的时候了,因为在项目中要进行使用.基础性文章,选择性阅读. [结构] java日志对调试,记录运行,问题定位都起到了很重要的 ...
- Java开发笔记(十八)上下求索的while循环
循环是流程控制的又一重要结构,“白天-黑夜-白天-黑夜”属于时间上的循环,古人“年复一年.日复一日”的“日出而作.日落而息”便是每天周而复始的生活.计算机程序处理循环结构时,给定一段每次都要执行的代码 ...
- .Net开发笔记(十九) 创建一个可以可视化设计的对象
阅读本篇博客之前需要了解VS窗体设计器的工作原理,详细可参见本系列博客(十).(十一).(十二).必须需要知道的一条结论就是:处于窗体设计器(Form Designer)中的任何组件(包含控件,下同) ...
- 安卓开发笔记(十九):异步消息处理机制实现更新软件UI
主界面代码 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andr ...
- 论文阅读笔记四十九:ScratchDet: Training Single-Shot Object Detectors from Scratch(CVPR2019)
论文原址:https://arxiv.org/abs/1810.08425 github:https://github.com/KimSoybean/ScratchDet 摘要 当前较为流行的检测算法 ...
- Java开发笔记(十)一元运算符的技巧
前面讲到赋值运算符的时候,提到“x = x+7”可以被“x += 7”所取代,当然Java编程中给某个变量自加7并不常见,常见的是给某变量自加1,就像走台阶,一般都是一级一级台阶地走,犯不着一下子跳上 ...
随机推荐
- react-native 组件整理
好早之前整理的部分组件,不全 怕丢
- RabbitMQ进程结构分析与性能调优
RabbitMQ是一个流行的开源消息队列系统,是AMQP(高级消息队列协议)标准的实现,由以高性能.健壮.可伸缩性出名的Erlang语言开发,并继承了这些优点.业界有较多项目使用RabbitMQ,包括 ...
- 动态规划----最长递增子序列问题(LIS)
题目: 输出最长递增子序列的长度,如输入 4 2 3 1 5 6,输出 4 (因为 2 3 5 6组成了最长递增子序列). 暴力破解法:这种方法很简单,两层for循环搞定,时间复杂度是O(N2). 动 ...
- Android OpenSL ES 开发:使用 OpenSL 播放 PCM 数据
OpenSL ES 是基于NDK也就是c语言的底层开发音频的公开API,通过使用它能够做到标准化, 高性能,低响应时间的音频功能实现方法. 这次是使用OpenSL ES来做一个音乐播放器,它能够播放m ...
- 【从零开始搭建自己的.NET Core Api框架】(六)泛型仓储的作用
系列目录 一. 创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...
- [SQL]LeetCode176. 第二高的薪水 | Second Highest Salary
Write a SQL query to get the second highest salary from the Employee table. +----+--------+ | Id | S ...
- [Swift]LeetCode801. 使序列递增的最小交换次数 | Minimum Swaps To Make Sequences Increasing
We have two integer sequences A and B of the same non-zero length. We are allowed to swap elements A ...
- 使用jQuery获取元素的宽度或高度的几种情况
今天说说使用jQuery获取元素大小的遇到几种情况 使用jQuery获取元素的宽度或高度的有几种情况: 1.使用width(),它只能获取当前元素的内容的宽度: 2.使用innerWidth(),它只 ...
- iOS学习——浅谈RunLoop
RunLoop的字面意思是运行循环.跑圈,一个App启动后能一直执行,就是因为启动后进入了一个循环,在这个循环中不断监听各种状态.手势动作,并做出相应的响应.这个循环就是我们今天要探究的RunLoop ...
- Java IO 导入导出TXT文件
字节流和字符流 区别: 读写单位:顾名思义,字节流以字节(byte)为读写单位,而字符流以字符为读写单位,根据码表映射字符,一次可能读入多个字符. 处理对象:字节流可以处理所有类型的数据(包括图片等) ...