我创建了一个小型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"
答案 0 :(得分:2)
Java中有两种类型的初始化器,实例和静态。
在字节码级别,所有静态初始值设定项(加上非最终静态字段的初始化或初始化为非常量表达式)都会被编译并连接成一个名为<clinit>
的方法。这是一个特殊的方法,在加载类之后由JVM自动调用,但是普通代码不能调用它。请注意,无论您在源代码级别拥有多少单独的静态初始化程序块,都只有一种临时方法。
实例初始化器在字节码中没有直接的等价物。在从超类构造函数调用返回之后,所有实例初始值设定项以及实例字段的初始化程序都会被编译并插入到类构造函数方法中。很明显,这意味着可以在调用initalizers之前观察实例的状态,这就是为什么你不应该从构造函数中调用虚方法。
答案 1 :(得分:0)
它不是一个完全正确的方法,它是一个初始化器,就像构造函数一样,但是在类级别。 &#34;静态块&#34;不是正确的用语;它是&#34;静态初始化程序块&#34;,所以它是有道理的。它与构造函数一样多或类似于void方法。