Frage

Betrachten Sie die folgende Schnittstelle in Java:

public interface I {
    public final String KEY = "a";
}

Und die folgende Klasse:

public class A implements I {
    public String KEY = "b";

    public String getKey() {
        return KEY;
    }
}

Warum ist es möglich, für die Klasse A zu kommen und Schnittstelle ich die endgültige Konstante außer Kraft setzen?

Versuchen Sie selbst:

A a = new A();
String s = a.getKey(); // returns "b"!!!
War es hilfreich?

Lösung

Trotz der Tatsache, dass Sie die Variable Shadowing es ganz interessant zu wissen, dass Sie letzte Felder in Java ändern können, wie Sie hier :

  

Java 5 - "final" ist nicht endgültig mehr

     

Narve Saetre von Machina Networks in Norwegen schickte mir eine Notiz gestern,   zu erwähnen, dass es schade war, dass wir den Griff zu einem ändern könnten   final Array. Ich verstand ihn falsch und begann geduldig zu erklären   dass wir nicht einen Array konstant machen, und dass es keine Möglichkeit,   den Inhalt eines Feldes schützt. „Nein“, sagte er, „wir ändern können   letzter Griff Reflexion verwendet wird. "

     

Ich habe versucht Narve den Beispielcode und unglaublich, Java 5 erlaubt mir   ändern, um eine endgültige Griff, auch mit einem Griff zu einem primitiven Feld! Ich wusste, dass   es verwendete, an einem gewissen Punkt zu dürfen, aber das war es dann nicht zulässig,   so lief ich ein paar Tests mit älteren Versionen von Java. Erstens brauchen wir eine   Klasse mit dem letzten Feldern:

public class Person {
  private final String name;
  private final int age;
  private final int iq = 110;
  private final Object country = "South Africa";

  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String toString() {
    return name + ", " + age + " of IQ=" + iq + " from " + country;
  }
}
     

JDK 1.1.x

     

In JDK 1.1.x waren wir private Felder für den Zugriff auf nicht in der Lage verwenden   Reflexion. Wir konnten jedoch erstellen Sie eine andere Person mit öffentlichen   Felder, dann ist unsere Klasse gegen das kompilieren, und tauschen Sie die Person   Klassen. Es gab keinen Zugang zur Laufzeit zu überprüfen, ob wir liefen   gegen eine andere Klasse der, die wir gegen zusammengestellt.   Allerdings können wir nicht endgültig Felder zur Laufzeit erneut binden entweder   Klasse-Swapping oder Reflexion.

     

Das JDK 1.1.8 JavaDocs für java.lang.reflect.Field hatte folgende   zu sagen:

     
      
  • Wenn dieses Feld Objekt Java erzwingt Sprache Zugriffskontrolle und das darunter liegende Feld ist nicht zugänglich, wobei das Verfahren wirft eine   Illegal.
  •   
  • Wenn das zugrunde liegende Feld endgültig ist, wirft die Methode eine Illegal.
  •   
     

JDK 1.2.x

     

In JDK 1.2.x, änderte sich dies ein wenig. Wir können nun private Felder machen   zugänglich mit dem setAccessible (true) Methode. Der Zugang von Feldern war   jetzt zur Laufzeit überprüft, so konnten wir nicht die Klasse Swapping Trick   private Felder zuzugreifen. Allerdings konnten wir jetzt plötzlich rebind endgültig   Felder! Schauen Sie sich diesen Code:

import java.lang.reflect.Field;

public class FinalFieldChange {
  private static void change(Person p, String name, Object value)
      throws NoSuchFieldException, IllegalAccessException {
    Field firstNameField = Person.class.getDeclaredField(name);
    firstNameField.setAccessible(true);
    firstNameField.set(p, value);
  }
  public static void main(String[] args) throws Exception {
    Person heinz = new Person("Heinz Kabutz", 32);
    change(heinz, "name", "Ng Keng Yap");
    change(heinz, "age", new Integer(27));
    change(heinz, "iq", new Integer(150));
    change(heinz, "country", "Malaysia");
    System.out.println(heinz);
  }
}
     

Als ich lief die in JDK 1.2.2_014, ich hatte folgendes Ergebnis:

Ng Keng Yap, 27 of IQ=110 from Malaysia    Note, no exceptions, no complaints, and an incorrect IQ result. It seems that if we set a
     

letzte Feld eines primitiven bei der Deklaration wird der Wert inlined,   wenn der Typ ist primitiv oder ein String.

     

JDK 1.3.x und 1.4.x

     

