引入
在学习这两个新概念前,必须要了解继承,向上转型,向下转型等知识点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即可,但子类必须有,重写即为子类中方法的名字,参数列表必须时相同的,这样才可以达成覆盖父类的方法。这一过程就是方法的重写
多态中的转型
- 向上转型
- 父类持有子类的对象
- 向下转型
- 父类引用持有父类对象不能向下转型,即使是强制类型转换也不行
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可以帮我们检查父类中有没有该方法,没有会提醒我们要加上。
多态的成员访问特点
- 成员变量:编译看左边(父类),执行看左边(父类)
- 在 Java 中,成员变量是没有“覆盖(Override)”这个概念的! 只有成员方法才可以被覆盖
- 子类中有和父类相同的成员变量时,变量的隐藏(Hiding): 只是遮挡,并没有消失。子类定义了同名变量,只是把父类的变量“藏”起来了,父类的变量依然完好无损地活在内存里。
- 父类的引用只能访问子类从父类继承过来的变量,如果执行修改也是修改的子类对象中继承的父类的成员变量
class Fu { private int num = 10; // 私有化 public int getNum() { return num; } public void setNum(int num) { this.num = num; } } class Zi extends Fu { private int num = 20; // 私有化 // 子类重写 getter 方法 @Override public int getNum() { return num; } // 子类重写 setter 方法 @Override public void setNum(int num) { this.num = num; } } public class TestPolymorphism { public static void main(String[] args) { Fu obj = new Zi(); // 编译看左边(Fu类有setNum),执行看右边(执行Zi类的setNum) // 此时毫无歧义,明确修改的是子类的变量! obj.setNum(30); // 编译看左边(Fu类有getNum),执行看右边(执行Zi类的getNum) System.out.println("变量 num 的值: " + obj.getNum()); // 打印结果:30 } }- 实际上这里的20就子类对象从父类继承来的变量,虽然子类也有num但父类的引用是无法访问,他只能拿到内存中没有隐藏的子类成员变量。
- 如果子类的成员变量不叫
num(比如叫num2),就彻底不能通过父类引用访问到class Fu { private int num = 10; public int getNum() { return num; } public void setNum(int num) { this.num = num; } } class Zi extends Fu { private int num2 = 20; // 子类重写 getter 方法 @Override public int getNum() { return num; } // 子类重写 setter 方法 @Override public void setNum(int num) { this.num = num; } } public class TestPolymorphism { public static void main(String[] args) { Fu obj = new Zi(); // 编译看左边(Fu类有setNum),执行看右边(执行Zi类的setNum) // 此时毫无歧义,明确修改的是子类的变量! obj.setNum(30); // 编译看左边(Fu类有getNum),执行看右边(执行Zi类的getNum) System.out.println("变量 num 的值: " + obj.getNum()); // 打印结果:30 } }
- 一般使用多态的时候不建议定义子类自己的成员变量,直接继承使用父类的成员变量就好了
class Fu { private int num = 10; public int getNum() { return num; } public void setNum(int num) { this.num = num; } } class Zi extends Fu { // 子类重写 getter 方法 @Override public int getNum() { return num; } // 子类重写 setter 方法 @Override public void setNum(int num) { this.num = num; } } public class TestPolymorphism { public static void main(String[] args) { Fu obj = new Zi(); // 编译看左边(Fu类有setNum),执行看右边(执行Zi类的setNum) // 此时毫无歧义,明确修改的是子类的变量! obj.setNum(30); // 编译看左边(Fu类有getNum),执行看右边(执行Zi类的getNum) System.out.println("变量 num 的值: " + obj.getNum()); // 打印结果:30 } } - 成员方法:编译看左边(父类),执行看右边(子类)