继承
java 是面向对象的,对象与对象之间有时会有很多相同的行为以及部分不同的行为,如果两个不同的对象,要有相同的行为,写两个类会出现代码的复用,所以出现了继承
继承,是子与父的关系,子类继承父类,所以子类可以拥有父类下的成员和方法,具有跟父类相同的行为,而且子类还可以拥有属于自己独特的行为
继承可以实现代码的复用,使得子类将父类作为基础,在父类拥有的基础上添加新的功能
继承格式
使用 extends 关键字进行对类的继承
1 | class 父类{ |
父类,又称基类或超类;子类,可以称为派生类
使用继承的情况
继承并不是随意使用的,一般情况下,只有子类需要父类的所有方法,才进行继承
子类对父类是“是”的关系,需要继承,如果是“像”的关系,不需要继承;在父类中不需要考虑子类的特殊性,只需要写一些通用的方法
如:中国人是人,name中国人可以继承人;大卡车像汽车,都有轮胎,但是大卡车不是汽车,所以不能继承
对于继承,可以将多个类的共有方法和属性抽取出来,定义成父类,让这些类继承,然后再各自实现自己的新成员和独有行为方法
继承的特性
- 子类拥有父类除 private 之外的所有属性、方法;不能继承父类的构造方法,默认调用无参构造方法,或使用super显式调用
- 只能单继承:一个子类只能继承一个父类
- 可以多重继承:可以多个类同时继承一个父类
- 子类中可以用自己的独特方式实现父类的方法,并且实现自己的属性和方法,对父类进行扩展
继承的两种方式
子类继承父类,在子类中会有两种方式使得子类与分类不同
- 子类中添加新的成员变量或成员方法
- 覆盖重写父类的方法
添加新的成员
如果父类中没有子类需要的方法,那么子类中可以添加自己的成员变量或成员方法
覆盖
如果父类中方法的功能不能满足子类,那子类可以覆盖父类的方法,重写方法的功能,这也叫重写;
方法名称和方法参数必须相同,使用 @Override 注解;
如果在子类重写父类的方法中,还想要调用父类的这个方法,可以使用 super 调用
覆盖父类的方法,方法名、方法参数和父类中的方法必须一致
调用方法
在子类中调用方法:在子类中查找是否有该方法,有-直接执行子类方法;没有-进入父类查找方法
1 | public class Father{ |
运行结果:
1 | methodShow son |
构造器
子类除了不能继承父类的 private 方法,还有构造器也不能继承,
虽然不能继承,但是子类必须调用父类的构造器:
- 父类和子类中都没有构造器或者有无参构造器:子类会默认调用父类无参构造器,程序运行系统自动调用
- 父类中没有无参构造器,只有有参构造器:
- 给父类添加无参构造方法
- 子类在构造器中用 super(参数) 显示调用父类有参构造器
子类继承父类后初始化的顺序
在子类继承父类后,在子类初始化之前,必须进行父类的初始化
类的初始化过程:静态代码块->构造代码块->构造方法
存在子类和父类:
父类static -> 子类static -> 父类构造代码块、成员变量 -> 父类构造器 -> 子类构造代码块、成员变量->子类构造器
- 加载子类,发现子类继承父类
- 进入父类,初始化父类的 static 部分
- 然后进入子类,初始化子类的 static 部分
- 执行 new 子类时,会先 new 父类,初始化父类的成员变量,然后调用父类构造器
- 接着调用子类自身的构造器
eg1
1 | class Fu{ |
运行结果:
1 | fu 静态代码块 |
- 先进入 TestDemo,运行 main()
- 加载 Zi 类,Zi 类继承了 Fu 类,所以先初始化 Fu 类,初始化静态代码块;
- 加载子类,初始化静态代码块
- 进入子类构造器,默认先调用父类构造器,去 Fu 中初始化创建对象
- 进入 Fu 初始化构造代码,然后 new Fu,调用 Fu 构造器
- 回到子类,初始化子类 Zi 的构造代码块,然后执行 Zi 的构造器
eg2
1 | public class Person{ |
运行结果:
1 | person static |
在上面的程序中,初始化顺序是这样的:
先加载 Person 类,初始化 Person 的 static 部分:“person static”
运行 main() 方法,运行子类 Student ,初始化 static 部分,按照static 在程序的先后顺序:“student static1,student static2”
进行 new Student(),但是有父类 Person 存在,所以默认先调用父类 Person 无参构造器:new Person(),在 new 之前,初始化 Person 的成员变量:animalP
初始化 animalP:进入 Animal 初始化 static:animal static;调用 new Animal(“person and animal”):“animal:person and animal”
接着父类 Person 成员变量初始化完成后,调用父类无参构造器:person constructor
父类 new 完成,回到子类 Student:初始化子类成员变量:animalS,此时 Animal 已经加载过,所以 static 部分不再执行,然后直接调用 new Animal(“cat”):“animal:cat”
Student 初始化完成,调用子类无参构造器:“student constructor”
注意
记住,
在调用之前,加载类时最先初始化父类、子类中的 static 代码块!!!
在子类中无论显式使用 super() 还是系统默认调用 super() 调用父类构造方法,都会先进入父类初始化父类的构造代码块和成员变量,然后再调用父类构造器!调用完成之后,在子类中同样是先初始化子类中的构造代码块和成员变量