5. 面向对象编程(中)
5.1 面向对象特征之二: 继承性
1. 为什么要有继承
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
2. 继承性的好处
- 继承的出现减少了代码冗余,提高了代码的复用性。
- 继承的出现,更有利于功能的扩展。
- 继承的出现让类与类之间产生了关系,提供了多态的前提。
3. 继承性的格式
class A extends B{}
- A:子类、派生类、subclass
- B:父类、超类、基类、superclass
例子
// 定义Person类作为父类
public class Person {
String name;
private int age;
public void eat(){
System.out.println("吃饭");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
// 定义Student类继承Person类
public class Student extends Person {
String major;
public void study(){
System.out.println("学习");
}
public void show(){
System.out.println("name:" + name + ",age:" + getAge());
}
}
// 测试
public static void main(String[] args) {
Student s1 = new Student();
s1.eat(); // 继承的父类中的方法
s1.name = "Tom"; // 继承的父类中的属性
s1.study(); // 子类特有的方法
}
}
4. 继承性的体现
- 子类继承了父类,就继承了父类的方法和属性。特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
- 在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
5. Java中关于继承性的规定
-
一个类可以被多个子类继承。
-
Java中类的单继承性:一个类只能有一个父类
-
子父类是相对的概念。
-
子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
-
子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
6. 其他注意事项
- 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
- 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类,即所有的java类具有java.lang.Object类声明的功能。
5.2 方法的重写 (override/overwrite)
1. 相关概念
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
2. 要求
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限;特别的,子类不能重写父类中声明为private权限的方法
- 子类方法抛出的异常不能大于父类被重写方法的异常
3. 注意点
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
4. 例子
// 定义Person类作为父类
public class Person {
public void eat(){
System.out.println("吃饭");
}
}
// 定义Student类继承Person类
public class Student extends Person {
public void eat(){
System.out.println("学生要吃饭");
}
}
// 测试
public static void main(String[] args) {
Student s1 = new Student();
s1.eat(); // 调用的是重写后的方法,输出的是"学生要吃饭"
}
}
5.3 关键字:super
1. super关键字的使用
在Java类中使用super来调用父类中的指定操作,即super关键字可以理解为“父类的”意思
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造器中调用父类的构造器
例子
// 定义Person类作为父类
public class Person {
public void eat(){
System.out.println("吃饭");
}
}
// 定义Student类继承Person类
public class Student extends Person {
public void eat(){
System.out.println("学生要吃饭");
}
public void study() {
System.out.println("学生要学习");
eat(); // 调用重写的eat方法
super.eat(); // 调用父类中的eat方法
}
}
// 测试
public static void main(String[] args) {
Student s1 = new Student();
s1.study();
}
}
2. 注意事项
- 当子父类出现同名成员时,可以用super表明调用的是父类中的成员
- super的追溯不仅限于直接父类
- super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
3. 调用父类的构造器
注意事项
-
可以在子类的构造器中显式的使用"super(参数列表)"的方式,调用父类中声明的指定的构造器
-
"super(参数列表)"的使用,必须声明在子类构造器的首行
-
在类的构造器中,针对于"this(参数列表)"或"super(参数列表)"只能二选一,不能同时出现
-
在构造器的首行,没有显式的声明"this(参数列表)“或"super(参数列表)”,则默认调用的是父类中空参的构造器:super()
-
当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。
-
在类的多个构造器中,至少有一个类的构造器中使用了"super(参数列表)",调用父类中的构造器
例子
// 定义Person类作为父类
public class Person {
String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
}
// 定义Student类继承Person类
public class Student extends Person {
String idNumber;
public Student(String name, String idNumber) {
super(name);
this.idNumber = idNumber;
}
}
// 测试
public static void main(String[] args) {
Student s1 = new Student("张三", "1001");
}
}
4. this和super的区别
区别点 | this | super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 直接访问父类中的属性 |
调用方法 | 访问本类中的方法,如果本类没有此方法则从父类中继续查找 | 直接访问父类中的方法 |
调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
5.4 子类对象实例化过程
1. 从结果上来看:(继承性)
子类继承父类以后,就获取了父类中声明的属性或方法。即创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
2. 从过程上来看:
当通过子类的构造器创建子类对象时,一定会直接或间接的调用其父类的构造器,进而调用父类构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以内存中才有父类中的结构,子类对象才可以考虑进行调用。
3. 注意事项
虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。