目录
- 1、继承
- 1.1、继承的概念
- 1.2、继承的语法
- 2、子类访问父类成员
- 2.1、子类中访问父类的成员变量
- 2.2、子类中访问父类的成员方法
- 2.3、super 关键字
- 3、子类构造方法
1、继承
在 Java 中,类对现实中的实体进行描述,而类实例化的对象用来表示现实中的实体。但是现实世界的实体多种多样,它们之间必然会存在一定关系,那么在设计程序时就需要我们考虑。
上述是 Dog 和 Cat 类的成员信息,不难看出,Dog 和 Cat 类都有共同的成员变量和成员方法,且每次定义宠物,相同的字段和方法又会重复出现,这样会导致做很多重复的事情,代码的可读性也不高,此时我们可以将这些类的共性抽取出来,从而实现代码复用,即需要用到继承。
1.1、继承的概念
继承机制:是面向对象程序设计实现代码复用的重要手段,它允许程序员在保持原有特性的基础上进行扩展,增加新功能,这样产生的新的类称为派生类。继承呈现了面向对象程序的层次结构,体现了从简单到复杂的认知过程,继承主要解决的问题是抽取共性,实现代码复用,实现多态。
对上述Dog 和 Cat 类抽象并扩展:
如上图所示,抽取出来的 Animal 类称为父类/基类/超类,扩展产生的新类称为子类/派生类。
1.2、继承的语法
在 Java 中如果要表示类之间的继承关系,需要借助 extends 关键字:
修饰符 class 子类 extends 父类 {
//...
}
对上述的场景使用继承方式设计:
// 父类
public class Animal {
public String name;
public int age;
public float weight;
public void eat(){
System.out.println(name + "在吃饭~~~");
}
public void sleep(){
System.out.println(name + "在睡觉~~~");
}
}
// 子类
public class Cat extends Animal {
// 是对基类Animal进行扩增
public void mew(){
System.out.println(name + "喵喵喵~~~");
}
}
// 子类
public class Dog extends Animal {
// 是对基类Animal进行扩增
public void bark(){
System.out.println(name + "旺旺旺~~~");
}
}
注意:
- 子类会继承父类的成员变量和成员方法。
- 子类继承父类后,必须要添加自己特有的成员,否则就没有必要继承。
2、子类访问父类成员
在继承体系中,子类将父类中的方法和字段继承下来了,那么子类是否可以直接访问父类中继承下来的成员。
2.1、子类中访问父类的成员变量
- 子类和父类不存在同名成员变量,那么子类可以直接访问父类的成员变量。
- 子类和父类存在同名成员变量:
- 如果访问的成员变量子类中有,那么优先访问自己的成员变量,从父类继承下来的同名成员变量不能被直接访问,称为同名隐藏。
- 如果访问的成员变量子类中无,则访问父类继承下来的,如果父类中也没有,则编译报错。
2.2、子类中访问父类的成员方法
- 子类和父类不存在同名成员方法,那么在子类方法或者通过子类对象访问方法时,则优先访问自己的,自己没有再去父类中找,如果父类也没有则编译报错。
- 子类和父类存在同名成员方法:
- 访问子类和父类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
- 访问子类和父类中同名方法时,如果子类和父类的同名方法实现了重写,即只有方法体不同,其他都相同时,优先调用子类的方法,无法直接调用到父类的方法。
- 访问子类和父类中同名方法时,如果子类和父类的同名方法实现了重载,即只有参数列表和方法体不同,其他都相同时,根据传递的实参的类型来确定调用的是父类还是子类的方法。
那么当子类和父类的成员变量名相同,如何直接访问到父类的成员变量呢?可以在父类中提供对应的方法来操作,也可以使用 super 关键字。当子类和父类的成员方法实现了重写时,如何直接访问到父类的成员方法呢?需要用到 super 关键字。
2.3、super 关键字
super 关键字的作用主要是在子类方法中访问父类的成员。
当子类和父类的成员变量名相同,如果直接访问该成员变量,那么优先访问到的是子类中的成员变量,但是利用 super.成员变量名 则访问到的是父类中的成员变量。
当子类和父类的成员方法构成重写,如果直接访问该成员方法,那么优先访问到的是子类中的成员方法,但是利用 super.成员方法名 则访问到的是父类中的成员方法。
// 父类
public class Base {
int a;
public void func1(){
System.out.println("Base.func1()");
}
}
// 子类
public class Derived extends Base {
int a;
// func1() 实现了重写
public void func1(){
System.out.println("Derived.func1()");
}
public void method(){
a = 10; // 此时改变的是子类中 a 的值
super.a = 20; // 此时改变的是父类中 a 的值
func1(); // 此时调用的是子类中的 func1() 方法
super.func1(); // 此时调用的是父类中的 func1() 方法
}
}
注意:
- super 关键字只能在非静态方法中使用。
- super 后面必须跟上 .类成员,不能单独使用,否则报错。
- 使用 super 访问父类的成员时,如果有则访问,没有则编译报错。
3、子类构造方法
构造方法用来初始化成员变量,那么子类继承了父类的成员,也需要调用父类的构造方法来初始化从父类继承下来的成员变量,即:在构造子类对象时,需要先调用父类的构造方法,然后执行子类的构造方法。
- 如果父类没有显示定义任何的构造方法或者显示定义了无参的构造方法时,则子类可以定义也可以不定义构造方法,此时编译器会自动生成无参的构造方法。当编译器在编译子类构造方法时,会在子类构造方法中新增加一条语句,即调用父类的无参构造方法。
- 如果基类显示定义了有参数的构造方法,此时子类就必须要显式定义带有参数的构造方法,否则编译报错。编译器无法自动调用父类中有参数的构造方法,因为编译器调用的时候不知道传递什么参数,所以子类必须在其构造方法的内部通过 super 关键字显式调用父类带有参数的构造方法。