Warum wird die String-Klasse in Java als endgültig deklariert?

139

Als ich erfuhr, dass die Klasse java.lang.Stringin Java als endgültig deklariert ist, habe ich mich gefragt, warum das so ist. Ich habe damals keine Antwort gefunden, aber diesen Beitrag: Wie erstelle ich ein Replikat der String-Klasse in Java? erinnerte mich an meine Anfrage.

Sicher, String bietet alle Funktionen, die ich jemals benötigt habe, und ich habe nie an eine Operation gedacht, die eine Erweiterung der Klasse String erfordern würde, aber Sie werden trotzdem nie wissen, was jemand benötigt!

Weiß jemand, was die Absicht der Designer war, als sie beschlossen, es endgültig zu machen?

Alex Ntousias
quelle
Vielen Dank für Ihre Antworten, insbesondere TrueWill, Bruno Reis und Thilo! Ich wünschte, ich könnte mehr als eine Antwort als die beste auswählen, aber leider ...!
Alex Ntousias
1
Bedenken Sie auch die Verbreitung von "Oh, ich brauche nur noch ein paar nützliche Methoden für String" -Projekte, die auftauchen würden - alle konnten sich nicht gegenseitig Strings verwenden, weil sie eine andere Klasse waren.
Thorbjørn Ravn Andersen
Vielen Dank für diese Antwort, es ist sehr nützlich. Wir haben jetzt zwei Fakten. Ein String ist eine Final-Klasse und unveränderlich, da er nicht geändert werden kann, sondern auf ein anderes Objekt verwiesen werden kann. aber was ist mit: - String a = neuer String ("test1"); dann ist s = "test2"; Wenn String ein Final Class-Objekt ist, wie kann es dann geändert werden? Wie kann ich ein modifiziertes Endobjekt verwenden? Bitte lassen Sie mich, wenn ich etwas falsch gefragt habe.
Suresh Sharma
Sie können diesen guten Artikel sehen .
Aniket Thakur
4
Eine Sache, die wir dankenswerterweise in Java vermieden haben, ist "jeder hat seine eigene Unterklasse von String mit vielen zusätzlichen Methoden, und keine davon ist miteinander kompatibel".
Thorbjørn Ravn Andersen

Antworten:

88

Es ist sehr nützlich, Zeichenfolgen als unveränderliche Objekte zu implementieren . Sie sollten über Unveränderlichkeit lesen , um mehr darüber zu verstehen.

Ein Vorteil von unveränderlichen Objekten ist das

Sie können Duplikate freigeben, indem Sie sie auf eine einzelne Instanz verweisen.

(von hier ).

Wenn String nicht endgültig wäre, könnten Sie eine Unterklasse erstellen und zwei Strings haben, die sich ähnlich sehen, wenn sie als Strings angesehen werden, aber tatsächlich unterschiedlich sind.

Bruno Reis
quelle
70
Sofern es keinen Zusammenhang zwischen Abschlussklassen und unveränderlichen Objekten gibt, den ich nicht sehe, sehe ich nicht, wie sich Ihre Antwort auf die Frage bezieht.
sepp2k
9
Denn wenn es nicht endgültig ist, können Sie ein StringChild als String-Parameter an eine Methode übergeben, und es kann veränderlich sein (weil sich der Status einer untergeordneten Klasse ändert).
Helios
4
Beeindruckend! Downvotes? Verstehst du nicht, wie Unterklassen mit Unveränderlichkeit zusammenhängen? Ich würde mich über eine Erklärung freuen, wo das Problem liegt.
Bruno Reis
7
@Bruno, re: downvotes: Ich habe Sie nicht downvotiert, aber Sie könnten einen Satz hinzufügen, wie das Verhindern von Unterklassen die Unveränderlichkeit erzwingt. Im Moment ist es eine Art halbe Antwort.
Thilo
12
@BrunoReis - fand einen netten Artikel Sie , dass in Verbindung bringen könnte ein Interview mit James Gosling (Schöpfer von Java) , wo er kurz Gespräche über dieses Thema hat hier . Hier ist ein interessanter Ausschnitt: "Eines der Dinge, die Strings dazu zwangen, unveränderlich zu sein, war die Sicherheit. Sie haben eine Methode zum Öffnen von Dateien. Sie übergeben einen String daran. Anschließend werden alle Arten von Authentifizierungsprüfungen durchgeführt, bevor das Betriebssystem ausgeführt wird." Wenn Sie es schaffen, nach der Sicherheitsüberprüfung und vor dem Aufruf des Betriebssystems etwas zu tun, das den String effektiv mutiert, dann boomen Sie ... "
Anurag
60

