String ist eine unveränderliche Klasse in Java. Eine unveränderliche Klasse ist einfach eine Klasse, deren Instanzen nicht geändert werden können. Warum wählt die Java-Programmiersprache Objekte der Klasse String unveränderlich?
String ist eine unveränderliche Klasse in Java. Eine unveränderliche Klasse ist einfach eine Klasse, deren Instanzen nicht geändert werden können. Warum wählt die Java-Programmiersprache Objekte der Klasse String unveränderlich?
Antworten:
Dieses Problem hängt stark mit der Vorstellung zusammen, was es bedeutet, eine Instanz einer Klasse zu sein. Streng objektorientiert hat eine Klasse eine zugehörige Invariante: ein Prädikat, das beim Beenden einer (öffentlichen) Methode der Klasse immer gilt. Ein solcher Begriff ist von zentraler Bedeutung, um beispielsweise sicherzustellen, dass die Vererbung genau definiert ist (er ist Teil des Liskov-Substitutionsprinzips ).
Eines der schädlichsten Probleme mit Java ist, dass es schwierig ist zu verhindern, dass Clientcode Klasseninvarianten bricht.
Betrachten Sie beispielsweise die folgende 'ZipCode'-Klasse:
class ZipCode {
private String zipCode;
public ZipCode(String value){
if(!isValidZipCode(value))
throw new IllegalArgumentException();
zipCode = value;
assert(invariant());
}
public String get() { return zipCode; }
public boolean invariant() {
return isValidZipCode( zipCode );
}
}
Wenn String nicht unveränderlich wäre, könnte ein Benutzer von ZipCode jederzeit 'get' aufrufen und die Zeichen ändern, wodurch die Invariante gebrochen und die konzeptionelle Integrität zerstört wird, die die Kapselung des ZipCode-Konzepts bietet.
Da diese Art von Integrität für die Gewährleistung der Gültigkeit großer Systeme von entscheidender Bedeutung ist, wirft diese Antwort auf Ihre Frage die umfassendere Frage auf:
"Warum unterstützt Java kein Analogon von C ++ const oder bietet zumindest unveränderliche Versionen von mehr Bibliotheksklassen an?"
Dinge wie Zeichenfolgen und Datumsangaben sind natürlich Werte. In C ++ erwarten wir, dass sie einen Kopierkonstruktor, einen Zuweisungsoperator und einen Gleichheitsoperator haben, aber wir erwarten nie, dass sie ihre Adresse annehmen. Daher erwarten wir nicht, dass sie einzeln auf dem Heap zugewiesen werden. Virtuelle Methoden machen keinen Sinn.
Domänenobjekte sind natürlich Referenzen. C ++ - Operatoren haben keinen Kopierkonstruktor, Zuweisungsoperator oder Gleichheitsoperator (sie sind nur dann gleich, wenn sie identisch sind). Wir können ihre Adresse übernehmen und erwarten, dass ihnen ein Heap zugewiesen wird. Methoden sind in der Regel virtuell.
Java hat keine Wertklassen, nur Referenzklassen. Werte werden mit unveränderlichen Objekten gefälscht. Dies gilt für Zeichenfolgen, leider nicht für Daten. Die Veränderbarkeit von Java-Daten hat häufig zu Problemen geführt und ist jetzt veraltet. Veränderbare Werte können beispielsweise nicht als Grundlage für einen Hash verwendet werden.
Java wurde entwickelt, um die Ausführung von Unterabschnitten des Programmcodes in Umgebungen mit eingeschränkten Sicherheitsanforderungen zu ermöglichen. Diese Anforderung wurde implementiert, indem ein "SecurityManager" für einen Thread festgelegt wurde, der Zugriff auf die Parameter bestimmter kritischer Vorgänge erhält (z. B. Öffnen einer Datei), und gefragt wurde, ob der Vorgang ausgeführt werden darf oder nicht. Wenn Java-Zeichenfolgen veränderbar wären, könnte ein Programm solche Einschränkungen umgehen, indem es zwei Threads erstellt, von denen einer eine zulässige Operation zum Öffnen einer Datei ausführt, während die andere die Zeichenfolge, in der der Dateiname gespeichert ist, in einen nicht zulässigen ändert. Es besteht dann die Möglichkeit, dass der Sicherheitsmanager die ursprüngliche Zeichenfolge liest und die Operation akzeptiert, die an den Dateiöffnungscode weitergeleitet wird, der dann vor dem Öffnen der zweiten (nicht zulässigen) Datei angezeigt wird.
Die letztere Möglichkeit würde dazu führen, dass alle derartigen Vorgänge langsamer ablaufen und die Implementierung mit größerer Wahrscheinlichkeit Fehler enthält. Daher war die Verwendung unveränderlicher Zeichenfolgen die sinnvollste Entscheidung.
Im Allgemeinen sind unveränderliche Objekte nützlich, da sie die Freigabe ermöglichen, ohne dass defensive Kopien erstellt werden müssen (was auch in nicht sicherheitskritischem Code erforderlich sein kann, um Fehler zu vermeiden, wenn sich die Quelldaten ändern). Daher wäre die Entscheidung auch ohne diese Anforderung weiterhin getroffen eine vernünftige.