In JDK 1.3.x verschärfte Sun den Zugang ein wenig, und hinderte uns   ein Endfeld mit Reflexion zu verändern. Dies war auch der Fall bei   JDK 1.4.x. Wenn wir die FinalFieldChange Klasse versuchen, laufen erneut zu binden   die letzten Felder zur Laufzeit mithilfe von Reflektion, würden wir bekommen:

     
    

java version "1.3.1_12": Ausnahme thread "main"     Illegal: Feld ist endgültig             bei java.lang.reflect.Field.set (native Methode)             bei FinalFieldChange.change (FinalFieldChange.java:8)             bei FinalFieldChange.main (FinalFieldChange.java:12)

         

java version "1.4.2_05" Exception thread "main"     Illegal: Feld ist endgültig             bei java.lang.reflect.Field.set (Field.java:519)             bei FinalFieldChange.change (FinalFieldChange.java:8)             bei FinalFieldChange.main (FinalFieldChange.java:12)

  
     

JDK 5.x

     

Nun kommen wir zum JDK 5.x. Die FinalFieldChange Klasse hat die gleiche Leistung   wie in JDK 1.2.x:

Ng Keng Yap, 27 of IQ=110 from Malaysia    When Narve Saetre mailed me that he managed to change a final field in JDK 5 using
     

Reflexion, ich hatte gehofft, dass ein Fehler in das JDK gekrochen war. Jedoch,   wir beide das Gefühl, dass es unwahrscheinlich, vor allem eine solche grundlegenden Fehler.   Nach einiger Suche fand ich die JSR-133: Java-Speichermodell und   Gewinde Spezifikation. Der größte Teil der Spezifikation ist hart Lesen underinnert mich an meine Studienzeit (habe ich so zu schreiben ;-)   Allerdings JSR-133 ist so wichtig, dass sie das Lesen erforderlich sein sollten   für alle Java-Programmierer. (Viel Glück)

     

Beginnen Sie mit Kapitel 9 Schlussfeld Semantics, auf Seite 25. Genauer gesagt,   Abschnitt 9.1.1 Post-Bau Modifikation von Final Feldern lesen. Es   Sinn macht Aktuelles zu endgültigen Feldern zu ermöglichen. Zum Beispiel könnten wir   entspannen die Anforderung haben Felder Nicht-Finale in JDO.

     

Wenn wir Abschnitt 9.1.1 sorgfältig zu lesen, sehen wir, dass wir nur ändern sollten   letzte Felder im Rahmen unserer Bauprozess. Der Anwendungsfall ist   wo wir deserialisieren ein Objekt, und dann, sobald wir aufgebaut haben, die   Objekt, initialisieren wir die letzten Felder, bevor es vorbei an. Sobald wir   das Objekt zur Verfügung zu einem anderen Thread gemacht haben, sollten wir nicht ändern   letzte Felder mit Reflexion. Das Ergebnis wäre nicht vorhersehbar ist.

     

Es gibt sogar sagt: Wenn ein abschließendes Feld zu einem Kompilierung-initialisiert   in der Felddeklaration konstant, Änderungen an dem letzten Feld kann nicht   beobachtet werden, da Anwendungen von diesem letzten Feld bei der Kompilierung ersetzt werden   Zeit mit der Compile-Zeit konstant. Dies erklärt, warum unser iq Feld   bleibt gleich, aber Land ändert.

     

Seltsam, 5 JDK unterscheidet sich geringfügig von JDK 1.2.x, dass Sie nicht können   Ändern ein statisches letztes Feld.

import java.lang.reflect.Field;

public class FinalStaticFieldChange {
  /** Static fields of type String or primitive would get inlined */
  private static final String stringValue = "original value";
  private static final Object objValue = stringValue;

  private static void changeStaticField(String name)
      throws NoSuchFieldException, IllegalAccessException {
    Field statFinField = FinalStaticFieldChange.class.getDeclaredField(name);
    statFinField.setAccessible(true);
    statFinField.set(null, "new Value");
  }

  public static void main(String[] args) throws Exception {
    changeStaticField("stringValue");
    changeStaticField("objValue");
    System.out.println("stringValue = " + stringValue);
    System.out.println("objValue = " + objValue);
    System.out.println();
  }
}
     

Wenn wir dies mit JDK 1.2.x und 5.x JDK ausführen, erhalten wir die folgende   Ausgang:

     
    

java version "1.2.2_014": String = ursprünglicher Wert objValue = new     Wert

         

java version "1.5.0" Exception thread "main" Illegal:     Feld ist endgültig bei java.lang.reflect.Field.set (Field.java:656) bei     FinalStaticFieldChange.changeStaticField (12) an     FinalStaticFieldChange.main (16)

  
     

