设为首页 加入收藏

TOP

几个关于 Class 文件的问题分析(二)
2018-01-30 12:42:37 】 浏览:230
Tags:关于 Class 文件 问题 分析
来看一下Kotlin中sum函数的字节码是怎样的:

https://wx4.sinaimg.cn/mw690/c0755e72gy1fg25xxdnv5j20kk0vytbu.jpg

可以看到,在上面的sum函数中并没有存在对sum自身的调用,而取而代之的是,是第17条的goto命令。所以,Kotlin尾递归背后的黑魔法就是将递归改成循环结构。上面的代码翻译成我们容易理解的代码就是如下形式:

public int sum(int start, int end , int acc){
    for(;;){
        if(start > end){
            return acc;
        }else{
            acc = start + acc;
            start = start + 1;
        }
    }    
}

通过上述的分析我们可以看到,递归是通过转化为循环来降低内存的占用。但这并不意味着写递归就是很差的编程习惯。在Java这种面向对象的语言中我们更倾向于将递归改成循环,而在Haskell这类函数式编程语言中是将循环都改为了递归。在思想上并没有优劣之分,只是解决问题的思维上的差异而已,具体表现就是落实到具体语言上对这两种方法的支持程度不同而已(Java没有尾递归,Haskell没有for、while循环)。

类的初始化顺序是怎样的?

这个问题对于正在找工作的人可能比较有感觉,起码当时我在毕业准备面试题时就遇到了这个问题,并且也机械的记忆了答案。不过我们更期待的是这个答案背后的理论依据是什么。我们尝试从class文件中找到答案。来看这样的一段代码:

public class InitialOrderTest {
    public static String staticField = "   StaticField";
    public String fieldFromMethod = getStrFromMethod();
    public String fieldFromInit = "   InitField";
    static {
        System.out.println( "Call Init Static Code" );
        System.out.println( staticField );
    }
    {
        System.out.println( "Call Init Block Code" );
        System.out.println( fieldFromInit );
        System.out.println( fieldFromMethod );
    }
    public InitialOrderTest()
    {
        System.out.println( "Call Constructor" );
    }
    public String getStrFromMethod(){
        System.out.println("Call getStrFromMethod Method");
        return "   MethodField" ;
    }
    public static void main( String[] args )
    {
        new InitialOrderTest();
    }
}

它运行后的结果是什么呢?结果如下:

image

我们来一一来看一下它的class文件中的内容,首先是有一个static方法区:

static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: ldc           #14                 // String    StaticField
         2: putstatic     #15                 // Field staticField:Ljava/lang/String;
         5: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         8: ldc           #16                 // String Call Init Static Code
        10: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        16: getstatic     #15                 // Field staticField:Ljava/lang/String;
        19: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        22: return

Java编译器在编译阶段会将所有static的代码块收集到一起,形成一个特殊的方法,这个方法的名字叫做<clinit>, 这个名字容易让我们联想到构造函数的名称叫做<init>,但与构造函数不同,这个方法在Java层中是调用不到的,并且,这个函数是在这个类被加载时,由虚拟机进行调用。注意的是,是类被加载,而不是类被初始化成实例。所以,静态代码块的加载优先于普通的代码块,也优先于构造函数。这属于虚拟机规定的范畴,我们不做更深入的探讨。

在Class文件中,是没有为普通方法区开辟类似于<clinit>这种方法的,而是将所有普通方法区的代码都合并到了构造函数中,我们直接来看构造函数:

public InitialOrderTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: aload_0
         6: invokevirtual #2                  // Method getStr:()Ljava/lang/String;
         9: putfield      #3                  // Field field:Ljava/lang/String;
        12: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        15: aload_0
        16: getfield      #3                  // Field field:Ljava/lang/String;
        19: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        22: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        25: ldc           #6                  // String Init Block
        27: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        30: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        33: ldc           #7                  // String Constructor
        35: invokevirtual #5                  // Method java/io/Prin
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇HTMLParser 源码解析 下一篇几个关于 Class 文件的问题分析

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目