Diese Frage bezieht sich nicht primär auf Strings. Aus wissenschaftlicher Neugier möchte ich wissen, wie der Modifikator final
auf einer Variablen das Verhalten eines Programms verändern kann. Das folgende Beispiel zeigt, dass es möglich ist.
Diese Zeilen drucken true
aber diese Zeilen drucken false
Gibt es neben String
interning noch weitere Dinge, die dazu führen können, dass sich das Verhalten eines Programms ändert, wenn der Modifizierer final
aus einer Variablendeklaration entfernt wird? Ich gehe davon aus, dass das Programm mit oder ohne Modifikator kompiliert.
Bitte stimmen Sie nicht ab, um es als Duplikat von zu schließen Strings mit == vergleichen, die in Java als final deklariert werden . Ich verstehe das String
s Beispiel.
Ich frage, ob es andere Gründe gibt, einen final
Modifikator zu entfernen, kann einen Unterschied machen. Bitte kann jemand auf eine Antwort verlinken oder die Frage beantworten. Vielen Dank.
Der Modifizierer final
stellt nur sicher, dass die Variable definitiv zugewiesen , und verbietet jede Neuzuweisung zu und von dieser Variable.
Die einzigen speziellen Fälle, die beobachtet werden können, sind ausdrücklich in der JLS angegeben :
Eine Variable vom primitiven Typ oder Typ String, die final ist und mit einem konstanten Ausdruck zur Kompilierungszeit initialisiert wird (§15.28), wird als konstante Variable bezeichnet.
Ob eine Variable eine konstante Variable ist oder nicht, kann Auswirkungen auf die Klasseninitialisierung (§12.4.1), die Binärkompatibilität (§13.1, §13.4.9) und die eindeutige Zuweisung (§16) haben.
Es gibt eine anständige Menge an JLS-Lektüre und um den Hauptpunkt zu behandeln: By JLS §13.4.9 , werden nicht beim Entfernen der% co_de irgendwelche negativen Auswirkungen haben % Modifikator.
Allerdings von JLS 17.5 Wenn Sie sich auf die Garantie eines Threads verlassen, nur die definitiv zugewiesenen Variablen in einem Objekt zu sehen, das beobachtet werden kann, dann wird das Entfernen der Variable final
dazu führen, dass diese Variablen für einen anderen Thread nicht mehr sichtbar sind.
Also, wenn wir uns Klasseninitialisierung
Ein Klassen- oder Schnittstellentyp T wird unmittelbar vor dem ersten Auftreten eines der folgenden Ereignisse initialisiert:
- T ist eine Klasse und eine Instanz von T wird erstellt.
- T ist eine Klasse und eine durch T deklarierte statische Methode wird aufgerufen.
- Ein statisches Feld, das von T deklariert ist, wird zugewiesen.
- Es wird ein statisches Feld verwendet, das von T deklariert wird, und das Feld ist keine konstante Variable (§4.12.4).
In JLS §13.1 ist es Ausgesprochen, dass das Ändern eines Felds in final
brechen kann Binärkompatibilität :
Verweise auf Felder, die konstante Variablen sind (§4.12.4), werden zur Kompilierzeit auf den angegebenen konstanten Wert aufgelöst. Kein Verweis auf ein solches Feld sollte in dem Code in einer Binärdatei vorhanden sein (außer in der Klasse oder der Schnittstelle, die das Feld enthält, das einen Code haben wird, um es zu initialisieren). Ein solches Feld muss immer als initialisiert erscheinen (§12.4.2); Der Standardwert für den Typ eines solchen Feldes darf niemals eingehalten werden. Siehe § 13.4.9 für eine Diskussion.
Ab 13.4.9:
Wenn ein Feld, das nicht als endgültig deklariert wurde, geändert wird, um deklariert zu werden Endgültig, dann kann es Kompatibilität mit bereits vorhandenen Binärdateien brechen Versuchen Sie, dem Feld neue Werte zuzuweisen.
Löschen Sie das Schlüsselwort final oder ändern Sie den Wert, zu dem ein Feld gehört initialisiert nicht die Kompatibilität mit vorhandenen Binärdateien.
Wenn ein Feld eine konstante Variable ist (§4.12.4), löschen Sie das Schlüsselwort final oder Ändern seines Wertes wird die Kompatibilität mit nicht brechen Vorhandene Binärdateien, indem sie verursachen, dass sie nicht laufen, aber sie werden nicht einen neuen Wert für die Verwendung des Felds sehen, sofern dies nicht der Fall ist neu kompiliert. Dies gilt auch dann, wenn die Verwendung selbst keine ist Kompilierzeitkonstantenausdruck (§15.28).
Dieses Ergebnis ist ein Nebeneffekt der Entscheidung, konditional zu unterstützen Zusammenstellung, wie am Ende von §14.21 diskutiert.
Achte also darauf, die Felder plötzlich in final
zu ändern. Entfernen das Feld ist sicher .
... aber das gilt nur für eine single-threaded Welt. Von JLS 17.5 :
Felder, die als final deklariert sind, werden einmal initialisiert, aber nie geändert normale Umstände. Die detaillierte Semantik der Endfelder ist etwas anders als bei normalen Feldern. Bestimmtes, Compiler haben eine große Freiheit, die Lesevorgänge der letzten Felder zu verschieben über Synchronisationsbarrieren und Aufrufe an beliebige oder unbekannte Methoden. Entsprechend dürfen Compiler den Wert von a behalten Letztes Feld in einem Register zwischengespeichert und nicht aus dem Speicher in neu geladen Situationen, in denen ein nicht endgültiges Feld neu geladen werden müsste.
Mitfinal fields können Programmierer auch Thread-safe unveränderlich implementieren Objekte ohne Synchronisation.Ein Thread-sicheres unveränderliches Objekt ist von allen Threads als unveränderbar angesehen, selbst wenn ein Datenrennen zum Bestehen verwendet wird Verweise auf das unveränderliche Objekt zwischen Threads. Dies kann zur Verfügung stellen Sicherheitsgarantien gegen Missbrauch einer unveränderlichen Klasse durch falsche oder Schadcode. Die letzten Felder müssen korrekt verwendet werden, um a Garantie der Unveränderbarkeit.
Ein Objekt wird als vollständig initialisiert betrachtet, wenn es Konstruktor endet. Ein Thread, der nur einen Verweis auf einen Objekt, nachdem das Objekt vollständig initialisiert wurde, ist garantiert um die korrekt initialisierten Werte für das Finale dieses Objekts anzuzeigen Felder.
Wenn also Ihr Programm von der obigen Garantie abhängig ist, damit es normal funktioniert, dann hat das Entfernen des final
-Schlüsselwortes Konsequenzen beim Threading.