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保存着类的数据,在其中保存着方法表,方法表中保存着从父类继承下来,自己定义的所有方法,如果子类重写父类方法,那么在这个方法表中相同位置上的父类方法则会被覆盖。也就意味着从子类实例来说是无法找到父类该方法的。

每次调用查询这个方法表即可,加快了调用速度