Dies ist ein schöner Artikel , der zwei Gründe umreißt, die bereits in den obigen Antworten erwähnt wurden:

  1. Sicherheit : Das System kann vertrauliche schreibgeschützte Informationen weitergeben, ohne befürchten zu müssen, dass sie geändert werden
  2. Leistung : Unveränderliche Daten sind sehr nützlich, um die Sicherheit von Threads zu gewährleisten.

Und dies ist wahrscheinlich der ausführlichste Kommentar in diesem Artikel. Dies hat mit dem String-Pool in Java und Sicherheitsproblemen zu tun. Es geht darum, wie man entscheidet, was in den String-Pool geht. Angenommen, beide Zeichenfolgen sind gleich, wenn ihre Zeichenfolge gleich ist, dann haben wir eine Race-Bedingung, wer zuerst dort ankommt und damit Sicherheitsprobleme. Wenn nicht, enthält der Zeichenfolgenpool redundante Zeichenfolgen, wodurch der Vorteil verloren geht, dass er überhaupt vorhanden ist. Lesen Sie es einfach selbst vor, oder?


Das Erweitern des Strings würde mit Gleichen und Praktikanten Chaos anrichten. JavaDoc sagt gleich:

Vergleicht diese Zeichenfolge mit dem angegebenen Objekt. Das Ergebnis ist genau dann wahr, wenn das Argument nicht null ist und ein String-Objekt ist, das dieselbe Zeichenfolge wie dieses Objekt darstellt.

Vorausgesetzt, es java.lang.Stringwar nicht endgültig, SafeStringkönnte a gleich a seinString und umgekehrt; weil sie die gleiche Folge von Zeichen darstellen würden.

Was würde passieren, wenn Sie sich internbei a bewerben SafeStringwürden - würde das SafeStringin den String-Pool der JVM gehen? Die ClassLoaderund alle Objekte, auf die SafeStringdie Referenzen verweisen, werden dann für die Lebensdauer der JVM an Ort und Stelle gesperrt. Sie würden eine Rennbedingung darüber erhalten, wer als erster eine Folge von Charakteren internieren könnte - vielleicht SafeStringwürden Sie gewinnen, vielleicht eine Stringoder vielleicht eineSafeString von einem anderen Klassenlader geladene (also eine andere Klasse).

Wenn Sie das Rennen in den Pool gewinnen würden, wäre dies ein echter Singleton und die Leute könnten durch Reflexion und auf Ihre gesamte Umgebung (Sandbox) zugreifen secretKey.intern().getClass().getClassLoader() .

Oder die JVM könnte dieses Loch blockieren, indem sie sicherstellt, dass nur konkrete String-Objekte (und keine Unterklassen) zum Pool hinzugefügt wurden.

Wenn equals so implementiert wurde, dass SafeString! = StringThen SafeString.intern! = String.internUnd SafeStringzum Pool hinzugefügt werden müsste. Der Pool würde dann zu einem Pool von <Class, String>statt <String>und alles, was Sie brauchen, um den Pool zu betreten, wäre ein frischer Klassenlader.

Anurag
quelle
2
Der Leistungsgrund ist natürlich ein Irrtum: Wäre String eine Schnittstelle gewesen, hätte ich eine Implementierung bereitstellen können, die in meiner Anwendung eine bessere Leistung erbringt.
Stephan Eggermont
28

Der absolut wichtigste Grund dafür, dass String unveränderlich oder endgültig ist, ist, dass er vom Klassenlademechanismus verwendet wird und daher tiefgreifende und grundlegende Sicherheitsaspekte aufweist.

Wäre String veränderlich oder nicht endgültig gewesen, hätte eine Anforderung zum Laden von "java.io.Writer" in "mil.vogoon.DiskErasingWriter" geändert werden können.

Referenz: Warum String in Java unveränderlich ist

Jackob
quelle
15

String ist eine sehr wichtige Klasse in Java. Viele Dinge hängen davon ab, dass sie auf eine bestimmte Weise funktioniert, zum Beispiel unveränderlich.

Durch das Erstellen der Klasse finalwerden Unterklassen verhindert, die diese Annahmen verletzen könnten.

Beachten Sie, dass Sie auch jetzt, wenn Sie Reflection verwenden, Strings unterbrechen können (ändern Sie deren Wert oder Hashcode). Die Reflexion kann mit einem Sicherheitsmanager gestoppt werden. Wenn Stringnicht final, könnte es jeder tun.

