静态方法会在类加载期就进行解析,而静态方法显然也是可以拥有重载版本的,选择重载版本的过程也是通过静态分派完成的。
public class StaticDispatch{ public void sayHello(Human guy){ System.out.println("hello,guy!"); } public void sayHello(Man guy){ System.out.println("hello,gentleman!"); } public void sayHello(Woman guy){ System.out.println("hello,lady!"); } public static void main(String[]args){ Human man=new Man(); Human woman=new Woman(); StaticDispatch sr=new StaticDispatch(); sr.sayHello(man); sr.sayHello((Man)man); sr.sayHello((Woman) woman); }
}
//结果
hello,guy!
hello,guy!
hello,gentleman!
hello,lady!
我们把上面代码中的“Human”称为变量的静态类型(Static Type),或者叫做的外观类型(Apparent Type),后面的“Man”则称为变量的实际类型(Actual Type),静态类型和实际类型在程序中都可以发生一些变化,区别是静态类型的变化仅仅在使用时发生,变量本身的静态类型不会被改变,并且最终的静态类型是在编译期可知的;而实际类型变化的结果在运行期才可确定,编译器在编译程序的时候并不知道一个对象的实际类型是什么。
所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。静态分派的典型应用是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。
下面的代码更适合演示在编译期间确实调用方法的版本
public class maintest { public static void sayHello(Object arg) { System.out.println("hello Object"); } public static void sayHello(int arg) { System.out.println("hello int"); } public static void sayHello(long arg) { System.out.println("hello long"); } public static void sayHello(Character arg) { System.out.println("hello Character"); } public static void sayHello(char arg) { System.out.println("hello char"); } public static void sayHello(char... arg) { System.out.println("hello char……"); } public static void main(String[] args) { sayHello('a'); } }
执行结果:
//执行结果 hello char 对应字节码 0: bipush 97 2: invokestatic #10 // Method sayHello:(C)V 5: return
这很好理解,'a'是一个char类型的数据,自然会寻找参数类型为char的重载方法,如果注释掉sayHello(char arg)方法,那输出会变为:
//hello int //对应字节码 0: bipush 97 2: invokestatic #9 // Method sayHello:(I)V 5: return
这时发生了一次自动类型转换,'a'除了可以代表一个字符串,还可以代表数字97(字符'a'的Unicode数值为十进制数字97),因此参数类型为int的重载也是合适的。
我们继续注释掉sayHello(int arg)方法,那输出会变为:
hello long //对应字节码 0: ldc2_w #8 // long 97l 3: invokestatic #10 // Method sayHello:(J)V 6: return
实际上自动转型还能继续发生多次,按照char->int->long->float->double的顺序转型进行匹配.