Java面向对象-多态

本文详细介绍了Java中的方法重写概念、规则,以及与重载的区别。通过实例展示了如何在Dog和Penguin类中重写父类方法实现多态,并讨论了Object类中的toString方法重写。此外,文章还涵盖了抽象类、抽象方法、向上转型、向下转型和instanceof运算符的使用。
摘要由CSDN通过智能技术生成

多态



前言

实现方法重写,深入理解继承相关概念,了解Object类,使用重写实现多态机制,使用向上、向下转型以及使用instanceof运算符


一、方法重写的概念

父类中定义的方法,无法输出子类中特有的属性

方法的重写或方法的覆盖

1)子类根据需求对从父类继承的方法

2)重写时,可以用super.方法的方式来保留父类的方法

3)构造方法不能被重写

二、方法重写的规则

1)方法名相同

2)参数列表相同

3)返回值类型相同或者是其子类

4)访问权限不能严于父类

5)父类的静态方法不能被子类覆盖为非静态方法,父类的非静态方法不能被子类覆盖为静态方法

(保持一致)

6)子类可以定义与父类同名的静态方法,以便在子类中隐藏父类的静态方法

(注:静态方法中无法使用super)

7)父类的私有方法不能被子类覆盖

8)不能抛出比父类方法更多的异常

三、方法重写与重载的比较

在这里插入图片描述

四、方法重写案例

public class Pet{
    // 定义Dog类、Penguin类、Cat类、...等类中相同的代码
    private String name;
    private int health;
    private int love;

    public Pet() {
    }

    public Pet(String name, int health, int love) {
        this.name = name;
        this.health = health;
        this.love = love;
    }

    public String getName() {
        return name;
    }

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

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        this.health = health;
    }

    public int getLove() {
        return love;
    }

    public void setLove(int love) {
        this.love = love;
    }

    // 定义一个方法输出对象的name、health、love属性值
    public void printInfo(){
        System.out.println("昵称:"+this.getName()+",健康值:"+this.getHealth()+",亲密度:"+this.getLove());
    }
}
public class Dog extends Pet {
    // 这里只需要定义Dog类中特有的属性
    private String strain; // 品种

    public Dog() {
    }

    public Dog(String name, int health, int love, String strain) {
        super(name, health, love); // super关键字
        this.strain = strain;
    }

    public String getStrain() {
        return strain;
    }

    public void setStrain(String strain) {
        this.strain = strain;
    }

    // 重新定义输出dog类对象信息的方法
    public void printInfo(){
        //对于name、health、love属性值,父类Pet中已经做出了输出操作,所有可以调用父类Pet中的printInfo()方法
        super.printInfo();
        // 加一个输出Dog类中特有属性
        System.out.println("品种:"+this.getStrain());
    }
    
}
public class Test {

    public static void main(String[] args) {

        // 使用有参构造方法创建Dog类对象
        Dog dog1 = new Dog("来福",100,100,"哈士奇");
        // 调用Pet类中的printInfo()方法输出dog1对象的信息
        // 父类Pet中的printInfo()方法只能输出dog1对象的name、health、love的属性值
        // 不能够输出dog1对象的strain信息,也就说明Pet类中的printInfo()方法不足以满足子类使用,这时候可以在子类中重新定义输出方法
        dog1.printInfo();
        
    }
}

五、Object类

Object类是所有类的父类

Object类被子类经常重写的方法
在这里插入图片描述

重写toString()方法

public class Student {

    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class StudentTest {
    public static void main(String[] args) {

        // 创建两个Student对象
        Student student1 = new Student("张三",20);
        Student student2 = new Student("张三",20);

        // 直接输出两个对象
        System.out.println("student1 = " + student1);
        System.out.println("student2 = " + student2);
        // 输出结果
        // student1 = methodagain.demo02.Student@1b6d3586
        // student2 = methodagain.demo02.Student@4554617c

        System.out.println("------ ------ ------ ------ ------ ------");

        // 通过Student类对象调用toString()方法输出信息
        String result1 = student1.toString(); // Student类中没有,默认继承Object类中的toString()方法
        String result2 = student2.toString();
        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
        // 输出结果
        // result1 = methodagain.demo02.Student@1b6d3586
        // result2 = methodagain.demo02.Student@4554617c

        System.out.println("------ ------ ------ ------ ------ ------");
        // getClass().getName() + '@' + Integer.toHexString(hashCode())
        // 自行调用方法实现输出对象的地址值
        String result3 = student1.getClass().getName() + '@' + Integer.toHexString(student1.hashCode());
        System.out.println("result3 = " + result3);
        // 输出结果
        // result3 = methodagain.demo02.Student@1b6d3586

        /*
         * 上面代码的操作输出的都是对象在内存中的地址值,输出无意义
         * 我们希望直接输出对象名或者通过对象名调用toString()方法,输出的是对象的所有属性值
         * 由此说明,父类(Object)中的toString()方法不足以满足子类的使用,所以可以对父类中的toString()方法进行重写
         */

    }
}
public class Student {

    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    /*public String toString() {
        return "qwert";
    }*/ // 第一次重写toString()方法

