一、继承
继承机制:是面向对象程序设计使代码可以复用利用的重要手段,允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生的新类称为派生类
目的:代码重用,类重用
概念:父类(基类、超类),子类(派生类)
关键字:extends
继承主要解决共性抽取的问题
1. 背景
当有多个类存在一些代码有的一样,有的不一样,又希望能够进行代码重用,此时就需要通过继承来实现
未使用继承:
Animal类:
public class Animal {
public String name;
public void eat(String food){
System.out.println(name + "正在吃" + food);
}
}
Cat类:
public class Cat {
public String name;
public void eat(String food){
System.out.println(name + "正在吃" + food);
}
}
Bird类:
public class Bird {
public String name;
public void eat(String food){
System.out.println(name + "正在吃" + food);
}
}
使用继承后:
Cat类、Bird类除了继承了Animal类的属性、方法,还可以有自己的方法、属性。
Animal类:
public class Animal {
public String name;
public void eat(String food){
System.out.println(name + "正在吃" + food);
}
}
Cat类:
public class Cat extends Animal{
public void jump(){
System.out.println(name + "正在跳");
}
}
Bird类:
public class Bird extends Animal{
public void fly(){
System.out.println(name + "正在飞");
}
}
继承就是为了代码重用,为了把多个类之间的共同代码提取出来,放到“父类”中,然后再由各个子类分别继承这个父类,从而就可以把这些重复的代码消灭了。
2. 语法规则
- 使用extends指定父类
- 单继承,一个子类只能继承自一个父类(一个子类只能有一个父类,但父类也可以继承自其他,避免出错,继承层数尽量少)
- 子类会继承父类的所有字段和方法,但private修饰的成员在子类中无法访问
- 子类的实例中也包含父类的实例,通过super来获取父类实例的引用,通过this来获取子类实例的引用
- 每个子类实例里面都包含一个父类实例,有几个子类实例就有几个父类实例(子类实例中包含父类实例,父类实例中包含父类属性)
当Cat继承自Animal时
-
当父类含有一个属性,子类继承自父类时,this.name和super.name对应的是同一个属性,修改与调用都对应同一内容
- 当子类含有与父类相同的属性时,this.name和super.name不是同一内容,此时修改this.name不会对super.name 造成影响
- 父类子类有不同属性,只能通过super.来调用父类属性,通过this.来调用子类属性
3. 构造方法
当子类继承自父类时,构造方法的创建会有如下方式:
每个类都有构造方法,如果不显式的创建构造方法,编译器就会给这个类自动生成一个没有参数的构造方法,以上问题是由于父类创建了一个含参构造方法,而子类未创建则报错,故需要显式创建一个含参构造函数来解决。
- 当父类里面没有创建构造方法,就被自动生成了没有参数版本的构造方法,此时如果直接new子类实例,就会调用到刚才父类这个没有参数版本的构造方法。都不写构造方法相当于隐式创建了无参构造方法;若父类只创建了一个不含参构造方法,子类则不需要显式定义构造方法。
Animal类:
public class Animal {
public String name;
public void eat(String food){
System.out.println(name + "正在吃" + food);
}
}
Cat类:
public class Cat extends Animal{
public void jump(){
System.out.println(name + "正在跳");
}
}
Bird类:
public class Bird extends Animal{
public void fly(){
System.out.println(name + "正在飞");
}
}
相当于:
Animal类:
public class Animal {
public String name;
public Animal(){
}
public void eat(String food){
System.out.println(name + "正在吃" + food);
}
}
Cat类:
public class Cat extends Animal{
public Cat(){
}
public void jump(){
System.out.println(name + "正在跳");
}
}
Bird类:
public class Bird extends Animal{
public Bird(){
}
public void fly(){
System.out.println(name + "正在飞");
}
}
- 当父类里面创建了含参构造方法的时候,编译器就不会自动生成无参版本的构造方法,此时再创建子类实例时,子类就必须显式的调用父类的这个构造方法,并且进行传参,否则创建不出来父类的实例,就会编译出错
Animal类:
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food){
System.out.println(name + "正在吃" + food);
}
}
Cat类:
public class Cat extends Animal{
public Cat(String name) {
super(name);
}
public void jump(){
System.out.println(name + "正在跳");
}
}
Bird类:
public class Bird extends Animal{
public Bird(String name) {
super(name);
}
public void fly(){
System.out.println(name + "正在飞");
}
}
- 若父类创建了多个构造方法,子类在创建时就要显式的决定使用哪个构造方法,否则会报错
- static不能修饰构造方法
- 子类继承父类后,子类需要先构造父类。在创建子类实例的时候,先构造父类对象(执行父类构造方法的逻辑),再构造子类对象(再执行子类构造方法的逻辑),如果是显式调用父类的构造方法,那么必须把父类的构造方法放在第一行代码去执行,否则报错。
- 父类构造方法在子类构造方法之前执行
Animal类:
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
System.out.println("这是父类构造方法");
}
public void eat(String food){
System.out.println(name + "正在吃" + food);
}
}
Cat类:
public class Cat extends Animal{
public Cat(String name) {
super(name);
System.out.println("这是子类构造方法");
}
public void jump(){
System.out.println(name + "正在跳");
}
}
Main类:
public class Main {
public static void main(String[] args) {
Cat cat = new Cat("小黑");
cat.eat("鱼");
cat.jump();
}
}
-----------------------------------
运行结果:
这是父类构造方法
这是子类构造方法
小黑正在吃鱼
小黑正在跳
4.protected关键字
若把字段设为private,子类无法访问,但是设成public就违背了我们的“封装”初衷,两全其美的方法就是使用protected关键字。
- 对于类的子类和同一个包的其他类来说,protected修饰的字段和方法是可以访问的
四种访问权限:
- private:只能在类内部被访问 权限要求最高
- default:包级权限,可以被同包内的其他类访问
- protected:可以被子类访问,也可以被同包的其他类访问,还能被其他包的子类访问
- public:可以在类外部访问 权限要求最低
类前面的public表示这个类可以被其他包使用,不加只能在包内使用
属性前面四种访问权限都可加;类前面只能加public 或不加
5. final关键字
类之间继承层数不宜过多,一般要求不超三层,若过多可考虑重构,要限制继承可使用final关键字修饰类来显式禁止继承,防止继承被滥用。
public final class Animal {
}
public class Cat extends Animal{
}
--------------------
运行结果:
Error:(3, 26) java: 无法从最终Java.Animal进行继承
二、 组合
和继承类似,组合也是一种表达类与类之间关系的方式,也能够达到代码重用的效果
与继承区别:
- 组合(has a,拥有XXX)
- 继承(is a,是XXX)
public class SchoolMaster {
}
public class Student {
}
public class Teacher {
}
public class School {
public SchoolMaster SchoolMaster = new SchoolMaster();
public Student student1 = new Student();
public Student student2 = new Student();
public Student student3 = new Student();
public Student student4 = new Student();
public Teacher teacher1 = new Teacher();
public Teacher teacher2 = new Teacher();
}
//表示一个学校有:一个校长、四个学生、两个老师