一,多态的概念
注:多态是指对象(具体),而不是指类(抽象)。
二,多态的格式和使用
代码当中体现多态性,其实就是一句话:父类引用指向子类对象。(左父右子)
格式:父类名称 对象名 = new 子类名称();
或者:
接口名称 对象名 = new 实现类名称()
public class Fu {
public void method() {
System.out.println("父类方法");
}
public void methodFu() {
System.out.println("父类特有方法");
}
}
注:
public class Zi extends Fu {
@Override
public void method() {
System.out.println("子类方法");
}
}
public class Demo01Multi {
public static void main(String[] args) {
// 使用多态的写法
// 左侧父类的引用,指向了右侧子类的对象
Fu obj = new Zi();
obj.method(); //打印输出:子类方法
obj.methodFu(); //打印输出:父类特有方法
}
}
注:“Fu obj = new Zi(); ”中体现了多态,调用对象的方法时,右侧new的谁就运行谁。若子类没有,再找父类。
三,多态中成员变量的使用
访问成员变量的两种方式: 1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。 2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
public class Fu {
int num = 10;
public void showNum() {
System.out.println(num);
}
}
public class Zi extends Fu {
int num = 20;
int age = 16;
@Override
public void showNum() {
System.out.println(num);
}
}
public class Demo01MultiField {
public static void main(String[] args) {
// 使用多态的写法,父类引用指向子类对象
Fu obj = new Zi();
//1)通过对象访问成员变量
System.out.println(obj.num); // 父:10
// System.out.println(obj.age); // 错误写法!
//2)通过成员方法访问成员变量
// 子类没有覆盖重写,就是父:10
// 子类如果覆盖重写,就是子:20
obj.showNum();
}
}
注意:错误写法 “System.out.println(obj.age);”,因为父类中没有成员变量age,只能向上Object类找(也没有),不能向下子类中找。所以该语句会报错!!!
四,多态中成员方法的使用特点
在多态的代码当中,成员方法的访问规则是: 看new的是谁,就优先用谁,没有则向上找。
总结:成员变量看左,成员方法看右。
public class Fu {
public void method() {
System.out.println("父类方法");
}
public void methodFu() {
System.out.println("父类特有方法");
}
}
public class Zi extends Fu {
@Override
public void method() {
System.out.println("子类方法");
}
public void methodZi() {
System.out.println("子类特有方法");
}
}
public class Demo02MultiMethod {
public static void main(String[] args) {
Fu obj = new Zi(); // 多态
//父子都有,优先用子
obj.method(); // 打印输出:子类方法
//子类没有,父类有,向上找到父类
obj.methodFu(); // 打印输出:父类特有方法
// obj.methodZi(); // 错误写法!
}
}
注:obj.methodZi是错误写法!
五,使用多态的好处
六,对象的上下转型
1.向上转型
示例:
public abstract class Animal {
public abstract void eat();
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
public void catchMouse() {
System.out.println("猫抓老鼠");
}
}
弊端:
解决方案:用对象的向下转型【还原】
2.向下转型
1)什么时候要向下转型:想要使用子类专有方法时。
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃SHIT");
}
public void watchHouse() {
System.out.println("狗看家");
}
}
public class Demo01Main {
public static void main(String[] args) {
// 对象的向上转型,就是:父类引用指向之类对象。
Animal animal = new Cat(); // 本来创建的时候是一只猫
animal.eat(); // 猫吃鱼
// animal.catchMouse(); // 错误写法!
// 向下转型,进行“还原”动作
Cat cat = (Cat) animal;
cat.catchMouse(); // 猫抓老鼠
// 下面是错误的向下转型
// 本来new的时候是一只猫,现在非要当做狗
// 错误写法!编译不会报错,但是运行会出现异常:
// java.lang.ClassCastException,类转换异常
Dog dog = (Dog) animal;
}
}
2)格式:
原本的向上转型(多态的实现):
Animal animal = new Cat();
由于想使用子类Cat类中的catchMouse子类专有方法,所以要转回去——把本来是Animal父类的对象animal转为Cat子类,并将对象重命名为cat。
Cat cat = (Cat) animal;
3)向下转型的条件
注:向下转型不一定安全。只能将之前向上转型的转回来。若之前没有发生过向上转型,则该对象不可实现向下转型。
注:没有创建狗的对象,却向下转型为狗,则会报“类转型错误”的异常。
3.instanceof关键字:用于判断父类引用的是哪个子类
public class Demo02Instanceof {
public static void main(String[] args) {
Animal animal = new Dog(); // 本来是一只狗
animal.eat(); // 狗吃SHIT
// 如果希望掉用子类特有方法,需要向下转型
// 判断一下父类引用animal本来是不是Dog
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
// 判断一下animal本来是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
giveMeAPet(new Dog());
}
public static void giveMeAPet(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
}
注:这段断码段为常用固定写法,用于判断,防止报错。
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
// 判断一下animal本来是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}