java系列(三)多态

一、多态的基本概念
1.多态的定义:
指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
2.多态的发生条件:
(1)有继承
(2)重写
(3)父类引用指向子类对象(就是用子类的对象给父类的引用赋值)

father f =new son();

3.多态的作用:
(1).可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
(2).可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
(3).接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
(4).灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
(5).简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
具体看下面的代码例子:

class Bird{
    private Wing wing;
    public void moo(){
    System.out.println("鸟叫声");
    }
}

鹦鹉类:

class Parrot extends Bird{
    public void moo(){
        System.out.println("鹦鹉的叫声");
    }
}

麻雀类:

class Sparrow extends Bird{
    public void moo(){
        System.out.println("麻雀的叫声");
    }
}
class Wife{
    public void listen(Bird bird){//wife这里的参数是bird,之后可以用
        bird.moo();     //子类的鹦鹉和麻雀给它赋值,这样调用的就是不一样的
    }                  //moo()方法,体现了多态性。

//这时多态就很好的体现了,你妻子想听鸟叫,无论什么鸟都可以给她,但是你想让她和鹦鹉
//说话,你就买了一只鹦鹉传给listen方法,结果你妻子听到了鹦鹉的叫声,
//程序输出:鹦鹉的叫声

public class demo{
public static void main(String[] args) {
    new Wife().listen(new Parrot());
    }
}

二、多态的向上转型
多态的向上转型就是多态的常见表现方式,也就是用子类的对象给父类的引用赋值,这样在编译器看来,这个子类对象变成了父类的对象。
1.转型过程中需要注意的问题
(1)向上转型时,子类单独定义的方法会丢失。比如上面Dog类中定义的run方法,当animal引用指向Dog类实例时是访问不到run方法的,animal.run()会报错。原因是,在编译器看来,这个对象是Animal类型的,不会有run这个方法。
(2)子类引用不能指向父类对象。Cat c = (Cat)new Animal()这样是不行的。但是,在一个父类引用已经引用了子类对象的时候,可以让这个父类引用向下转型(用强制转换的办法),然后就能调用子类特有的方法。
2.向上转型的好处
(1)减少重复代码,使代码变得简洁。
(2)提高系统扩展性。

class Animal {
    public void eat(){
        System.out.println("animal eatting...");
    }
}
 
class Cat extends Animal{
    public void eat(){
        System.out.println("我吃鱼");
    }
}
 
class Dog extends Animal{
    public void eat(){
        System.out.println("我吃骨头");
    }
    public void run(){
        System.out.println("我会跑");
    }
}
 
public class Main {
    public static void main(String[] args) {
        Animal animal = new Cat(); //向上转型
        animal.eat();
        animal = new Dog();
        animal.eat();
    } 
}
 
//结果:
//我吃鱼
//我吃骨头

三、多态的向下转型:
与向上转型相对应的就是向下转型了。向下转型是把父类对象转为子类对象。但是,需要这个父类引用之前是指向子类对象的,而且向下转型只能变成原先的那个子类。
案例驱动,先看一个例子:

//还是上面的animal和cat dog
Animal a = new Cat();
Cat c = ((Cat) a);
c.eat();//输出  我吃鱼
Dog d = ((Dog) a);//a是一个Cat对象向上转型的,向下转型只能变成Cat,不能变成Dog
d.eat();// 报错 : java.lang.ClassCastException:com.chengfan.animal.Cat cannot be cast to com.chengfan.animal.Dog

解读:因为 a 本身就是 Cat 对象,所以它理所当然的可以向下转型为 Cat,也理所当然的不能转为 Dog,你见过一条狗突然就变成一只猫这种操蛋现象。
向下转型注意事项
向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
向下转型只能转型为本类对象(猫是不能变成狗的)。

四、多态的方法调用规则(很重要)
当父类对象引用变量引用子类对象时,父类决定了可以调用的方法有哪些,父类中有的方法才能被调用,否则编译会出错;子类的类型决定了调用谁的成员方法,如果子类中没有覆盖该方法,那么会去父类中寻找。

class X {
    public void show(Y y){
        System.out.println("x and y");
    }
    public void show(){
        System.out.println("only x");
    }
}
 
class Y extends X {
    public void show(Y y){
        System.out.println("y and y");
    }
    public void show(int i){
        System.out.println(i);
    }
}
 
class main{
    public static void main(String[] args) {
        X x = new Y();
        x.show(new Y());
        x.show();
        x.show(3)//这里会报错,因为父类中没有show(int i)方法,所以多态中
        //无法调用这个方法
    }
}
//结果
//y and y
//only x

首先父类是X,父类中定义了show(Y y)和show()两种方法,那么在多态中,可以调用这两种方法。所以x.show(new Y)和x.show()可以被执行,执行的时候,由于子类Y重写了这个方法,所以调用子类的方法;而x.show()没有被子类重写,所以去调用父类的show()方法。
而父类中没有show(int i)这个方法,所以x.show(3)调用会编译出错。

再来一个例子:

class A {
    public String show(D obj) {
        return ("A and D");
    }
 
    public String show(A obj) {
        return ("A and A");
    }
 
}
 
class B extends A{
    public String show(B obj){
        return ("B and B");
    }
 
    public String show(A obj){
        return ("B and A");
    }
}
 
class C extends B{
 
}
 
class D extends B{
 
}
 
public class Demo {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
 
        System.out.println("1--" + a1.show(b));
        System.out.println("2--" + a1.show(c));
        System.out.println("3--" + a1.show(d));
        System.out.println("4--" + a2.show(b));
        System.out.println("5--" + a2.show(c));
        System.out.println("6--" + a2.show(d));
        System.out.println("7--" + b.show(b));
        System.out.println("8--" + b.show(c));
        System.out.println("9--" + b.show(d));
    }
}
//结果:
//1--A and A
//2--A and A
//3--A and D
//4--B and A
//5--B and A
//6--A and D
//7--B and B
//8--B and B
//9--A and D

继承链中对象方法的调用的优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
如果你能理解这个调用关系,那么多态你就掌握了。我们回到那个复杂的例子:
abcd 的关系是这样的:C/D —> B —> A
我们先来分析4 : a2.show(b)
首先,a2是类型为A的引用类型,它指向类型为B的对象。A确定可调用的方法:show(D obj)和show(A obj)。
a2.show(b) ==> this.show(b),这里this指的是B。
然后.在B类中找show(B obj),找到了,可惜没用,因为show(B obj)方法不在可调用范围内,this.show(O)失败,进入下一级别:super.show(O),super指的是A。
在A 中寻找show(B obj),失败,因为没用定义这个方法。进入第三级别:this.show((super)O),this指的是B。
在B中找show((A)O),找到了:show(A obj),选择调用该方法。
输出:B and A
如果你能看懂这个过程,并且能分析出其他的情况,那你就真的掌握了。
我们再来看一下9:b.show(d)
首先,b为类型为B的引用对象,指向类型为B的对象。没有涉及向上转型,只会调用本类中的方法。
在B中寻找show(D obj),方法。现在你不会说没找到了吧?找到了,直接调用该方法。
输出 A and D。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值