Java 之多态
- 概述
- 好处
- 弊端
- 转型
- 类型判断
- 成员变量
- 成员函数
- 静态函数
概述
定义:某一类事物的多种存在形态.
// 对象的多态性
class 动物
{}
class 猫 extends 动物
{}
class 狗 extends 动物
{}
猫 x = new 猫();
动物 x = new 猫(); // 一个对象, 两种形态
// 猫这类事物即具备着猫的形态, 又具备着动物的形态, 这就是对象的多态性
// 简单说: 就是一个对象对应着不同类型.
// 多态在代码中的体现: 父类或者接口的引用指向其子类的对象.
多态的好处
// 未使用多态之前
abstract class Animal
{
abstract void eat();
}
class Dog extends Animal
{
// 覆盖父类 eat() 方法
void eat()
{
System.out.println("啃骨头");
}
// 狗类特有方法 看家
void lookHome()
{
System.out.println("看家");
}
}
class Cat extends Animal
{
// 覆盖父类 eat() 方法
void eat()
{
System.out.println("吃鱼");
}
// 猫类特有方法 抓老鼠
void catchMouse()
{
System.out.println("抓老鼠");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
Cat c = new Cat();
Dog d = new Dog();
// 调用猫 eat() 方法
method(c);
// 调用狗 eat() 方法
method(d);
}
// 为提高代码复用性, 创建方法
public static void method(Cat c)
{
c.eat();
}
// 函数重载
public static void method(Dog d)
{
d.eat();
}
}
// 问题分析: 随着动物的增多, 都需要增加与该动物相匹配的 eat() 方法.
// 升级版
class DuoTaiDemo
{
public static void main(String[] args)
{
Cat c = new Cat();
Dog d = new Dog();
method(c); // Animal a = new Cat();
method(d); // Animal d = new Dog();
}
// 多态, 此处只需要定义一个方法, 狗类和猫类都能使用
public static void method(Animal a)
{
a.eat();
}
}
多态的好处:
- 提高了代码的扩展性, 前期定义的代码可以使用后期的内容.
例如: 前期定义的 method() 方法, 在新增加动物(如猪, 属于后期的内容)也可以使用.
多态的弊端:
- 前期定义的内容不能使用(调用)后期子类的特有内容
多态的前提:
- 必须有关系, 要么继承,要么实现
- 要有覆盖
转型
- 向上转型: 限制对特有功能的访问
- 向下转型: 为了使用子类中的特有方法
class DuoTaiDemo
{
public static void main(String[] args)
{
Animal a = new Cat(); //此处猫对象提升为了动物类型, 即自动类型提升. 猫类型的特有功能无法访问
// 自动类型提升的作用: 就是限制对特有功能的访问
// 专业讲: **向上转型**
a.eat();
// 如果需要使用具体动物猫的特有功能
// 可以将该对象进行**向下转型**
Cat c = (Cat)a; // 向下转型的目的是为了使用子类中的特有方法
c.catchMouse(); // 调用猫的特有方法
// 注意: 对于转型, 自始至终都是子类对象(同一种动物)在做着类型的变化
Animal a1 = new Dog();
Cat c1 = (Cat)a1; // 错误: ClassCastException 类型转换异常
}
}
对象类型判断 instanceof
instanceof: 用于判断对象的具体类型, 只能用于引用数据类型判断.
通常在向下转型前用于健壮性的判断.
多态时,成员的特点
- 成员变量
- 成员函数
- 静态变量
成员变量(了解)
编译时: 参考引用型变量所属的类中是否有调用的成员变量, 有, 编译通过; 没有, 编译失败.
运行时: 参考引用型变量所属的类中是否有调用的成员变量, 并运行该所属类中的成员变量.
简单说, 编译和运行都参考等号左边.
class Fu
{
int num = 3;
void show()
{
System.out.println("fu show");
}
static void method()
{
System.out.println("fu static method");
}
}
class Zi extends Fu
{
int num = 4;
void show()
{
System.out.println("zi show");
}
static void method()
{
System.out.println("zi static method");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
Fu f = new Zi(); // 子类对象初始化完成后,子类对象内存地址中既有子类的num, 又有父类的 num.
System.out.println(f.num); // 结果为 3
}
}
成员函数 (非静态函数)
编译时, 参考引用类型变量所属的类中是否有调用的函数. 有, 编译通过; 没有, 编译失败.
运行时, 参考的是对象所属的类中是否有调用的函数.
简单说, 编译看左边, 运行看右边.
备注: 非静态函数只能通过对象实现调用, 引用类型变量执行对象所在的内存地址.
class DuoTaiDemo
{
public static void main(String[] args)
{
Fu f = new Zi();
f.show(); // 结果为 zi show
}
}
// 示例2: 写出程序结果
interface A{}
class B implements A
{
public String func()
{
return "func";
}
}
class Demo
{
public static void main (String[] args)
{
A a = new B();
System.out.println(a.func()); //多态, 对于非静态方法, 编译看左边,运行看右边
// 编译失败, 因为 a 所属的 A 接口中没有定义 func 方法
}
}
静态函数
编译时, 参考引用类型变量所属的类中是否有调用的静态方法;
运行时, 参考引用类型变量所属的类中是否有调用的静态方法;
简单说, 编译和运行都看左边.
备注: 静态函数的调用不需要依赖对象, 直接用类名调用即可.因此,静态函数不涉及多态性.
class DuoTaiDemo
{
public static void main(String[] args)
{
Fu f = new Zi();
f.method(); // 结果 fu static method
}
}
_参考资料_ - [JavaSE 基础视频](https://www.bilibili.com/video/av3096713/#page=2)