欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-21 20:25:28
...

一、多态的定义:

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);
        }
    }
    
  • 结果

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

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就能访问子类的专属方法和变量了