1.继承
1.1什么是继承
继承是面向对象程序设计的一种重要手段,其最终目的是为了实现共性的提取和代码的复用性,当我们发现一个类A的功能不足以满足我们的需求,而需要重新建一个类B,但是类B又恰好需要类A的全部方法,这时我们就可以让类B去继承类A,语法是class B extends A{}。这就表示,类B继承了类A,其中类B叫做子类或派生类,类A叫做父类(超类,基类)。子类会继承父类的所有属性和方法。举个例子,就像手机的发展,手机每出现新的一代,是在旧的一代的基础上,在添加新的功能,可以说新手机继承了旧手机的功能,并在其基础上开发新的功能的。所以继承就是在保持原有类特性的基础上进行扩展,增加新功能,从而实现代码的复用性
1.2如何继承
一个类通过 extends关键字来继承另一个类的所有方法(包括构造方法)和属性,子类必须要有新的方法来不同于父类,否则就没有继承的必要了。
//注意!!!不可以为了用另一个类中的方法而,特意去继承另一个类,子类和父类之间必须存在所属关系才可以继承,必须满足 "A is a/an B",就比如猫是动物的一种,所有动物都要吃饭休息,猫也要,但是猫除了这些所有动物都有的特性之外,它还可以跳和跑等等,所以猫类可以继承动物类,猫 is a/an 动物。
1.2.3父类成员变量的访问
1.子类和父类不存在同名成员变量
当父类的成员变量子类没有时,子类可以直接访问父类的成员变量。
2.子类和父类成员变量同名
当子类有的成员变量父类也有时优先访问子类自己的。
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。
1.2.3父类成员方法的访问
1. 成员方法名字不同
子类可以直接访问子类没有而父类有的成员方法
成员方法的访问也会遵循就近原则,优先访问子类自己的。
2. 成员方法名字相同
通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法传递的参数选择合适的方法访问。
1.3super关键字
super是子类用来引用父类的关键字,其用法和this关键字类似,this代表当前类的对象的引用(哪个对象调用这个方法this就指向那个对象,就是this代表当前类对象的引用)。super调用的是子类从父类哪里继承过来的类和成员变量(或者说是对父类对象的引用)。
1.4继承中的初始化和访问构造方法
1.4.1访问构造方法
super(...)用于调用父类构造方法,而且super必须是构造方法的第一条语句,当子类中没有构造方法时会默认有一个无参的构造方法,里面会有一条super();语句。这时你可能会有疑问当时在类和对象中讲过this();语句也必须放在第一条语句,那么当两者同时出现时会怎么样,答案是报错,在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现。
那么还有一个疑问。为什么super必须在第一条语句?因为子类会继承父类中的数据并使用,所以在初始化子类时必须先初始化父类。
如果子类和父类都是带参构造方法,那么在创建对象时要把子类和父类构造方法的参数一起传过去。
1.4.2继承中的初始化
我们先来看看下面这个代码
可见在实例化对象时最先执行静态代码块,然后执行实例代码块,最后执行构造方法,并且静态代码块只会执行一次。这是因为静态代码块是在类加载阶段执行的,他的生命周期随类诞生随类消亡。
现在我们再增加一个父类
这个代码会输出什么呢?
答案是
通过分析执行结果,得出以下结论:
1、父类静态代码块优先于子类静态代码块执行,且是最早执行
2、父类实例代码块和父类构造方法紧接着执行
3、子类的实例代码块和子类构造方法紧接着再执行
4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
1.5继承方式
Java只支持单继承,多层继承,多个类继承一个类,不支持多继承。比如:
一个类a可以继承类b,类b可以在继承类c,但是不可以类a同时继承类b和类c。不过一般继承关系最好不用超过三层,如果继承层次太多, 就需要考虑对代码进行重构了。
1.6final关键字
final关键可以用来修饰变量、成员方法和类。
- 修饰变量或字段,表示常量(即不能修改)
- 修饰类:表示此类不能被继承
- 修饰方法:表示该方法不能被重写(后序介绍)
- String 字符串类, 就是用 final 修饰的, 不能被继承
1.7继承与组合
组合和继承一样目的都是为了实现代码的复用,不过之前有说过继承是 is a 的关系 而组合是 have a的关系。组合没有专门的关键字,或者说它只是一种方法,当我们在一个类中需要用到另一个类时,就可以用组合。
这里就不多做解释了直接上代码(用一个汽车类做例子):
//如果两个类没有父子关系不适合使用继承,而且组合不占用继承位置,继承和组合的使用依据场景而定,一般能用组合就用组合。
2.多态
2.1多态的概念
当我们要完成一个动作时,不同的对象完成动作的形式不同,这就是多态的意思,不同的对象完成的状态不同,就比如上课这个动作,不同专业的学生都在进行上课这个动作,但是不同专业的学生上课内容不同。体现在代码上就是当传递不同类对象时,会调用对应类中的方法。
2.2实现多态
在java中要实现多态,必须要满足如下几个条件,缺一不可:
- 必须在继承体系下
- 子类必须要对父类中方法进行重写
- 通过父类的引用调用重写的方法
想要实现多态,要先知道一个对象的编译类型和运行类型是可以不一样的,编译类型不能改变,而运行类型是可以改变的(编译类型就是定义时,等号左边的类型,运行类型就是定义时,等号右边的类型)。
先举个例子
2.3重写
重写(覆盖),重写是对父类方法(非静态、非private修饰,非final修饰,非构造方法等)实现的过程进行重新编写,返回值和形参要和父类相同,方法的内容要重新编写,重写的好处在于子类可以根据需要,定义特定 于自己的行为。 也就是说子类能够根据需要实现父类的方法。
- 被重写的方法返回值可以不同,但是必须是具有父子关系的
- 子类中重写的方法的访问权限不能比父类中被重写的,方法的访问权限低
- 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心 将方法名字拼写错了
注意//重写和重载还是有一定区别的,重写的参数列表不能修改,而重载的参数列表必须修改,重载的返回值类型也可以修改,而重写的返回值类型如果没有构成父子类关系的话,就不能修改,重载的访问限定符可以修改,重写的访问限定符必须保证,子类的访问权限大于等于父类的访问权限
//具体例子参考上面代码
2.4向上转型和向下转型
2.4.1向上转型
向上转型实际上就是,父类引用指向子类对象,格式如下:
向上转型的实现方法有三种:
1.直接赋值法
2.方法传参
这样的好处是形参为父类型引用,可以接收任意子类的对象
3.方法返回值返回
这样的好处是可以返回任意子类对象
- 向上转型的优点:让代码实现更简单灵活
- 向上转型的缺陷:不能调用到子类特有的方法。
2.4.2向下转型
当一个自的对象经过向上转型之后,我们就把它当做父类方法来使用,就无法再调用子类特有的方法,但是有时候我们又需要去调用这些子类特有的方法,那么,我们将父类引用再度还原为子类对象即可,这就是向下转型。在向下转型之后就可以调用子类所有的方法了。
举例如下:
但是向下转型有时会不安全
Java中为了提高向下转型的安全性防止将该对象转成其他的对象,引入了instanceof用来对象的类型 ,如果该表达式为true,则可以安全转换。
2.5动态绑定
动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体 调用那个类的方法。
堆中的对象是student类型,我们现在要调用其中的study方法但是这个方法已经被重写了,在编译阶段我们不知道要调用哪一个,所以在程序运行阶段对象中就和study方法绑定。
//动态绑定是多态的基础,先向上转型,通过父类引用调用这个父类和子类重写的方法,这个过程就叫做运行时绑定/动态绑定
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代 表函数重载。
2.6多态的优缺点
优点
- 使代码更加灵活,当有一个新的对象要完成这个动作时只需要把右边的替换成这个对象就可以
- 可以降低代码的复杂度避免使用大量的if-else
缺点
- 会降低运行效率