Java 继承 多态

目录

一.继承

 1.1继承的语法:

 1.2继承的方式

  1.3 final修饰词

二 多态

2.1什么是多态

2.2多态的实现条件

2.3动态绑定,静态绑定和向上转型 

2.4向下转型

 2.4重写 

2.5多态的优缺点


一.继承

 继承的概念: 对共性的抽取,实现代码的复用

 1.1继承的语法:

 使用extends关键字 如下:

//Static代表子类,Dog代表父类
public class Static extends Dog{
  //使用extends 关键字 实现子类继承父类的关系
}

当然注意:

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

 继承代码使用场景:

class Animal{//这是我们创建的父类Animal
    //父类成员变量
    public String name = "父类name";
    public int age = 99;  //注意父类中有姓名和年龄

    //父类成员方法
    public void eat(){
        System.out.println(this.name + "父类正在吃饭");
    }
    public void sleep(){
        System.out.println(this.name + "父类正在睡觉"); //父类中还有吃饭和睡觉
    }
}
class Dog extends Animal{//这是我们创建的子类狗类。让它去继承Animal
    public String name = "子类name";
    public int height = 30;

    public void show(){
        System.out.println(name);                              //1.子类的
        System.out.println(this.name);                         //2.子类的
        System.out.println(super.name);                        //3.父类的
        System.out.println("父类成员变量age:"+age);       //4.父类的
        System.out.println("父类成员变量age:"+this.age);    //5.父类的
        System.out.println("子类成员变量height:"+height);      //6.子类的
        System.out.println("子类成员变量height:"+this.height); //7.子类的
        eat();            //8.    //子类
        this.eat();       //9.    //子类
        super.eat();      //10.父类
        sleep();          //11.父类
        this.sleep();     //12.父类
        super.sleep();    //13.父类 
        wangWang();       //14.子类
        this.wangWang();  //15.子类
    }
    public void eat(){
        System.out.println(this.name + "子类正在吃饭");
    }
    void wangWang(){
        System.out.println(this.name + "子类喜欢汪汪叫!");
    }
}
 
public class GJiCheng {
    public static void main(String[] args) {
        //用Dog类实例化一个对象
        System.out.println("--------------------------------");
        Dog dog1 = new Dog();
        dog1.show();
        System.out.println("--------------------------------");
    }
}

在子类方法中 或者 通过子类对象访问成员时

  • 如果访问的成员变量子类中有,优先访问自己的成员变量
  • 如果访问的成员变量子类无,则访问父类继承下来的,如果父类也没有定义,则编译报错
  • 如果访问的成员变量与父类的成员变量同名时,则优先访问自己的 

 成员变量访问遵循就近原则,子类有优先子类的,如果没有再去父类中找

子类构造方法中调用父类的构造方法:

class eat{
    private String name;
    private int age;
    //父类带参数的构造方法
    eat(String name,int age){
        this.age =age;
        this.name =name;
        System.out.println("父类构造方法"+this.name+this.age);
    }
}
//Static代表子类,eat代表父类
public class Static extends eat{
    //子类构造方法中调用·父类的构造方法
    Static(){
        //super里面的参数顺序,个数,类型必需和父类的构造参数一样,否则报错
        super("父类",15);
    }
}

注意:

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

想要访问子类对象的成员或者方法时使用 this.   访问父类对象的成员和方法时 使用 super.

java 封装 (内部类)-优快云博客 这篇博客 我写了有关this关键字和super关键字 让大家更好的去理解

 在继承关系下代码的执行顺序:

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person:构造方法执行");
    }
    {
        System.out.println("Person:实例代码块执行");
    }
    static {
        System.out.println("Person:静态代码块执行");
    }
}
class Student extends Person{
    public Student(String name,int age) {
        super(name,age);
        System.out.println("Student:构造方法执行");
    }
    {
        System.out.println("Student:实例代码块执行");
    }
    static {
        System.out.println("Student:静态代码块执行");
    }
}
public class TestDemo4 {
    public static void main(String[] args) {
        Student student1 = new Student("张三",19);
        System.out.println("===========================");
        Student student2 = new Student("gaobo",20);
    }
}

执行结果:

Person:静态代码块执行
Student:静态代码块执行
Person:实例代码块执行
Person:构造方法执行
Student:实例代码块执行
Student:构造方法执行
===========================
Person:实例代码块执行
Person:构造方法执行
Student:实例代码块执行
Student:构造方法执行

