目录
前言
代码中创建的类, 主要是为了抽象现实中的一些事物(包含属性和方法).
有的时候客观事物之间就存在一些关联关系, 那么在表示成类和对象的时候也会存在一定的关联.
一、继承的引入
例如,设计一个类来表示动物
//Animal类 class Animal { //定义动物的名字 protected String name; //定义动物的有参构造方法 public Animal(String name) { this.name = name; } //定义动物的无参构造方法 public Animal() { } //定义动物吃食物的方法 public void eat(String food) { System.out.println(this.name + "正在吃" + food); } }
//Cat类 class Cat extends Animal{ //定义猫的名字 protected String name; public Cat() { } public Cat(String name) { this.name = name; } //定义猫吃食物的方法 public void eat(String food) { System.out.println(this.name + "正在吃" + food); } //定义猫跑的方法 public void run() { System.out.println(this.name + "正在跑"); } }
//Bird类 class Bird extends Animal { //定义鸟的名字 protected String name; public Bird() { } public Bird(String name) { this.name = name; } //定义鸟吃食物的方法 public void eat(String food) { System.out.println(this.name + "正在吃" + food); } //定义鸟飞的方法 public void fly() { System.out.println(this.name + "正在飞"); } }
我们对上面的三个实例进行分析,发现其中存在大量的重复代码,或者说意义一致的代码;
仔细分析,我们其实也发现Animal 和 Cat 以及 Bird 这几个类本身就存在一定的关联关系
这三个类都具有一个相同的eat方法,而且行为是完全一样的
这三个类都具有一个相同的name属性,而且意义是完全一样的
从逻辑上来说,Cat 和 Bird 都是Animal的一种(即is – a 的关系)
于是为了优化代码,增加这些重复代码的利用率,减少重复代码的书写次数,我们引进了继承这个概念;
二、继承是什么?
继承:是指将父类的成员方法和成员变量赋予给子类,使子类可以在不写这些属性的同时也可以使用这些属性
在回到我们举得实例中,实例中像Animal 这种被继承的类,我们称之为 父类 ,基类,或超类,而实例中像Cat 和Bird 这样的类,我们就称之为子类,派生类 ,类似于现实中的儿子继承父亲的财产,子类也会继承父类的字段和方法,以此来达到代码复用的效果
三、语法规则
基本语法
Class 子类 extends 父类 {
}
使用 extends 指定父类.
Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承).
子类会继承父类的所有 public 的字段和方法.
但是对于父类的 private 的字段和方法, 子类中是无法访问的.
对于上面的代码, 可以使用继承进行改进. 此时我们让 Cat 和 Bird 继承自 Animal 类, 那么 Cat 在定义的时候就不必再写 name 字段和 eat 方法.
即原代码将变成下面的样子
//Animal类 class Animal { //定义动物的名字 protected String name; //定义动物的有参构造方法 public Animal(String name) { this.name = name; } //定义动物的无参构造方法 public Animal() { } //定义动物吃食物的方法 public void eat(String food) { System.out.println(this.name + "正在吃" + food); } } //Cat类 class Cat extends Animal{ public Cat() { } public Cat(String name) { this.name = name; } //定义猫跑的方法 public void run() { System.out.println(this.name + "正在跑"); } } //Bird类 class Bird extends Animal { public Bird() { } public Bird(String name) { this.name = name; } //定义鸟飞的方法 public void fly() { System.out.println(this.name + "正在飞"); } }
extends 英文原意指 "扩展". 而我们所写的类的继承, 也可以理解成基于父类进行代码上的 "扩展".
例如我们写的 Bird 类, 就是在 Animal 的基础上扩展出了 fly 方法.
之前我们学了private权限修饰符,他用于修饰私有属性,即被private修饰的属性将无法在类外使用,所以如果把 name 改成 private, 那么此时子类就不能访问了.
四、protected关键字
刚才, 如果把字段设为 private, 子类不能访问. 但是设成 public, 又违背了我们 "封装" 的初衷.两全其美的办法就是 protected 关键字.
对于类的调用者来说, protected 修饰的字段和方法是不能访问的
对于类的 子类 和 同一个包的其他类 来说, protected 修饰的字段和方法是可以访问的
补充说明:Java的四种权限修饰符
private: 类内部能访问, 类外部不能访问
默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
public : 类内部和类的调用者都能访问
那么在什么时候该使用哪一种呢?
我们希望类要尽量做到 "封装", 即隐藏内部实现细节, 只暴露出 必要 的信息给类的调用者.
因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限. 例如如果一个方法能用 private, 就尽量不要用public.
另外, 还有一种 简单粗暴 的做法: 将所有的字段设为 private, 将所有的方法设为 public. 不过这种方式属于是对访问权限的滥用, 还是更希望同学们能写代码的时候认真思考, 该类提供的字段方法到底给 "谁" 使用(是类内部自己用, 还是类的调用者使用, 还是子类使用).
五、Final关键字
之前的实例中我们仅仅涉及到了Animal ,Cat,Bird 三种类,但是如果情况更复杂一些呢?
就比如针对Cat我们进行扩充,我们就将得到下面的情况
那么这个时候我们在使用继承方式来表达,就会涉及到更复杂的体系
像上面的例子我们将这种继承方式称为多层继承,即子类还可以进一步的再派生出新的子类.
但是我们并不希望类与类之间的继承层次性过于复杂,就好比一般一个家庭很难出现五世同堂甚至是六世同堂的情况,所以为了避免出现超过三层的继承关系.我们都会对继承进行语法上的限制,而限制的手段就是通过final关键字
final 关键字
曾经我们也学习过 final 关键字, 当它用于修饰一个变量或者字段的时候, 表示这个变量是一个常量 (即不能修改).
所以final 关键字也能修饰类, 此时它就表示被修饰的类将无法被其他类所继承.
补充说明:final关键字修饰用法
六:总结
在没有接触继承之前我们面对出现大量重复代码都是选择使用方法将这些代码封装起来,而方法只能在一个类中使用,这就说明必须寻找一个新的解决方案来解决我们遇到的这个新问题,所以继承才会应运而生,继承是当类与类之间存在大量重复的代码而引入的一个新概念,它用于描述类与类之间的关系;
继承既然是一个全新的概念,那么就会牵扯到一些新的关键字或者一些关键字的新用法,比如extends关键字,private关键字,protected关键字,final关键字;extends关键字用于子类指定父类;private关键字用于修饰私有属性,被private关键字修饰的属性将私有化,即无法在类外直接调用该属性;protected关键字也用于修饰属性,被protected关键字修饰的属性将受保护,即属性可以在被指定的子类中所调用,最后是final关键字,final是最终的意思,所以被final关键字所修饰的属性将无法改变;