美文网首页
【JAVA学习笔记】面向对象三大特性之“重写”与“多态”

【JAVA学习笔记】面向对象三大特性之“重写”与“多态”

作者: Geekero | 来源:发表于2021-04-12 07:58 被阅读0次

学习自华为开发者学院陈璇老师的JAVA系列课程

三、方法重写

3.1 重写的定义


在有父子类关系的两个类中的同名方法

3.2 super关键字的功能

通过supper访问父类的成员



需要父类中的成员是非private

3.3 super关键字的使用

super使用场景:

  • 子类方法重写父类方法
  • 在子类中定义了和父类同名的成员变量,super可以使被屏蔽的成员可见


案例1 宠物类

Pet.java:

//父类:提取共性的代码
public class Pet {
    private String name = "无名氏"; //昵称
    private int health = 100; //健康值,默认100,0~100之间,小于60为不健康
    private int love = 0; //亲密度
    private String strain = "聪明的拉布拉多犬"; //品种
    int age = 1;

    public Pet(){
        System.out.println("父类无参构造方法");
    }

    public Pet(String name){
        this.name = name;
        System.out.println("父类带参构造方法");
    }

    public Pet(String name, int health, int love){
        //this(name); //this可调用本类的构造方法,且必须放在第一行
        this.name = name;
        this.health = health;
        this.love = love;
        System.out.println("父类带参构造方法");
    }

    public int getAge() {
        return age;
    }

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

    public void setHealth(int health){
        if(health<0 || health>100){
            System.out.println("宠物的健康值只能在0~100之间!");
            this.health = 60;
            return;
        }
        this.health = health;
    }

    public int getHealth(){
        return this.health;
    }


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

    public String getName(){
        return this.name;
    }

    public void setLove(int love){
        if(love<0 || love>100){
            System.out.println("宠物的亲密度只能在0~100之间!");
            this.love = 60;
            return;
        }
        this.love = love;
    }

    public int getLove(){
        return this.love;
    }



    /*
     * 输出宠物信息
     */
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫" + this.name +
                ",健康值是" + this.health + ",和主人的亲密度是"
                + this.love);
    }
}

Dog.java:

/*
* 宠物狗类
*/

public class Dog extends Pet{
    //1. 隐藏属性
    private String strain = "聪明的拉布拉多犬"; //品种
    private int age = 10;

    public Dog(){
        System.out.println("子类狗狗的无参构造方法");
    }

    public Dog(String name, int health, int love, String strain){
        //通过super调用父类的构造方法,且必须是第一行
        //super();
        super(name, health, love);
        this.strain = strain;
        System.out.println("子类狗狗的带参构造方法");
    }

    //添加属性的setter/getter方法,并加入属性控制语句
    public void setStrain(String strain){
        this.strain = strain;
    }

    public String getStrain(){
        return this.strain;
    }

    //重写print()方法
    public void print() {
        //调用父类的非private的print()
        super.print();
        System.out.println("我是一只:"+this.strain);
    }

    public void m1(){
        //super不可以调用父类的private属性
        //System.out.println(super.name);
        //super可以调用父类的非private属性
        System.out.println(super.age);
    }

    public void m2(){
        //子类会覆盖父类的同名成员
        System.out.println(this.age);
        //可以使用super调用父类被子类覆盖的同名成员
        System.out.println(super.age);
    }
}

Penguin.java:

/*
* 宠物企鹅类
*/
public class Penguin extends Pet{
    private String sex = "Q仔";

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

    //重写print()方法
    public void print() {
        //调用父类的非private的print()
        super.print();
        System.out.println("我的性别是:"+this.sex);
    }
}

TestPet.java:

import java.util.Scanner;

