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

  • 38
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值