首先复习两个概念
- Overload:
- 也叫静态多态(static dynamic) 是通过同名方法定义不同(参数类型、个数、顺序)来实现的,因此在调用时就可以知道具体执行的哪个方法(根据调用时传的参数就好~),所以发生在编译期,这个过程叫做静态绑定
- 用 private/static/final参数修饰的方法一定是静态绑定的
- Override:
- 通过子类重写父类方法+父类引用可以指向子类对象实现,因此调用的是父类or子类的方法要通过具体调用时的引用类型来确定
再引入两个概念:编译类型和运行类型
- 通过子类重写父类方法+父类引用可以指向子类对象实现,因此调用的是父类or子类的方法要通过具体调用时的引用类型来确定
- 编译类型:就是变量在定义时的类型,在写代码的时候就确定了,所以说是编译类型,比如Son son,那么son就是Son类型的引用
- 运行类型:变量真正指向的类型,因为父类引用可以指向子类对象,所以父类引用的运行类型可能并不是父类
这里看一个栗子:
public class Test {
public boolean equals( Test other ) {
System.out.println( "Inside of Test.equals" );
return false;
}
public static void main( String [] args ) {
Object t1 = new Test();
Object t2 = new Test();
Test t3 = new Test();
Object o1 = new Object();
int count = 0;
System.out.println( count++ );// prints 0
t1.equals( t2 ) ;
System.out.println( count++ );// prints 1
t1.equals( t3 );
System.out.println( count++ );// prints 2
t3.equals( o1 );
System.out.println( count++ );// prints 3
t3.equals(t3);
System.out.println( count++ );// prints 4
t3.equals(t2);
}
}
只会显示出一句话~ 在3后面
因为Test中的equals方法并没有Override Object类中的equals方法(参数类型不同,Object类中的equals方法参数为Object),所以只有t3.equals(t3)符合Test类中定义的equals方法,因此只会输出一句话,这是在编译期间就确定的
补充一个小芝士:
记得在python中写类的方法时需要显示传入一个this参数,代表该类本身,其实java中在类里定义方法时也会传入this对象,只不过是隐式的。
那么假设现在有一个类,内部有一个方法,参数是一个引用,那么在具体调用时编译器会按照以下类型来寻找
this.func(this) // 查看自己有木有一毛一样的方法
super.func(this) // 查看父类有木有一毛一样的方法
this.func(super) // 把this对象转为super,再查看自己有木有一毛一样的方法
super.func(super) // 把this对象转为super,再查看父类有木有一毛一样的方法
为了充分理解,来一个栗子二:
class Super {
public void methodC(Super arg) {
System.out.println("C1");
}
public void methodC(Sub arg) {
System.out.println("C2");
}
}
class Sub extends Super {
public void methodC(Super arg) {
System.out.println("C3");
}
public void methodC(Sub arg) {
System.out.println("C4");
}
}
public class Main {
public static void main(String[] args) {
Super one = new Sub();
Super two = new Sub();
Sub three = new Sub();
one.methodC(two);
one.methodC(three);
}
}
因为我们知道实例方法执行时因为有动态绑定机制,所以在运行时执行的是Sub类的方法(因为one指针所指的是Sub对象),因此第一次调用匹配的是Sub类中的第一个方法,第二次调用匹配的是Sub类中的第二个方法。注意,通过引用调用方法时会有多态性,先看该引用的运行类型,如果引用是作为参数出现的,那么先看他的编译类型,也就是说参数匹配时首先考虑的还是编译类型~
下面再通过一个栗子理解一下上面说的调用方法的四部曲~
- this.func(this)
class Super {
public void methodC(Super arg) {
System.out.println("C1");
}
public void methodC(Sub arg) {
System.out.println("C2");
}
}
class Sub extends Super {
public void methodC(Super arg) {
System.out.println("C3");
}
public void methodC(Sub arg) {
System.out.println("C4");
}
}
public class Main {
public static void main(String[] args) {
Sub one = new Sub();
Sub two = new Sub();
one.methodC(two);// C4
}
}
- super.func(this)
class Super {
public void methodC(Super arg) {
System.out.println("C1");
}
public void methodC(Sub arg) {
System.out.println("C2");
}
}
class Sub extends Super {
public void methodC(Super arg) {
System.out.println("C3");
}
}
public class Main {
public static void main(String[] args) {
Sub one = new Sub();
Sub two = new Sub();
one.methodC(two); // C2
}
}
- this.func(super)
class Super {
public void methodC(Super arg) {
System.out.println("C1");
}
}
class Sub extends Super {
public void methodC(Super arg) {
System.out.println("C3");
}
}
public class Main {
public static void main(String[] args) {
Sub one = new Sub();
Sub two = new Sub();
one.methodC(two); // C3
}
}
- super.func(super)
class Super {
public void methodC(Super arg) {
System.out.println("C1");
}
}
class Sub extends Super {
}
public class Main {
public static void main(String[] args) {
Sub one = new Sub();
Sub two = new Sub();
one.methodC(two);// C1
}
}
参考资料:
java-dynamic-binding-and-method-overriding
static-vs-dynamic-binding-in-java
java-dynamic-binding-confusion