So, JDK 5 ist wie JDK 1.2.x, nur anders?

     

Fazit

     

Sie wissen, wenn JDK 1.3.0 veröffentlicht wurde? Ich kämpfte, um herauszufinden, so dass ich   heruntergeladen und installiert. Die readme.txt-Datei hat das Datum   2000.06.02 13.10. So ist es mehr als 4 Jahre alt (Güte mir, es   fühlt sich an wie gestern). JDK 1.3.0 wurde mehrere Monate, bevor ich entlassen   begann Newsletter The Java (tm) Spezialisten schreiben! Ich denke, es würde   sicher sein, dass nur sehr wenige Java-Entwickler zu sagen, können die Details erinnern   von Pre-JDK1.3.0. Ahh, Nostalgie ist nicht, was es früher! Machst du   erinnert Java zum ersten Mal ausgeführt wird und immer diese Fehlermeldung:   "Kann nicht Threads initialisieren: nicht Klasse java / lang / Thema finden"?

Andere Tipps

Sie verstecken es, es ist ein Feature von „Scope“. Jedes Mal, wenn Sie in einem kleineren Umfang sind, können Sie alle Variablen definieren Sie mögen und die äußeren Umfang Variablen werden „Beschattet“

Durch die Art und Weise, können Sie es Umfang wieder, wenn Sie mögen:

public class A implements I {
    public String KEY = "b";

    public String getKey() {
        String KEY = "c";
        return KEY;
    }
}

Jetzt wird Return-Taste "c";

Edited, weil die ursprüngliche sog beim erneuten Lesen.

Es sieht aus wie Ihre Klasse ist einfach die Variable versteckt, nicht überschrieben wird es:

public class A implements I {
    public String   KEY = "B";

    public static void main(String args[])
    {
        A t = new A();
        System.out.println(t.KEY);
        System.out.println(((I) t).KEY);
    }
}

Dies wird gedruckt "B" und "A", wie Sie gefunden. Sie können sogar zuweisen, da die A.KEY Variable nicht als endgültig definiert ist.

 A.KEY="C" <-- this compiles.

Aber -

public class C implements I{

    public static void main (String args[])
    {
        C t = new C();
        c.KEY="V"; <--- compiler error ! can't assign to final

    }
}

Sie sollten keinen Zugriff auf Ihre Konstante auf diese Weise kann die statische Referenz statt:

I.KEY //returns "a"
B.KEY //returns "b"

Als Design-Betrachtung,

public interface I {
    public final String KEY = "a";
}

Die statischen Methoden liefern immer den übergeordneten Schlüssel.

public class A implements I {
    public String KEY = "b";

    public String getKey() {
        return KEY; // returns "b"
    }

    public static String getParentKey(){
        return KEY; // returns "a"
    }
}

Genau wie Jom hat bemerkt. Die Gestaltung von statischen Methoden neu definierten Schnittstelle Mitglieder verwenden könnte ein schweres Problem. In der Regel versucht Verwendung die gleichen Namen für die Konstante zu vermeiden.

Statische Felder und Methoden sind an die Klasse / Schnittstelle sie zu deklarieren (obwohl Schnittstellen nicht statische Methoden erklären können, wie sie ganz abstrakte Klassen sind, die umgesetzt werden müssen).

Wenn Sie also eine Schnittstelle mit einem öffentlichen statischen haben (Vartype) (varname), das Feld an diese Schnittstelle angeschlossen ist.

Wenn Sie eine Klasse, die eine Schnittstelle implementieren, die Compiler-Trick-Transformationen (dies.) Varname in InterfaceName.varname. Aber, wenn Sie Ihre Klasse varname definiert, wird eine neue Konstante mit dem Namen varname Ihre Klasse angebracht ist, und der Compiler weiß jetzt übersetzen (diese). Varname in NewClass.varname. Gleiches gilt für Methoden:., Wenn die neue Klasse nicht das Verfahren neu definieren, (. Diese) methoden in SuperClass.methodName übersetzt wird, andernfalls (. Diese) methoden in CurrentClass.methodName übersetzt

Aus diesem Grund werden Sie die Warnung begegnen „x Feld / Methode in einer statischen Art und Weise zugegriffen werden sollte“. Der Compiler sagen Ihnen, dass, obwohl es den Trick verwenden kann, würde es vorziehen, dass Sie ClassName.method / Feldnamen verwendet, da es noch deutlicher aus Gründen der Lesbarkeit ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top