1. 程式人生 > 其它 >jvm原始碼解讀--20 結合jvm原始碼理解 java 設計模式 模板方法

jvm原始碼解讀--20 結合jvm原始碼理解 java 設計模式 模板方法

write by 張豔濤

前言:

在學習jvm之前,看過設計模式的書,知道模板方法的設計模式,今天在看java併發程式設計的藝術裡面關於AbstractQueuedSynchronizer 用法,這個就使用了模板方法了,開始沒注意到,回想過去的設計模式的知識,才看清楚了,那麼模板方法的原理是什麼呢?結合jvm虛擬機器原理? 我想答案是多型

那麼,現在寫一篇分析過程,來解析這個過程

新進一個Man類 和一個Person類

package com.zyt.java_concurrency_in_practice.templatemode;

public class Man extends Person{
    @Override
    
protected void sleep() { System.out.println("子類在執行 Sleep~~~~"); } public static void main(String[] args) { Man man = new Man(); man.sayHi(); } } //========= package com.zyt.java_concurrency_in_practice.templatemode; public abstract class Person { protected void
sleep(){ System.out.println("父類,在執行sleeping 方法"); }; protected void sayHi(){ sleep(); } }

列印執行結果

子類在執行 Sleep~~~~

Process finished with exit code 0

這個問題關鍵點是在父類方法裡面的sleep()方法是子類的方法,不是父類的sleep方法!!!

關鍵點是呼叫方法,會有一個this物件的引數,比如說 子類物件.function() ;那麼 this 就是子類物件,static 方法 沒有this物件

那麼就來解析模板方法的實現原理

    public static void main(String[] args) {
        Man man = new Man();
        man.sayHi();
    }

==========================================

 0 new #5 <com/zyt/java_concurrency_in_practice/templatemode/Man>
 3 dup
 4 invokespecial #6 <com/zyt/java_concurrency_in_practice/templatemode/Man.<init>>
 7 astore_1
 8 aload_1
 9 invokevirtual #7 <com/zyt/java_concurrency_in_practice/templatemode/Man.sayHi>
12 return

逐句分析

  1. 編號0的第一句是新建物件 man,現在這個man物件在運算元棧上
  2. dup 是在運算元棧上覆制man物件
  3. invokespecial init 是呼叫初始化方法,返回時候會消耗一個dup出來的man物件
  4. 編號7將man物件儲存在本地變量表裡面
  5. 編號8 將 位置為1的變數載入到運算元棧上
  6. 編號9 呼叫Man.sayHi方法

進入到父類方法執行

public abstract class Person {
    protected  void  sleep(){
        System.out.println("父類,在執行sleeping 方法");
    };
    protected  void sayHi(){
        sleep();
    }
}

//===============sayHi()==========

0 aload_0
1 invokevirtual #2 <com/zyt/java_concurrency_in_practice/templatemode/Person.sleep>
4 return

呼叫新方法,會準備新的棧結構,而且會服用運算元棧,和本地變量表

這裡的話,應該會將子類的運算元棧中的man物件,複用為父類方法的本地變量表變數

  1. 那麼編號為0 的aload_0就是載入子類的man物件(在本地變量表中),到新的運算元棧
  2. 那麼呼叫 #2 Person.sleep父類的sleep方法,其中的引數是子類的man物件,那麼實際上呼叫的就是子類的sleep方法
  3. 子類有一個JVM虛表,先排列父類方法,接著排列子類方法,如果子類重寫父類方法sleep,那麼子類的虛表的父類部分的sleep方法會設定為子類方法,從而執行的是子類方法,若未重寫,則方法父類方法