    public String toString() {
        return "姓名:"+this.getName()+"年龄:"+this.getAge();
    } // 第二次重写toString()方法
}
public class StudentTest {
    public static void main(String[] args) {

        // 创建两个Student对象
        Student student1 = new Student("张三",20);
        Student student2 = new Student("张三",20);

        // 直接输出两个对象
        System.out.println("student1 = " + student1);
        System.out.println("student2 = " + student2);
        // 输出结果
        // student1 = qwert 第一次重写输出结果
        // student2 = qwert
        // student1 = 姓名:张三年龄:20 第二次重写输出结果
        // student2 = 姓名:张三年龄:20

        System.out.println("------ ------ ------ ------ ------ ------");

        // 通过Student类对象调用toString()方法输出信息
        String result1 = student1.toString(); // Student类中没有,默认继承Object类中的toString()方法
        String result2 = student2.toString();
        System.out.println("result1 = " + result1);
        System.out.println("result2 = " + result2);
        // 输出结果
        // result1 = qwert
        // result2 = qwert
        // result1 = 姓名:张三年龄:20
        // result2 = 姓名:张三年龄:20

        System.out.println("------ ------ ------ ------ ------ ------");
        // getClass().getName() + '@' + Integer.toHexString(hashCode())
        // 自行调用方法实现输出对象的地址值
        String result3 = student1.getClass().getName() + '@' + Integer.toHexString(student1.hashCode());
        System.out.println("result3 = " + result3);
        // 输出结果
        // result3 = methodagain.demo02.Student@1b6d3586
        // 这里严格调用输出地址的方法,所以输出还是地址值
        
    }
}

六、多态的概念和案例

1、多态案例

// 定义父类Pet类,默认继承Object类
public class Pet {

    // 定义子类中共有的属性和方法
    private String name;
    private int health;
    private int love;

    public Pet() {
    }

    public Pet(String name, int health, int love) {
        this.name = name;
        this.health = health;
        this.love = love;
    }

    public String getName() {
        return name;
    }

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

    public int getHealth() {
        return health;
    }

    public void setHealth(int health) {
        if (health > 0) {
            this.health = health;
        } else {
            this.health = 60;
        }
    }

    public int getLove() {
        return love;
    }

    public void setLove(int love) {
        this.love = love;
    }

    // 重写toString()方法
    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                ", health=" + health +
                ", love=" + love +
                '}';
    }

    // 定义一个方法输出宠物的name health love属性值
    // this.getName() 与 this.name 效果一样
    // 因为private修饰属性在本类中可以调用
    public void printInfo() {
        System.out.println("昵称:"+this.getName()+
                ",健康值:"+this.getHealth()+
                ",亲密度:"+this.getLove());
    }
}
public class Dog extends Pet {

    // 定义Dog类中特有的属性
    private String strain;

    public Dog() {
        // 默认调用父类Pet类中的无参构造方法
    }

    public Dog(String name, int health, int love, String strain) {
        super(name, health, love);
        this.strain = strain;
    }

    public String getStrain() {
        return strain;
    }

    public void setStrain(String strain) {
        this.strain = strain;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "strain='" + strain + '\'' +
                '}';
    }

    // 重写Pet类中的printInfo()方法
    public void printInfo() {
        // 调用父类的printInfo
        super.printInfo();
        // 添加输出Dog类中的strain属性值
        System.out.println("品种:"+this.getStrain());
    }

    public void eat() {
        System.out.println("狗狗吃狗粮");
    }
}
public class Penguin extends Pet {

    private String sex;

    public Penguin() {
    }

