基本介绍
多态:多【多种】态【状态】。
方法或对象具有多种形态,面向对象的第三大特征。
多态是建立在封装和继承基础之上的。
具体体现
方法的多态
方法的重写和重载都可以体现出多态
示例:
public class PolyMethod {
public static void main(String[] args) {
//方法重载体现多态
A a = new A();
//通过不同的参数个数去调用sum方法,就会去调用不同的方法
//对sum方法来说,就是多态的体现。
System.out.println(a.sum(10,20)); //调用两个参数的sum方法
System.out.println(a.sum(10,20,30)); //调用三个参数的sum方法
//方法重写体现多态
B b = new B();
a.say();//调用A类里面的say方法
b.say();//调用B类里面的say方法
}
}
class B{//父类
public void say(){
System.out.println("B say() 方法被调用...");
}
}
class A extends B{//子类
public int sum(int n1,int n2){ //和下面sum构成重载
return n1 + n2;
}
public int sum(int n1,int n2, int n3){
return n1 + n2 + n3;
}
public void say(){
System.out.println("A say() 方法被调用...");
}
}
结果:
30
60
A say() 方法被调用...
B say() 方法被调用...
对象的多态【核心】
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时=号左边,运行类型看=号右边
Animal animal = new Dog();#animal编译类型是Animal,运行类型是Dog
animal = new Cat();#animal运行类型变成了Cat,编译类型仍然是Animal
即父类的引用可以指向子类的对象。
示例:
public class Animal {
public void cry(){
System.out.println("Animal cry()");
}
}
public class Cat extends Animal {
public void cry(){
System.out.println("Cat cry() 小猫");
}
}
public class Dog extends Animal{
public void cry(){
System.out.println("Dog cry() 小狗");
}
}
public class PoluObject {
public static void main(String[] args) {
//体验对象多态的特点
//animal 编译类型是Animal 运行类型是Dog
Animal animal = new Dog();
//运行时,执行到下一行,animal的运行类型是Dog,所以cry就是Dog类中的cry
animal.cry();
//这时,animal的编译类型是Animal,运行类型变成了Cat
animal = new Cat();
//运行到下一行时,animal的运行类型是Cat,所以cry是Cat类中的cry
animal.cry();
}
}
Cat和Dog类继承了Animal类。
结果:
Dog cry() 小狗
Cat cry() 小猫
主人喂食物问题
题目:主人需要每天给猫喂鱼,给狗喂骨头,那么我们用java代码怎么体现主人的动作?
没有使用多态:
一个动物类,Dog和Cat都继承了动物类
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 class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
一个食物类,Fish和Bone都继承了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;
}
}
public class Fish extends Food {
public Fish(String name) {
super(name);
}
}
public class Bone extends Food{
public Bone(String name) {
super(name);
}
}
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("主人" + name + "给" + dog.getName() + "吃" + bone.getName());
}
//主人给小猫 喂 鱼,方法重载
public void feed(Cat cat,Fish fish){
System.out.println("主人" + name + "给" + cat.getName() + "吃" + fish.getName());
}
}
可以看到,在没有使用多态的时候,主人给狗喂骨头和主人给猫喂鱼的方法都得写出来,那么试想下,如果主人给动物喂食的东西逐渐变多的时候,我们需要重载的feed方法也就越来越多,这样对整个代码的复用性和可维护性都不太友好。
public class Poly01 {
public static void main(String[] args) {
Master tom = new Master("tom");
Dog bigye = new Dog("bigye");
Bone bone = new Bone("bone");
tom.feed(bigye,bone);
Cat cat = new Cat("cat");
Fish fish = new Fish("fish");
tom.feed(cat,fish);
}
}
结果;
主人tom给bigye吃bone
主人tom给cat吃fish
使用多态:
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;
}
//使用多态机制,统一管理主人喂食的问题
//animal编译类型是Animal,可以指向Animal子类的对象
//food编译类型是Food,可以指向Food子类的对象
public void feed(Animal animal, Food food)
{
System.out.println("主人" + name + "给" + animal.getName() + "吃" + food.getName());
}
// //主人给小狗 喂食 骨头
// public void feed(Dog dog, Bone bone){
// System.out.println("主人" + name + "给" + dog.getName() + "吃" + bone.getName());
// }
//
// //主人给小猫 喂 鱼,方法重载
// public void feed(Cat cat,Fish fish){
// System.out.println("主人" + name + "给" + cat.getName() + "吃" + fish.getName());
// }
}
使用多态后,我们用animal指向Animal子类的对象,用food指向Food子类的对象,就可以很轻松的将方法重载多的问题解决。
结果:
主人tom给bigye吃bone
主人tom给cat吃fish
可以看出结果完全一致,但是使用多态后,就可以提高代码的复用性和可维护性。
多态的注意事项和细节
前提:两个对象或类存在继承关系。
多态向上转型
父类的引用指向子类的对象。【本质】
语法:父类类型 引用名 = new 子类类型();
特点:编译类型看=左边,运行类型看=右边
可以调用父类中的所有成员
不能调用子类中特定成员
最终的运行结果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法,然后调用。
Animal animal = new Cat();
animal.eat();
animal.run();//当前运行类型没有时,才会去查找父类的,若当前运行类型有的话,则直接执行当前运行类型的。
animal.show();
多态的向下转型
语法:子类类型 引用名 = (子类类型) 父类引用;
- 只能强制转换父类的引用,不能强制转换父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
public class Cat extends Animal {
public void cry(){
System.out.println("Cat cry() 小猫");
}
public void catchMoush(){
System.out.println("catchMoush");
}
}
public class Animal {
public void cry(){
System.out.println("Animal cry()");
}
}
public class PoluObject {
public static void main(String[] args) {
//体验对象多态的特点
//animal 编译类型是Animal 运行类型是Cat
Animal animal = new Cat();
//希望可以调用Cat中的catchMoush方法
//多态向下转型
//can not resolve mothed catchMoush()
//animal.catchMoush();
//编译类型是Cat,运行类型是Cat
Cat cat = (Cat) animal;
cat.catchMoush();
}
}
从上面的图可以看出,要求父类的引用必须指向的是当前目标类型的对象,也就是原来的 animal就是指向Cat对象的,如果animal之前指向Cat对象,再强制转换成Dog,就会报错,如下:
public class PoluObject {
public static void main(String[] args) {
//体验对象多态的特点
//animal 编译类型是Animal 运行类型是Dog
Animal animal = new Dog();
Cat cat = (Cat) animal;
cat.catchMoush();
}
}
结果:
Exception in thread "main" java.lang.ClassCastException: com.company.poly_.objectpoly_.Dog cannot be cast to com.company.poly_.objectpoly_.Cat
at com.company.poly_.objectpoly_.PoluObject.main(PoluObject.java:8)
属性没有重写之说,属性的值看编译类型
public class PolyDetail02 {
public static void main(String[] args) {
//属性值看编译类型
Base base = new Sub();
System.out.println(base.count);
Sub sub = new Sub();
System.out.println(sub.count);
}
}
class Base{
int count = 10;
}
class Sub extends Base{
int count = 20;
}
结果:
10
20
instanceOf 比较操作符
用于判断对象的类型(运行类型)是否是XX类型或者XX类型的子类型。
问题:是判断编译类型还是运行类型?运行类型
public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);
System.out.println(bb instanceof AA);
//aa 编译类型 AA,运行类型 BB
AA aa = new BB();
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);
Object o = new Object();
System.out.println(o instanceof AA);
System.out.println(o instanceof BB);
}
}
class AA{} //父类
class BB extends AA{} //子类
结果:
true
true
true
true
false
false