Unterschied zwischen statischem Modifikator und statischem Block [duplicate]

Lesezeit: 12 Minuten

Benutzer-Avatar
Fabio_M

Jemand erklärt mir die Unterschiede zwischen den folgenden beiden Aussagen?

EIN static final Variable initialisiert durch a static Codeblock:

private static final String foo;
static { foo = "foo"; }

EIN static final Variable, die durch eine Zuweisung initialisiert wird:

private static final String foo = "foo";

Benutzer-Avatar
Jon Skeet

In diesem Beispiel gibt es einen subtil Unterschied – in Ihrem ersten Beispiel foo ist nicht als Kompilierzeitkonstante bestimmt, daher kann es nicht als Fall verwendet werden switch Blöcke (und würden nicht in anderen Code eingebettet werden); in Ihrem zweiten Beispiel ist es. Also zum Beispiel:

switch (args[0]) {
    case foo:
        System.out.println("Yes");
        break;
}

Das gilt wann foo gilt als konstanter Ausdruck, aber nicht, wenn es “nur” eine statische Endvariable ist.

Statische Initialisierungsblöcke sind es jedoch normalerweise Wird verwendet, wenn Sie einen komplizierteren Initialisierungscode haben – z. B. das Auffüllen einer Sammlung.

Das zeitliche Koordinierung zur Initialisierung ist in beschrieben JLS 12.4.2; alle statischen Endfelder, die als Kompilierungszeitkonstanten betrachtet werden, werden zuerst initialisiert (Schritt 6) und Initialisierer werden später ausgeführt (Schritt 9); Alle Initialisierer (egal ob Feldinitialisierer oder statische Initialisierer) werden in Textreihenfolge ausgeführt.

  • @FabioMarano in Java 7 ist es möglich

    – Madhawa Priyashantha

    17. April 2015 um 6:14 Uhr

  • @FabioMarano: Sie können Zeichenfolgenkonstanten verwenden. Wie Fast Snail sagt, wurde dies in Java 7 eingeführt …

    – Jon Skeet

    17. April 2015 um 6:15 Uhr

  • @AnkitLamba – Du solltest googeln Jon Skeet-Fakten auf Google und klicken Sie auf den ersten Link, der Sie zu Meta weiterleitet. Es tut uns leid Jonsie müssen dein Geheimnis kennen 😛

    – TheLostMind

    17. April 2015 um 6:18 Uhr


  • @TheLostMind: Ja, aber das sind sie Auch auf den Feldern vorhanden. Wenn Sie also einen statischen Initialisierer haben, der auftritt Vor ein Feld, das mit einer Konstante zur Kompilierzeit initialisiert wird, und Sie verwenden Reflektion, um auf das Feld zuzugreifen, sehen Sie immer noch den konstanten Wert im Feld. (Ich habe gerade nachgesehen.)

    – Jon Skeet

    17. April 2015 um 6:29 Uhr


  • Um etwas genauer zu sein, werden Konstanten zur Kompilierzeit beibehalten static final Variablen werden von der JVM initialisiert, bevor der statische Initialisierungscode ausgeführt wird, während Nicht-static final Felder können auch Konstanten zur Kompilierzeit enthalten, werden aber wie gewöhnliche Instanzfelder vom Konstruktor initialisiert. Sie benötigen jedoch Reflection, um den Unterschied zu erkennen, da jeder normale Verweis auf ein solches Feld zur Kompilierzeit durch den konstanten Wert ersetzt wird …

    – Holger

    17. April 2015 um 8:23 Uhr

 private static final String foo;
 static { foo ="foo";}

Der Wert von foo ist initialisiert wann der Unterricht ist geladen und statische Initialisierer werden ausgeführt.

private static final String foo = "foo";

