Java中类的继承、继承的自动转型以及多态详解

1.类的继承

  • 我们生活中包含所属关系的事物,在代码层面来说就是一种继承关系
  • 类的继承格式:public class  子类(派生类)   extends  父类(超类/基类){  }
  • 每一个子类它的父类都是唯一的
  • 接口支持多继承,而类只能单继承

Java中继承的特点:

  1. 单继承(一对多):每个类的父类是唯一的,一个父类可以有多个子类,多个子类只能有一个父类
  2. 多层次继承:父类可以有父类,子类可以有子类
  • 继承的作用:子类会继承父类中所有的属性和方法,但是私有属性和方法不能直接使用(可以通过set,get方法来使用)

2.方法重写/覆盖(前提:在不同类中,且有继承关系)

  • 方法重写/覆盖的目的:父类里面设计好了几个方法,在子类里面也要用,但是从父类继承过来的方法,它的功能在子类中已经不适用了/不能满足子类的需求(父类功能不能满足子类需求),但是整个方法的定义还是相同,只是去修改它的功能。

方法重写/覆盖的两个条件:

  1. 有继承关系的两个类之间(因为重写是重写父类里面的方法)
  2. 访问修饰符大于等于父类,方法名,返回值类型,参数类型(个数,顺序)完全一样

重写方法有两种情况:

  1. 完全覆盖/替换掉了父类的功能
  2. 在原有父类的功能的基础上去增加新的功能(保留父类的功能):

                    a.保留父类原有的功能:调用父类的方法

                                     super(超类):表示当前类的父类对象     this:表示当前类的对象

                    b. 重写方法后新添加的功能

方法重载(前提:在同一类中):可以让同一个方法实现不同的功能

                           1.方法名相同/重名 

                           2.形参列表不同(方法的参数类型,个数,顺序不一样)

构造方法重载的特点:利用不同的构造方法可以创建出不同需求的对象。

package com.com;

public class Student {
    // 类的结构:类里面可以定义属性和方法
    // 属性:  格式: 访问修饰符  数据类型 变量名/属性名;
    // 4种可见修饰符:public protected friendly(default)  private
    // 加了private修饰符,只能在当前类访问
    private  String name;
    private int score;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public void study(){
        System.out.println(name + "正在学习!学分是:" + score);
    }
}
package com.com;

/**
 *  定义大学生类
 */
// 继承的格式: public class 子类 extends 父类
public class UniversityStudent extends Student{
    public void printName(){
        System.out.println("Name = " + getName());
    }

    // 方法重写
    public void study(){
        // 保留父类原有的功能
        super.study(); // super:表示当前类的父类对象
        // 重写方法后添加新的功能
        System.out.println("子类重写父类的方法!");
    }
}
package com.com;

public class Manage {
    // 主函数
    public static void main(String[] args) {
        // 创建对象
        UniversityStudent us = new UniversityStudent();
        us.setName("小李");
        us.setScore(80);
        us.printName(); // Name = 小李
        us.study(); // 小李正在学习!学分是:80
                    // 子类重写父类的方法!
        System.out.println("学生的学分是:" + us.getScore()); // 学生的学分是:80
    }
}

3.继承的自动转型

1.向上转型(自动转型):把子类对象转成父类类型

  • 前提:B  extends  A  ===>  才能A   a   =    new   B( );
  • 如果没有这种继承关系,就不存在什么自动转型,这种创建对象的过程我们把它叫做向上转型/自动转型。
  • 父类引用指向子类对象,可以把子类对象转成父类类型
  • 当我们把一个子类对象转型成父类类型之后,拿着这个对象去调用的每一个方法,它都是从父类中开始检测有没有这个方法,没有肯定是不能执行的,如果有,看子类有没有重写,如果子类重写优先执行子类重写的方法,因为这个对象是从子类转型过来的。
  • 自动转型后,该对象只能调用子类中重写的方法
  • 它可以在程序运行过程中,给你的对象向上转型。

