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
逐句分析
- 编号0的第一句是新建对象 man,现在这个man对象在操作数栈上
- dup 是在操作数栈上复制man对象
- invokespecial init 是调用初始化方法,返回时候会消耗一个dup出来的man对象
- 编号7将man对象存储在本地变量表里面
- 编号8 将 位置为1的变量加载到操作数栈上
- 编号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对象,复用为父类方法的本地变量表变量
- 那么编号为0 的aload_0就是加载子类的man对象(在本地变量表中),到新的操作数栈
- 那么调用 #2 Person.sleep父类的sleep方法,其中的参数是子类的man对象,那么实际上调用的就是子类的sleep方法
- 子类有一个JVM虚表,先排列父类方法,接着排列子类方法,如果子类重写父类方法sleep,那么子类的虚表的父类部分的sleep方法会设置为子类方法,从而执行的是子类方法,若未重写,则方法父类方法