继承
继承可以解决代码复用,让我们的编程更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends来声明继承父类即可。
class 子类 extends 父类{
}
//自乐你自动拥有父类定义的属性和方法
//父类又叫超类、基类
//子类又叫派生类
继承 — 细节
-
子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
-
子类没有继承父类的构造器,但必须调用父类的构造器, 完成父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]
-
如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
-
super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
-
super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
-
java 所有类都是 Object 类的子类, Object 是所有类的基类.
-
父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
-
子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
- 思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
-
不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
super
super 代表父类的引用,用于访问父类的属性、方法、构造器
//访问父类的属性, 但不能访问父类的private属性
super.属性名;
//访问父类的方法, 不能访问父类的private方法
super.方法名(参数列表) ;
//访问父类的构造器
super(参数列表) ;
只能放在构造器的第一句, 只能出现一句!
super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用 super 去访问爷爷类的成员
上面这句话个人的理解是,跳过本类(即便本类有也不看),逐级往上查找,查到有为止,如果有不能访问就报没权限,如果没有就说找不到。
调用父类方法或属性的查找规则
public class B extends A {
public void sum() {
//希望调用父类-A 的 cal 方法
//3种查找方法
cal();
this.cal(); //和第一个cal();等价
super.cal(); //跳过本类直接查找上一级父类,其他规则一样
//找 cal 方法时(cal() 和 this.cal()),顺序是:
// (1)先找本类,如果有,则调用
// (2)如果没有,则找父类(如果有,并可以调用,则调用)
// (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object 类
// 提示:如果查找方法的过程中,找到了,但是不能访问(private), 则报错, cannot access
// 提示:如果查找方法的过程中,没有找到,则提示方法不存在
}
}
---------------------------------------------------------------
//查找属性的方法同上
super 细节
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名, 使用super、this、直接访问是一样的效果!
- super的访问不限于直接父类, 如果爷爷类和本类中有同名的成员, 也可以使用
- super去访问爷爷类的成员; 如果多个基类(上级类) 中都有同名的成员, 使用super访问遵循就近原则。A->B->C,当然也需要遵守访问权限的相关规则
super 和 this
区别 | this | super | |
---|---|---|---|
1 | 访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 从父类开始查找属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没有此方法则从父类继续查找 | 从父类开始查找方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
4 | 特殊 | 表示当前对象 | 子类中访问父类对象 |
方法重写
方法重写也叫方法覆盖,需要满足下面的条件
- 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
比如父类返回类型是Object, 子类方法返回类型是String【演示】
public Object get Info(){}
public String get Info(){}
- 子类方法不能缩小父类方法的访问权限
void say Ok() {}
public void say Ok() {}
重写和重载的区别
重载(overload) | 重写(override) | |
---|---|---|
发生范围 | 本类中 | 子类父类中 |
方法名 | 必须一致 | 必须一致 |
参数列表 | 必需不一样(类型,个数或者顺序至少有一个不同) | 一致 |
返回类型 | 可以不一样(无要求) | 和父类一致,或是返回父类的子类 |
修饰符 | 无要求 | 可以一样或者扩大范围,但是不能缩小 |
多态(重点难点)
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
- 方法的多态:重写和重载就体现多态
- 对象的多态 (核心,困难,重点):
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时=号的左边,运行类型看=号的右边
Animal animal = new Dog();
//animal编译类型是Animal,运行类型Dog
animal = new Cat();
//animal的运行类型变成了Cat,编译类型仍是Animal
多态细节(向上转型)
多态的前提是:两个对象(类)存在继承关系
//向上转型调用方法的规则如下:
//(1)可以调用父类中的所有成员(需遵守访问权限)
//(2)但是不能调用子类的特有的成员
//(3)因为在编译阶段,能调用哪些成员,是由编译类型来决定的 //animal.catchMouse();错误
//(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法
//,然后调用,规则我前面我们讲的方法调用规则一致。
多态细节(向下转型)
//语法:子类类型 引用名 = (子类类型)父类引用;
//只能强转父类的引用,不能强转父类的对象
//要求父类的引用必须指向的是当前目标类型的对象
//当向下转型后,可以调用子类类型中所有的成员