美文网首页Java 杂谈Java
深入理解 Java 中的 override 和 overload

深入理解 Java 中的 override 和 overload

作者: 落英坠露 | 来源:发表于2019-07-20 11:27 被阅读22次

    override 和 overload,分别翻译为覆写和重载,是 Java 多态(Polymorphism)的两种代表类型。下面详细分析一下 override 和 overload 的使用方式。

    1. override

    override 出现在继承关系中,子类覆写父类的实例方法,为它提供专门的实现。举个例子:

    public class Animal {
        public void bark() {
            System.out.println("动物叫声");
        }
    }
    
    public class Cat extends Animal {
        @Override
        public void bark() {
            System.out.println("喵喵");
        }
    }
    
        public static void main(String[] args) {
            // 向上转型
                    Animal cat = new Cat();
            cat.bark(); // 打印 喵喵
        }
    

    override 的绑定发生在运行期,属于动态绑定,由实例对象决定调用哪个方法,动态绑定是多态性得以实现的重要因素。这里有几点需要注意的地方:

    • 子类的方法访问权限只能相同或变大。比如父类方法是 protected,子类可以是 public,不能是 private。
    • 抛出异常和返回值只能变小,它们能够转型成父类对象。比如父类方法返回值 Number,子类可以返回 Integer,不能返回 Object。
    • 方法签名必须完全相同。方法签名包括方法名称和参数列表,是 JVM 标识方法的唯一索引,不包括返回值,其中参数列表分为类型和个数。
    • 覆写方法必须要加上 @Override 注解,为了使编译器自动检查覆写是否满足规则。

    覆写只能用于类的不被 final 和 private 修饰的实例方法,不能用于静态方法,如果父类和子类中存在同名的静态方法,那么两者都可以被正常调用。

    2. overload

    overload 出现在同一个类中,多个方法具有相同名字、不同的参数。举个例子:

    public class Cat {
        public void bark() {
            System.out.println("喵喵");
        }
    
        public void bark(int num) {
            for (int i = 1; i <= num; i++) {
                System.out.println("第 " + i + " 声喵喵");
            }
        }
    }
    

    overload 的绑定发生在编译期,属于静态绑定,由方法签名决定调用哪个方法。需要注意的是,方法签名包括方法名称、参数类型和个数,不包括返回值。

    当重载的方法参数比较复杂时,JVM 选择合适的目标方法的顺序如下:

    • 精确匹配
    • 如果是基本数据类型,自动转换成更大表示范围的基本类型
    • 通过自动拆箱与装箱
    • 通过子类向上转型继承路线依次匹配
    • 通过可变参数匹配

    下面通过例子说明。

    1. 基本数据类型优先于包装数据类型。
        public void mo(int a) {
            System.out.println("primary int type");
        }
    
        public void mo(Integer a) {
            System.out.println("wrapper int type");
        }
    
        Cat cat = new Cat();
        cat.mo(1); // 打印 primary int type
    
    1. 更大范围的基本数据类型优先于包装数据类型。
        public void mo(long a) {
            System.out.println("primary long type");
        }
    
        public void mo(Integer a) {
            System.out.println("wrapper int type");
        }
        
        Cat cat = new Cat();
        cat.mo(1); // 打印 primary long type
    
    1. null 可以匹配任何类对象,从最底层子类依次向上查找。
        public void mo(Integer a) {
            System.out.println("wrapper int type");
        }
    
        public void mo(Object a) {
            System.out.println("object type");
        }
    
    //  如果包含 String 参数,那么编译器不知道匹配哪个,直接报错。
    //  public void mo(String s) {
    //      System.out.println("string type");
    //  }
    
        Cat cat = new Cat();
        cat.mo(null); // 打印 wrapper int type
    
    1. 自动装箱和拆箱优先于可变参数。
        public void mo(Integer a, Integer b){
            System.out.println("double wrapper int type");
        }
    
        public void mo(int... a) {
            System.out.println("primary int array type");
        }
        
        Cat cat = new Cat();
        cat.mo(1, 2); // 打印 double wrapper int type
    

    ...

    重载的参数类型应该简洁些,这样才能提高代码的可读性。

    3. 总结

    override 和 overload 有很多不同之处:

    • override 属于动态绑定,在运行期通过实例对象决定调用的方法;overload 属于静态绑定,在编译期通过方法签名决定调用的方法。
    • override 基于继承关系,需要父类和子类参与;overload 出现在同一个类中,通过方法签名区分。
    • static、final、private 方法不能被 override,但是可以被 overload。
    • override 关注方法返回值,overload 不关注返回值。
    • override 的方法参数必须一样,overload 的方法参数必须不同。
    • overload 的性能比 override 更好些,应为它是静态绑定。

    参考:

    相关文章

      网友评论

        本文标题:深入理解 Java 中的 override 和 overload

        本文链接:https://www.haomeiwen.com/subject/nobylctx.html