类级静态块是否表示为字节代码中的方法?

时间:2016-12-25 05:46:31

标签: java bytecode

我创建了一个小型Java类,并针对相应的.class文件运行javap命令。我只是这样做是为了理解类文件是如何组织的(是的,我也经历过JVM规范)。我可能是错的,但从我对类文件结构的理解。似乎将类级静态块列为方法。

我是否正确地解释了这一点(静态块被表示为方法)?如果是的话,静态块是否被认为等同于Java中的方法?

源文件:

package test;

public class TestClass
{
    static {
        System.out.println("abcd");
    }

    public void method(String s)
    {
        System.out.println("hello world");
    }
}

javap输出

Classfile /C:/TestClass.class
  Last modified Dec 25, 2016; size 477 bytes
  MD5 checksum 7571c8f98e814fb8bb53885f073c6048
  Compiled from "TestClass.java"
public class test.TestClass
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#17         // java/lang/Object."<init>":()V
   #2 = Fieldref           #18.#19        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #20            // hello world
   #4 = Methodref          #21.#22        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = String             #23            // abcd
   #6 = Class              #24            // test/TestClass
   #7 = Class              #25            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               method
  #13 = Utf8               (Ljava/lang/String;)V
  #14 = Utf8               <clinit>
  #15 = Utf8               SourceFile
  #16 = Utf8               TestClass.java
  #17 = NameAndType        #8:#9          // "<init>":()V
  #18 = Class              #26            // java/lang/System
  #19 = NameAndType        #27:#28        // out:Ljava/io/PrintStream;
  #20 = Utf8               hello world
  #21 = Class              #29            // java/io/PrintStream
  #22 = NameAndType        #30:#13        // println:(Ljava/lang/String;)V
  #23 = Utf8               abcd
  #24 = Utf8               test/TestClass
  #25 = Utf8               java/lang/Object
  #26 = Utf8               java/lang/System
  #27 = Utf8               out
  #28 = Utf8               Ljava/io/PrintStream;
  #29 = Utf8               java/io/PrintStream
  #30 = Utf8               println
{
  public test.TestClass();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0

  public void method(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String hello world
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 11: 0
        line 12: 8

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #5                  // String abcd
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
}
SourceFile: "TestClass.java"

2 个答案:

答案 0 :(得分:2)

Java中有两种类型的初始化器,实例和静态。

在字节码级别,所有静态初始值设定项(加上非最终静态字段的初始化或初始化为非常量表达式)都会被编译并连接成一个名为<clinit>的方法。这是一个特殊的方法,在加载类之后由JVM自动调用,但是普通代码不能调用它。请注意,无论您在源代码级别拥有多少单独的静态初始化程序块,都只有一种临时方法。

实例初始化器在字节码中没有直接的等价物。在从超类构造函数调用返回之后,所有实例初始值设定项以及实例字段的初始化程序都会被编译并插入到类构造函数方法中。很明显,这意味着可以在调用initalizers之前观察实例的状态,这就是为什么你不应该从构造函数中调用虚方法。

答案 1 :(得分:0)

它不是一个完全正确的方法,它是一个初始化器,就像构造函数一样,但是在类级别。 &#34;静态块&#34;不是正确的用语;它是&#34;静态初始化程序块&#34;,所以它是有道理的。它与构造函数一样多或类似于void方法。