Hier ist der Wert von foo wird ein … sein Kompilierzeit Konstante. Also in Wirklichkeit "foo" wird als Teil von th verfügbar sein Byte-Code selbst.

  • Wie Sie sagten, wird die statische Variable als Teil des Bytecodes selbst verfügbar sein. Ich möchte bestätigen, dass der Bytecode im Falle einer statischen Methode das Verhalten dieser statischen Methode kennt oder dies behoben wird, wenn die Methode aufgerufen [email protected]

    Benutzer4768611

    17. April 2015 um 10:38 Uhr

  • @dubey-theHarcourtians – Methoden verhalten sich in a anders als Felder. Alle Methodenaufrufe (statisch oder anders) wird während gelöst Kompilierzeit. Währenddessen wird der eigentliche Anruf getätigt Laufzeit. Das Byte-Code hat noch Informationen darüber, welche Methode aufgerufen werden soll und mit welchen Argumenten

    – TheLostMind

    17. April 2015 um 10:43 Uhr

  • @dubey-theHarcourtians – Ja. Methoden, die während der Kompilierzeit gebunden werden, können nicht sein übersteuert. Das Bytecode-Anweisung verwendet wird sein invokestatic . Alle Methodenreferenzen werden tatsächlich im konstanten Pool-/Methodenbereich von Heap gespeichert (statisch oder anderweitig). Zum Beispiel Level-Calls werden es sein invokevirtual.

    – TheLostMind

    17. April 2015 um 10:50 Uhr

  • @dubey-theHarcourtians – Objekte existieren nicht bei Instanzebene oder Klassenstufe, sie gehen alle in den Haufen. Es sind die Referenzen, die sind statisch / nicht statisch. Im Falle von Kompositiondie Verweise auf zusammengesetzte Objekte gehen in den Haufen als Teil der übergeordnetes Objekt. Bei Methoden befinden sich Verweise auf der Stapelrahmen der Methode. Lokale Primitive befinden sich immer auf dem Stack. Alle Methoden, unabhängig von statischen oder anderen, gehen in den Methodenbereich.

    – TheLostMind

    17. April 2015 um 11:14 Uhr

  • danke @TheLostMind. Noch etwas, selbst ich weiß, dass sich das Objekt im Heap befindet und fast der gesamte Code in den Methodenbereich geht und diese Methode in einem separaten Stapel ausgeführt wird. Ich möchte bestätigen, wo i) – Instanzvariable ii) – statische Variable iii) -Lokale Variable iv)-Statischer Methodencode befindet sich im Speicher von JVM.Bitte lassen Sie mich bestätigen, dass ich verwirrt bin, weil verschiedene Autoren verschiedene Details [email protected] Skeet

    Benutzer4768611

    17. April 2015 um 11:17 Uhr

Im zweiten Fall Wert von foo ist früh gebunden dh der Compiler identifiziert und weist der Variablen den Wert foo zu FOOdie nicht geändert werden kann, und dies wird sein verfügbar außer mit Byte-Code selbst.

private static final String FOO = "foo";

und Im Ist-Fall-Wert von foo initialisieren kurz nach dem Laden der Klasse als allererste Zuweisung vor zugewiesener Instanzvariable, auch hier Sie kann Ausnahmen abfangen oder statisches Feld sein kann Zuweisen durch Aufrufen statischer Methoden im statischen Block.

private static final String FOO;
static { FOO ="foo";}

Also, wann immer es eine Bedingung gibt, kommen Sie an wenn der Compiler den Wert identifizieren muss der Variablen foo, Bedingung II wird funktionieren, zum Beispiel-wie Fallwert: in Schalterfällen.

Benutzer-Avatar
Boann

Das JLS beschreibt einige spezielle Verhaltensweisen dessen, was es aufruft konstante Variablenwelche sind final Variablen (ob static oder nicht), die mit konstanten Ausdrücken von initialisiert werden String oder primitiver Typ.

Konstante Variablen haben einen großen Unterschied in Bezug auf die Binärkompatibilität: die Werte von konstanten Variablen werden Teil der API der Klasse, soweit es den Compiler betrifft.

Ein Beispiel:

class X {
    public static final String XFOO = "xfoo";
}

class Y {
    public static final String YFOO;
    static { YFOO = "yfoo"; }
}

class Z {
    public static void main(String[] args) {
        System.out.println(X.XFOO);
        System.out.println(Y.YFOO);
    }
}

Hier, XFOO ist eine “konstante Variable” und YFOO nicht, aber sie sind ansonsten gleichwertig. Klasse Z druckt jeden von ihnen aus. Kompilieren Sie diese Klassen und zerlegen Sie sie dann mit javap -v X Y Zund hier ist die Ausgabe:

Klasse X:

Constant pool:
   #1 = Methodref          #3.#11         //  java/lang/Object."<init>":()V
   #2 = Class              #12            //  X
   #3 = Class              #13            //  java/lang/Object
   #4 = Utf8               XFOO
   #5 = Utf8               Ljava/lang/String;
   #6 = Utf8               ConstantValue
   #7 = String             #14            //  xfoo
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = NameAndType        #8:#9          //  "<init>":()V
  #12 = Utf8               X
  #13 = Utf8               java/lang/Object
  #14 = Utf8               xfoo
{
  public static final java.lang.String XFOO;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: String xfoo


  X();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
}

Klasse Y:

