Java多态

1 多态定义

对于多态,官方的定义是“允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)”,这里采用比较大众话的解释,就是:当父类对象引用变量引用子类对象时,被引用对象的类型(而不是引用对象的类型)决定了调用谁的成员方法,但是这个被调用的方法必须是父类中已经定义的,即被子类重写的方法。

一般来说,要实现多态必须具备三个条件:继承、重写、向上转型。
(1)继承:在多态中必须存在有继承关系的子类和父类。
(2)重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
(3)向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备调用父类方法和子类方的能力。

2 静态绑定与多态

所谓绑定,是指将方法的调用与方法所在的类关联起来,一般分为静态绑定和动态绑定。
(1)静态绑定:在编译期间,确定方法、变量的类型;
(2)动态绑定:在运行期间,确定方法、变量的类型。多态是这里的动态绑定。

哪些是静态绑定呢?对于实例变量、静态变量、静态方法、private方法,都是静态绑定。因此,针对上述四类,即使子类有和父类重名的方法、变量,也是不具备多态的前提条件的,即发生访问时,父类对象就调用父类的变量、方法,子类对象就调用子类的变量、方法。举个例子说明下:

public class BaseDTO {
    public static String staticValue = "static_base";
    public String commonValue = "base";

    public static void staticMethod(){
        System.out.println("base static method:" + staticValue);
    }
}

public class ChildDTO extends BaseDTO {
    public static String staticValue = "child_base";
    public String commonValue = "child";

    public static void staticMethod(){
        System.out.println("child static method:" + staticValue);
    }
}

public class StaticBindDemo {
    public static void main(String[] args) {
        ChildDTO childDTO = new ChildDTO();
        BaseDTO baseDTO = childDTO;

        System.out.println(baseDTO.staticValue);
        System.out.println(baseDTO.commonValue);
        baseDTO.staticMethod();

        System.out.println(childDTO.staticValue);
        System.out.println(childDTO.commonValue);
        childDTO.staticMethod();

        // result如下:
        // static_base
        // base
        // base static method:static_base
        // child_base
        // child
        // child static method:child_base
    }
}

3 向上转型说明

向上转型,即将子类型的对象赋值给父类型的引用变量。同理,向下转型,是将父类型的引用变量赋值给子类型的变量。从语法层面,向上转型和向下转型(强制转换)均能转换成功,但在运行期间,向下转型有些场景会抛错。具体是哪些场景呢?一个父类的变量,能不能转换为一个子类的变量,取决于这个父类变量的动态类型(即引用对象类型)是不是这个子类或这个子类的子类。

下面这段代码,base的动态类型是ChildDTO,则可以正常向下转型。

BaseDTO base = new ChildDTO();
ChildDTO child = (ChildDTO) base;

下面这段代码,base的动态类型是BaseDTO,编译期间不会报错,但是运行时会报错。

BaseDTO base = new BaseDTO();
ChildDTO child = (ChildDTO) base;

在实际编码过程中,如果想知道其到底是不是某个子类的对象,可以通过instanceof关键字判断,代码样例如下:

public boolean canCast(BaseDTO base){
    return base instanceof ChildDTO;
}

4 多态举例

/**
 * 重载、重写demo
 */
public class Demo {

    class Base {
        Base() {
            g();
        }

        private void g() {
            System.out.println("base g");
        }

        public void f(int num) {
            System.out.println("base f" + num);
        }
    }

    class SubBase extends Base {
        // 1.重写前提条件是:子类方法可见性要大于等于父类
        // 2.子类的g如果返参定义为非void,会报错,因为无法构成重写条件,必须和父类返参相同
        public void g() {
            System.out.println("SubBase g");
        }

        // 和父类的方法f产生的是重载关系
        public void f(long num) {
            System.out.println("SubBase f" + num);
        }
    }

    public static void main(String[] args) {
        Demo demo = new Demo();
        demo.initDemo();
    }

    private void initDemo() {
        Base base = new SubBase();
        base.f(5);
//        base.f(5L);    多态具有编译时类型和运行时类型,此处在编译期会报错,因父类无f(long num)方法
    }

    // result如下:
    // base g
    // base f5
}

5 多态在web项目中使用

在web项目中,service和dao层都会同时定义1个接口和1个实现类。这是为什么呢?

我觉得主要是为了方便以后的扩展(多态)和重用。举个例子吧 ,比如有个需求,需要保存客户信息,有些客户从网站来,有些从手机客户端来,有些从后台管理系统录入,假设不同来源的客户有不同的处理业务流程,这个时候我定义接口来提供一个保存客户的方法,然后不同平台实现我这个保存客户的接口,以后保存客户的话, 我只用知道这个接口就可以了,具体调用哪个方法 去实例化具体你需要用的类,这也就是JAVA的多态的体现。而如果你不用接口的话,首先我需要哪个方法,我就去实例化哪个类,冗余很高,其次扩展很差。接口就是一个规范,其子类都会用它提供的方法,作统一管理。

你可能感兴趣的:(Java多态)