Java继承+多态

目录

继承

继承的概念

继承的语法

父类成员变量访问

super关键字

super.data

super.func()

super()

super与this对比

相同点

不同点

初始化顺序

protected关键字

继承方式

继承与组合

 多态 

向上转型

         重写 

         理解多态

向下转型

 多态的优缺点

优点

缺点

避免在构造方法中调用重写的方法


在之前类和对象的博客中我介绍了封装,今天我来介绍一下继承和多态

继承

在了解继承前我们先看一段代码

class Cat {
    String name;
    int age;
    String Sex;
    public void eat() {
        System.out.println(this.name+"eat");
    }
}
class Dog {
    String name;
    int age;
    String Sex;
    public void eat() {
        System.out.println(this.name+"eat");
    }
}

我们定义了两个类,但是类的变量和方法有着很多共性的地方,我们能不能将共性的代码提取出来呢,答案是肯定的

class Animal {
    String name;
    int age;
    String Sex;
    public void eat() {
        System.out.println(this.name+"eat");
    }
}
class Cat extends Animal {
   public void mew() {
       System.out.println(this.name+"cat()::mew");
   }
}
class Dog extends Animal {
    public void bark() {
        System.out.println(this.name+"dog()::bark");
    }
}

继承的概念

继承就是对共性的抽取从而达到代码的复用

 继承机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,继承主要解决的问题是:共性的抽取,实现代码复用

继承的语法

在Java中如果要表示类之间的继承关系,需要借助extends关键字

修饰符 class 子类 extends 父类 {
}

注意:

1.子类会将父类中的成员变量或者成员方法继承到子类中了
2.子类继承父类之后,要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

3继承是 is-a 的关系

父类成员变量访问

子类和父类成员变量不同名

class Base {
    public int a=1;
    public int b=2;
}
class Derived extends Base {
    public int c=3;
    public int d=4;
    public void test() {
        System.out.println(a);//1
        System.out.println(b);//2
        System.out.println(c);//3
        System.out.println(d);//4
    }
}

子类和父类成员变量同名

class Base {
    public int a=1;
    public int b=2;
}
class Derived extends Base {
    public int a=3;
    public int d=4;
    public void test() {
        System.out.println(a);//3
        System.out.println(b);//2
        System.out.println(d);//4
    }
}

 可以看到子类成员方法在访问同名成员变量时优先子类成员变量,那如果想访问父类成员变量要如何修改呢,答案是要使用super关键字

 总结:

如果访问的成员变量子类中有,优先访问自己的成员变量
如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错
如果访问的成员变量与父类中成员变量同名,则优先访问自己的,即:子类将父类同名成员隐藏了
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找
super:准确来说就是一个普通的关键字,让程序或人能够知道,易读

 子类中访问父类中的成员方法

 总结:成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时再到父类中找,如果父类中也没有则报错

此时子类test与父类test方法构成重载,所以在有继承关系的类中依旧可以重载

 总结:

通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错
通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;如果父类和子类同名方法的原型一致(重写),则只能访问到子类的,父类的无法通过派生类对象直接访问到

super关键字

super.data

super.func()

 注意 

1.只能在非静态方法中使用
2. 在子类方法中,访问父类的成员变量和方法

super()

要了解super()就要先了解子类构造方法

父子父子,先有父再有子,即:子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法

class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name+"正在吃");
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
class Cat extends Animal {
    public String hair;
    public void mew() {
        System.out.println(this.name+"正在叫");
    }
    public Cat(String name,int age,String hair) {
        super(name,age);//显示调用父类构造方法,来初始化子类从父类继承过来的东西
        this.hair=hair;
    }
}
public class Test {
    public static void main(String[] args) {
        Cat cat1=new Cat("家家",12,"白色");
        cat1.eat();
        cat1.mew();
    }
}

使用super()先执行父类构造在执行子类构造,若父类无参或默认构造编译器默认提供隐式的super()调用

注意:

1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
2. 如果父类构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败
3. 在子类构造方法中,super()调用父类构造时,必须是子类构造函数中第一条语句
4. super()只能在子类构造方法中出现一次,并且不能和this()同时出现

super与this对比

super与this有很多相同点但区别还是很明显的,我来总结一下

相同点

1. 都是Java中的关键字
2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在,要注意是this()与super()不能同时存在

不同点

1.this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用

2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性

3.this是非静态成员方法的一个隐藏参数super不是隐藏的参数

4.在构造方法中:this()用于调用本类构造方法super()用于调用父类构造方法,两种调用不能同时在构造方法中出现

5.构造方法中一定会存在super()的调用,用户没有写编译器也会增加,但是this()用户不写则没有

