Are class-level static blocks represented as methods in byte code?

464 Views Asked by At

I created a small Java class and ran the javap command against the corresponding .class file. I'm just doing this in an effort to understand how the class file is organized (yes, I've been going through the JVM specs too). I could be wrong, but from what I understand of the class file structure. it seems that the class-level static block is being listed as a method.

Am I interpreting this correctly (that the static block is being represented as a method)? If yes, is a static block somehow considered equivalent to a method in Java?

Source file:

package test;

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

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

javap output:

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

There are 2 best solutions below

0
On

It isn't exactly a method, it's an initializer, like a constructor but at the class level. "Static block" isn't the right term; it's "static initializer block", so it makes sense. It is as much or little like a void method as a constructor is.

0
On

There are two types of initializers in Java, instance and static.

At the bytecode level, all of the static initializers (plus initialization of static fields that are nonfinal or initialized to a nonconstant expression) are compiled and concatenated into one method, called <clinit>. This is a special method which is called automatically by the JVM after a class is loaded but can't be called by normal code. Note that no matter how many seperate static initializer blocks you have at the source code level, there is only one clinit method.

Instance initalizers don't have a direct equivalent in bytecode. All the instance initializers, plus initializers of instance fields, are compiled and inserted into the classes constructor methods right after returning from the superclass constructor call. Incidently, this means it is possible to observe an instance's state prior to the initalizers being invoked, which is why you shouldn't call virtual methods from a constructor.