从零开始的Java编程之多态详解

一、多态的定义:

1.一个对象变量可以指示多种数据类型的现象称为多态,是同一种行为具有不同的表现现象的能力。

2.在运行时能自动的选择调用那个方法的现象称为动态绑定。

二、多态的三个条件以及优缺点:

  • 三个条件

    1. 要有继承关系。
    2. 子类要重写父类的方法。
    3. 父类引用指向子类对象。
  • 优点

    1. 消除类型之间的耦合关系。
    2. 可替换性
    3. 可扩充性
    4. 接口性
    5. 灵活性
    6. 简化性
  • 缺点

    无法直接访问子类特有成员

三、案例

  • 父类
class Employee{
    String name = "员工"static int age = 25;
    public void work(){
        System.out.print("员工的工作");
    }
    
    public static void sleep(){
        System.out.print("员工睡觉了")}
    
    public void holiday(){
        System.out.print("员工放假了")}
}
  • 子类

    class Programmer extends Employee{//子类继承父类
    	String name = "程序员";
        static int age = 90;
        String myname = "小明";
        public void work(){ //重写方法
            System.out.print("小明在敲代码");
        }
        
        public static void sleep(){
            System.out.print("小明在加班");
        }
        
        public void noholiday(){ //子类特有的属性
        	System.out.print("小明在公司度假")
        }
    }
    
  • 测试类

    class Test{
        public static void main(String[] args){
            Employee p = new Programmer(); //父类的引用指向子类的对象
            p.work();
            p.sleep;
            p.holiday;
            //以下两个方法的调用就是多态的缺点,无法直接访问子类的特有成员
            //p.noholiday();
            //System.out.print(p.myname);
            System.out.println(p.name);
            System.out.println(p.age);
        }
    }
    
  • 结果

    img

1.分析结果:

假设调用x.f(param),隐式参数声明为B类的对象(参考上述代码的子类与父类的关系)

  1. 编译器查看对象的声明类型的方法名

    有可能存在多个名为f的方法,但参数的类型不一样,如f(int)和f(String),编译器会一一列举所有B类中名为f的方法和其超类A中可访问属性且名为f的方法

    至此编译器获得所有可能被调用的候选方法

  2. 接下来查看调用方法时提供的参数类型

    如果所有候选方法中存在一个与提供的参数类型完全匹配,就选择这个方法。这个过程被称为重载解析。例如,对于调用x.f(“Hello”)来说,编译器将会挑选 f(String) 而不是 f(int)。由于允许类型转换(int转换为double,Programmer可以转换成Employee等等),所有的过程很复杂。如果编译器没有找到与参数类型匹配的方法,或者转换后有多个方法与之可以匹配就会报错。

    至此编译器已获得需要调用的方法名字和参数类型。

  3. 根据访问修饰符选择绑定方式

    如果是privatestaticfinal 方法或者构造器,那么编译器将可以准确的知道应该调用那个方法,我们将这种调用方式称为 静态绑定 。与之对应的时调用的方法依赖x的实际类型,并且在运行中实现的 动态绑定 。上面的例子中,P的实际类型为Programmer ,编译器采用动态方法的方式生成一条调用方法的指令。

  4. 程序运行,且采用动态绑定调用方法时

    虚拟机一定调用与x所引用对象的实际类型最合适的那个方法,假设x的类型是B,B是A的子类,如果B定义的方法f(String),就直接调用它,否则就在B的超类中寻找f(String) ,以此类推。

    每次调用方法都要进行搜索,时间开销相当的大,因此虚拟机预先为每一个类创建一个方法表,其中列出所有方法的签名(签名是方法的名字与参数列表) 与实际调用的方法,在调用方法时,虚拟机仅查询这个表就行了。

三、调用总结:

  • 那么我们可以根据以上的情况总结出多态成员的访问特点了

    1. 成员变量

      编译看左边(父类) 运行时看左边(父类)

    2. 成员方法

      编译看左边(父类) 运行时看右边(子类)

    3. 静态方法

      编译看左边(父类) 运行时看左边(父类)

四、类型转换

  • 向上转型

    Employee p = new Programme();
    

    子类对象Programme转化为父类对象Employee,向上转型时,这个时候Programme这个引用调用的方法是子类方法。子类单独定义的方法会丢失(nohliday)。

    子类引用不能指向父类对象。Programme p = (Programme) new Employee(); 是会报错的。

    向上转型的好处:

    1. 减少重复代码。
    2. 是代码变得整洁。
    3. 提高系统的扩展性。
  • 向下转型(使用场景:当需要使用子类的特殊方法时)

    //向下转型
    Employee e = new Programme();
    Programme p = null;
    p = (Programme) e //也可简写成 Programme p = (Programme) e; 这个时候p就能访问子类的专属方法和变量了
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值