public class TestPet {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("欢迎您来到宠物店!");
        System.out.print("请输入要领养宠物的名字:");
        String name = input.next();
        System.out.print("请输入要领养宠物的类型:1、狗狗;2、企鹅 ");
        int typeNo = input.nextInt();
        switch (typeNo){
            case 1:
                //创建狗狗对象
//                Dog dog = new Dog();
//                dog.setName(name);
//                //狗狗其他属性
//                dog.setHealth(-1000);
//                System.out.println(dog.getHealth());
//
//                dog.setLove(-9);
//
//                dog.setStrain("吉娃娃");
//                dog.print();
//                dog.m1();
                Dog dog = new Dog("多多", 100, 89, "吉娃娃");
                dog.print();
                dog.m2();
                break;
            case 2:
                //接受用户键盘录入信息
                System.out.print("请输入企鹅的性别:1、Q妹;2、Q仔  ");
                int SexId = input.nextInt();
                String sex = (SexId==1) ? "Q妹" : "Q仔";
                System.out.print("请输入企鹅的健康值:");
                int health = input.nextInt();
                System.out.print("请输入企鹅的亲密度:");
                int love = input.nextInt();

                //创建企鹅对象
                Penguin p = new Penguin();
                p.setName(name);
                p.setSex(sex);
                p.setHealth(health);
                p.setLove(love);
                p.print();
                break;

            default:
                System.out.println("暂时没有这个类型的宠物!");
                break;
        }
    }
}

注意问题:

  1. Java只支持单根继承
    • 一个类只能有一个直接父类
    • 一个类可以有多个间接父类
  2. 继承并非越复杂越好
    • 继承树分支太多,旁支不好扩展
    • 虚拟机对分支复杂的继承关系,会出现多态绑定的问题
    • 继承关系越复杂,对象模型就越复杂,不利于程序扩展和维护

一般开发代码:2~3层的继承关系就可以了

案例2:super调用父类的父类的成员
Animal.java:

package cn.com.superdemo;

//爷爷类:动物类
public class Animal
{
    private int age;
    private String sex;

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

    public String getSex() {
        return sex;
    }

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

    public void print(){
        System.out.println("爷爷类的一个方法");
    }
}

Person.java:

package cn.com.superdemo;

//父类:人类
public class Person extends Animal{
    private String name;

    public String getName() {
        return name;
    }

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

Student.java:

package cn.com.superdemo;

//孙子类:学生类
public class Student extends Person{
    private String sid;

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public void print(){
        //调用父类的父类的方法,也是通过super.就可以了,而不用super.super.
        super.print();
        System.out.println("孙子类");
    }

    public static void main(String[] args) {
        Student s = new Student();
        s.print();
    }
}

3.4 继承下的构造方法


验证1:
TestPet.java
import java.util.Scanner;

public class TestPet {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("欢迎您来到宠物店!");
        System.out.print("请输入要领养宠物的名字:");
        String name = input.next();
        System.out.print("请输入要领养宠物的类型:1、狗狗;2、企鹅 ");
        int typeNo = input.nextInt();
        switch (typeNo){
            case 1:
                //创建狗狗对象
                Dog dog = new Dog();
                dog.setName(name);
                //狗狗其他属性
                dog.setHealth(-1000);
                System.out.println(dog.getHealth());

                dog.setLove(-9);

                dog.setStrain("吉娃娃");
                dog.print();
//                dog.m1();
//                Dog dog = new Dog("多多", 100, 89, "吉娃娃");
//                dog.print();
//                dog.m2();
                break;
            case 2:
                //接受用户键盘录入信息
                System.out.print("请输入企鹅的性别:1、Q妹;2、Q仔  ");
                int SexId = input.nextInt();
                String sex = (SexId==1) ? "Q妹" : "Q仔";
                System.out.print("请输入企鹅的健康值:");
                int health = input.nextInt();
                System.out.print("请输入企鹅的亲密度:");
                int love = input.nextInt();

                //创建企鹅对象
                Penguin p = new Penguin();
                p.setName(name);
                p.setSex(sex);
                p.setHealth(health);
                p.setLove(love);
                p.print();
                break;

            default:
                System.out.println("暂时没有这个类型的宠物!");
                break;
        }


    }
}

输出:

欢迎您来到宠物店!
请输入要领养宠物的名字:多多
请输入要领养宠物的类型:1、狗狗;2、企鹅 1
父类无参构造方法
子类狗狗的无参构造方法
宠物的健康值只能在0~100之间!
60
宠物的亲密度只能在0~100之间!
宠物的自白:
我的名字叫多多,健康值是60,和主人的亲密度是60
我是一只:吉娃娃

Process finished with exit code 0

检查方法1:打印输出测试语句
检查方法2:在想测试的地方进行断点调试

验证2:

Dog.java:

/*
* 宠物狗类
*/

public class Dog extends Pet{
  //1. 隐藏属性
  private String strain = "聪明的拉布拉多犬"; //品种
  private int age = 10;

  public Dog(){
      System.out.println("子类狗狗的无参构造方法");
      System.out.println("子类狗狗带3个参数的构造方法");
  }

  public Dog(String name, int health, int love){
      super(name, health, love);
      System.out.println("子类狗狗带三个参数的构造方法");
  }

  public Dog(String name, int health, int love, String strain){
      //通过super调用父类的构造方法,且必须是第一行
      //super();
      //super(name, health, love);
      this(name, health, love);
      this.strain = strain;
      System.out.println("子类狗狗带4个参数的构造方法");
  }

  //添加属性的setter/getter方法,并加入属性控制语句
  public void setStrain(String strain){
      this.strain = strain;
  }

  public String getStrain(){
      return this.strain;
  }

  //重写print()方法
  public void print() {
      //调用父类的非private的print()
      super.print();
      System.out.println("我是一只:"+this.strain);
  }

  public void m1(){
      //super不可以调用父类的private属性
      //System.out.println(super.name);
      //super可以调用父类的非private属性
      System.out.println(super.age);
  }

  public void m2(){
      //子类会覆盖父类的同名成员
      System.out.println(this.age);
      //可以使用super调用父类被子类覆盖的同名成员
      System.out.println(super.age);
  }
}

TestPet.java:

import java.util.Scanner;

public class TestPet {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.println("欢迎您来到宠物店!");
        System.out.print("请输入要领养宠物的名字:");
        String name = input.next();
        System.out.print("请输入要领养宠物的类型:1、狗狗;2、企鹅 ");
        int typeNo = input.nextInt();
        switch (typeNo){
            case 1:
//                //创建狗狗对象
//                Dog dog = new Dog();
//                dog.setName(name);
//                //狗狗其他属性
//                dog.setHealth(-1000);
//                System.out.println(dog.getHealth());
//
//                dog.setLove(-9);
//
//                dog.setStrain("吉娃娃");
//                dog.print();
//                dog.m1();
                Dog dog = new Dog("多多", 100, 89, "吉娃娃");
                dog.print();
                dog.m2();
                break;
            case 2:
                //接受用户键盘录入信息
                System.out.print("请输入企鹅的性别:1、Q妹;2、Q仔  ");
                int SexId = input.nextInt();
                String sex = (SexId==1) ? "Q妹" : "Q仔";
                System.out.print("请输入企鹅的健康值:");
                int health = input.nextInt();
                System.out.print("请输入企鹅的亲密度:");
                int love = input.nextInt();

                //创建企鹅对象
                Penguin p = new Penguin();
                p.setName(name);
                p.setSex(sex);
                p.setHealth(health);
                p.setLove(love);
                p.print();
                break;

            default:
                System.out.println("暂时没有这个类型的宠物!");
                break;
        }
    }
}

输出:

欢迎您来到宠物店!
请输入要领养宠物的名字:多多
请输入要领养宠物的类型:1、狗狗;2、企鹅 1
父类带参构造方法
子类狗狗带三个参数的构造方法
子类狗狗带4个参数的构造方法
宠物的自白:
我的名字叫多多,健康值是100,和主人的亲密度是89
我是一只:吉娃娃
10
1

3.5 深入了解重写

由于多态具有动态绑定机制,JAVA虚拟机会判断绑定不同子类的相应方法。如果一旦子类的方法访问权限比父类的还小,于是绑定不上,所以多态执行会出现问题


方法重写与方法重载的区别

方法重载:

  • 写在同一个类里面
  • 同名不同参

方法重写:

  • 有继承关系的父子类中
  • 同名同参

Father.java
//父类
public class Father {
    void m1() {
        System.out.println("父类的m1方法");
    }

    //方法重载: 在同一个类里面同名不同参的方法
    public String m1(int num1) {
        System.out.println("父类的m1方法");
        return "Test";
    }


    //方法返回值类型可以是自定义的数据类型
    public Father m2() {
        System.out.println("父类的m2方法");
        return new Father();
    }

    public static void m3() {
        System.out.println("父类的静态方法m3");
    }

    private void m4() {
        System.out.println("父类的私有方法m4");
    }
}

Son.java:

//子类
public class Son extends Father{
    //子类重写方法不可以比父类方法访问权限小,但可以扩大方法的访问权限
    //子类方法只要不严于父类,就构成了方法重写
    public void m1() {
        System.out.println("子类重写后的m1方法");
    }

    //子类方法返回值类型可以是父类方法方法返回值类型的子类,也是方法重写
    public Son m2() {
        System.out.println("子类重写后的m2方法");
        return new Son();
    }

    //父类的静态方法不能被重写为非静态方法
    //反之,父类的非静态方法也不能被重写为静态方法
//    public void m3() {
//        System.out.println("子类的非静态方法m3");
//    }

    //在子类中可以定义和父类一模一样的静态方法,只是覆盖而非重写
    public static void m3() {
        //在静态方法中,不能使用super
        //super.m3();
        Father.m3();
        System.out.println("子类的静态方法m3");
        Son.m3();
    }

    //子类无法重写父类的private方法
    public void m4() {
        System.out.println("子类的私有方法m4");
    }

    public static void main(String[] args) {
        Son son = new Son();
        son.m1();
        son.m2();
    }
}

3.6 Object类

Object是所有类的直接或者间接父类
Object的equals() 与 == 作用相同(引用类型),判断两个对象是否为同一个对象


package cn.com.objdemo;

public class Student{
    private int sid;
    private String name;
    private int age;
    private int weight;

    public Student() {}

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

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    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 int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public static void main(String[] args) {
        Student s1 = new Student(1,"张三",18,50);
        Student s2 = new Student(1,"张三",18,50);
        //判断两个对象是否为同一个对象
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));

        Student s3 = s1; //传递的是地址
        //判断两个对象是否为同一个对象
        System.out.println(s1 == s3);
        System.out.println(s1.equals(s3));
    }
}

3.7 重写Object类方法

package cn.com.objdemo;

public class Student{
    private int sid;
    private String name;
    private int age;
    private int weight;

    public Student() {}

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

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    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 int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    //把equals()方法体自定义为我们自己的比较规则即可
    //判断obj和this(当前学生对象)是否为同一对象
    public boolean equals(Object obj) {
        if (this == obj){
            //内存地址相同
            return true;
        }
        //如果传进来的obj对象不是Student类型,结果一定是false
        if (!(obj instanceof Student)) {
            return false;
        }
        //如果obj是Student类型
        Student s = (Student)obj;
        //比较s 和 this的sid及name 即可
        if (this.sid == s.sid && this.name.equals(s.name)) {
            return true;
        }else{
            return false;
        }

    }

    //重写toString(): 返回学生姓名


    public String toString() {
        return this.name;
    }

    public static void main(String[] args) {
        Student s1 = new Student(1,"张三",18,50);
        Student s2 = new Student(1,"张三",18,50);
        //判断两个对象是否为同一个对象
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));

        Student s3 = s1; //传递的是地址
        //判断两个对象是否为同一个对象
        System.out.println(s1 == s3);
        System.out.println(s1.equals(s3));

        System.out.println("_______________________________________");
        //输出一个对象时,等同于调用了它的toString()
        System.out.println(s1.toString());
        System.out.println(s1);

        //String不仅重写了equals(),也重写了toString()方法
        String s = "test";
        System.out.println(s.toString());
        System.out.println(s);
    }
}

四、多态

4.1 多态的实现

多态:同一个引用类型,使用不同的实例而执行不同的不同的操作

父类类型(父类引用)指向子类对象,虽然传的是父类类型,实际传的是子类对象,JAVA虚拟机会根据具体父类类型指向的具体的子类对象,于是调用具体子类中重写以后的方法,JAVA运行时会去动态绑定,是多态的一种体现。JAVA虚拟机在多态的机制上,动态绑定相对具体对象相应重写以后的方法

继承和方法重写,是实现多态的基础。

Pet.java

//父类:提取共性的代码
public class Pet {
    private String name = "无名氏"; //昵称
    private int health = 100; //健康值,默认100,0~100之间,小于60为不健康
    private int love = 0; //亲密度
    private String strain = "聪明的拉布拉多犬"; //品种
//    int age = 1;

    public Pet(){
        System.out.println("父类无参构造方法");
    }

    public Pet(String name){
        this.name = name;
        System.out.println("父类带参构造方法");
    }

    public Pet(String name, int health, int love){
        //this(name); //this可调用本类的构造方法,且必须放在第一行
        this.name = name;
        this.health = health;
        this.love = love;
        System.out.println("父类带参构造方法");
    }

//    public int getAge() {
//        return age;
//    }
//
//    public void setAge(int age) {
//        this.age = age;
//    }
//
    public void setHealth(int health){
        if(health<0 || health>100){
            System.out.println("宠物的健康值只能在0~100之间!");
            this.health = 60;
            return;
        }
        this.health = health;
    }

    public int getHealth(){
        return this.health;
    }


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

    public String getName(){
        return this.name;
    }

    public void setLove(int love){
        if(love<0 || love>100){
            System.out.println("宠物的亲密度只能在0~100之间!");
            this.love = 60;
            return;
        }
        this.love = love;
    }

    public int getLove(){
        return this.love;
    }



    /*
     * 输出宠物信息
     */
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫" + this.name +
                ",健康值是" + this.health + ",和主人的亲密度是"
                + this.love);
    }

    //宠物生病后看病
    public void toHospital() {
    }
}

Dog.java

/*
* 宠物狗类
*/

public class Dog extends Pet{
    //1. 隐藏属性
    private String strain = "聪明的拉布拉多犬"; //品种
//    private int age = 10;

    public Dog(){
        System.out.println("子类狗狗的无参构造方法");
        System.out.println("子类狗狗带3个参数的构造方法");
    }

    public Dog(String name, int health, int love){
        super(name, health, love);
        System.out.println("子类狗狗带三个参数的构造方法");
    }

    public Dog(String name, int health, int love, String strain){
        //通过super调用父类的构造方法,且必须是第一行
        //super();
        //super(name, health, love);
        this(name, health, love);
        this.strain = strain;
        System.out.println("子类狗狗带4个参数的构造方法");
    }

    //添加属性的setter/getter方法,并加入属性控制语句
    public void setStrain(String strain){
        this.strain = strain;
    }

    public String getStrain(){
        return this.strain;
    }

    //重写print()方法
    public void print() {
        //调用父类的非private的print()
        super.print();
        System.out.println("我是一只:"+this.strain);
    }

    public void toHospital() {
        System.out.println("打针、吃药");
        this.setHealth(60);
    }
}

Penguin.java

import java.awt.*;

/*
* 宠物企鹅类
*/
public class Penguin extends Pet{
    private String sex = "Q仔";

    public Penguin(){};

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

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

    //重写print()方法
    public void print() {
        //调用父类的非private的print()
        super.print();
        System.out.println("我的性别是:"+this.sex);
    }

    //为企鹅看病
    public void toHospital() {
        System.out.println("打针、疗养");
        this.setHealth(60);
    }
}

Master.java

//主人类
public class Master {
    //为宠物看病,如果宠物健康值小于50,就要去宠物医院看病
    public void cure(Pet pet){
        if (pet.getHealth()<50){
            pet.toHospital();
            pet.setHealth(60);
        }
    }
}

TestPet.java

import java.util.Scanner;

