public class Solution {
public void fun(Object i){
System.out.println("obbject");
}
public void fun(Integer i){
System.out.println("Integer");
}
public void fun(Float i){
System.out.println("Float");
}
public static void main(String[] args) {
Solution s = new Solution();
Object f = new Integer(12);
Object f1 = new Float(12.0f);
s.fun(f);
s.fun(f1);
}
}
这串代码的输出是什么?
obbject
obbject
为什么会这样?
我之前的答案是类型最接近的那个才是真正调用的那个方法。后面我发现我的理解还是有点出路,重新学习这里之后,分享一下
首先我们把Object f = new Integer(12);前面Object称之为静态类型,后面的Integer为实际类型,静态类型确定下来就不会变化的,而实际类型是会变化的,如上面我可以把f = new Float(12f) 也就是说无法在编译期间确定实际类型的类型。而重载时的方法的调用完全依赖静态类型。因为由于根据静态类型确定那个方法调用,所以类加载过程中解析过程,符号引用转换为直接引用的时期,方法的符号引用就直接转换为确定的直接引用。所以上面的输出是oobject
但是我之前记得重载是找类型最近的方法参数然后进行调用,我记得没错啊,这是什么情况呢???
public class Solution {
public void fun(Object i){
System.out.println("obbject");
}
public void fun(Integer i){
System.out.println("Integer");
}
public void fun(Float i){
System.out.println("Float");
}
public static void main(String[] args) {
Solution s = new Solution();
s.fun(12);
s.fun(12.0f);
}
}
Integer
Float
这里代码区别上面,我是直接使用字面量作为参数,因为字面量只有实际类型没有静态类型,故编译器会自然推断最接近的类型进行方法调用。本质上还是静态分派,通过静态类型进行调用,只不过你没有写出静态类型,编译器自动推断静态类型。因为编译器推断故每个类型优先级是不同的,例如 ‘a’ 首先是char类型,也可以是int类型。
动态分派
public class Solution {
public static void main(String[] args) {
sub f = new person();
f.fun();
}
}
class sub {
public void fun() {
System.out.println("fun");
}
}
class person extends sub {
public void fun() {
System.out.println("not fun");
}
}
输出很明显 not fun
因为子类重写父类的方法,在方法调用时候,jvm其实是使用了invokevirtual指令,这个指令主要的操作是:
1、坚持栈顶元素实际类型C
2、查看C中方法区的是否有方法签名相同的方法
3、2查询失败则查询C父类,重复2
4、查询到则返回,不然则异常
在实际中当然不可能每次都进行这些步骤,java中采用oop-klass二分模型表示一个对象。klass保存着类的数据,在其中保存着方法表,方法表中保存着从父类继承下来,自己定义的所有方法,如果子类重写父类方法,那么在这个方法表中相同位置上的父类方法则会被覆盖。也就意味着从子类实例来说是无法找到父类该方法的。
每次调用查询这个方法表即可,加快了调用速度