多态的概述
多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中:同一个方法,具体实现会完全不同。
多态的必要条件:
- 继承是多态的前提
- 子类重写父类方法
- 父类引用指向子类对象
多态的使用场合:
- 使用父类做方法的形参,实参可以是任意子类类型
- 使用父类做方法的返回值类型,返回值可以是任意子类的对象
多态的实现
多态的实现主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。
【示例】基于继承实现的多态---管理员给动物喂食
class Animal {
public void eat() {
System.out.println("动物 eat");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("dog 吃骨头");
}
}
class Pig extends Animal {
@Override
public void eat() {
System.out.println("Pig 吃饲料");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("cat 吃鱼儿");
}
}
// 管理员
class Admin {
/
多态满足条件
1.Cat、Dag和Pig继承Animal
2.Cat、Dag和Pig重写了Animal的eat()方法
3.父类引用指向子类对象(实参赋值给形参)
此处如果没有使用多态,管理员需要分别给每一个动物添加一个喂食的方法!!!
/
public void feedAnimal(Animal a) {
a.eat(); // 动态绑定
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
// 初始化一个管理员对象
Admin admin = new Admin();
// 管理员给动物喂食
admin.feedAnimal(new Dog());
admin.feedAnimal(new Pig());
admin.feedAnimal(new Cat());
}
}
引用数据类型转换
回顾基本数据类型转换:
【示例】基本数据类型转换案例
// 整数(int)转换成小数(double) int x = 10; double y = x; // 小数(double)转换成整数(int) double n = 2.2; int m = (int)n; |
而引用数据类型转换主要包括向上转型和向下转型。
向上转型:父类引用指向子类对象,属于自动类型转换。
格式:父类类型 变量名 = 子类对象;
向下转型:子类引用指向父类对象,属于强制类型转换。
格式:子类类型 变量名 = (子类类型) 父类对象;
【示例】引用类型转换案例
// 父类
class Person {
public void eat() {
System.out.println("person eat ...");
}
}
// 子类
public class Student extends Person {
public void study() {
System.out.println("Student study ...");
}
}
// 测试类
public class PolymorphismDemo {
public static void main(String[] args) {
// 向上转型:父类引用指向子类对象
Person p = new Student();
p.eat(); // 调用Person的eat()方法
// p.study(); // 编译失败,不能调用Student的study()方法
// 向下转型:子类引用指向父类对象
Student stu = (Student)p;
stu.eat(); // 调用Person的eat()方法
stu.study(); // 调用Student的study()方法
}
}
向上转型:
优点:隐藏了子类类型,提高了代码的扩展性,多态本身就是向上转型的过程。
缺点:只能使用父类共性的内容,不能调用子类特有方法!
向下转型:
优点:向下转型之后,可以调用子类特有方法
缺点:但是向下转型有风险,容易发生ClassCastException异常!
instanceof运算符
当要使用子类特有功能时,就需要使用向下转型。但是面对具体的子类对象,在向下转型时容易发生ClassCastException类型转换异常,在转换之前必须做类型判断。
java中的instanceof运算符,用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
语法格式:boolean result = object instanceof class
注意:class可以是类,也可以是接口!
作用:判断左边的“对象”是否是右边“类或接口”的实例,如果满足条件则返回true,否则返回false。
什么是编译时类型呢?什么是运行时类型呢???
Animal animal = new Tiger();
编译时类型 运行时类型
instanceof二元运算符编译时的特点
a)当obj为null的情况
当左边obj为null,则右边的“类或接口”可以是任意类型。
b)当obj不是null的情况
当右边“类或接口”是左边“对象”的父类、本身类和子类时,编译通过,否则编译失败。
--> 注意:此处的“本身类”指的就是“编译时类型”。
instanceof二元运算符运行时的特点
a)当obj为null的情况
当obj为null,则返回的结果肯定是false。
b)当obj不是null的情况
当右边“类或接口”是左边“对象”的父类和本身类,返回结果就是true;
当右边“类或接口”是左边“对象”的子类和兄弟类,返回结果就是false。
--> 注意:此处的“本身类”指的就是“运行时类型”。
【示例】多态中成员变量特点:无论编译和运行,都参考等号左边(引用类型变量所属的类)。
class Father {
int num = 10;
}
class Son extends Father {
int num = 20;
String name = "小明";
}
public class InstanceDemo {
public static void main(String[] args) {
Father father = new Son();
// 多态中的成员变量:无论编译和运行,都参考等号左边的类
System.out.println("num:" + father.num); // 编译通过,输出父类中的num值
// System.out.println("name:" + father.name); 编译错误
}
}
多态中成员方法特点
编译看左边,参考等号左边引用对象所属的类是否有该方法。
运行看右边,在执行期间判断引用对象的实际类型,然后根据其实际的类型调用其相应的方法,又称为动态绑定。
class Father {
public void eat() {
System.out.println("父类中的eat方法");
}
}
class Son extends Father {
public void study() {
System.out.println("子类中的show方法");
}
public void eat() {
System.out.println("子类中的eat方法");
}
}
public class InstanceDemo {
public static void main(String[] args) {
Father father = new Son();
father.eat(); // 编译通过,调用子类中的eat方法
// father.study(); 编译错误
}
}