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 选择合适的目标方法的顺序如下:
- 精确匹配
- 如果是基本数据类型,自动转换成更大表示范围的基本类型
- 通过自动拆箱与装箱
- 通过子类向上转型继承路线依次匹配
- 通过可变参数匹配
下面通过例子说明。
- 基本数据类型优先于包装数据类型。
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
- 更大范围的基本数据类型优先于包装数据类型。
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
- 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
- 自动装箱和拆箱优先于可变参数。
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开发手册》
- Static Vs. Dynamic Binding in Java
网友评论