public class TestPet {
    public static void main(String[] args) {
        Master master = new Master();
        Scanner input = new Scanner(System.in);
        System.out.println("欢迎您来到宠物店!");
        System.out.print("请输入要领养宠物的名字:");
        String name = input.next();
        System.out.print("请输入要领养宠物的类型:1、狗狗;2、企鹅 ");
        int typeNo = input.nextInt();
        switch (typeNo){
            case 1:
                //父类类型指向子类对象
                Pet dog = new Dog("多多", 30, 89, "吉娃娃");
                dog.print();

                System.out.println("*************************");
                //主人为狗狗看病
                master.cure(dog);

                System.out.println("*************************");
                dog.print();


                break;
            case 2:
                //接受用户键盘录入信息
                System.out.print("请输入企鹅的性别:1、Q妹;2、Q仔  ");
                int SexId = input.nextInt();
                String sex = (SexId==1) ? "Q妹" : "Q仔";
                System.out.print("请输入企鹅的健康值:");
                int health = input.nextInt();
                System.out.print("请输入企鹅的亲密度:");
                int love = input.nextInt();

                //创建企鹅对象
                Pet p = new Penguin(name, health, love, sex);
                p.print();

                System.out.println("*************************");
                //主人为企鹅看病
                master.cure(p);

                System.out.println("*************************");
                p.print();

                break;

            default:
                System.out.println("暂时没有这个类型的宠物!");
                break;
        }
    }
}

4.2 抽象类和抽象方法

抽象方法

抽象类


抽象方法所在的类必须定义为抽象类
抽象类中不一定要有抽象方法

一个抽象类的抽象方法必须被子类实现,除非其子类也是抽象类

//父类:提取共性的代码
//抽象类不能被实例化,实例化抽象类没有任何实际意义
public abstract class Pet {
    private String name = "无名氏"; //昵称
    private int health = 100; //健康值,默认100,0~100之间,小于60为不健康
    private int love = 0; //亲密度
    private String strain = "聪明的拉布拉多犬"; //品种
//    int age = 1;

    public Pet(){
        System.out.println("父类无参构造方法");
    }

    public Pet(String name){
        this.name = name;
        System.out.println("父类带参构造方法");
    }

    public Pet(String name, int health, int love){
        //this(name); //this可调用本类的构造方法,且必须放在第一行
        this.name = name;
        this.health = health;
        this.love = love;
        System.out.println("父类带参构造方法");
    }

    public void setHealth(int health){
        if(health<0 || health>100){
            System.out.println("宠物的健康值只能在0~100之间!");
            this.health = 60;
            return;
        }
        this.health = health;
    }

    public int getHealth(){
        return this.health;
    }


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

    public String getName(){
        return this.name;
    }

    public void setLove(int love){
        if(love<0 || love>100){
            System.out.println("宠物的亲密度只能在0~100之间!");
            this.love = 60;
            return;
        }
        this.love = love;
    }

    public int getLove(){
        return this.love;
    }



    /*
     * 输出宠物信息
     */
    public void print(){
        System.out.println("宠物的自白:\n我的名字叫" + this.name +
                ",健康值是" + this.health + ",和主人的亲密度是"
                + this.love);
    }

    //宠物生病后看病
    //抽象方法所在的类必须定义为抽象类
    //抽象类中不一定要有抽象方法
    //一个抽象类的抽象方法必须被(普通)子类实现
    //除非其子类也是抽象类
    public abstract void toHospital();
}

4.3 向上转型与向下转型

向上转型 类似自动类型转换


向下转型 类似强制类型转换

向下转型,如果没有没有转换为真实的子类类型,会引发ClassCastException,所以需要使用instanceof进行类型判断

TestPet.java

import java.util.Scanner;

