重载和重写
public class aa { public static void invoke(Object obj, Object... args) { } public static void invoke(String a, Object obj, Object... args) { } public static void main(String[] args) { invoke(null, 1);//2 invoke(null, 1, 2);//2 invoke(null, new Object[]{1});//手动绕开可变参数语法糖,调1 } }
选择重载方法
- 不考虑基本类型自动装拆箱,可变参数选取重载方法
- 1没找到,允许自动装拆箱,不允许可变参数情况下选择重载方法
- 2没找到,允许自动装拆箱和可变参数情况下选择重载方法
找到多个:找最贴切的:参数类型继承关系
String是Object子类,Java编译器认为更贴切
父子类重载(非私有方法)
拨打10086->根据你所在地连接到当地客服
JVM的静态绑定和动态绑定
JVM怎么识别方法?
类名、方法名、方法描述符(方法参数,返回类型)
JVM和Java语言不同,JVM不限制名字和参数类型相同,但返回类型不同的方法出现在一个类,对于调用这些方法的字节码来说,由于字节码所附带的方法描述符包含返回类型,因此JVM能够准确识别目标方法
JVM判定重写基于方法描述符,如果子类父类非私有方法名字,只有参数类型和返回类型一致JVM才会判定为重写
Java语言重写,JVM不重写,编译器通过桥接方法实现Java中重写语义
重载区分在编译阶段完成,JVM不存在重载这一概念,因此重载也叫静态绑定(直接识别),或者编译时多态,重写被叫动态绑定(动态识别)
Java字节码调用指令5种:
- invokestatic:调用静态方法,JVM直接识别
- invokespecial:调用私有实例方法。构造器,super实例方法,构造器,所实现接口的default方法,JVM直接识别
- invokevirtual:非私有实例方法,动态识别
- invokeinterface:接口方法,动态识别
- invokedynamic:动态方法
import java.util.Random; interface Customer { boolean isVIP(); } class Merchants { public double discountPrice(double price, Customer customer) { return price * 0.8d; } } class profiteers extends Merchants { @Override public double discountPrice(double price, Customer customer) { if (customer.isVIP()) {//invokeinterface return price * 价格歧视();//invokestatic } else { return super.discountPrice(price, customer);//invokespecial } } public static double 价格歧视() { return new Random()//invokespecial,Random构造器 .nextDouble();//invokevirtual } }
例外:JVM能确定目标方法只有一个,final[3] [4],直接识别
调用符号引用
存储在class文件的常量池中,根据目标方法是否为接口方法,这些引用可分为接口符号引用和非接口符号引用
- 非接口引用
假设指向类C,JVM:
- 在C中找符合名字及描述符的方法
- 没找到,在C父类找,until Object类
- 没找到,在C直接实现或间接实现的接口找,目标方法必须是非私有,非静态,如果目标方法在间接实现的接口中,则需要满足C和此接口直接没有其他符合条件的目标方法,如果有多个,任意返回一个
- 子类静态方法会隐藏父类中同名,同描述符的静态方法
- 静态方法可以通过子类调用
- 接口符号引用,假设指向I,JVM
- 在I中找符号名字和描述符的方法
- 没找到,在Object类中公有实例方法中找
- 没找到,在I的超接口找,目标方法非私有,非静态
符号引用->实际引用
静态绑定方法:实际引用是一个指向方法的指针
动态绑定方法:实际引用是一个方法表的索引