Constant pool:
   #1 = Methodref          #5.#12         //  java/lang/Object."<init>":()V
   #2 = String             #13            //  yfoo
   #3 = Fieldref           #4.#14         //  Y.YFOO:Ljava/lang/String;
   #4 = Class              #15            //  Y
   #5 = Class              #16            //  java/lang/Object
   #6 = Utf8               YFOO
   #7 = Utf8               Ljava/lang/String;
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               <clinit>
  #12 = NameAndType        #8:#9          //  "<init>":()V
  #13 = Utf8               yfoo
  #14 = NameAndType        #6:#7          //  YFOO:Ljava/lang/String;
  #15 = Utf8               Y
  #16 = Utf8               java/lang/Object
{
  public static final java.lang.String YFOO;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL


  Y();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #2                  // String yfoo
         2: putstatic     #3                  // Field YFOO:Ljava/lang/String;
         5: return
}

Klasse Z:

Konstantenpool: #1 = Methodref #8.#14 // java/lang/Object."":()V #2 = Fieldref #15.#16 // java/lang/System.out:Ljava/ io/PrintStream;  #3 = Klasse #17 // X #4 = String #18 // xfoo #5 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V #6 = Fieldref #21.#22 // Y.YFOO:Ljava/lang/String;  #7 = Klasse #23 // Z #8 = Klasse #24 // java/lang/Object #9 = Utf8  #10 = Utf8 ()V #11 = Utf8 Code #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V
  #14 = NameAndType        #9:#10         //  "<init>":()V
  #15 = Class              #25            //  java/lang/System
  #16 = NameAndType        #26:#27        //  out:Ljava/io/PrintStream;
  #17 = Utf8               X
  #18 = Utf8               xfoo
  #19 = Class              #28            //  java/io/PrintStream
  #20 = NameAndType        #29:#30        //  println:(Ljava/lang/String;)V
  #21 = Class              #31            //  Y
  #22 = NameAndType        #32:#33        //  YFOO:Ljava/lang/String;
  #23 = Utf8               Z
  #24 = Utf8               java/lang/Object
  #25 = Utf8               java/lang/System
  #26 = Utf8               out
  #27 = Utf8               Ljava/io/PrintStream;
  #28 = Utf8               java/io/PrintStream
  #29 = Utf8               println
  #30 = Utf8               (Ljava/lang/String;)V
  #31 = Utf8               Y
  #32 = Utf8               YFOO
  #33 = Utf8               Ljava/lang/String;
{
  Z();
    descriptor: ()V
    flags:
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return

  public static void main(java.lang.String[]);  Beschreibung: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #4                  // String xfoo
         5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: getstatic     #6                  // Field Y.YFOO:Ljava/lang/String;
        14: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        17: return
}

Things to notice in the disassembly, which tell you the differences between X and Y run deeper than syntactic sugar:

  • XFOO has a ConstantValue attribute, signifying that its value is a compile-time constant. Whereas YFOO does not, and uses a static block with a putstatic instruction to initialize the value at runtime.

  • The String constant "xfoo" has become part of class Z‘s constant pool, but "yfoo" has not.

  • Z.main uses the ldc (load constant) instruction to load "xfoo" onto the stack directly from its own constant pool, but it uses a getstatic instruction to load the value of Y.YFOO.

Other differences you will find:

  • If you change the value of XFOO and recompile X.java but not Z.java, you have a problem: class Z is still using the old value. If you change the value of YFOO and recompile Y.java, class Z uses the new value whether you recompile Z.java or not.

  • If you delete the X.class file entirely, class Z still runs correctly. Z has no runtime dependency on X. Whereas if you delete the Y.class file, class Z fails to initialize with a ClassNotFoundException: Y.

  • If you generate documentation for the classes with javadoc, the “Constant Field Values” page will document the value of XFOO, but not the value of YFOO.

The JLS describes the above effects constant variables have on compiled class files in §13.1.3:

A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable’s initializer.

If such a field is static, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field. Such a field must always appear to have been initialized (§12.4.2); the default initial value for the field (if different than V) must never be observed.

If such a field is non-static, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has only static fields.) The class should have code to set the field’s value to V during instance creation (§12.5).

And in §13.4.9:

If a field is a constant variable (§4.12.4), and moreover is static, then deleting the keyword final or changing its value will not break compatibility with pre-existing binaries by causing them not to run, but they will not see any new value for a usage of the field unless they are recompiled.

[…]

Der beste Weg, um Probleme mit “inkonstanten Konstanten” in weit verbreitetem Code zu vermeiden, ist die Verwendung von static konstante Variablen nur für Werte, die sich wirklich nie ändern werden. Abgesehen von echten mathematischen Konstanten empfehlen wir, Quellcode sehr sparsam zu verwenden static konstante Variablen.