public class TestPet {
    public static void main(String[] args) {
        Master master = new Master();
        Scanner input = new Scanner(System.in);
        System.out.println("欢迎您来到宠物店!");
        System.out.print("请输入要领养宠物的名字:");
        String name = input.next();
        System.out.print("请输入要领养宠物的类型:1、狗狗;2、企鹅 ");
        int typeNo = input.nextInt();
        switch (typeNo){
            case 1:
                //创建狗狗对象
                //父类类型指向子类对象,向上转型
                Pet dog = new Dog("多多", 30, 89, "吉娃娃");

                //使用向上转型无法调用子类独有的方法
                //可使用向下转型
                //向下转型,如果没有没有转换为真实的子类类型,会引发ClassCastException
                //所以需要使用`instanceof`进行类型判断
                if (dog instanceof Dog) {
                    Dog d = (Dog)dog;
                    d.catchFly();
                }else if (dog instanceof Penguin) {
                    Penguin p= (Penguin)dog;
                    p.swim();
                }

                dog.print();
//                dog.m2();

                System.out.println("*************************");
                //主人为狗狗看病
                master.cure(dog);
                //主人为狗狗喂食
                master.feed(dog);

                System.out.println("*************************");
                dog.print();

                break;
            case 2:
                //接受用户键盘录入信息
                System.out.print("请输入企鹅的性别:1、Q妹;2、Q仔  ");
                int SexId = input.nextInt();
                String sex = (SexId==1) ? "Q妹" : "Q仔";
                System.out.print("请输入企鹅的健康值:");
                int health = input.nextInt();
                System.out.print("请输入企鹅的亲密度:");
                int love = input.nextInt();

                //创建企鹅对象
                Pet p = new Penguin(name, health, love, sex);


                if (p instanceof Dog) {
                    Dog d = (Dog)p;
                    d.catchFly();
                }else if (p instanceof Penguin) {
                    Penguin penguin= (Penguin)p;
                    penguin.swim();
                }

                Penguin penguin = (Penguin)p;
                penguin.swim();
                p.print();

                System.out.println("*************************");
                //主人为企鹅看病
                master.cure(p);
                //主人为企鹅喂食
                master.feed(p);

                System.out.println("*************************");
                p.print();

                break;

            default:
                System.out.println("暂时没有这个类型的宠物!");
                break;
        }
    }
}

4.4 多态的两种使用方式

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

案例1
Animal.java

//动物类
public abstract class Animal {
    //动物叫
    public abstract void shout();
}

Cat.java

public class Cat extends Animal{
    public void shout() {
        System.out.println("喵喵喵");
    }
}

Dog.java

public class Dog extends Animal{
    public void shout() {
        System.out.println("汪汪汪");
    }
}

Duck.java

public class Duck extends Animal{
    public void shout() {
        System.out.println("嘎嘎嘎");
    }
}

Master.java

import jdk.internal.org.objectweb.asm.tree.analysis.Analyzer;

//农场主人
public class Master {
    //赠送动物
    //将父类类型作为返回值
//    public Cat sendCat() {
//        return new Cat();
//    }
//
//    public Dog sendDog  () {
//        return new Dog();
//    }
//
//    public Duck sendDuck  () {
//        return new Duck();
//    }
    public Animal  sendAnimal(int type) {
        Animal animal = null;

        switch (type) {
            case 1:
                animal = new Cat();
                break;
            case 2:
                animal = new Dog();
                break;
            case 3:
                animal = new Duck();

                break;
            default:
                System.out.println("对不起朋友,农场暂时没有这种动物!");
                break;
        }
        return animal;
    }
}

Test.java

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        Master master = new Master();
        System.out.print("朋友,你喜欢什么动物:1.猫 2.狗 3.鸭子:");
        int type = input.nextInt();

        Animal animal = master.sendAnimal(type);
        //如果是4会出现空指针异常,需要用循环升级解决
        //多态:根据具体动物子类,调用其重写后的shout()方法
        animal.shout();
    }
}

案例2:
Goods.java

public abstract class Goods {
    //打印输出商品价格
    public abstract void printPrice();
}

Foods.java

//食品类
public class Foods extends Goods{
    public void printPrice() {
        System.out.println("打印输出食品价格");
    }
}

TVs.java

//电视类
public class TVs extends Goods{
    public void printPrice() {
        System.out.println("打印输出电视价格");
    }
}

Factory.java