Andere Klassen, die nicht deklariert sind, finalermöglichen es Ihnen, etwas Listfehlerhafte Unterklassen zu definieren (Sie könnten beispielsweise eine haben , die zur falschen Position hinzufügt), aber zumindest hängt die JVM nicht von denen für ihre Kernoperationen ab.

Thilo
quelle
6
finalauf eine Klasse garantiert keine Unveränderlichkeit. Es garantiert nur, dass die Invarianten einer Klasse (von denen eine Unveränderlichkeit sein kann) nicht von einer Unterklasse geändert werden können.
Kevin Brock
1
@ Kevin: ja. Das Finale einer Klasse garantiert, dass es keine Unterklassen gibt. Hat nichts mit Unveränderlichkeit zu tun.
Thilo
4
Ein Klassenfinale zu machen, macht es an sich nicht unbeweglich. Ein unveränderliches Klassenfinale stellt jedoch sicher, dass niemand eine Unterklasse erstellt, die die Unveränderlichkeit bricht. Vielleicht waren die Leute, die sich mit Unveränderlichkeit befassten, nicht genau klar, was sie meinten, aber ihre Aussagen sind richtig, wenn sie im Kontext verstanden werden.
Jay
Vor einiger Zeit habe ich diese Antwort gelesen und dachte, das sei eine gute Antwort. Dann habe ich Hashcode & Equals aus 'The Effective Java' gelesen und festgestellt, dass dies eine sehr gute Antwort ist. Jeder braucht Erklärungen, ich empfehle ieam 8 & iteam 9 des gleichen Buches.
Abhishek Singh
6

Wie Bruno sagte, geht es um Unveränderlichkeit. Es geht nicht nur um Strings, sondern auch um Wrapper, z. B. Double, Integer, Character usw. Dafür gibt es viele Gründe:

  • Gewindesicherheit
  • Sicherheit
  • Heap, der von Java selbst verwaltet wird (anders als gewöhnlicher Heap, der auf unterschiedliche Weise Garbage Collected ist)
  • Speicherverwaltung

Grundsätzlich können Sie als Programmierer sicher sein, dass Ihre Zeichenfolge niemals geändert wird. Wenn Sie wissen, wie es funktioniert, kann es auch das Speichermanagement verbessern. Versuchen Sie, zwei identische Zeichenfolgen nacheinander zu erstellen, z. B. "Hallo". Wenn Sie debuggen, werden Sie feststellen, dass sie identische IDs haben, was bedeutet, dass sie genau DIE GLEICHEN Objekte sind. Dies liegt an der Tatsache, dass Sie es mit Java tun können. Dies wäre nicht möglich, wenn die Saiten muttable wären. Sie können das gleiche haben, was ich usw. hätte, weil sie sich nie ändern werden. Wenn Sie sich also jemals dazu entschließen, 1.000.000 Zeichenfolgen "Hallo" zu erstellen, würden Sie wirklich 1.000.000 Zeiger auf "Hallo" erstellen. Wenn Sie eine Funktion für eine Zeichenfolge oder Wrapper aus diesem Grund verwenden, wird auch ein anderes Objekt erstellt (sehen Sie sich erneut die Objekt-ID an - sie ändert sich).

Ein zusätzliches Finale in Java bedeutet nicht unbedingt , dass sich das Objekt nicht ändern kann (es unterscheidet sich beispielsweise von C ++). Dies bedeutet, dass sich die Adresse, auf die sie verweist, nicht ändern kann, Sie jedoch die Eigenschaften und / oder Attribute ändern können. Daher kann es in einigen Fällen sehr wichtig sein, den Unterschied zwischen Unveränderlichkeit und Endgültigkeit zu verstehen.

HTH

Verweise:

Artur
quelle
1
Ich glaube nicht, dass Strings auf einen anderen Heap gehen oder eine andere Speicherverwaltung verwenden. Sie sind sicherlich Müllsammler.
Thilo
2
Außerdem unterscheidet sich das endgültige Schlüsselwort für eine Klasse vollständig vom endgültigen Schlüsselwort für ein Feld.
Thilo
1
Okay, auf Suns JVM können intern () ed-Strings in die Dauerwelle gelangen, die nicht Teil des Heaps ist. Aber das passiert definitiv nicht für alle Strings oder alle JVM.
Thilo
2
Nicht alle Saiten gehen in diesen Bereich, nur die internierten Saiten. Das Internieren erfolgt automatisch für wörtliche Zeichenfolgen. (@Thilo, tippt, während ihr deinen Kommentar abgibt).
Kevin Brock
Vielen Dank für diese Antwort, es ist sehr nützlich. Wir haben jetzt zwei Fakten. Ein String ist eine Final-Klasse und unveränderlich, da er nicht geändert werden kann, sondern auf ein anderes Objekt verwiesen werden kann. aber was ist mit: - String a = neuer String ("test1"); dann ist s = "test2"; Wenn String ein Final Class-Objekt ist, wie kann es dann geändert werden? Wie kann ich ein modifiziertes Endobjekt verwenden? Bitte lassen Sie mich, wenn ich etwas falsch gefragt habe.
Suresh Sharma
2

