多态
java面向对象三大基本特征之一
对于不同的对象,同一个方法调用,造成不同的结果。
比如主人对猫和狗喂食,但是猫与狗吃的食物不同,按照之前的写法需要重写喂食方法,以便传入不同的参数执行不同的语句。下面使用多态可以简化步骤。
写出Animal类,有name属性,由Cat和Dog类继承。
class Animal {
private String name;
Animal(String name) {
this.name = name;
}
public String name {
return name;
}
}
class Cat extends Animal{
Cat(String name) {
super(name);
}
}
class Dog extends Animal{
Dog(String name) {
super(name);
}
}
写出Food类,有name属性,由Fish和Bone类继承。
class Food {
private String name;
Animal(String name) {
this.name = name;
}
public String name {
return name;
}
}
class Fish extends Food{
Fish(String name) {
super(name);
}
}
class Bone extends Food{
Bone(String name) {
super(name);
}
}
写出Monster类,创建feed方法
class Monster {
private String name;
Monster(String name) {
this.name = name;
}
//传统做法,若添加了Animal或Food的子类则需要在此处添加相关方法
// public void feed(Cat cat,Fish fish){
// System.out.println(name + "对" + cat.getName() + "喂食" + fish.getName());
// }
// public void feed(Dog dog,Bone bone){
// System.out.println(name + "对" + dog.getName() + "喂食" + bone.getName());
// }
public String feed(Animal animal,Food food) {
return name + "对" + animal.getName() + "喂食" + food.getName();
}
}
在main方法调用测试
public class main {
public static void main(String[] args) {
Cat cat1 = new Cat("tom");
Fish fish1 = new Fish("fish");
Dog dog1 = new Dog("spike");
Bone bone1 = new Bone("bone");
Monster monster = new Monster("我");
System.out.println(monster.feed(tom1,fish1));
System.out.println(monster.feed(dog1,bone1));
}
}
如上,以子类参数调用feed方法是多态的体现
输出
我对tom喂食fish
我对spike喂食bone
向上转型与向下转型
向上转型:父类的引用指向了子类的对象
父类名 对象名 = new 子类名();
如上的main方法中创建cat1、fish1对象可以写成
//编译类型在等号左边,运行类型在等号右边
//编译类型确定后不能改变,运行类型可以改变
Animal cat1 = new Cat("tom");//运行类型为Cat,编译类型为Animal
Food fish1 = new Fish("fish");//运行类型为Fish,编译类型为Food
可以调用父类中的所有成员(须遵守访问权限), 但是不能调用子类特有的成员,也就是说,不能以调用cat1对象,调用其Cat类方法,但是可以调用Animal类方法。即使重写了方法,运行时还是优先从子类寻找方法,找不到再找父类方法。
向下转型
子类名 对象名 = (子类名) 已向上转型对象名;
将上面已经向上转型的cat1、fish1对象向下转型,指向新对象cat0,fish0
Cat cat0 = (Cat) cat1;//cat0的运行类型和编译类型均为Cat
Food fish0 = (Fish) fish1;
对象名必须为当前目标类型的对象 Animal可以转成Cat,Cat不能转成Dog
Dog dog = (Dog) cat1; //运行时抛出异常ClassCastException
instance比较操作符
用于判断对象的运行类型是否为XX类型或XX类型的子类型
public class main0 {
public static void main(String[] args){
//属性没有重写,属性的值看编译类型
A a = new B();//向上转型
System.out.println(a.num);//10
B b = new B();
System.out.println(b.num);//20
//instanceof 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型,是返回true,否返回false
BB aa = new BB();
System.out.println(aa instanceof AA);//true
System.out.println(aa instanceof BB);//true
AA bb = new BB();
System.out.println(bb instanceof AA);//true
System.out.println(bb instanceof BB);//true
}
}
class A{
int num = 10;
}
class B extends A{
int num = 20;
}
class AA{
}
class BB extends AA{
}
多态数组
Animal[] animal = new Animal[4];
animal[0] = new Cat("tom10");
aniaml[1] = new Cat("tom100");
animal[2] = new Animal("jack10");
animal[3] = new Dog("spike10");
//编译类型为Animal,运行类型是根据实际情况由jvm判断
可根据下标获取对应的对象。本类或其子类的对象才能写入到数组中。
动态访问机制
看看下面会输出什么
public class DynamicBinding {
public static void main(String[] args) {
A a = new B();
System.out.println(a.sum());
}
}
class A{
public int num = 100;
public int sum(){
return getNum() + 200;
}
public int getNum(){
return num;
}
}
class B extends A{
public int num = 10;
public int getNum(){
return num;
}
}
当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定;当调用对象属性时,没有动态绑定机制,哪里声明,就在哪里使用
运行类型是B,就在B类中查找sum()方法,若没有找到,就向父类查找
在父类中调用方法getNum(),就要回去运行类型查找方法,即在子类找到,返回给父类方法sum()
输出
210