    public Penguin(String name, int health, int love, String sex) {
        super(name, health, love);
        this.sex = sex;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Penguin{" +
                "sex='" + sex + '\'' +
                '}';
    }

    // 重写Pet父类中的printInfo()方法
    public void printInfo() {
        super.printInfo();
        // 添加输出Penguin类中sex属性的语句
        System.out.println("性别:"+this.getSex());
    }

    // 定义一个方法
    public void swimming() {
        System.out.println("企鹅会游泳");
    }

}

2、向上转型

<父类型> <引用变量名> = new <子类型>();

注意:

1)此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法,不是父类的方法

2)此时通过父类引用变量无法调用子类特有的方法

public class Test02 {
    public static void main(String[] args) {

        // 创建Dog对象
        // 父类的引用(对象名/变量名)指向子类的实例(对象)
        // 即:向上转型
        Pet pet = new Dog("一千",100,100,"萨摩耶");
        // 写的时候是从左向右写,看的时候是从右向左看

        // 使用pet对象调用printInfo()方法,调用的是Dog类重写后的printInfo()方法,也能输出品种
        pet.printInfo();
        // 输出结果
        // 昵称:一千,健康值:100,亲密度:100
        // 品种:萨摩耶

        System.out.println("------ ------ ------");

        // 将pet引用指向Penguin类对象
        pet = new Penguin("帝王",100,100,"母");
        // 使用pet对象调用printInfo()方法,调用的是Penguin类重写后的printInfo()方法,也能输出性别
        pet.printInfo();
        // 输出结果
        // 昵称:帝王,健康值:100,亲密度:100
        // 性别:母

        // 方法重写是实现多态的前提

    }
}

3、多态的概念

同一个父类引用,指向的不同的子类实例,执行操作不一样

操作不一样,体现在子类重写的方法中,所以说方法重写是实现多态的前提

4、向下转型

<子类型> <引用变量名> = (<子类型>) <父类型的引用变量>;

注意:

在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常

5、instanceof运算符

public class Test03 {
    public static void main(String[] args) {

        Pet pet = new Dog("一千",100,100,"萨摩耶");
        pet.printInfo();
        // 输出结果
        // 昵称:一千,健康值:100,亲密度:100
        // 品种:萨摩耶

        // 使用pet对象调用Dog类中特有的方法eat()
        // 父类的引用无法直接调用子类中特有的方法
        // pet.eat(); 报错

        // 如果要调用,需要向下转型(理解为基本数据类型中的强制类型转换)
        // 向下转型
        // 子类的引用指向父类的引用
        Dog dog = (Dog)pet;
        dog.eat();
        // 输出结果 狗狗吃狗粮

        System.out.println("------ ------ ------");

        pet = new Penguin("帝王",100,100,"母");
        // ------ ------ ------ ------ ------ ------
        pet.printInfo();
        // 输出结果
        // 昵称:帝王,健康值:100,亲密度:100
        // 性别:母
        Penguin penguin1 = (Penguin)pet;
        penguin1.swimming();
        // 输出结果 企鹅会游泳
        // ------ ------ ------ ------ ------ ------

        // 如果在pet指向Penguin对象后进行如下操作:
        // Dog dog2 = (Dog)pet;
        // dog2.eat();
        /*
        * 报错:ClassCastException 类型转换异常
        * */
        // 因为pet指向的是Penguin类对象,在向下转型中应该将其转换为Penguin对象,不能将其转换为其它子类对象

        // 为了避免在向下转型中过程中,出现类型转换异常
        // 可以使用instanceof运算符进行类型判断,instanceof运算符的结果是布尔值
        if (pet instanceof Dog) {
            Dog dog2 = (Dog)pet;
            dog2.eat();
        } else if(pet instanceof Penguin) {
            Penguin penguin2 = (Penguin)pet;
            penguin2.swimming(); // 输出结果 企鹅会游泳
        }

    }
}

七、抽象类与抽象方法

1、抽象方法的概念

使用abstract修饰的方法为抽象方法

2、抽象方法的特点

1)抽象方法没有方法体

2)抽象方法所在的类要定义为抽象类

3)抽象方法必须在子类中重写,如果子类不重写父类中的抽象方法,那么子类也要定义为抽象类

(一般都会在子类中重写)

3、抽象类的概念

使用abstract修饰的类为抽象类

4、抽象类的特点

1)抽象类不能直接实例化(不能直接通过new的形式来创建抽象类的引用(对象))

因为实例化抽象类没有意义,所以我们一般将父类定义为抽象类

2)抽象类中可以没有抽象方法,也可以定义抽象方法,可以有普通方法

3)如果抽象类中定义可多个抽象方法,那么子类中必须重写这个抽象类中所有的抽象方法,否则子类也要定义为抽象类

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值