Möglicherweise wurde die Implementierung vereinfacht. Wenn Sie eine Klasse entwerfen, die von Benutzern der Klasse vererbt werden kann, müssen Sie eine ganze Reihe neuer Anwendungsfälle in Ihr Design einbeziehen. Was passiert, wenn sie dies oder jenes mit X-geschütztem Feld tun? Wenn sie es endgültig machen, können sie sich darauf konzentrieren, dass die öffentliche Benutzeroberfläche ordnungsgemäß funktioniert, und sicherstellen, dass sie solide ist.

AaronLS
quelle
3
+1 für "Entwerfen für Vererbung ist schwer". Übrigens, das wird in Blochs "Effective Java" sehr gut erklärt.
Sleske
2

Mit vielen guten Punkten, die bereits erwähnt wurden, möchte ich noch einen hinzufügen. Einer der Gründe, warum String in Java unveränderlich ist, besteht darin, String zu erlauben , seinen Hashcode zwischenzuspeichern , und String in Java seinen Hashcode zwischenzuspeichern und nicht jeden zu berechnen Mal rufen wir die Hashcode-Methode von String auf , was es sehr schnell macht, als Hashmap-Schlüssel in Hashmap in Java verwendet zu werden.

Kurz gesagt, da String unveränderlich ist, kann niemand seinen Inhalt nach seiner Erstellung ändern, was garantiert, dass der Hashcode von String bei mehreren Aufrufen gleich ist.

Wenn Sie sehen, dass StringKlasse hat, wird als deklariert

/** Cache the hash code for the string */
private int hash; // Default to 0

und hashcode()Funktion ist wie folgt -

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

Wenn es sich bereits um einen Computer handelt, geben Sie einfach den Wert zurück.

Aniket Thakur
quelle
2

Um sicherzustellen, dass wir keine bessere Implementierung erhalten. Es sollte natürlich eine Schnittstelle gewesen sein.

Ah, ich bekomme mehr ahnungslose Abstimmungen. Die Antwort ist absolut ernst. Ich musste mich mehrmals um die blöde String-Implementierung herum programmieren, was zu erheblichen Leistungs- und Produktivitätsverlusten führte

Stephan Eggermont
quelle
2

Abgesehen von den offensichtlichen Gründen, die in anderen Antworten vorgeschlagen wurden, könnte ein Gedanke, die String-Klasse endgültig zu machen, auch mit dem Leistungsaufwand für virtuelle Methoden zusammenhängen. Denken Sie daran, dass String eine schwere Klasse ist. Dies bedeutet, dass dies keine Unterimplementierung bedeutet und dass kein Overhead für Indirektionsaufrufe erforderlich ist. Natürlich haben wir jetzt Dinge wie Virtual Invoke und andere, die diese Art der Optimierung immer für Sie durchführen.

Abhishek Singh
quelle
2

Zusätzlich zu den in anderen Antworten genannten Gründen (Sicherheit, Unveränderlichkeit, Leistung) sollte beachtet werden, dass Stringeine spezielle Sprachunterstützung vorliegt. Sie können StringLiterale schreiben und es gibt Unterstützung für die+ Operator wird unterstützt. Das Zulassen von Unterklassen für Programmierer Stringwürde Hacks fördern wie:

class MyComplex extends String { ... }

MyComplex a = new MyComplex("5+3i");
MyComplex b = new MyComplex("7+4i");
MyComplex c = new MyComplex(a + b);   // would work since a and b are strings,
                                      // and a string + a string is a string.
aioobe
quelle
1

Nun, ich habe einen anderen Gedanken, ich bin nicht sicher, ob ich richtig bin oder nicht, aber in Java ist String das einzige Objekt, das auch als primitiver Datentyp behandelt werden kann. Ich meine, wir können ein String-Objekt als String name = "java erstellen " . Wie bei anderen primitiven Datentypen, die nach Wert kopiert und nicht nach Referenz kopiert werden, wird erwartet, dass String dasselbe Verhalten aufweist, weshalb String endgültig ist. Das habe ich mir gedacht. Bitte ignorieren Sie, wenn es völlig unlogisch ist.

