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 {
}