继承(掌握)
1.引入:
- 把多个类中相同的成员给提取出来定义到一个独立的类中。然后让这多个类和该独立的类产生一个关系,这多个类就具备了这些内容。这个关系叫继承
- 有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员
2.Java中如何表示继承呢?格式是什么呢?
- 用关键字extends表示
- 格式:class 子类名 extends 父类名 {}
3.继承的好处:
- 提高了代码的复用性和维护性
- 让类与类产生了一个关系,是多态的前提 //这点是好处也是弊端
4.继承的弊端:
-
让类的耦合性增强。这样某个类的改变,就会影响其他和该类相关的类
我们开发要遵循的原则:低耦合,高内聚。
耦合:类与类的关系
内聚:自己完成某件事情的能力 -
打破了封装性
5.Java中继承的特点
- Java中类只支持单继承 //一个儿子只有一个父亲
- Java中可以多层(重)继承(继承体系) //儿子继承父亲,父亲又继承爷爷,儿子就有了爷爷的功能
6.继承的注意事项:
- 子类不能继承父类的私有成员
class Father {
private int num = 10;
public int num2 = 20;
//私有方法,子类不能继承
private void method() {
System.out.println(num);
System.out.println(num2);
}
public void show() {
System.out.println(num);
System.out.println(num2);
}
}
class Son extends Father {
public void function() {
System.out.println(num); //子类不能继承父类的私有成员变量
System.out.println(num2);
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
// 创建对象
Son s = new Son();
//s.method(); //报错:找不到方法,因为子类不能继承父类的私有成员方法
s.show();//可以,因为show方法可以继承,相当于父类用自己的方法访问自己的私有成员变量
//s.function();//报错:num可以在Father中访问private,因为子类不能继承父类的私有成员变量
}
}
- 子类不能继承父类的构造方法,但是可以通过super去访问
- 不要为了部分功能而去继承
可以用假设法:
如果有两个类A,B,只有符合A是B的一种,或者B是A的一种时,再考虑使用继承
7.什么时候使用继承呢?
- 继承体现的是:is a的关系。
- 用假设法
8.Java继承中的成员关系
-
成员变量的关系
-
子类的成员变量名称和父类中的成员变量名称一样时,怎么访问呢?
子类的方法访问变量的查找顺序:
(1)在子类方法的局部范围找,有就使用
(2)在子类的成员范围找,有就使用
(3)在父类的成员范围找,有就使用
(4)找不到,就报错 -
成员方法的关系
-
子类的成员方法和父类中的成员方法名称一样(方法重写,详见第9点),这个怎么访问呢?
通过子类对象访问一个方法的查找顺序:
(1)在子类中找,有就使用
(2)在父类中找,有就使用
(3)找不到,就报错 -
构造方法的关系
-
调用子类的构造方法前,都会默认先调用父类的无参构造方法
为什么呢?
因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化
所以,
其实子类的每一个构造方法里的第一行都默认隐含了super(); -
父类中如果没有无参构造方法,怎么办? //会报错
方案一:子类通过super去明确调用带参构造
方案二:子类通过this调用本身的其他构造,但是一定要有一个去访问了父类的构造,否则父类数据就没有初始化
class Father {
/*
public Father() {
System.out.println("Father的无参构造方法");
}
*/
public Father(String name) {
System.out.println("Father的带参构造方法");
}
}
class Son extends Father {
public Son() {
super("随便给");
System.out.println("Son的无参构造方法");
//super("随便给");
}
public Son(String name) {
//super("随便给");
this();
System.out.println("Son的带参构造方法");
}
}
class ExtendsDemo7 {
public static void main(String[] args) {
Son s = new Son();
System.out.println("----------------");
Son ss = new Son("林青霞");
}
}
9.方法重写和重载
-
方法重写(Override):
-
定义:子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法复写 //这里一模一样包括:方法名、返回值类型、参数列表
-
应用:当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
class Phone { public void call(String name) { System.out.println("给"+name+"打电话"); } } class NewPhone extends Phone { public void call(String name) { super.call(name); //用super作父类引用 System.out.println("可以听天气预报了"); } }
-
注意事项:
父类中私有方法不能被重写 //因为父类私有方法子类无法继承,也就是根本看不到,所以谈不上重写
子类重写父类方法时,访问权限不能更低
父类静态方法,子类也必须通过静态方法进行重写.(多态中会讲解)所以,子类重写父类方法时,最好方法的声明保持一模一样
-
方法重载(Overload):详见
day05-1学习过程笔记
-
定义:同一个类中,出现了方法名一样,参数列表不同的方法。与返回值无关!
10.this和super
-
二者区别
-
this代表本类对应的引用
-
super代表父类存储空间的标识(可以理解为父类引用)
-
各自用法
-
调用成员变量
this.成员变量 super.成员变量
-
调用构造方法(…表示参数)
this(…) super(…)
-
调用成员方法
this.成员方法() super.成员方法()
11.面试题:数据初始化
- 一个类的初始化过程
- 先:成员变量的初始化
默认初始化
显示初始化 - 后:构造方法初始化
- 子父类的初始化(分层初始化)
- 先进行父类初始化,完了才开始进行子类初始化
- 结果:
YXYZ - 问题:
-
虽然子类中构造方法默认有一个super()
但初始化的时候,不是按照那个顺序进行的,而是按照分层初始化进行的
super()仅仅表示要先初始化父类数据,再初始化子类数据。
class X {
Y b = new Y();
X() {
System.out.print("X");
}
}
class Y {
Y() {
System.out.print("Y");
}
}
public class Z extends X {
Y y = new Y();
Z() {
//super();//默认有这么一行
System.out.print("Z");
}
public static void main(String[] args) {
new Z();
}
}
12.案例:
- 学生和老师案例:
2019-08-18com.inherit:Person+Student+Teacher+Test1
- 猫狗案例的分析和实现:
2019-08-18com.inherit