18、jvm源码解读 - 结合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、 invokespecialinit是调用初始化方法,返回时候会消耗一个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对象(在本地变量表中),到新的操作数栈;

  1. 那么调用 #2 Person.sleep父类的sleep方法,其中的参数是子类的man对象,那么实际上调用的就是子类的sleep方法
    3、 子类有一个JVM虚表,先排列父类方法,接着排列子类方法,如果子类重写父类方法sleep,那么子类的虚表的父类部分的sleep方法会设置为子类方法,从而执行的是子类方法,若未重写,则方法父类方法;