方法调用不等于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法)。一切方法调用在Class文件里存储的都只是符号引用,而不是方法在实际运行时内存中的入口地址。因此需要在类的加载期间,甚至到运行期间才能确定目标方法的直接引用(及方法在内存中的入口地址)。
解析:调用目标在程序写好,编译器进行编译时就已经确定下来,这类方法的调用称为解析。主要包括静态方法、私有方法、实例构造器、父类方法以及被final修饰的方法。
分派
先看一段代码
static abstract class Human {
}
static class Man extends Human {
}
static class Woman extends Human {
}
public void sayHello(Human guy) {
System.out.println(“Hello, guy");
}
public void sayHello(Man guy) {
System.out.println(“Hello, man");
}
public void sayHello(Human guy) {
System.out.println(“Hello, woman");
}
public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
sayHello(man);
sayHello(woman);
}
输出为:
Hello, guy
Hello, guy
Javac编译器会根据参数的静态类型即外观类型决定调用哪一个重载版本。
可通过改变静态类型来改变调用的重载版本
sayHello((Man)man);
sayHello((Woman)woman);
多态时:
1,成员变量。
编译时:参考引用型变量所属的类中的是否有调用的成员变量,有,编译通过,没有,编译失败。
运行时:参考引用型变量所属的类中的是否有调用的成员变量,并运行该所属类中的成员变量。
简单说:编译和运行都参考等号的左边。
class Fu{
int num = 3;
}
class Zi extends Fu{
int num = 4;
}
class DuoTaiDemo{
public static void main(String[] args){
Zi z = new Zi();
System.out.println(z.num); //控制台输出4
Fu f = new Zi();
System.out.println(f.num); //控制台输出3
}
}
2,成员函数(非静态)。
编译时:参考引用型变量所属的类中的是否有调用的函数。有,编译通过,没有,编译失败。
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译看左边,运行看右边。
编译器会需找引用类型来决定你是否可以调用该引用的特定方法;在执行期间,Java虚拟机需找的并不是引用所指的类型,而是在堆上的对象。
因为成员函数存在覆盖特性。
class Fu{
void show(){
System.out.println("fu show");
}
}
class Zi extends Fu{
void show(){
System.out.println("zi show");
}
}
class DuoTaiDemo3{
public static void main(String[] args){
Fu f = new Zi();
f.show(); //控制台输出“zi show”
}
}
3,静态函数。
编译时:参考引用型变量所属的类中的是否有调用的静态方法。
运行时:参考引用型变量所属的类中的是否有调用的静态方法。
简单说,编译和运行都看左边。
其实对于静态方法,是不需要对象的。直接用类名调用即可。
class Fu{
static void method(){
System.out.println("fu static method");
}
}
class Zi extends Fu{
static void method(){
System.out.println("zi static method");
}
}
class DuoTaiDemo3{
public static void main(String[] args){
Fu f = new Zi();
f.method(); // 控制台输出“fu static method”
Fu.method();
Zi.method();
}
}