目录
继承的好处
继承是面向对象的三大特征之一。继承可以通过共性抽取(多个类中相同的代码),降低代码编写的冗余度,提高代码的复用性,提高编程的效率。
如何理解继承关系
一般情况下,我们将相同类型的“类”中的共同属性提取出来,作为这几个类的“父类”,共同属性只需在父类中体现,这些被提取出共性代码的类叫做“子类”,子类可以通过关键字extends(继承),来使用父类中的成员变量,成员方法等等。
举个例子,学校中有很多学生,他们拥有一些共同的属性,如:姓名,年龄,学号,性别等等。
继承的特点
一个子类中年有一个直接父类——Java是单继承
一个baby只能有一个爸比。
Jave 是多级继承
子类有父类,父类也可以有父类,子类继承父类时也会继承父类的父类。
baby有爸比,爸比也有爸比,baby也继承爷耶。
一个父类可以有多个子类
一个爸比可以有多个
如果一个类没有显示的继承父类,就默认继承Object(java.lang包下的顶级父类) ,没有写相当于省略extends Object。
注意:父类不能使用子类的成员信息
继承关系的书写格式
权限修饰符的使用
注意,父类的成员变量如果想让其子类能够直接使用,需将成员变量的修饰符更改为非private的权限修饰符,但我们又不想让其他类能够直接访问这些成员信息,因而我们使用protected。(附一张权限修饰符使用情况说明图)
根据上述所举例子,我们可以编写一个父类Student来写这些内容,为了方便后续创建对象与修改信息,我们提前编写好get、set方法以及构造器,并且书写一个简单地成员方法:学习。
public class Student {
//学生姓名
protected String stuName;
//学生年龄
protected int stuAge;
//学生学号
protected String stuId;
//学生性别
protected char stuGender;
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
public String getStuId() {
return stuId;
}
public void setStuId(String stuId) {
this.stuId = stuId;
}
public char getStuGender() {
return stuGender;
}
public void setStuGender(char stuGender) {
this.stuGender = stuGender;
}
//无参构造
public Student() {
super();
}
//有参构造
public Student(String stuName, int stuAge, String stuId, char stuGender) {
super();
this.stuName = stuName;
this.stuAge = stuAge;
this.stuId = stuId;
this.stuGender = stuGender;
}
//成员方法
public void study() {
System.out.println("我爱学习,皮肤好好!");
}
}
这里的super()是什么意思我们稍后解释。
实现继承(子类的书写格式):
public class 子类名称 extends(扩展) 父类名称{
//代码块
}
我们现在可以想想有什么类可以继承Student父类呢?子类是在父类的基础上有一些自己特有的属性及方法。那么我们想到,学生干部是学生中较为特殊的群体,他们特有的成员变量有职务名称,而独有的方法有工作内容。
那么我们就来定义一个学生干部类.
public class StuLeader extends Student{
//学生职务
private String job;
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
//无参构造
public StuLeader() {
super();
}
}
当我们使用Alt+Shift+S快捷键来创建构造方法时,可通过下面的方式来创建可以传入父类参数的有参构造。
效果如下:
public StuLeader(String stuName, int stuAge, String stuId, char stuGender, String job) {
super(stuName, stuAge, stuId, stuGender);
this.job = job;
}
我们发现,super字样又出现了!
super关键字
其实父类与子类都有多个别称:
父类(parent class) = 超类(super class) = 基类(base class)
子类(subclass) = 扩展类(extended class)
因此,super就表示父类的意思。
super关键字的三种用法
①super.父类的成员变量
//仅在父类中定义了变量a
public class Fu{
public int a = 10;
}
此时输出a、this.a、super.a的结果都为10。(this在后面会说到)
//父类
public class Fu{
public int a = 10;
}
//子类
public class Zi extends Fu{
public int a = 20;
}
此时输出a与输出this.a的结果都为20,输出super.a的结果为10。我们可以看做在变量名相同的情况下,子类的优先级高于父类,因为父类需要特别说明。
//父类
public class Fu{
public int a = 10;
}
//子类
public class Zi extends Fu{
public int a = 20;
//子类的成员方法中
public void method(){
int a = 30;
}
}
此时输出a的结果为30,输出this.a的结果为20,输出super.a的结果为10。我们可以看做局部变量的优先级>子类成员变量 > 父类成员变量。
②super.父类的成员方法
当子类中没有与父类重名(被重载/重写)的方法时,method()=super.method()。当然,前提是这些方法的权限修饰符是你现在所处位置可以访问到的。
③super(参数)
这是调用父类有参构造方法的格式,但是前提是父类中必须有有参构造,并且这种形式只能在子类的构造方法中书写。上文实例代码中就是这种结构。
public StuLeader(String stuName, int stuAge, String stuId, char stuGender, String job) {
super(stuName, stuAge, stuId, stuGender);
this.job = job;
}
this关键字的三种用法
this表示当前类型对象,谁调用该方法谁是this。
this.本类成员变量
this.本类成员方法
this(可有参数) 调用本类的其他构造方法
注意:
①必须写在构造方法中
②必须是第一个语句,因此super()、this()不能同时出现
③写了this()不再赠送super(),但是也会调用父类的构造方法,因为调用的那个本类其他构造方法中一定有super(),无论是否显示的写,绝对不会发生用不调用父类的情况。
//下面写的是子类中的两个构造方法
//无参构造
public Zi(){
this(10);
}
//有参构造
public Zi(int a){
this();
}
构造方法不能递归调用,上面的这些代码会报错。
区分子类方法中同名的三个变量
局部变量 | 直接使用 |
子类成员变量 | this |
父类成员变量 | super |
继承关系下成员信息的访问特点
主要从访问成员变量、成员方法、构造方法三个方面叙述。
继承关系下成员变量的访问特点
语法上子类中可以有相同的成员变量(但是不建议这么写,第一可能会造成代码冗余,第二可能无法准确访问到你想要的数据,如上述内容中讲的局部变量的优先级>子类成员变量 > 父类成员变量),使用子类.变量会访问到子类的成员变量。
在多态中也会运用到的口诀是:
等号左边是谁,访问的就是谁。
等号左边的一般为对象,用什么类创建什么对象就能调用谁的成员变量。
继承关系下成员方法的访问特点
在多态中也会运用到的口诀是:
new的是谁的,访问的就是谁的
继承关系下构造方法的访问特点
- 在子类的构造方法中,如果没有显示的写super(),默认给一个super(),这表示调用父类无参的构造方法
- 如果显示的写一个super(),并且必须是第一个语句(;为一个语句的结束),则不再赠送(再赠送的那一个与自己写的有冲突,有一个super不是第一个语句,会报错)
- super(参数),这里的参数要写实参,表示调用父类有参构造方法,并且父类中必须有有参构造方法
在创建子类对象的时候,一定会调用父类的构造方法:
调用无参构造的情况:不写super()/ 写super()
调用有参构造的情况:写super(参数)
重写与重载
这其实是两个完全不同的概念,重写的前提是在继承关系或者接口与实现类关系,而重载是在同一个类中。
什么是覆盖重写
在继承、实现类关系下,方法名相同,参数项相同,子类的修饰符不能比父类的修饰符更严格(当父类是public时,子类只能是public),返回值相同(或者子类的返回值是父类返回值的子类,如String是Object的子类)。
存在的原因:
父类提供的方法可以直接使用,当功能不完善或者与你需要的功能完全不一致时可以重新这个方法。
@Override注释:检查是否为正确的覆盖重写
什么是重载
在同一个类中,方法名相同,参数项不同(个数、顺序、类型)的方法,与返回值无关,返回值不同也可以是重载。
调用时,由方法名、参数项共同确定一个方法。
好处是方便调用,同种类型的方法可以对不同数据做相同的操作。
如:add方法可以处理两个整数,可以处理两个浮点数,可以处理一个整数和一个浮点数。这避免我们需要记很多名字不同但是功能相同的方法。