1

jdk:1.8_144 os:debian 9

I compile this class:

public class Singleton {
    private static class SingletonHolder {
        private static final SingleTonInner INSTANCE = new SingleTonInner();
    }

    private static class SingleTonInner{
        public void out(){
            System.out.println("SingleTonInner:out");
        }
    }

    private Singleton() {
    }

    public static final SingleTonInner getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

But I find 4 .class file on my disk:

Singleton$1.class
Singleton.class
Singleton$SingletonHolder.class
Singleton$SingleTonInner.class

Here is a class Singleton$1.class ? what is it?

I use reflection to get constructor but it has no constructor ! It is nothing!

what is this class and where it come from?

Kaan
  • 5,434
  • 3
  • 19
  • 41
Hong
  • 11
  • 1

1 Answers1

1

Original question

Why does the posted code produce a class file named "Singleton$1.class"? Additionally, you shared that this was found using JDK version 1.8_144.

Here's a simpler version of the original code, which produces the same original behavior:

  • class Singleton
  • static class SingleTonInner
  • static class SingletonHolder
public class Singleton {
    private Singleton() { } 
    private static class SingletonInner { } 
    private static class SingletonHolder {
        static { new SingletonInner(); }   
    }   
}

What you have encountered is an anonymous inner class – that's the "Singleton$1.class" naming.

From JLS 15.9.5 - Anonymous Class Declarations

An anonymous class declaration is automatically derived from a class instance creation expression by the Java compiler.

And from JLS 13.1 - The Form of a Binary, this is where the "$" number naming is defined:

The binary name of a local class (§14.3) consists of the binary name of its immediately enclosing type, followed by $, followed by a non-empty sequence of digits, followed by the simple name of the local class.

As you observed, your code doesn't appear to be doing anything obvious to create that anonymous inner class.

Observations: JDK 1.8

I was able to reproduce the same thing – generating "Singleton$1.class" – with code posted above using JDK 1.8.0_341 (latest available version from Oracle).

Here's an attempt with 1.8, first wiping all class files, clearly showing "Singleton$1.class" was created:

% rm *.class && javac -version && javac Singleton.java
javac 1.8.0_341

% ls -l
175 Aug  6 18:09 Singleton$1.class
444 Aug  6 18:09 Singleton$SingletonHolder.class
362 Aug  6 18:09 Singleton$SingletonInner.class
352 Aug  6 18:09 Singleton.class
195 Aug  6 18:09 Singleton.java

Observations: JDK 11 + 17

Here's the same test after switching to JDK 11 – no more "$1.class":

% rm *.class && javac -version && javac Singleton.java
javac 11.0.14

% ls -l
413 Aug  6 18:09 Singleton$SingletonHolder.class
289 Aug  6 18:09 Singleton$SingletonInner.class
353 Aug  6 18:09 Singleton.class
195 Aug  6 18:09 Singleton.java

Here's the same test with JDK 17, no sign of the anonymous inner class:

% rm *.class && javac -version && javac Singleton.java
javac 17.0.1

% ls -l
413 Aug  6 18:09 Singleton$SingletonHolder.class
289 Aug  6 18:09 Singleton$SingletonInner.class
353 Aug  6 18:09 Singleton.class
195 Aug  6 18:09 Singleton.java

Explanation

I was unable to find a specific reason for this change in behavior, though the evidence must exist. I suspect this was a bug that was addressed at some point, and while I'm curious to see the details, my patience got the better of me. ;-) I did spend time looking through the Java bug database for various search phrases, including "anonymous class" (currently shows 200+ results), but the search tool is quite bad and page loads are slow.

Without a specific reason, we can still make a few observations about what's going on:

  • anonymous classes are a deliberate language feature
  • if something worked one way in JDK 1.8, and a different way in JDK 11 (and later), that was either a deliberate change, or a bug – but if it were a bug, it seems reasonable that said bug would have been identified and corrected years ago (and we would continue to see empty anonymous inner classes in recent JDK versions)

The output from javap supports the idea that the change across JDK versions was deliberate:

  • the output for the parent class "Singleton" lists a few, normal-looking things:
    % javap -c -p Singleton.class
    Compiled from "Singleton.java"
    public class Singleton {
      private Singleton();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    }
    
  • but the anonymous "Singleton$1" is completely empty:
    % javap -c -p Singleton\$1.class
    Compiled from "Singleton.java"
    class Singleton$1 {
    }
    
Kaan
  • 5,434
  • 3
  • 19
  • 41