分析执行结果,得出以下结论:

  1. 最先执行父类静态代码块 接着执行子类静态代码块
  2. 父类实例代码块和父类构造方法紧接着执行
  3. 子类的实例代码块和子类的构造方法紧接着在执行
  4. 第二次实例化对象时,父类和子类的静态代码块都不会在执行(静态代码块仅执行一

 1.2继承的方式

        继承的几种方式:

       

       注意 Java中不支持多继承     最好不要继承关系超过三层

  1.3 final修饰词

   final关键可以用来修饰变量、成员方法以及类。

1.final修饰的变量,不可以修改,表示常量


final int n=10;
 n=20; //就会编译报错

2. final修饰的方法,不可以被重写,但可以有多个重载


final void fun(){

}

​

3. 用final修饰的类,不可以被继承

final class Dog{

}
class Animal extends Dog{    //继承final修饰的类就直接报错了

}

4.组合

class Dog{
   //小狗
}
class Cat{
    //小猫
}
class Bird{
    //小鸟
}
class Animal{
    Dog dog = new Dog(); //可以复用小狗的属性和方法
    Cat cat = new Cat();//可以复用小猫的属性和方法
    Bird bird = new Bird();//可以复用小鸟的属性和方法
}
public class Text extends Animal {
    //动物类里面包含了 狗 猫 鸟的属性和方法  只需要继承Animal类就可以直接使用了
}
和继承类似 , 组合也是一种表达类之间关系的方式 , 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法
( 诸如 extends 这样的关键字 ), 仅仅是将一个类的实例作为另外一个类的字段。

组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,
一般建议:能用组合尽量用

二 多态

2.1什么是多态

        父类引用 子类对象不一样的时候,调用重写的方法,所表现出的行为不一样,这种思想————多态

        (大白话)不同对象的引用,调用同一个方法,产生的结果不一样————多态

2.2多态的实现条件

 java中实现多态,必须要满足如下几个条件,缺一不可:

1.继承关系

   向上转型

2. 方法重写

    子类必须重写父类的方法

3.通过父类对象引用调用重写的方法

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

2.3动态绑定,静态绑定和向上转型 

class Animal{
    public Animal() {
    }

    public void eat(){
        System.out.println("父类正在吃饭");
    }
}
class Dog extends Animal{
    @Override   //重写父类 eat方法
    public void eat(){
        System.out.println("子类Dog在吃饭");
    }
}
class cat extends Animal{
    @Override   //重写父类 eat方法
    public void eat() {

        System.out.println("子类CAT在吃饭");

    }
}
public class JiCheng {
    //产生多态方法二
    public static void fun(Animal animal){
        animal.eat();
    }
    public static void main(String[] args) {
        //产生多态方法一
        //创建实例局部变量来调用重写的方法
        Animal animal1 = new Dog();
        Animal animal2= new cat();
        animal1.eat();
        animal2.eat();
        //产生多态方法二
        //不需要创建实例 直接调用方法配合 上面的静态fun方法 来调用重写的方法
        fun(new Animal());
        fun(new Dog());
        fun(new cat());
    }
}

重点:

    方法一:属于静态绑定,没有编译之前已经确定的具体具体调用那个方法

    方法二: 属于动态绑定  编译期间不能确定方法的行为,在程序运行时才能确定具体调用哪个类

上面的两种方法均产生了向上转型 : 

        方法一 直接赋值:子类对象赋值给父类对象

        方法二 方法传参:形参为父类型引用,可以接收任意子类的对象

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

2.4向下转型

向上转型之后可以当成父类对象使用,若需要调用子类特有的方法,则需要将父类对象再还原为子类对象。这就称作向下转型。

子类类型 子类引用名 = (子类类型) 父类引用
Dog dog = (Dog) animal; 

 2.4重写 

重写 (override) :也称为覆盖。重写是子类对父类非静态、非 private 修饰,非 final 修饰,非构造方法等的实现过程
进行重新编写 , 返回值和形参都不能改变 即外壳不变,核心重写! 重写的好处在于子类可以根据需要,定义特定
于自己的行为。 也就是说子类能够根据需要实现父类的方法。

 方法重写的规则

  • 子类在重写父类的方法时,必须和父类的 返回值类型 (返回类型也可以是父子关系 方法名 (参数列表) 要完全一致
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方 法就不能声明为protected
  • 父类被static、private修饰的方法、构造方法都不能被重写。
  • 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验.

重写与重载的区别:

  • 重载(Overload)指的是在同一个类中,根据方法的参数列表的不同,定义多个具有相同名称但参数类型或个数不同的方法。重载的方法具有相同的名称,但方法签名不同。

  • 重写(Override)指的是子类重新定义和实现了从父类中继承的方法。重写的方法具有与父类方法相同的名称、参数列表和返回类型。

即:方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现 

2.5多态的优缺点

多态的优点:

降低代码的圈复杂度

        一段代码中条件语句和循环语句越多,说明该代码圈复杂度越高

可扩展能力更强

        想更改功能,只需在进行方法重写

 多态缺陷:

        1. 属性没有多态性

                当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

        2. 构造方法没有多态性
class B {
    public B() {
        // do nothing
        func();
    }
    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
    private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}
public class Test {
    public static void main(String[] args) {
    D d = new D();
    }
}

//最终结果 为0,
  • 构造 D 对象的同时, 会调用 B 的构造方法.
  • B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  • 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0. 如果具备多态性,num的值应该是1.
  • 所以在构造函数内,尽量避免使用实例方法,除了finalprivate方法。

注意:

尽量不要在构造器中调用方法 ( 如果这个方法被子类重写 , 就会触 发动态绑定, 但是此时子类对象还没构造完成 ), 可能会出现一些隐藏的但是又极难发现的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值