Das Ergebnis ist, dass, wenn Ihre öffentliche Bibliothek konstante Variablen offenlegt, Sie darf niemals ändern Sie ihre Werte, wenn Ihre neue Bibliotheksversion ansonsten mit Code kompatibel sein soll, der mit alten Versionen der Bibliothek kompiliert wurde. Es wird nicht unbedingt einen Fehler verursachen, aber der vorhandene Code wird wahrscheinlich nicht funktionieren, da er veraltete Vorstellungen über die Werte von Konstanten hat. (Wenn Ihre neue Bibliotheksversion für Klassen, die sie verwenden, ohnehin neu kompiliert werden muss, verursacht das Ändern von Konstanten dieses Problem nicht.)

Wenn Sie also eine Konstante mit einem Block initialisieren, haben Sie mehr Freiheit, ihren Wert zu ändern, da es verhindert, dass der Compiler den Wert in andere Klassen einbettet.

Der einzige Unterschied ist die Initialisierungszeit.

Java initialisiert zuerst die Mitglieder und dann die statischen Blöcke.

  • Ich dachte auch, das wäre der einzige Unterschied – ist es aber nicht. Siehe meine Antwort.

    – Jon Skeet

    17. April 2015 um 6:09 Uhr

  • Und auch Initialisierer – beides Feld und Statische Initialisierer – werden in Textreihenfolge ausgeführt. (Ich habe es gerade überprüft.) Wenn Sie also ein statisches Feld mit einem Initialisierer haben, dann einen statischen Block, dann ein weiteres statisches Feld mit einem Initialisierer, wird der statische Block ausgeführt Vor der endgültige Initialisierer. Nur Konstantenwerte werden zuerst initialisiert. Sehen docs.oracle.com/javase/specs/jls/se8/html/…

    – Jon Skeet

    17. April 2015 um 6:12 Uhr


Ein zusätzlicher Aspekt: ​​Betrachten Sie den Fall, wenn Sie mehrere statische Felder haben, und ja, dies ist ein Eckfall …

Wie in der Antwort von Jon Skeet angegeben, definiert das JLS die genaue Reihenfolge der Initialisierung. Wenn Sie jedoch aus irgendeinem Grund mehrere statische Attribute in einer bestimmten Reihenfolge initialisieren müssen, möchten Sie die Initialisierungssequenz möglicherweise im Code deutlich sichtbar machen. Bei Verwendung der direkten Feldinitialisierung: Einige Code-Formatierer (und Entwickler) können irgendwann entscheiden, Felder anders zu sortieren, dies wirkt sich direkt darauf aus, wie die Felder initialisiert werden, und führt zu unerwünschten Effekten.

Übrigens, wenn Sie den gängigen Java-Codierungskonventionen folgen möchten, sollten Sie bei der Definition von “Konstanten” (endgültige statische Felder) Großbuchstaben verwenden.

— bearbeitet, um die Kommentare von Jon Skeet widerzuspiegeln —

  • Ich dachte auch, das wäre der einzige Unterschied – ist es aber nicht. Siehe meine Antwort.

    – Jon Skeet

    17. April 2015 um 6:09 Uhr

  • Und auch Initialisierer – beides Feld und Statische Initialisierer – werden in Textreihenfolge ausgeführt. (Ich habe es gerade überprüft.) Wenn Sie also ein statisches Feld mit einem Initialisierer haben, dann einen statischen Block, dann ein weiteres statisches Feld mit einem Initialisierer, wird der statische Block ausgeführt Vor der endgültige Initialisierer. Nur Konstantenwerte werden zuerst initialisiert. Sehen docs.oracle.com/javase/specs/jls/se8/html/…

    – Jon Skeet

    17. April 2015 um 6:12 Uhr


Benutzer-Avatar
szarlih

Statische Blöcke geben Ihnen mehr als nur eine einfache Aussage. In diesem speziellen Fall ist es dasselbe. Der statische Abschnitt wird zur Klassenladezeit ausgeführt, bevor Instanzen erstellt werden. Hier können Sie Methoden aufrufen und deren Ergebnisse statischen Feldern zuweisen. Und Sie können Ausnahmen in statischen Blöcken abfangen.

  • Das spricht jedoch nicht wirklich über den Unterschied zwischen statischen Initialisierungsblöcken und statischen Feldinitialisierern, was die Frage war.

    – Jon Skeet

    17. April 2015 um 7:22 Uhr

1229040cookie-checkUnterschied zwischen statischem Modifikator und statischem Block [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy