• Java方法重写与多态


    一、方法重写的概念

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

    方法的重写或方法的覆盖

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

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

    3)构造方法不能被重写

    二、方法重写的规则

    1)方法名相同

    2)参数列表相同

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

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

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

    (保持一致)

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

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

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

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

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

    比较项位置方法名参数表返回值访问修饰符
    方法重载同类相同不相同无关无关
    方法重写子类相同相同相同或是其子类不能比父类严格

    四、方法重写案例

    package com.learn.demo06;

    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());
        }
    }

    package com.learn.demo06;

    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());
        }
        
    }

    package com.learn.demo06;

    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();
            
        }
    }

    public class Test { // 自行测试
        public static void main(String[] args) {

            // 使用无参构造方法
            /*Penguin penguin1 = new Penguin();
            penguin1.setName("QQ");
            penguin1.setHealth(66);
            penguin1.setLove(52);
            penguin1.setSex("公");
            penguin1.printInfo();*/
            // 输出结果 昵称:QQ,健康值:66,亲密度:52
            // 输出结果无法显示出子类中的特有属性sex

            System.out.println("*在Dog子类中进行方法重写后*");

            // 使用有参构造方法
            Dog dog1 = new Dog("狗儿",77,88,"中华田园犬");
            dog1.printInfo();
            // 输出结果 昵称:狗儿,健康值:77,亲密度:88
            // 品种为:中华田园犬

        }
    }

    五、Object类

    Object类是所有类的父类

            // 使用Cat类的无参构造方法创建对象
            Cat cat1 = new Cat();
            // 此时调用了几个无参构造方法
            // Cat类继承Pet类,Object是Pet类的父类
            // 所以调用了三个构造方法Cat()、Pet()、Object()

    Object类被子类经常重写的方法

    方法说明
    toString()返回当前对象本身的有关信息,按字符串对象返回
    equals()比较两个对象是否是同一个对象,是则返回true
    hashCode()返回该对象的哈希代码值
    getClass()获取当前对象所属的类信息,返回Class对象
    1、重写toString()方法

    package methodagain.demo02;

    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;
        }
    }

    package methodagain.demo02;

    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()方法进行重写
             */

        }
    }

    package methodagain.demo03;

    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()方法
    }

    package methodagain.demo03;

    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
            // 这里严格调用输出地址的方法,所以输出还是地址值
            
        }
    }

    2、重写equals()方法

    package methodagain.demo04;

    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 "姓名:"+this.getName()+",年龄:"+this.getAge();
        }

        @Override
        public boolean equals(Object obj) {
            // 如果调用此方法比较的两个对象是同一个对象,地址值肯定相同
            if (this == obj) {
                return true;
            }
            // 如果比较的地址值不相同,说明这是两个不同的对象
            // 首先将传递过来的student判断一下是否是Student类型的对象
            if (obj instanceof Student) {
                Student student = (Student)obj;
                // 比较当前对象和传递过来对象的属性值
                if (this.getName().equals(student.getName()) &&
                        this.getAge() == student.getAge()) {
                    return true;
                }

            }
            return false;
        }
    }

    package methodagain.demo04;

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

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

            // 直接输出两个对象
            System.out.println("student1 = " + student1); // student1 = 姓名:张三年龄:24
            System.out.println("student2 = " + student2); // student2 = 姓名:张三年龄:24
            // 输出两个对象在内存中的地址
            System.out.println(
                    "student1的地址值:"+student1.getClass().getName() + '@' + Integer.toHexString(student1.hashCode()));
            System.out.println(
                    "student1的地址值:"+student2.getClass().getName() + '@' + Integer.toHexString(student2.hashCode()));
            // 输出结果
            // student1的地址值:methodagain.demo04.Student@1b6d3586
            // student1的地址值:methodagain.demo04.Student@4554617c

            // 比较两个对象
            /*
            * 关系运算符(比较运算符):
            *  >  >=  <  <=只能比较数值类型的数据
            *  ==  !=  既可以比较数值类型的数据,还可以比较引用数据类型的数据,比较引用数据类型比较的地址值
            * */
            boolean result1 = student1 == student2;
            System.out.println("student1对象和student2对象相等:"+result1); // false
            boolean result2 = student1 != student2;
            System.out.println("student1对象和student2对象不相等:"+result2); // true

            // 通过Object类中给的equals()方法比较两个对象
            boolean result3 = student1.equals(student2);
            System.out.println("student1对象和student2对象相等:"+result3); // false
            /*
            * student1对象调用的equals()方法在Student类中是不存在,但是可以调用
            * 因为Student类默认继承父类Object类中存在,所以Student类可以调用
            *
            * 这是Object类中equals()方法代码
            * public boolean equals(Object obj) {
                    return (this == obj);
                }
            * 可见返回值底层逻辑还是 == ,比较的还是地址值
            * 因此equals()方法比较的也是两个对象的地址值
            * */

            /*
            * 面试题:
            * == 和 equals() 的区别:
            *   == 既可以比较基本数据类型,也可以比较引用数据类型
            *       比较基本数据类型比较的是数值,比较引用数据类型比较的是地址值
            *   equals() 方法是Object类中的方法,比较的是两个对象的地址值
            *       因为底层逻辑是 ==
            * */

            /*
            * 那么,
            * 比较两个对象时,不再比较地址,比较属性值
            * 如果两个对象的name和age属性值相同,则认为他们是同一个对象
            * 所以,
            * Student类的父类Object类中的equals()方法满足不了Student类的需求,
            * 因此需要在Student类中进行重写
            * */

            // 重写equals()方法之后
            boolean result4 = student1.equals(student2);
            System.out.println("student1对象和student2对象相等:"+result4); // true
        }
    }

    String类中的equals()

    package methodagain.demo05;

    public class StringDemo01 {

        public static void main(String[] args) {

            String string1 = "qwert";
            String string2 = "yuiop";

            // 比较两个字符串的内容
            System.out.println("两个字符串内容相同:"+string1.equals(string2));
            // 输出结果 false

            String string3 = "qwert";
            String string4 = "qwert";

            // 比较两个字符串的内容
            System.out.println("两个字符串内容相同:"+string3.equals(string4));
            // 输出结果 true

            // Ctrl点击equals()方法
            // String类重写了Object类中的方法,比较两个字符串之间的内容
            /*
            * 说明:
            * 比较两个字符串的内容
            * String类也是Object的子类
            * Object类中的equals()方法比较的是两个对象的地址值,不能满足String类中要求比较两个对象的内容需求
            * 所以在String类中重写equals()方法
            * String类中的equals()方法比较的是两个字符串的内容
            * */
        }
    }

    六、多态的概念和案例

    1、多态案例

    package polymorphic.demo01;

    // 定义父类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());
        }
    }

    package polymorphic.demo01;

    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("狗狗吃狗粮");
        }
    }

    package polymorphic.demo01;

    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("企鹅会游泳");
        }

    }

    package polymorphic.demo01;

    public class Test01 {

        public static void main(String[] args) {

            // 使用Dog类的有参构造方法创建对象
            Dog dog1 = new Dog("壮实",100,100,"萨摩耶");
            // dog1对象调用printInfo()方法,调用的是Dog类中重写的printInfo()方法
            // 对象调用方法先从自己的类查找,没有再查找父类
            dog1.printInfo();
            // 输出结果
            // 昵称:壮实,健康值:100,亲密度:100
            // 品种:萨摩耶
            dog1.eat(); // 输出结果 狗狗吃狗粮

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

            /*// 使用Penguin类的有参构造方法创建对象
            // 测试一下 传参
            Penguin penguin1 = new Penguin("Q1",-60,100,"公");
            penguin1.printInfo();
            // 输出结果
            // 昵称:Q1,健康值:-60,亲密度:100
            // 性别:公
            Penguin penguin2 = new Penguin();
            penguin2.setName("Q2");
            penguin2.setHealth(-60);
            penguin2.setLove(100);
            penguin2.setSex("母");
            penguin2.printInfo();
            // 输出结果
            // 昵称:Q2,健康值:60,亲密度:100
            // 性别:母
            // 这里通过get/set方法对属性合理进行判断
            // 而通过创建对象时,利用有参构造方法传参则无法对属性值进行合理判断*/

            // 使用Penguin类的有参构造方法创建对象
            Penguin penguin1 = new Penguin("QQ",100,100,"公");
            // penguin1对象调用printInfo()方法,调用Penguin类中重写后的printInfo()方法
            penguin1.printInfo();
            // 调用Penguin类中的swimming()方法
            penguin1.swimming();
            // 输出结果
            // 昵称:QQ,健康值:100,亲密度:100
            // 性别:公
            // 企鹅会游泳

        }
    }

    2、向上转型

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

    注意:

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

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

    package polymorphic.demo01;

    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、向下转型

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

    注意:

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

    5、instanceof运算符

    思考:那么父类的引用能不能调用子类编写特有的方法

    package polymorphic.demo01;

    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(); // 输出结果 企鹅会游泳
            }

        }
    }

    6、多态案例宠物看病

    package methodagain02.demo01;

    public class Pet {

        private String name;
        private int health;
        private int love;

        public Pet() {
            // super()
        }

        public Pet(String name, int health, int love) {
            // super()
            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;
        }

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

        public void printInfo() {
            System.out.println("昵称:"+this.getName()+
                    ",健康值:"+this.getHealth()+
            ",亲密度:"+this.getLove());
        } //在子类重写

        // Pet类中定义一个普通方法
        public void petMethod() {
            System.out.println("在子类中不进行重写");
        }
    }

    package methodagain02.demo01;

    public class Dog extends Pet {

        // 定义Dog类继承Pet类
        private String strain;

        public Dog() {
        }

        public Dog(String strain) {
            this.strain = strain;
        }

        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() {
            super.printInfo();
            System.out.println("品种:" + this.getStrain());
        }

        // 在Dog类中定义特有方法
        public void eat() {
            System.out.println("狗狗吃狗粮");
        }
    }

    package methodagain02.demo01;

    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;
        }

        // toString方法重写就是输出对象信息
        @Override
        public String toString() {
            return "Penguin{" +
                    "sex='" + sex + '\'' +
                    '}';
        }

        public void printInfo() {
            super.printInfo();
            System.out.println("性别:"+ this.getSex());
        }

        public void swimming() {
            System.out.println("企鹅会游泳");
        }

    }

    package methodagain02.demo01;

    public class Master {

        // 在该类中定义给各种宠物看病的方法

        // 定义一个给Dog类看病的方法
        public void cure(Dog dog) {
            // 狗的健康值小于60
            if (dog.getHealth() < 60) {
                System.out.println("狗看病打针,健康值恢复到80");
                dog.setHealth(80);
                return;
            }
        }

        // 定义一个给Penguin类看病的方法
        public void cure (Penguin penguin) {
            if (penguin.getHealth() < 60) {
                System.out.println("企鹅生病治疗,健康值恢复到70");
                penguin.setHealth(70);
            }
        }

        // 需要定义一个给Cat类对象看病的方法
        public void cure (Cat cat) {
            if (cat.getHealth() < 60) {
                System.out.println("猫咪生病挂水,健康值恢复到90");
                cat.setHealth(90);
            }
        }

    }

    package methodagain02.demo01;

    public class Cat extends Pet {

        private String color;

        public Cat() {
        }

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

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

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

        public void printInfo() {
            super.printInfo();
            System.out.println("颜色:"+this.getColor());
        }

        // 定义一个Cat类特有的方法
        public void play() {
            System.out.println("猫咪喜欢躲迷藏");
        }
    }

    package methodagain02.demo01;

    public class Test {

        public static void main(String[] args) {

            // 创建Dog对象
            Dog dog1 = new Dog("旺财",55,100,"金毛");
            dog1.printInfo();
            // 昵称:旺财,健康值:55,亲密度:100
            // 品种:金毛

            // 创建Master对象
            Master master = new Master();
            // 带Dog对象看病
            master.cure(dog1); // 狗看病打针,健康值恢复到80
            dog1.printInfo();
            // 昵称:旺财,健康值:80,亲密度:100
            // 品种:金毛

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

            // 创建Penguin对象
            Penguin penguin1 = new Penguin("企鹅",45,100,"公");
            penguin1.printInfo();
            // 昵称:企鹅,健康值:45,亲密度:100
            // 性别:公
            master.cure(penguin1);
            penguin1.printInfo();
            // 企鹅生病治疗,健康值恢复到70
            // 昵称:企鹅,健康值:70,亲密度:100
            // 性别:公

            // ------ ------ ------ ------ ------ ------
            // 然后又养了一只猫咪,需要定义一个Cat类
            Cat cat1 = new Cat("Tom",30,100,"浅蓝色");
            cat1.printInfo();
            // 昵称:Tom,健康值:30,亲密度:100
            // 颜色:浅蓝色

            // 这时想要给cat1去看病,还需要在Master类中重新定义一个方法
            // 在Master类中定义方法之后才能调用
            master.cure(cat1);
            cat1.printInfo();
            // 猫咪生病挂水,健康值恢复到90
            // 昵称:Tom,健康值:90,亲密度:100
            // 颜色:浅蓝色

            /*
            * 问题:
            * 后续如果创建其它类,像老虎、狮子、大象等
            * 还需要看病的话,还得在Master类中重新定义方法,过于繁琐
            * */
        }
    }

     利用多态重写之后

    package methodagain02.demo02;

    public class Pet {

        private String name;
        private int health;
        private int love;

        //
        public Pet() {
            // super()
        }

        public Pet(String name, int health, int love) {
            // super()
            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;
        }

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

        public void printInfo() {
            System.out.println("昵称:"+this.getName()+
                    ",健康值:"+this.getHealth()+
            ",亲密度:"+this.getLove());
        } //在子类重写

        // Pet类中定义一个普通方法
        public void petMethod() {
            System.out.println("在子类中不进行重写");
        }

        // 定义一个带宠物看病的方法
        public void toHospital() {
            System.out.println("宠物生病,需要看病");
        }
    }

    package methodagain02.demo02;

    public class Dog extends Pet {

        // 定义Dog类继承Pet类
        private String strain;

        public Dog() {
        }

        public Dog(String strain) {
            this.strain = strain;
        }

        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() {
            super.printInfo();
            System.out.println("品种:" + this.getStrain());
        }

        // 在Dog类中定义特有方法
        public void eat() {
            System.out.println("狗狗吃狗粮");
        }

        // 父类Pet中toHospital()方法满足不了Dog类看病的需求
        // 所以进行重写
        public void toHospital() {
            if (this.getHealth() < 60) {
                System.out.println("狗看病打针,健康值恢复到80");
                this.setHealth(80);
            }
        }


    }

    package methodagain02.demo02;

    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;
        }

        // toString方法重写就是输出对象信息
        @Override
        public String toString() {
            return "Penguin{" +
                    "sex='" + sex + '\'' +
                    '}';
        }

        public void printInfo() {
            super.printInfo();
            System.out.println("性别:"+ this.getSex());
        }

        public void swimming() {
            System.out.println("企鹅会游泳");
        }


        public void toHospital() {
            if (this.getHealth() < 60) {
                System.out.println("企鹅生病治疗,健康值恢复到70");
                this.setHealth(70);
            }
        }

    }

    package methodagain02.demo02;

    public class Master {

        // 定义一个给宠物Pet看病的方法
        public void cure(Pet pet) {
            // 当宠物pet的健康值小于60的时候,需要看病
            if (pet.getHealth() < 60) {
                // 在这里,从代码上看,调用的是Pet类中的toHospital()方法
                // 实际上调用的是pet引用指向的子类中重写的toHospital()方法
                pet.toHospital();
            }
        }

    }

    package methodagain02.demo02;

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

            /*// 创建Dog类对象,自己给自己看病,笑死了
            Dog dog1 = new Dog("神医",55,100,"边牧");
            dog1.printInfo();
            // 昵称:神医,健康值:55,亲密度:100
            // 品种:边牧
            System.out.println("------");
            dog1.toHospital();
            // 狗看病打针,健康值恢复到80
            System.out.println("------");
            dog1.printInfo();
            // 昵称:神医,健康值:80,亲密度:100
            // 品种:边牧

            // 在父类Pet定义的toHospital()方法,在子类中重写,子类可以调用,但是不符合现实逻辑
            // 需要创建一个主人对象,带宠物看病,哈哈哈*/

            // ------ ------ ------ ------ ------ ------

            // 创建Master对象
            Master master = new Master();
            // 创建Dog对象
            Dog dog1 = new Dog("来福",45,100,"金毛");
            dog1.printInfo();
            // 带宠物看病
            master.cure(dog1); // 这里传Pet类对象,Pet子类的对象是可以的
            dog1.printInfo();

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

            // 向上转型:父类引用(对象名)指向子类的实例(对象)
            Pet pet = new Dog("旺财",50,100,"金毛");
            // 这里从代码来看,调用的是Pet类中的printInfo()方法
            // 实际上调用的是pet引用指向子类Dog类中重写后的printInfo()方法
            pet.printInfo();
            System.out.println("------");
            master.cure(pet); // 狗看病打针,健康值恢复到80
            System.out.println("------");
            pet.printInfo();
            // 昵称:旺财,健康值:80,亲密度:100
            // 品种:金毛

            pet = new Penguin("QQ",30,100,"公");
            pet.printInfo();
            System.out.println("------");
            master.cure(pet); // 企鹅生病治疗,健康值恢复到70
            System.out.println("------");
            pet.printInfo();
            // 昵称:QQ,健康值:70,亲密度:100
            // 性别:公

        }
    }

    七、抽象类与抽象方法

    1、抽象方法的概念

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

    2、抽象方法的特点

    1)抽象方法没有方法体

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

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

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

    3、抽象类的概念

    使用abstract修饰的类为抽象类

    4、抽象类的特点

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

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

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

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

    package methodagain02.demo03;

    //public class Pet {
    public abstract class Pet { // 改成抽象类
        private String name;
        private int health;
        private int love;

        //
        public Pet() {
            // super()
        }

        public Pet(String name, int health, int love) {
            // super()
            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;
        }

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

        public void printInfo() {
            System.out.println("昵称:"+this.getName()+
                    ",健康值:"+this.getHealth()+
            ",亲密度:"+this.getLove());
        } //在子类重写

        // Pet类中定义一个普通方法
        public void petMethod() {
            System.out.println("在子类中不进行重写");
        }

        // 定义一个带宠物看病的方法
        /*public void toHospital() {
            // System.out.println("宠物生病,需要看病");
            // 不用方法体
        }*/
        // 如上不用方法体,则可以直接写成
        // public void toHospital(); 报错
        // 再改
        // 需要写成抽象方法加一个abstract
        // public abstract void toHospital(); 报错
        // 抽象方法需要写在抽象类中
        // 所以Pet类需要改成 public abstract class Pet

        // 再试一下
        public abstract void toHospital();

    }

        // 在Dog类中定义特有方法
        public void eat() {
            System.out.println("狗狗吃狗粮");
        }

        // 父类Pet中toHospital()方法满足不了Dog类看病的需求
        // 所以进行重写
        public void toHospital() {
            if (this.getHealth() < 60) {
                System.out.println("狗看病打针,健康值恢复到80");
                this.setHealth(80);
            }
        }

    public void toHospital() {
            if (this.getHealth() < 60) {
                System.out.println("猫看病打针,健康值恢复到60");
                this.setHealth(60);
            }
        } 

    public class Master {

        // 定义一个给宠物Pet看病的方法
        public void cure(Pet pet) {
            // 当宠物pet的健康值小于60的时候,需要看病
            if (pet.getHealth() < 60) {
                // 在这里,从代码上看,调用的是Pet类中的toHospital()方法
                // 实际上调用的是pet引用指向的子类中重写的toHospital()方法
                pet.toHospital();
                // 在多态中因为pet引用指向的是子类中重写的toHospital()方法
                // 所以父类中的toHospital()方法体内可以不写任何内容
            }
        }

    }

    5、抽象类案例汽车租赁  

    package methodagain02.demo04;

    public abstract class Automobile {

        /*
        * 汽车租赁公司可以租赁轿车和客车,轿车和客车具有相同属性和方法,可以将相同的属性和方法抽取出来放在父类Autonobile类中
        * 轿车
        *   属性:品牌、车牌号、每日租金、型号
        *   方法:根据天数计算租金 (租金折扣)
        * 客车:
        *   属性:品牌、车牌号、每日租金、座位数
        *   方法:根据天数计算租金 (租金折扣)
        * */
        private String brand; // 品牌
        private String carNum; // 车牌号
        private double dayPrice; // 每日租金

        public Automobile() {
        }

        public Automobile(String brand, String carNum, double dayPrice) {
            this.brand = brand;
            this.carNum = carNum;
            this.dayPrice = dayPrice;
        }

        public String getBrand() {
            return brand;
        }

        public void setBrand(String brand) {
            this.brand = brand;
        }

        public String getCarNum() {
            return carNum;
        }

        public void setCarNum(String carNum) {
            this.carNum = carNum;
        }

        public double getDayPrice() {
            return dayPrice;
        }

        public void setDayPrice(double dayPrice) {
            this.dayPrice = dayPrice;
        }

        @Override
        public String toString() {
            return "Automobile{" +
                    "brand='" + brand + '\'' +
                    ", carNum='" + carNum + '\'' +
                    ", dayPrice=" + dayPrice +
                    '}';
        }

        // 定义一个计算租金的方法 轿车和客车租金计算不同 所以我们定义抽象方法
        public abstract double calcRentMoney(int days);

    }

    package methodagain02.demo04;

    public class Car extends Automobile {

        private String type;

        public Car() {
        }

        public Car(String brand, String carNum, double dayPrice, String type) {
            super(brand, carNum, dayPrice);
            this.type = type;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

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

        // 重写抽象父类Automobile中的抽象方法
        @Override
        public double calcRentMoney(int days) { // 方法重写参数表相同
            // 计算租金
            double money = days * this.getDayPrice();
            // 根据租赁天数不同,租金又折扣
            if (days < 5) {
                money *= 0.95;
            } else if (days >= 5 && days <= 10) {
                money *= 0.9;
            }else {
                money *= 0.8;
            }
            return money;
        }
    }

    package methodagain02.demo04;

    public class Bus extends Automobile {

        private int sites;

        public Bus() {
        }

        public Bus(String brand, String carNum, double dayPrice, int sites) {
            super(brand, carNum, dayPrice);
            this.sites = sites;
        }

        public int getSites() {
            return sites;
        }

        public void setSites(int sites) {
            this.sites = sites;
        }

        @Override
        public String toString() {
            return "Bus{" +
                    "sites=" + sites +
                    '}';
        }

        @Override
        public double calcRentMoney(int days) {
            // 计算租金
            double money = days * this.getDayPrice();
            // 根据天数打折
            if (days < 10) {
                money *= 0.95;
            }else if (days >= 10 && days <=20) {
                money *=0.9;
            }else {
                money *=0.66;
            }
            return money;
        }
    }

    package methodagain02.demo04;

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

            // Automobile是抽象类,不能直接new对象,可以通过多态形式,将Automobile的引用指向子类实例
            // Automobile automobile = new Automobile();

            Automobile automobile = new Car("奥迪","皖A88866",600,"RS7");
            // 租赁5天,计算租金
            double rentMoney1 = automobile.calcRentMoney(3);
            System.out.println(rentMoney1);

            automobile = new Bus("绿源","皖66688",300,9);
            double rentMoney2 = automobile.calcRentMoney(8);
            System.out.println(rentMoney2);

        }
    }

    6、多态的应用

    1)使用父类作为方法的形参,是Java中实现和使用多态的主要方式

    2)使用父类作为方法的返回值,也是Java中实现和使用多态的主要方式

            // 在Master类中定义一个赠送动物的方法
            public Pet givePet(String type) {
                Pet pet = null; // 局部变量需要有初始值
                if (type.equals("狗")) {
                    pet = new Dog("旺财",100,100,"哈士奇");
                } else if (type.equals("猫")) {
                    pet = new Cat("猫咪",100,100,"黑色");
                }
                return pet;
            }

            // 在main()方法中测试
            // 主人根据你的需求赠送动物给你
            Master master = new Master();
            Pet pet1 = master.givePet("狗");
            // 输出pet1信息
            pet1.printInfo();

            Pet pet2 = master.givePet("猫");
            pet2.printInfo;

            Pet pet3 = master.givePet("qwert");
            pet3.printInfo; // 报错 NullPointerException 空指针异常
            System.out.println(pet3); // 输出结果 null

  • 相关阅读:
    IntelliJ Idea 撤回git已经push的操作
    深入浅出MySQL Server System Variables
    操作系统(王道)
    L1-071 前世档案(Python3)
    舞蹈室如何打破拉新难的困境?
    Loongnix-server中ovirt-engine-4.4安装及使用方法
    【2023C卷题限时免费】20天拿下华为OD笔试之 【不定滑窗】2023C-最长的指定瑕疵度的元音子串【欧弟算法】全网注释最详细分类最全的华为OD真题题解
    Vim使用手册
    【JS】把Promise手写明白!
    ConcurrentHashMap
  • 原文地址:https://blog.csdn.net/2302_76764006/article/details/136197478