静态分派

Java面向对象的三个基本特征:继承,封装,多态,我们这次来看Java虚拟机如何实现,这里说的不是语法上的实现。
静态分派:
在Java语言中符合“编译期可知,运行期不可变”这个要求的方法,主要包括静态方法私有方法两大类,前者与类型直接关联,后者在外部不可被访问,这两种方法各自的特点决定了他们不可能通过继承或别的方式重写其他版本,因此他们适合在类加载阶段进行解析。
静态方法、私有方法、实例构造器、父类方法。这些方法称为非虚方法,它们在类加载的时候就会把符号引用解析为该方法的直接引用。与之相反,其他方法称为虚方法(除去final方法)。

public class StaticDiptch {

    /*
     * 方法静态分派演示
     */

     static abstract class Human{  
        }  
        static class Man extends Human{  
        }  
        static class Woman extends Human{  
        }  
        public static void sayHello(Human guy){  
            System.out.println("hello,guy!");  
        }  
        public static void sayHello(Man guy){  
            System.out.println("hello,gentlemen!");  
        }  
        public static void sayHello(Woman guy){  
            System.out.println("hello,lady!");  
        }  

        public static void main(String[] args) {  
            StaticDiptch at= new StaticDiptch();
            Human man=new Man();  
            Human woman=new Woman();  
            at.sayHello(man);  
            at.sayHello(woman);  
        }  


}

运行结果:
hello,guy!
hello,guy!

解释两个概念(重要嘞):
Human man=new Man();
我们把“Human”称为变量的静态类型,后面的“Man”称为变量的实际类型,静态类型和实际类型在程序中都可以发生一些变化,区别是静态类型的变化仅仅在使用时发生,变量本身的静态类型不会被改变,并且最终的静态类型在编译器可知;而实际类型变化的结果在运行期才确定,编译器在编译期并不知道一个对象的实际类型是什么。

在接受者都为at的情况下,重载的版本完全取决于传入的形参,但虚拟机在编译器的编译时期传入的参数,是通过静态类型决定的也就是Human决定,

俺们来看字节码指令:
俺只拿了main方法的和常量池类

Constant pool:
   #1 = Class              #2             //  diptch/StaticDiptch
   #2 = Utf8               diptch/StaticDiptch
   #3 = Class              #4             //  java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Methodref          #3.#9          //  java/lang/Object."<init>":()V
   #9 = NameAndType        #5:#6          //  "<init>":()V
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Ldiptch/StaticDiptch;
  #14 = Utf8               sayHello
  #15 = Utf8               (Ldiptch/StaticDiptch$Human;)V
  #16 = Fieldref           #17.#19        //  java/lang/System.out:Ljava/io/Prin
tStream;
  #17 = Class              #18            //  java/lang/System
  #18 = Utf8               java/lang/System
  #19 = NameAndType        #20:#21        //  out:Ljava/io/PrintStream;
  #20 = Utf8               out
  #21 = Utf8               Ljava/io/PrintStream;
  #22 = String             #23            //  hello,guy!
  #23 = Utf8               hello,guy!
  #24 = Methodref          #25.#27        //  java/io/PrintStream.println:(Ljava
/lang/String;)V
  #25 = Class              #26            //  java/io/PrintStream
  #26 = Utf8               java/io/PrintStream
  #27 = NameAndType        #28:#29        //  println:(Ljava/lang/String;)V
  #28 = Utf8               println
  #29 = Utf8               (Ljava/lang/String;)V
  #30 = Utf8               guy
  #31 = Utf8               Ldiptch/StaticDiptch$Human;
  #32 = Utf8               (Ldiptch/StaticDiptch$Man;)V
  #33 = String             #34            //  hello,gentlemen!
  #34 = Utf8               hello,gentlemen!
  #35 = Utf8               Ldiptch/StaticDiptch$Man;
  #36 = Utf8               (Ldiptch/StaticDiptch$Woman;)V
  #37 = String             #38            //  hello,lady!
  #38 = Utf8               hello,lady!
  #39 = Utf8               Ldiptch/StaticDiptch$Woman;
  #40 = Utf8               main
  #41 = Utf8               ([Ljava/lang/String;)V
  #42 = Methodref          #1.#9          //  diptch/StaticDiptch."<init>":()V
  #43 = Class              #44            //  diptch/StaticDiptch$Man
  #44 = Utf8               diptch/StaticDiptch$Man
  #45 = Methodref          #43.#9         //  diptch/StaticDiptch$Man."<init>":(
)V
  #46 = Class              #47            //  diptch/StaticDiptch$Woman
  #47 = Utf8               diptch/StaticDiptch$Woman
  #48 = Methodref          #46.#9         //  diptch/StaticDiptch$Woman."<init>"
:()V
  #49 = Methodref          #1.#50         //  diptch/StaticDiptch.sayHello:(Ldip
tch/StaticDiptch$Human;)V
  #50 = NameAndType        #14:#15        //  sayHello:(Ldiptch/StaticDiptch$Hum
an;)V
  #51 = Utf8               args
  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: new           #1                  // class diptch/StaticDiptch
         3: dup
         4: invokespecial #42                 // Method "<init>":()V
         7: astore_1
         8: new           #43                 // class diptch/StaticDiptch$Man
        11: dup
        12: invokespecial #45                 // Method diptch/StaticDiptch$Man.
"<init>":()V
        15: astore_2
        16: new           #46                 // class diptch/StaticDiptch$Woman

        19: dup
        20: invokespecial #48                 // Method diptch/StaticDiptch$Woma
n."<init>":()V
        23: astore_3
        24: aload_2
        25: invokestatic  #49                 // Method sayHello:(Ldiptch/Static
Diptch$Human;)V
        28: aload_3
        29: invokestatic  #49                 // Method sayHello:(Ldiptch/Static
Diptch$Human;)V
        32: return
      LineNumberTable:
        line 26: 0

俺们只看24-29行,分别在加载了局部变量表中索引为2,和索引为3的的引用也就是我们指向man,women的索引。,25和29行分别执行的是sayHello方法指向了俺们常量池索引为49滴位置,也就是父类Human的sayHello(),所以嘞。结果就一米了然嘞。

编译器在重载时是通过参数的静态类型而不是实际类型作为判定的依据。并且静态类型在编译期可知,因此,编译阶段,Javac编译器会根据参数的静态类型决定使用哪个重载版本。
所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。静态分派的典型应用就是方法重载。
静态分派发生在编译阶段因此确定静态分派的动作实际上不是由虚拟机来执行的,而是由编译器来完成。
但是,字面量没有显示的静态类型,它的静态类型只能通过语言上的规则去理解和推断。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值