2.向下转型(强制转型):把父类对象转成子类类型

  • 前提:强制转型的前提是该对象已经自动转型。
  • B  b  =  (B)a;把父类对象转成子类类型。
  • 在对象进行向下转型时,必须首先发生对象向上转型,否则运行时将会出现对象转换异常/强制类型转换异常(ClassCastException
  • 当我们把一个父类对象转成子类类型之后,拿着这个对象去调用每一个方法,它都是从子类中开始找有没有定义过这个方法,如果子类没有,看从父类里面有没有继承过。
package com.extendsoop;

public class Student {
    // 属性
    private String name;
    // 方法
    public void study(){
        System.out.println(name + "正在学习!");
    }

    public void setName(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }
}
package com.extendsoop;

// 定义大学生类: public class 子类(派生类)  extends 父类(超类/基类){}
// 接口支持多继承,类只能单继承
// 子类会继承父类中所有的属性和方法,但是私有属性和方法不能直接使用
public class UniversityStudent extends Student{
    // 方法
    public void printName(){
        System.out.println("Name = " + getName());
    }
    // 方法重写(Override)  从父类继承过来的方法已经不能满足子类的需求
    public void study(){
        // 1.保留父类原有的功能:调用父类的方法
        // this:表示本类对象/当前类对象  super:表示当前类的父类对象  每一个子类它的父类都是唯一的
        super.study();
        // 2.重写方法后添加新的功能
        System.out.println("子类重写父类的学习方法!");
    }
}
package com.extendsoop;

public class Manage {
    // 主函数
    public static void main(String[] args) {
        //  UniversityStudent extends Student
        // 向上转型/自动转型(前提:继承):把子类对象转成父类类型
        // 父类引用指向子类对象
        Student st = new UniversityStudent();
        st.study(); // 调用的是子类重写的方法
        st.setName("Rocket");
        // st.printName();  直接报错,因为在父类Student中没有这个方法

        // 强制转型/向下转型(前提:该对象已经发生自动转型/向上转型):把父类对象转成子类类型
        UniversityStudent us = (UniversityStudent)st;
        us.printName();
        us.study(); // 调用的是子类重写的方法
    }
}

 4.继承的自动转型的应用场景(多态的应用)

  • 方法需要什么类型的参数就创建什么类型的对象给它。
  • 每个类都会默认继承Object类,Object是所有类的父类。
  • 我们调用一个方法,它的参数类型除了可以给它本身这种类型之外,实际上只要是它的子类都可以。
  • 自动转型/向上转型的好处:使方法的参数类型变得不唯一了。
package com.extendsoop;

public class Student {
    // 属性
    private String name;
    // 方法
    public void study(){
        System.out.println(name + "正在学习!");
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}
package com.extendsoop;

// 定义大学生类: public class 子类(派生类)  extends 父类(超类/基类){}
// 接口支持多继承,类只能单继承
// 子类会继承父类中所有的属性和方法,但是私有属性和方法不能直接使用
public class UniversityStudent extends Student{
    // 方法
    public void printName(){
        System.out.println("Name = " + getName());
    }
    // 方法重写(Override)  从父类继承过来的方法已经不能满足子类的需求
    public void study(){
        // 1.保留父类原有的功能:调用父类的方法
        super.study();
        // 2.重写方法后添加新的功能
        System.out.println("子类重写父类的学习方法!");
    }
}
package com.extendsoop;

// 定义老师类
// 每个类都会默认继承Object类,Object类是所有类的父类
public class Teacher extends Object {
    public void teach(Student s){
        s.study();
    }
}
package com.extendsoop;

public class Manage {
    // 主函数
    public static void main(String[] args) {
        Teacher t = new Teacher(); // t.teach(Student s);
//        Student st = new Student();
//        t.teach(st); // 需要什么类型的参数就创建什么样的对象给它
        // 创建子类对象
        UniversityStudent us = new UniversityStudent();
//      调用的是子类重写study的方法
        t.teach(us); // 在程序运行的过程中,发生了自动转型/向上转型
    }
}

5.类的继承之构造方法

  • 子类不会继承父类的构造方法,父类的构造方法是不能被子类继承的,因为构造方法名要跟当前的类名保持一致。
  • 父类中如果定义了有参构造方法,那么就要在子类的构造方法中,一定要调用一次父类的构造方法,也可以在父类中重新定义一个无参构造方法,否则会报错!
  • 父类中的无参构造方法会被子类默认调用。
  • 调用父类的构造方法:super(参数1,参数2...);

 

 

2. 父类中如果定义了有参构造方法,那么一定要在子类的构造方法中调用一次父类的构造方法!

package com.extend.createmethod;

public class Student {
    // 属性
    private String name;
    // 构造方法
    public Student(String name){
       this.name = name;
    }
}
package com.extend.createmethod;

public class UniversityStudent extends Student{
    // 在子类的构造方法中,一定要调用一次父类的构造方法,否则会报错
    // 父类中的无参构造方法会被默认调用
    // 调用父类的构造方法:super(参数1,参数2,,,);
    public UniversityStudent(){
        super("");
    }
}

3.如何避免在子类中手动调用父类的构造方法?

  • 在父类中再定义一个无参构造方法,因为父类的无参构造方法会被子类默认调用!
package com.extend.createmethod;

public class Student {
    // 属性
    private String name;
    // 构造方法
    public Student(String name){
       this.name = name;
    }
    // 无参构造方法
    public Student(){
    }
}
package com.extend.createmethod;

public class UniversityStudent extends Student{
    // 在子类的构造方法中,一定要调用一次父类的构造方法,否则会报错
    // 父类中的无参构造方法会被默认调用
    // 调用父类的构造方法:super(参数1,参数2,,,);
//    public UniversityStudent(){
//        super("");
//    }
}

4.创建子类对象(调用构造方法),先调用的是父类的构造方法,然后才调用子类的构造方法,其实先创建的是父类对象,父类中的无参构造方法会被子类默认调用

package com.extend.createmethod;

public class Student {
    // 属性
    private String name;
    // 构造方法
    public Student(String name){
       this.name = name;
    }
    // 无参构造方法
    public Student(){
        System.out.println("父类的无参构造方法被调用!");
    }
}
package com.extend.createmethod;

public class UniversityStudent extends Student{
    // 在子类的构造方法中,一定要调用一次父类的构造方法,否则会报错
    // 父类中的无参构造方法会被默认调用
    // 调用父类的构造方法:super(参数1,参数2,,,);
    public UniversityStudent(){
      //  super("");   在子类的构造方法中会默认调用父类的无参构造方法
        System.out.println("子类的无参构造方法被调用!");
    }
}
package com.extend.createmethod;

public class Manage {
    // 主函数
    public static void main(String[] args) {
        // 创建子类对象   调用的是子类的构造方法
       // 用子类创建对象(调用构造方法),先调用的是父类的构造方法,然后才调用子类的构造方法,
        // 其实先创建的是父类对象,父类中的无参构造方法会被子类默认调用
        UniversityStudent us = new UniversityStudent();
    }
}

6.面向对象三大特征之三:多态

a.多态的概述

1、什么是多态?

  • 同类型的对象,执行同一个行为,会表现出不同的行为特征。

2、多态的常见形式:

  • 父类类型  对象名称  =  new   子类构造器;
  • 接口   对象名称  =   new   实现类(子类)构造器;

3、多态中成员访问特点

  • 方法调用:编译看左边,运行看右边(这样才能看到不同对象执行不同行为)。
  • 变量调用:编译看左边,运行也看左边,因为变量没有多态的概念(多态侧重行为多态)。

4、多态的前提:

  • 有继承/实现关系;有父类引用指向子类对象;有方法重写。
package com.gch.d1_polymorphic;

/**
    父类
 */
public class Animal {
    public String name = "动物名称";
    public void run(){
        System.out.println("动物可以跑~~~");
    }
}
package com.gch.d1_polymorphic;

public class Dog extends Animal {
    public String name = "狗名称";
    @Override
    public void run() {
        System.out.println("狗跑的贼溜~~~");
    }
}
package com.gch.d1_polymorphic;

/**
 * 定义乌龟类
 */
public class Tortoise extends Animal{
    public String name = "乌龟名称";
    @Override
    public void run() {
        System.out.println("乌龟跑的非常慢!!");
    }
}
package com.gch.d1_polymorphic;

public class Test {
    public static void main(String[] args) {
        // 目标:先认识多态的形式
        // 父类  对象名称 = new  子类构造器;
        // 1.狗这个类型本身也是动物类型
        // 2.小范围类型的变量可以赋给大范围类型的变量
        Animal a = new Dog();
        a.run(); // 方法调用:编译看左边,运行看右边
        System.out.println(a.name); // 变量调用:编译看左,运行也看左
        System.out.println();
        Animal a1 = new Tortoise();
        a1.run(); // 方法调用:编译看左,运行看右
        System.out.println(a1.name); // 变量调用:编译看左,运行也看左
    }
}

 b.多态的优势与劣势

1.多态的优势

  • 在多态形式下,右边对象可以实现解耦合(右边对象想换就换),便于扩展和维护。
  • Aniaml  a   =   new   Dog()  /  new Tortoise();
  • a.run();   // 后续业务行为随对象而变,后续代码无需修改
  • 定义方法的时候,使用父类类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。

2.多态的劣势

  • 劣势下不能使用子类的独有功能,因为多态下方法的调用:编译看左边。
  • 在多态劣势下不能调用子类的独有行为。
package com.gch.d2_polymorphic_advantage;

/**
    定义父类:动物类
 */
public class Animal {
    public String name = "动物名称";
    public void run(){
        System.out.println("动物可以跑~~~");
    }
}
package com.gch.d2_polymorphic_advantage;

/**
    定义狗类
 */
public class Dog extends Animal {
    public String name = "狗名称";
    @Override
    public void run() {
        System.out.println("狗跑的贼溜~~~");
    }

    /**
       独有功能
     */
    public void lookDoor(){
        System.out.println("狗在看门~~~");
    }
}
package com.gch.d2_polymorphic_advantage;

/**
 * 定义乌龟类
 */
public class Tortoise extends Animal {
    public String name = "乌龟名称";
    @Override
    public void run() {
        System.out.println("乌龟跑的非常慢!!");
    }
}
package com.gch.d2_polymorphic_advantage;

public class Test {
    public static void main(String[] args) {
        Animal d = new Dog();
        go(d);
//        d.lookDoor(); // 直接报错,因为父类Animal没有这个方法
        System.out.println();
        Animal t = new Tortoise();
        go(t);
    }

    /**
     * 希望这个方法可以接收一切子类动物对象
     * 这样方法的扩展性更强,也更便利
     * @param a:父类动物类型的对象
     */
    public static void go(Animal a){
        System.out.println("预备~~~");
        a.run(); // 对象回调:我把对象送给你,最终你还是调我对象的方法
        System.out.println("结束~~~");
    }
}

 c.多态下引用数据类型的类型转换

1.多态形式下引用数据类型的类型转换

  • 自动类型转换(向上转型):从子到父
  • 强制类型转换(向下转型):从父到子,从父到子必须进行强制类型转换,否则报错:子类  对象名称    =   (子类)父类类型的变量

2.强制类型转换的作用:强制类型转换可以转换成真正的子类类型,可以解决多态下的劣势,从而实现调用子类独有的功能。

3.注意:有继承/实现关系的2个类型就可以在编译阶段进行强制类型转换,编译无问题;但是,如果转型后的类型对象真实对象的类型不是同一种类型,那么在运行代码时,就会出现ClassCastException(类型转换异常)

  • Animal c  =  new  Cat();
  • Dog  d   =  (Dog)c; // 出现异常 ClassCastException

4.Java建议强制转换前使用instanceof判断当前对象的真实类型,再进行强制转换。

  • 对象变量名   instanceof   真是类型
  • 判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,是则返回true,反之false。
package com.gch.d3_polymorphic_convert;

/**
    定义父类:动物类
 */
public class Animal {
    public String name = "动物名称";
    public void run(){
        System.out.println("动物可以跑~~~");
    }
}
package com.gch.d3_polymorphic_convert;

/**
    定义狗类
 */
public class Dog extends Animal {
    public String name = "狗名称";
    @Override
    public void run() {
        System.out.println("狗跑的贼溜~~~");
    }

    /**
       独有功能
     */
    public void lookDoor(){
        System.out.println("狗在看门~~~");
    }
}
package com.gch.d3_polymorphic_convert;

/**
    定义乌龟类
 */
public class Tortoise extends Animal {
    public String name = "乌龟名称";
    @Override
    public void run() {
        System.out.println("乌龟跑的非常慢!!");
    }

    /**
        独有功能
     */
    public void layEggs(){
        System.out.println("乌龟在下蛋~~~");
    }
}
package com.gch.d3_polymorphic_convert;

public class Test {
    public static void main(String[] args) {
      // 目标:学习多态形式下的类型转换机制
        // 自动类型转换(向上转型)
        Animal a = new Dog();
        a.run();
//        a.lookDoor();  直接报错,多态下无法调用子类的独有功能

        // 强制类型转换(向下转型):可以实现调用子类的独有功能
        Dog d = (Dog) a;
        d.lookDoor();

        // 注意:多态下直接进行强制类型转换,可能出现类型转换异常
        // 规定:有继承或者实现关系的2个类型就可以强制类型转换,运行时可能出现问题
        // 动物类型的a变量和乌龟类型的t变量有继承关系
//        Tortoise t = (Tortoise) a; // 编译不报错,运行报错:java.lang.ClassCastException(类型转换异常)


        // 建议强制转换前,使用instanceof关键字先先判断变量指向对象的真实类型,再强制类型转换。
        if(a instanceof Tortoise){
            Tortoise t = (Tortoise) a;
            t.layEggs();
        }else if(a instanceof Dog){
            Dog d1 = (Dog) a;
            d1.lookDoor();
        }
        System.out.println("------------------");
        Animal a1 = new Tortoise();
        go(a1);

    }

    public static void go(Animal a){
        System.out.println("预备~~~");
        // 独有功能
        if(a instanceof Tortoise){
            Tortoise t = (Tortoise) a;
            t.layEggs();
        }else if(a instanceof Dog){
            Dog d1 = (Dog) a;
            d1.lookDoor();
        }
        System.out.println("结束~~~");
    }
}

 d.多态的综合案例

package com.gch.d4_polymorphic_test;

/**
    定义USB接口
 */
public interface USB {
     void insert(); // 插入
     void pullOut(); // 拔出
}
package com.gch.d4_polymorphic_test;

/**
   鼠标实现类(子类)
 */
public class Mouse implements USB{
    private String name;

    public Mouse(String name) {
        this.name = name;
    }

    @Override
    public void insert() {
        System.out.println(name + "成功的接入设备~~~");
    }

    @Override
    public void pullOut() {
        System.out.println(name + "成功的拔出设备~~~");
    }

    /**
        独有功能
     */
    public void click(){
        System.out.println(name + "双击点亮小红心~~~");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.gch.d4_polymorphic_test;

/**
    键盘实现类(子类)
 */
public class KeyBoard implements USB{
    private String name;

    public KeyBoard(String name) {
        this.name = name;
    }

    @Override
    public void insert() {
        System.out.println(name + "成功的接入设备~~~");
    }

    @Override
    public void pullOut() {
        System.out.println(name + "成功拔出设备~~~");
    }

    /**
        独有功能
     */
    public void control(){
        System.out.println(name + "正在控制电脑~~~");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.gch.d4_polymorphic_test;
/**
    定义电脑类
 */
public class Computer {
    /**
        提供一个安装的入口:行为。
     */
    public void installUSB(USB u){
        u.insert();

        // 独有功能
        if(u instanceof Mouse){
            Mouse m = (Mouse) u;
            m.click();
        }else if(u instanceof KeyBoard){
            KeyBoard k = (KeyBoard) u;
            k.control();
        }

        u.pullOut();
    }
}
package com.gch.d4_polymorphic_test;

/**
   目标:USB设备模拟
   1.定义USB接口:接入  拔出
   2.定义两个USB的实现类分别代表:鼠标、键盘
   3.创建一个电脑对象,再创建USB设备对象,安装启动
 */
public class Test {
    public static void main(String[] args) {
        // a.创建电脑对象
        Computer c = new Computer();
        // b.创建USB设备对象
        USB m = new Mouse("ROG鼠标");
        c.installUSB(m);
        System.out.println();
        USB k = new KeyBoard("罗技键盘");
        c.installUSB(k);
    }
}

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Surpass余sheng军

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值