webdev
quelle
1
Meiner Meinung nach verhält sich String nie wie ein primitiver Typ. Ein String-Literal wie "java" ist eigentlich ein Objekt der String-Klasse (Sie können den Punktoperator unmittelbar nach dem schließenden Anführungszeichen verwenden). Wenn Sie einer Zeichenfolgenvariablen ein Literal zuweisen, werden Objektreferenzen wie gewohnt zugewiesen. Was anders ist, ist, dass die String-Klasse eine Unterstützung auf Sprachebene im Compiler integriert hat ... und Dinge in doppelte Anführungszeichen in String-Objekte und den Operator + umwandelt, wie oben erwähnt.
Georgie
1

Die Endgültigkeit der Saiten verteidigt sie auch als Standard. In C ++ können Sie Unterklassen von Zeichenfolgen erstellen, sodass jeder Programmiershop eine eigene Version von Zeichenfolgen haben kann. Dies würde zu einem Mangel an einem starken Standard führen.

ncmathsadist
quelle
1

Angenommen, Sie haben eine EmployeeKlasse mit einer Methode greet. Wenn die greetMethode aufgerufen wird, wird sie einfach gedruckt Hello everyone!. Das ist also das erwartete Verhalten der greetMethode

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

Lassen Sie nun die GrumpyEmployeeUnterklasse Employeeund überschreiben Sie die greetMethode wie unten gezeigt.

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

Schauen Sie sich nun im folgenden Code die sayHelloMethode an. Es nimmt die EmployeeInstanz als Parameter und ruft die greet-Methode auf, in der Hoffnung, dass sie sagen würde. Hello everyone!Aber was wir bekommen, ist Get lost!. Diese Verhaltensänderung ist auf zurückzuführenEmployee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

Diese Situation kann vermieden werden, wenn der EmployeeUnterricht gemacht wurde final. Jetzt liegt es an Ihrer Vorstellungskraft, wie viel Chaos ein frecher Programmierer verursachen könnte, wenn StringClass nicht als deklariert würde final.

Andy
quelle
0

Weiß JVM, was unveränderlich ist? Die Antwort lautet Nein. Der Konstantenpool enthält alle unveränderlichen Felder, aber alle unveränderlichen Felder / Objekte werden nicht nur im Konstantenpool gespeichert. Nur wir implementieren es so, dass es Unveränderlichkeit und seine Eigenschaften erreicht. CustomString könnte implementiert werden, ohne es mit MarkerInterface endgültig zu machen, was ein spezielles Java-Verhalten für das Pooling bieten würde. Die Funktion wird noch erwartet!

hi.nitish
quelle
0

Die meisten Antworten beziehen sich auf die Unveränderlichkeit - warum ein Objekt vom Typ String nicht an Ort und Stelle aktualisiert werden kann. Hier gibt es viele gute Diskussionen, und die Java-Community würde gut daran tun, Unveränderlichkeit als Prinzip zu übernehmen. (Ich halte nicht den Atem an.)

Die Frage des OP ist jedoch, warum es endgültig ist - warum es nicht erweitert werden kann. Einige hier haben dies übernommen, aber ich würde dem OP zustimmen, dass es hier eine echte Lücke gibt. In anderen Sprachen können Entwickler neue Nominaltypen für einen Typ erstellen. Zum Beispiel kann ich in Haskell die folgenden neuen Typen erstellen, die zur Laufzeit mit Text identisch sind, aber beim Kompilieren Bindungssicherheit bieten.

newtype AccountCode = AccountCode Text
newtype FundCode = FundCode Text

Daher würde ich den folgenden Vorschlag als Erweiterung der Java-Sprache vorschlagen:

newtype AccountCode of String;
newtype FundCode of String;

AccountCode acctCode = "099876";
FundCode fundCode = "099876";

acctCode.equals(fundCode);  // evaluates to false;
acctCode.toString().equals(fundCode.toString());  // evaluates to true;

acctCode=fundCode;  // compile error
getAccount(fundCode);  // compile error

(Oder vielleicht könnten wir anfangen, uns von Java zu trennen)

Schmied
quelle
-1

Wenn Sie eine Zeichenfolge einmal erstellen Es wird berücksichtigt, dass es sich um ein Objekt handelt, wenn Sie dies ändern möchten. Es ist nicht möglich, dass ein neues Objekt erstellt wird.

Suresh
quelle
Bitte klären Sie Ihre Antwort.
Marko Popovic