Java面向对象之多态与重写

55 阅读5分钟

引入

在学习这两个新概念前,必须要了解继承,向上转型,向下转型等知识点Java面向对象之继承(extends) - 掘金

重写

  • 而在每个子类中都有一个和基类Object中的toString方法重名的方法,并且在方法体上方都标注了@Override,这时就是在子类中对父类方法进行了重写
  • 重写会受到访问修饰符的影响,在重写方法时,访问修饰符的访问权限不能降低,即public > protected > default > private

多态

  • 我们之前举得例子中,遍历输出Arraylist<People> peoples中的内容,内容中既持有Student类的对象,又有持有Teacher类的对象,而且我们输出的对象并不是一串对象所在的地址,而是对象的内容,这是因为我们在People的子类Student和Teacher中都创建了toString方法,在输出时持有子类对象的父类引用调用了子类的toString方法,那么对于People类的Arraylist<People> peoples容器中的Student对象和Teacher对象来说,遍历出的内容会自动匹配相对应的其类中toString方法进行输出,当子类中没有该方法的重写才会去调用基类中的toString方法,也就是输出对象所在的地址,这种特性就叫做多态。

  • 多态是java的一种特性,持有子类对象的父类引用访问子类中的方法,在不同的子类中如果有同名的方法会自动匹配,也就是持有子类的父类对象调用和父类的同名的子类方法,此时会根据调用对象的特征(持有的是哪个类型的子类)呈现多种状态。

// 定义抽象类
abstract class Animal {
    public abstract void makeSound();
}

// 定义具体子类
class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }
}

// 使用抽象类引用
public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();  // 使用抽象类引用指向具体子类对象
        Animal myCat = new Cat();

        myDog.makeSound();  // 输出 "Woof!"
        myCat.makeSound();  // 输出 "Meow!"
    }
}

多态形成条件

  • 实现多态,必须要存在继承的关系
  • 父类变量必须持有了子类的对象

方法的重写

  • 对于抽象类子类中必须重写父类的方法(即为必须都要有这个方法),没有只需return null即可,但子类必须有,重写即为子类中方法的名字,参数列表必须时相同的,这样才可以达成覆盖父类的方法。这一过程就是方法的重写

多态中的转型

image.png

  • 向上转型
    • 父类持有子类的对象
  • 向下转型
    • 父类引用持有父类对象不能向下转型,即使是强制类型转换也不行 Student stu1 = (Student)(new People());//这行代码是无法运行的,只有父类引用持有子类对象的对象引用才能向下转型
    • 即为,我们可以将向上转型的对象重新向下转型,以便于访问该对象原本类中的属性和方法
  • 多态中的转型问题
    • 在向下转型时,如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
    • 可以使用instanceof来做转型前的校验
    • 使用格式:
      • 对象名 instanceof 类型
      • 判断一个对象是否是一个类的实例,是向下转型操作的安全员
      • 通俗的理解:判断关键字左边的对象,是否是右边的类型,返回boolean类型结果
// 父类
class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}

// 子类1
class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
    
    // 子类特有方法
    public void watchHouse() {
        System.out.println("狗看家");
    }
}

// 子类2
class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
    
    // 子类特有方法
    public void catchMouse() {
        System.out.println("猫抓老鼠");
    }
}

// 测试类
public class InstanceofExample {
    public static void main(String[] args) {
        System.out.println("=== instanceof 关键字使用示例 ===");
        
        // 1. 创建对象
        Animal animal1 = new Dog();  // 向上转型
        Animal animal2 = new Cat();  // 向上转型
        Animal animal3 = new Animal();
        
        // 2. 使用 instanceof 进行类型判断
        System.out.println("\n=== 类型检查 ===");
        System.out.println("animal1 instanceof Animal: " + (animal1 instanceof Animal));  // true
        System.out.println("animal1 instanceof Dog: " + (animal1 instanceof Dog));        // true
        System.out.println("animal1 instanceof Cat: " + (animal1 instanceof Cat));      // false
        
        System.out.println("animal2 instanceof Animal: " + (animal2 instanceof Animal));  // true
        System.out.println("animal2 instanceof Dog: " + (animal2 instanceof Dog));        // false
        System.out.println("animal2 instanceof Cat: " + (animal2 instanceof Cat));        // true
        
        // 3. 安全向下转型(先判断再转型)
        System.out.println("\n=== 安全向下转型 ===");
        
        // 处理 animal1
        if (animal1 instanceof Dog) {
            Dog dog = (Dog) animal1;  // 向下转型
            dog.eat();        // 调用重写的方法
            dog.watchHouse(); // 调用Dog特有的方法
        } else {
            System.out.println("animal1 不是 Dog 类型");
        }
        
        // 处理 animal2
        if (animal2 instanceof Cat) {
            Cat cat = (Cat) animal2;  // 向下转型
            cat.eat();         // 调用重写的方法
            cat.catchMouse();  // 调用Cat特有的方法
        } else {
            System.out.println("animal2 不是 Cat 类型");
        }
        
        // 4. 多态集合处理
        System.out.println("\n=== 多态集合处理 ===");
        Animal[] animals = {new Dog(), new Cat(), new Dog(), new Animal(), new Cat()};
        
        int dogCount = 0;
        int catCount = 0;
        int animalCount = 0;
        
        for (Animal animal : animals) {
            if (animal instanceof Dog) {
                dogCount++;
                Dog dog = (Dog) animal;
                dog.watchHouse();
            } else if (animal instanceof Cat) {
                catCount++;
                Cat cat = (Cat) animal;
                cat.catchMouse();
            } else if (animal instanceof Animal) {
                animalCount++;
                System.out.println("这是一个普通动物");
            }
        }
        
        System.out.println("狗的数量: " + dogCount);
        System.out.println("猫的数量: " + catCount);
        System.out.println("普通动物的数量: " + animalCount);
    
    // 通用处理方法:根据不同类型执行不同操作
    public static void processAnimal(Animal animal) {
        System.out.println("\n处理动物:");
        
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            System.out.println("这是一只狗");
            dog.watchHouse();
        } else if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            System.out.println("这是一只猫");
            cat.catchMouse();
        } else if (animal instanceof Animal) {
            System.out.println("这是一个普通动物");
        }
        
        // 所有动物都会吃东西
        animal.eat();
    }
}

注释@Override的重要性

  • 注意被重写的方法最好加上注释@Override,这样ide可以帮我们检查父类中有没有该方法,没有会提醒我们要加上。