Warum ist Enum Singleton faul?

Sep 24 2020

Ich sah Antworten wie diese , versuchte sie durch Kommentare zu klären und war mit Beispielen hier nicht zufrieden .

Vielleicht ist es Zeit für diese spezielle Frage ...

Warum wird die Enum-Singleton-Implementierung als faul bezeichnet ?

public enum EnumLazySingleton {
    INSTANCE;
    EnumLazySingleton() {
        System.out.println("constructing: " + this);
    }
    public static void touchClass() {}
}

Wie unterscheidet es sich von einer eifrigen Implementierung?

public class BasicEagerSingleton {
    private static final BasicEagerSingleton instance = new BasicEagerSingleton();
    public static BasicEagerSingleton getInstance() {
        return instance;
    }
    private BasicEagerSingleton() {
        System.out.println("constructing: " + this);
    }
    public static void touchClass() {}
}

Beide initiieren die Instanz ohne Zugriff INSTANCE/getInstance()- z touchClass(). B. Aufruf .

public class TestSingleton {
    public static void main(String... args) {
        System.out.println("sleeping for 5 sec...");
        System.out.println("touching " + BasicEagerSingleton.class.getSimpleName());
        BasicEagerSingleton.touchClass();
        System.out.println("touching " + EnumLazySingleton.class.getSimpleName());
        EnumLazySingleton.touchClass();
    }
}

Ausgabe:

sleeping for 5 sec...
touching BasicEagerSingleton
constructing: BasicEagerSingleton@7bfcd12c
touching EnumLazySingleton
constructing: INSTANCE

Jetzt können wir sagen, dass beide faul sind . Was ist dann eifrig ?

Es ist klar, wie (z. B.) "doppelt geprüftes Sperren" tatsächlich faul (und chaotisch und langsam) ist. Aber wenn enum faul ist, dann ist jeder Singleton faul, weil die Klasse unvermeidlich geladen wird - tatsächlich ist alles faul. Ab wann macht diese Unterscheidung keinen Sinn mehr?

Antworten

3 jaco0646 Sep 24 2020 at 19:58

Die ersten beiden verknüpften Antworten (von Peter Lawrey und Joachim Sauer ) stimmen darin überein, dass Aufzählungen nicht träge initialisiert werden. Die Antworten im dritten Link sind einfach falsch, was eine verzögerte Initialisierung bedeutet.

Die Empfehlung, Enums als Singletons zu verwenden, stammt von Josh Blochs Effective Java. Insbesondere im Kapitel über Enum-Singletons wird Faulheit nicht erwähnt. Es gibt ein späteres Kapitel über die verzögerte Initialisierung, in dem ebenfalls keine Aufzählungen erwähnt werden. Das Kapitel enthält zwei Highlights.

  • Wenn Sie eine verzögerte Initialisierung für die Leistung in einem statischen Feld verwenden müssen, verwenden Sie die Klassensprache für die verzögerte Initialisierung.
  • Wenn Sie für die Leistung eines Instanzfelds eine verzögerte Initialisierung verwenden müssen, verwenden Sie die Doppelprüfungssprache.

Zweifellos wären Aufzählungen eine andere Redewendung auf dieser Liste, wenn sie in irgendeiner Weise träge initialisiert würden. Tatsächlich sind sie es nicht, obwohl Verwirrung über die Bedeutung der verzögerten Initialisierung zu einigen falschen Antworten führt, wie das OP zeigt.

Y2020-09 Sep 24 2020 at 18:58

Darf ich folgendes wetten:

Sie versuchen, 2 "Prozesse" zu identifizieren oder ... "things"(Lassen Sie uns dies leicht verständlich machen - denn wenn ich anfange, "Codeblöcke" zu sagen, klingt es schwieriger) ...

  • Irgendwann wird der Klassenlader ausgeführt, und Sie möchten wissen, was "things"ausgeführt wird, wenn der Klassenlader eine Klasse lädt.
  • An einem anderen Punkt führt das Aufrufen einer Methode für die Klasse dazu, dass eine andere "thing"ausgeführt / ausgeführt wird, und Sie möchten wissen, welche genau (welche "processes") starten würde.

Folgende Fakten sind relevant:

  • Statische Initialisierer werden ausgeführt, wenn der Klassenlader die Klasse lädt. Der Klassenladeprogramm lädt die Klasse erst, wenn der ausgeführte Code auf die Notwendigkeit stößt, sie zu laden (da eine Methode oder ein Feld aufgerufen wurde), z.touchClass()
  • Wenn eine Singleton-Instanz von WEDER einer Klasse ODER einem Aufzählungstyp eine hat field, die im staticTeil der Klasse initialisiert wird, wird sie geladen , sobald Sie die Klasse 'berühren' - da der Class-Loader alle static initializationsfür eine Klasse oder ausführt Aufzählung beim Laden.
  • Wahrscheinlich kommt es zu einem verzögerten Laden (und dies ist meine "Interpretation" dessen, was Sie fragen), wenn ein Methodenaufruf die Klasse auffordert, eine Singleton-Instanz zu erstellen - was einige Zeit nach dem "Laden" des classoder passieren kann enum.

Eine Klasse wie die folgende:

public class LazySingleton
{
    // At time of class-loading, this singleton is set to 'null'
    private static singleton = null;

    // This is a method that will not be invoked until it is called by
    // some other code-block (some other "thing")...  When "touchClass()"
    // is called, the singleton instance is not created.
    public static LazySingleton retrieveSingleton()
    {
        if (singleton == null) singleton = new LazySingleton();
        return singleton;
    }

    // DOES NOTHING...  The Singleton is *not* loaded, even though the
    // Class Loader has already loaded this Java ".class" file
    // into memory.
    public static void touchClass() { }

    private LazySingleton()
    { System.out.println("constructing: LazySingleton"); }
}

Hier auf der anderen Seite:

public enum EagerEnum
{
  // The class loader will run this constructor as soon as this 'enum'
  // is loaded from a '.class' file (in JAR or on disk) into memory
  MyEnumConstant();

  private EagerEnum()
  { System.out.println("Eager Enum Constructed"); }

  // This will cause the Class Loader to Load this enum from the
  // Java ".class" File immediately, and the "MyEnumConstant" will
  // also have to be loaded - meaning the constructor will be called.
  public static void touchEnum() { }
}

Der folgende Code würde also die Ausgabe erzeugen

LazySingleton.touchClass();            // Prints nothing
EagerEnum.touchClass();                // Prints "Eager Enum Constructed"
LazySingleton.getSingletonInstance();  // Prints "constructing: LazySingleton