刚开始学习Java的时候,就了解了Java这个比较有意思的特性:重写 和 重载。开始的有时候从名字上还总是容易弄混。我相信熟悉Java这门语言的同学都应该了解这两个特性,可能只是从语言层面上了解这种写法,但是jvm是如何实现他们的呢 ?
重载官方给出的介绍:
一. overload:
The Java programming language supports overloading methods, and Java can distinguish between methods with different method signatures. This means that methods within a class can have the same name if they have different parameter lists .
Overloaded methods are differentiated by the number and the type of the arguments passed into the method.
You cannot declare more than one method with the same name and the same number and type of arguments, because the compiler cannot tell them apart.
The compiler does not consider return type when differentiating methods, so you cannot declare two methods with the same signature even if they have a different return type.
首先看一段代码,来看看代码的执行结果:
public class OverrideTest {
class Father{}
class Sun extends Father {}
public void doSomething(Father father){
System.out.println("Father do something");
}
public void doSomething(Sun father){
System.out.println("Sun do something");
}
public static void main(String [] args){
OverrideTest overrideTest = new OverrideTest();
Father sun = overrideTest.new Sun();
Father father = overrideTest.new Father();
overrideTest.doSomething(father);
overrideTest.doSomething(sun);
}
}
看下这段代码的执行结果,最后会打印:
Father do something
Father do something
为什么会打印出这样的结果呢? 首先要介绍两个概念:静态分派和动态分派
静态分派:依赖静态类型来定位方法执行版本的分派动作称为静态分派
动态分派:运行期根据实际类型确定方法执行版本的分派过程。
他们的区别是:
1. 静态分派发生在编译期,动态分派发生在运行期;
2. private,static,final 方法发生在编译期,并且不能被重写,一旦发生了重写,将会在运行期处理。
3. 重载是静态分派,重写是动态分派
回到上面的问题,因为重载是发生在编译期,所以在编译期已经确定两次 doSomething 方法的参数都是Father类型,在class文件中已经指向了Father类的符号引用,所以最后会打印两次Father do something。
二. override:
An instance method in a subclass with the same signature (name, plus the number and the type of its parameters) and return type as an instance method in the superclass overrides the superclass's method.
The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is "close enough" and then to modify behavior as needed. The overriding method has the same name, number and type of parameters, and return type as the method that it overrides. An overriding method can also return a subtype of the type returned by the overridden method. This subtype is called a covariant return type.
还是上面那个代码,稍微改动下
public class OverrideTest {
class Father{}
class Sun extends Father {}
public void doSomething(){
System.out.println("Father do something");
}
public void doSomething(){
System.out.println("Sun do something");
}
public static void main(String [] args){
OverrideTest overrideTest = new OverrideTest();
Father sun = overrideTest.new Sun();
Father father = overrideTest.new Father();
overrideTest.doSomething();
overrideTest.doSomething();
}
}
最后会打印:
Father do something
Sun do something
相信大家都会知道这个结果,那么这个结果jvm是怎么实现的呢?
在编译期,只会识别到是调用Father类的doSomething方法,到运行期才会真正找到对象的实际类型。
首先该方法的执行,jvm会调用invokevirtual指令,该指令会找栈顶第一个元素所指向的对象的实际类型,如果该类型存在调用的方法,则会走验证流程,否则继续找其父类。这也是为什么子类可以直接调用父类具有访问权限的方法的原因。简而言之,就是在运行期才会去确定对象的实际类型,根据这个实际类型确定方法执行版本,这个过程称为动态分派。override 的实现依赖jvm的动态分派。