初始化顺序

 总结:

1. 静态代码块先执行,并且只执行一次,在类加载阶段执行
2. 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行

3.父类静态代码块优先于子类静态代码块执行,且是最早执行
4.父类实例代码块和父类构造方法紧接着执行
5.子类的实例代码块和子类构造方法紧接着再执行
6.第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行

protected关键字

protected 继承权限 可以被不同包中的子类访问到

 注意:父类必须是public修饰的

继承方式

Java支持:单继承,多层继承,不同类继承同一个类

 在Java中不支持多继承

如果我们不想让类被其他类继承时我们可以使用final来修饰类

 被finl修饰的类也叫密封类

final修饰变量使其成为常量

 final修饰方法使其不会被重写

继承与组合

继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车

class Student {

}
class Teacher {

}
class School {
    public Student[] student;
    public Teacher[] teacher;
}

将其他类的类型作为类的成员称为组合,一般建议使用组合

 多态 

通俗来说就是多种形态,具体就是去完成某个行为,不同对象去完成产生不同状态,要想了解多态就要了解向上转型和方法重写

向上转型

向上转型就是父类引用引用子类对象,我来举个例子

class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println(this.name+"吃饭");
    }
}
class Cat extends Animal {
    public String hair;
    public void mew() {
        System.out.println(this.name+"正在叫");
    }
}
public class testDemo3 {
    public static void main(String[] args) {
        Animal animal =new Cat();//向上转型 父类引用引用子类对象
        animal.eat();
    }
}

三种常见向上转型

1.直接赋值

 Animal animal =new Cat();

2.方法的参数

 public static void func(Animal animal) {
        animal.eat();
    }
    public static void main(String[] args) {
        Cat cat=new Cat();
        func(cat);//func(new cat());
    }

3.方法的返回值

public static Animal func2() {
        //return new Animal();
        return new Cat();
    }

 向上转型的优点:让代码实现更简单灵活
向上转型的缺陷:不能调用到子类特有的方法

重写 

重写也称为覆盖,覆写,是实现多态的重要条件,返回值和形参都不能改变即外壳不变,核心重写

看一下结果

 可以看到打印的并不是父类方法内容,而是子类方法内容这就涉及到方法的重写了

重写方法的特点

1.方法名称相同

2.方法放回值相同

3.方法的参数列表相同

此时产生了动态绑定也是多态的基础

可以看到在编译的时候还是Animal.eat(),但是在运行的时候变为了子类自己的eat(),在运行时才确定了要调用谁,我们将它称为运行时绑定 

静态绑定:在编译时就确定的方法,比如 重载

理解多态

有了上述知识的掌握我们就可以理解多态了

多态的前提条件

1.必须在继承体系下
2. 子类必须要对父类中方法进行重写
3. 通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法

重写总结

1.静态方法不能重写

2.private修饰的方法不能重写

3.子类的访问修饰限定符要大于父类的

4.被重写的方法返回值类型可以不同,但是必须是具有父子关系的

协变类型 返回值构成父子关系

 5.被final修饰的不能重写

向下转型

  public static void main(String[] args) {
        Animal animal = new Bird();
        Bird bird=(Bird)animal;//向下转型
        bird.fly();
    }

父类引用赋值给子类引用叫向上转型,但是向下转型不安全举个例子

public static void main(String[] args) {
        Animal animal = new Cat();
        Bird bird=(Bird)animal;//向下转型
        bird.fly();
    }

此时代码会报错,如何修改就需要使用instanceof来判断

 public static void main(String[] args) {
        Animal animal = new Cat();
        //判断animal这个引用的对象是否是Bird的对象
        if(animal instanceof Bird) {
            Bird bird=(Bird)animal;//向下转型
            bird.fly();
        }
    }

 多态的优缺点

优点

1.能够降低代码的 "圈复杂度", 避免使用大量的 if - else

圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如果有很多的条件分支或者循环语句, 就认为理解起来更复杂

2.可扩展能力更强
使用多态修改代码成本降低

缺点

代码的运行效率降低

避免在构造方法中调用重写的方法

看一段代码

我们创建两个类, B 是父类, D 是子类. D 中重写 func 方法. 并且在 B 的构造方法中调用 func此时是执行B的func(),还是执行D的func()就不容易看出来,看下结果


 为什么呢?

构造 D 对象的同时, 会调用 B 的构造方法
B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func()
此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0

所以不要在构造方法中调用重写的方法避免写出错误的代码

以上就是有关Java中继承+多态的总结,希望对你有帮助还请点赞评论会持续更新的!Po累Po累

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值