多态
多态指的是:父类型引用指向子类型对象。包括编译阶段和运行阶段。编译阶段:绑定父类的方法。运行阶段:动态绑定子类型对象的方法。
- 一个对象的编译类型和运行类型可以不一致、
- 编译类型在定义对象时就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时 = 的左边,运行类型看 = 的右边
Animal animal = new Cat(); animal的编译类型是Animal,运行类型是Cat。
代码分析
多态的初步了解
定义一个父类Animal类
public class Animal {
protected void call(){
System.out.println("动物叫唤====");
}
}
定义两个子类,Cat类和Dog类
public class Cat extends Animal {
@Override
public void call() {
System.out.println("喵喵喵======");
}
}
public class Dog extends Animal{
@Override
public void call() {
System.out.println("小狗汪汪汪====");
}
}
在主程序中
Animal animal = new Cat();
animal.call();
animal = new Dog();
animal.call();
输出结果是
问题深入
对对象的多态有了初步的了解后,我们再看另一个问题:
Food类的定义
public class Food {
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Fish类的定义,其他的类定义类似,这里不赘述
public class Fish extends Food{
public Fish(String name) {
super(name);
}
}
Animal类的定义
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void call(){
System.out.println("动物叫唤====");
}
}
Cat类的定义
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void call() {
System.out.println("小猫喵喵喵====");
}
}
主人类只有一个方法:给动物喂食 可以给狗喂骨头吃,也可以给猫喂鱼吃
public class Master {
private String name;
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void feed(Dog dog, Bone bone){
System.out.println("主人给\t" + dog.getName() + "\t喂\t" + bone.getName() + "\t吃");
}
public void feed(Cat cat, Fish fish){
System.out.println("主人给\t" + cat.getName() + "\t喂\t" + fish.getName() + "\t吃");
}
}
这里可以利用方法的重载完成这项功能
Master master = new Master("YMY");
Cat cat = new Cat("小花猫");
Fish fish = new Fish("秋刀鱼");
Dog dog = new Dog("哈士奇");
Bone bone = new Bone("大棒骨");
master.feed(cat,fish);
master.feed(dog,bone);
但是,如果Animal的子类越来越多,有了老虎,狮子。。。。 Food的子类也越来越多,那是不是得重载非常多次?
这时候就可以用多态来解决
public void feed(Animal animal, Food food){
System.out.println("主人给\t" + animal.getName() + "\t喂\t" + food.getName() + "\t吃");
}
feed方法这样写就可以解决重载冗余的问题
多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名=new 子类类型();
Animal animal=new Cat("小花猫");
这个就是向上转型,我实际的运行类是Cat类,但是我却用了一个父类的引用
- 此时 animal可以调用父类中的所有成员(需遵守访问权限),但是不可以调用子类中的特有成员(就是没有从父类中继承的),因为在编译阶段,能调用哪些成员是由编译类型决定的。
- 最终运行效果看子类的具体实现,调用方法时,按照从子类开始查找方法
在Cat类中只定义了两个方法,catchMouse方法在Animal类中没有
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void call() {
System.out.println("小猫喵喵喵====");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
Animal类中‘有run方法没有被重写
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void call(){
System.out.println("动物叫唤====");
}
public void run(){
System.out.println("动物跑");
}
}
Animal animal = new Cat("辛巴");
animal.call();
animal.run();
animal是不可以调用Cat类的特有方法的
多态的向下转型
还是刚才的例子:那我就是想用animal来调用Cat类中的特有方法catchMouse呢?
这时候就可以用向下转型
Cat cat = (Cat) animal;
cat.catchMouse();
这样据可以调用catchMouse方法了,**注意:**animal只能向下转型为Cat类,不可以转型为Dog类,因为一开始定义对象时 Animal animal = new Cat("辛巴");
注意事项
- 语法: 子类类型 引用名=(子类类型)父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用只能强转为当前目标运行类型的对象
Animal animal = new Cat("辛巴");
也就是说 animal这个对象只能转型为Cat类 - 可以调用子类中的所有成员
属性
属性没有重写之说,属性的值直接看编译类型
public class A {
public int count=10;
}
class B extends A{
public int count=20;
}
A a = new B();
System.out.println(a.count);
这时候输出的值应该是多少呢?
这可以是向上造型
instanceof 方法
- instanceof 方法是比较运算符,用于判断对象是否为 某种类型或者某种类型的子类
- instanceof可以在运行阶段动态判断引用指向的对象的类型。
- instanceof的语法: (引用 instanceof 类型) 运算结果只能是:true/false。
- eg:c instanceof Cat c是一个引用,c变量保存了内存地址指向了堆中的对象。假设结果为true表示: c引用指向的堆内存中的java对象是-一个cat;假设为false表示: c引用指向的堆内存中的java对象不是–个Cat.
B b = new B();
System.out.println(b instanceof B);
System.out.println(b instanceof A);
再深入一步,instanceof 方法是判断编译类型还是运行类型?
A a = new B();
System.out.println(a instanceof B);
System.out.println(a instanceof A);
输出结果也都为true,可以知道 instanceof 方法是判断其运行类型的
- instanceof 方法 前后写的两种类型必须是继承关系,否则会直接报错
String类型和Double类型毫不相关
属性看编译类型 方法看运行类型