//商品工厂,根据厂家需求生产不同的商品
public class Factory {
    //生产商品:使用父类作为方法返回值类型
    public Goods getGoods(String str) {
        if (str.equals("food")) {
            return new Foods();
        }else{
            return new TVs();
        }
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        Factory factory = new Factory();
        Goods goods = factory.getGoods("food");
        goods.printPrice();

        goods = factory.getGoods("TVs");
        goods.printPrice();
    }
}

4.5 动态绑定机制与静态绑定机制

实例方法(动态绑定机制)与引用变量实际引用的对象绑定,调用子类重写后的方法,有运行时JVM决定

静态方法(静态绑定机制)与引用变量所声明的类型绑定,实际上在编译阶段就做了绑定

成员变量(包括静态变量和实例变量,静态绑定机制)与引用变量所声明的类型绑定,实际上在编译阶段就做了绑定

普通的,非多态相关的:通过类名可以调用各自类的静态方法

Father.java


public class Father {
    int var1 = 10;
    static int staticVar = 9;

    public void m1() {
        System.out.println("父类中的实例方法m1");
    }

    public static void staticM1() {
        System.out.println("父类中的静态方法m1");
    }
}

Son.java


public class Son extends Father {
    int var1 = 100;
    static int staticVar = 90;

    public void m1() {
        System.out.println("子类重写后的m1方法");
    }

    //非重写,只是定义了一个与父类同名的静态方法
    public static void staticM1() {
        System.out.println("子类类中的静态方法m1");
    }

    public static void main(String[] args) {
        //多态的绑定机制
        //向上转型,父类引用指向子类对象
        Father f =  new Son();
        //实例方法(动态绑定机制)与引用变量实际引用的对象绑定,调用子类重写后的方法,有运行时JVM决定
        f.m1();
        //静态方法(静态绑定机制)与引用变量所声明的类型绑定,实际上在编译阶段就做了绑定
        f.staticM1();

        //普通的,非多态相关的:通过类名可以调用各自类的静态方法
        Father.staticM1();
        Son.staticM1();

        //成员变量(包括静态变量和实例变量,静态绑定机制)与引用变量所声明的类型绑定,实际上在编译阶段就做了绑定
        System.out.println(f.var1);
        System.out.println(f.staticVar);
    }
}

4.6 简单工厂模式

设计模式(23种):
代表了最佳的实践,是软件开发人员在软件开发过程中面临的一般问题的解决方案
分为:

  1. 创建型:在创建对象的时候隐藏创建逻辑的方式
    • 工厂模式
      • 简单工厂模式(特例)
      • 工厂方法模式
      • 抽象工厂模式
      • ...
    • 单例模式
    • ...
  2. 结构型
  3. 行为型

简单工厂模式一般涉及的类:

  1. 工厂类
  2. 客户端类
  3. 商品类
  4. 具体商品子类
  5. 涉及到接口的相关内容

缺点可以用工厂方法模式解决(涉及接口)

Factory.java

//商品工厂,根据厂家需求生产不同的商品
//简单工厂模式:可以加相应的业务代码(日志、判断)
//经常应用简单工厂模式时,生成实例的方法是静态方法
//简单工厂模式:工厂类
//涉及到接口的相关内容:工厂类  商品类(接口或抽象类)具体商品子类 客户端
public class Factory {
    //生产商品:使用父类作为方法返回值类型
    //简单工厂模式一般声明为静态方法
    public static Goods getGoods(String str) {
        if (str.equals("food")) {
            return new Foods();
        }else{
            return new TVs();
        }
    }
}

Test.java

//客户端,测试类
public class Test {
    public static void main(String[] args) {
        //简单工厂模式一般声明为静态方法,测试类里面不用再声明生成Factory类对象,可以通过类名直接调用Factory的静态方法
        //Factory factory = new Factory();
        Goods goods = Factory.getGoods("food");
        goods.printPrice();

        goods = Factory.getGoods("TVs");
        goods.printPrice();
    }
}

相关文章

网友评论

      本文标题:【JAVA学习笔记】面向对象三大特性之“重写”与“多态”

      本文链接:https://www.haomeiwen.com/subject/pvzskltx.html