一.关于继承
1.1继承的概念:
继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性
的基础上进行 扩展,增加新功能 ,这样产生新的类,称 派生类 。继承呈现了面向对象程序设计的层次结构, 体现了
由简单到复杂的认知过程。继承主要解决的问题是: 共性的抽取,实现代码复用 。其中,被继承的类称为父类、超类,基类,继承的类称为派生类或者子类
1.2关于继承中子类方法调用的是父类成员还是子类成员
1.2.1关于成员属性:①对于 不同名的成员属性 ,先访问子类中的属性,如果子类中不存在该属性,那么再去访问父类中的该属性,父类中不存在则报错。
②对于 同名的成员属性 ,子类方法不能直接访问父类中的同名成员属性,直接访问的是子类新增的成员属性。那我们如何去访问父类中的同名成员属性呢?引 入super关键字,使用super.属性的方式对父类同名成员属性的访问和调用
1.2.2关于成员方法:①如果在子类中定义了权限修饰符、返回值、方法名、参数列表以及参数列表顺序均和父类中方法相同的方法,那我们称 该方法为父类方法的重写方法 ,我们在子类中调用该方法,我们只能直接调用子类重写的方法,如果我们想去调用父类重写之前的方法,我们同样需要 引用super关键字(super.成员方法()) 。
②对于子 父类定义了不同的方法, 那么我们同样还是先在子类中进行访问,再去父类中进行访问,如果子父类中均找不到该方法,则报错。
③如果子类和父类中定义了 同名但参数列表不同的方法 ,则两者的关系为方法的重载,根据参数列表的不同编译器选择调用哪个方法。
1.3关于super关键字
1.super关键字不能单独使用,必须使用super.成员变量或者super.成员方法的方式进行调用。
2.super关键字只能在非静态成员方法中使用
3.在子类方法中访问父类的成员变量和方法
1.4关于子类构造器的声明
①如果父类中没有显式定义构造方法,那么子类可以定义也可以不定义构造方法,两者都不会报错
②如果父类中声明了无参的构造方法,子类中同样可以定义也可以不定义构造方法,同样不会报错
③父类构造器中显式定义了有参的构造方法,子类中必须显式声明super(参数1,参数...)这样与父类构造方法中同参数的构造方法否则会报错。
关于子类构造方法的调用原理如下:
super和this
【 相同点 】
1. 都是 Java 中的关键字
2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
【 不同点 】
1. this 是 当前对象的引用,当前对象即调用实例方法的对象 , super相当于是子类对象中从父类继承下来部分成员的引用
2. 在非静态成员方法中, this用来访问本类的方法和属性 , super用来访问父类继承下来的方法和属性
3. 在构造方法中: this(...)用于调用本类构造方法, super(...)用于调用父类构造方法,两种调用不能同时在构造
方法中出现
4. 构造方法中 一定会存在super(...)的调用,用户没有写编译器也会增加 , 但是this(...)用户不写则没有
1.在类加载时会调用静态代码块,执行顺序为先父类后子类(静态代码块只在类加载过程中执行一次,与创建多少个对象无关)
2.构造方法:在有继承关系的构造方法中,实例代码块会被拷贝到第一条语句之前(第一条默认语句之后),其表现为一般先调用父类实例代码块,再调用父类构造方法,再调用子类实例代码块,再调用子类构造方法。我们每创建一个对象,都会调用一次构造方法。
如图,当我们加载完父类和子类的信息后(完成了静态代码块的调用),我们即使在子类构造方法的第一句调用this(子类其他构造方法),仍然会先执行该语句,执行完该语句后再去调用子类的实例代码块。
我们最后的结论是:
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
protected关键字
notes:
protected修饰的成员属性或者方法,只能在其子类创建的main方法中被访问,其余无论在其他子类或者非子类
的main方法中创建的另一个子类的对象仍然编译不通过。
例子:
继承和组合
和继承类似 , 组合也是一种表达 类之间关系的方式 , 也是能够达到 代码重用的效果 。组合并没有涉及到特殊的语法
( 诸如 extends 这样的关键字 ), 仅仅是 将一个类的实例作为另外一个类的字段 。
继承表示对象之间是 is-a 的关系 ,比如:狗是动物,猫是动物
组合表示对象之间是 has-a 的关系 ,比如:汽车
.多态及多态实现的条件
多态的概念: :通俗来说,就是多种形态, 具体点就是去完成某个行为,当不同的对象去完成时会产生出不同 的状
态。
多态实现的条件:
1. 必须在 继承体系下
2. 子类必须要 对父类中方法进行重写
3. 通过父类的引用 调用重写的方法
多态体现:在代码运行时,当传递 不同类对象 时,会 调用对应类中的方法 。
需要解释的是:在调用实现多态的方法时,如果通过 父类调用实现多态的方法 ,在编 译阶段并不知道调用的实际是哪个方法 ,只有在程序运行起来了才知道调用的是哪个方法。
当类的调用者在编写 eat 这个方法的时候 , 参数类型为 Animal ( 父类 ), 此时在该方法内部并 不知道 , 也不关注 当前的
a 引用指向的是哪个类型 ( 哪个子类 ) 的实例 . 此时 a 这个引用调用 eat 方法可能会有多种不同的表现 ( 和 a 引用的实例
相关 ), 这种行为就称为 多态
方法的重写
重写 (override) :也称为覆盖。重写是子类对父类 非静态、非 private 修饰,非 fifinal 修饰,非构造方法等的实现过程
进行重新编写 , 返回值和形参都不能改变 。 即外壳不变,核心重写! 重写的好处在于 子类可以根据需要,定义特定
于自己的行为。 也就是说子类能够根据需要实现父类的方法。
【 方法重写的规则 】
1.子类在重写父类的方法时,一般必须 与父类方法原型 一致: 返回值类型 方法名 ( 参数列表 ) 要完全一致
2.被重写的方法返回值类型可以不同,但是必须是 具有父子关系 的
3. 访问权限不能比父类中被重写的方法的访问权限更低。 例如:如果父类方法被 public 修饰,则子类中重写该方
法就不能声明为 protected
4.父类被 static 、 private 修饰的方法、final、构造方法都不能被重写。
5.重写的方法 , 可以使用 @ Override 注解来显式指定 . 有了这个注解能帮我们进行一些合法性校验 . 例如不小心
将方法名字拼写错了 ( 比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法 , 就会编译报错 , 提示无法
构成重写
对静态、private、final修饰的方法以及构造方法不能实现重写的原因解释:
1.static:在实现多态时,需要 基类的引用调用被重写的方法 ,所以被重写的方法 需要this引用才能确定调用那个类中的方法 (实现多态时,编译器无法通过父类的 参数判断调用哪个子类实现重写的方法 ,只有在运行时 基类的引用指向具体的对象才能确定调用那个类中被重写的方法 ),而被static修饰的方法 没有this引用,无法实现多态,那么实现方法的重写毫无意义,被编译器禁止 。
2.private:private修饰的类 无法被子类访问 ,自然也就没办法实现 对父类方法的重写 。
3.final:编译器规定被final修饰的方法不能实现重写。
4.构造方法:构造方法作用在创建对象时,由编译器调用,将 对象初始化完整 ,如果子类对父类的构造方法进行重写, 父类构造方法没有调用 ,父类对象不完整, 无法实现多态 ,而当对象创建完整之后,构造方法已经完成调用,也无法实现多态。
向上和向下转型
向上转型
向上转型:实际就是 创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型 ()
animal 是 父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换
在我们日常的认知中,可以这样考虑:猫(子类)是动物(父类),狗(子类)是动物(父类)
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法
向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的
方法,此时:将父类引用再还原为子类对象即可,即向下转换。
多态的优缺点
看如下代码: