Frage

Warum haben sie beschlossen, Zeichenfolgen in Java und .NET (und einigen anderen Sprachen) unveränderlich zu machen?Warum haben sie es nicht veränderbar gemacht?

War es hilfreich?

Lösung

Entsprechend Effektives Java, Kapitel 4, Seite 73, 2. Auflage:

„Dafür gibt es viele gute Gründe:Unveränderliche Klassen sind einfacher zu entwerfen, zu implementieren und zu verwenden als veränderliche Klassen.Sie sind weniger fehlerhaft und sicherer.

[...]

"Unveränderliche Objekte sind einfach. Ein unveränderliches Objekt kann in genau einen Zustand, den Zustand, in dem es geschaffen wurde.Wenn Sie sicherstellen, dass dass alle Konstruktoren Klassen-Invarianten etablieren, dann ist es garantiert, dass diese Invarianten für alle Zeiten wahr bleiben, mit Keine Anstrengung Ihrerseits.

[...]

Unveränderliche Objekte sind von Natur aus threadsicher;sie erfordern keine Synchronisierung. Sie können nicht durch mehrere Threads beschädigt werden Zugreifen auf sie gleichzeitig.Das ist bei weitem der einfachste Weg. zur Gewindesicherheit.Tatsächlich kann kein Thread jemals irgendwelche Wirkung eines anderen Threads auf ein unveränderliches Objekt.Daher, Unveränderliche Objekte können frei geteilt werden

[...]

Weitere kleine Punkte aus demselben Kapitel:

Sie können nicht nur unveränderliche Objekte teilen, sondern auch deren Interna.

[...]

Unveränderliche Objekte sind großartige Bausteine ​​für andere Objekte, egal ob veränderlich oder unveränderlich.

[...]

Der einzige wirkliche Nachteil unveränderlicher Klassen besteht darin, dass sie für jeden unterschiedlichen Wert ein separates Objekt erfordern.

Andere Tipps

Es gibt mindestens zwei Gründe.

First - Sicherheit http://www.javafaq.nu/ java-article1060.html

  

Der Hauptgrund, warum String gemacht   unveränderlich war die Sicherheit. Schau dir das an   Beispiel: Wir haben eine Datei zu öffnen Methode   mit Login-Check. Wir passieren eine String   Dieses Verfahren zur Verarbeitung Authentifizierung   was notwendig ist, vor dem Aufruf   wird auf O weitergegeben werden. Wenn String war   wandelbar es möglich war, irgendwie   ändern ihren Inhalt nach dem   Authentifizierungsprüfung vor OS bekommt   Anfrage von Programm dann ist es   möglich, eine beliebige Datei anfordern. Also, wenn   Sie haben ein Recht Textdatei zu öffnen, in   Benutzerverzeichnis, aber dann im Fluge   wenn es irgendwie schaffen Sie das ändern   Dateinamen können Sie verlangen, öffnen   „Passwd“ -Datei oder andere. Dann ein   Datei kann geändert werden, und es wird   möglich, direkt an das Betriebssystem anmelden.

Second - Speichereffizienz http://hikrish.blogspot.com/2006/07/why-string-class-is-immutable.html

  

JVM führt intern die „String   Pool“. Um die Speicher achive   Effizienz wird JVM verweist den String   Objekt vom Pool. Es wird nicht erstellen   die neuen String-Objekte. Also, wann immer   Sie erstellen eine neue Stringliteral, JVM   wird prüfen, ob es in dem Pool   bereits vorhanden ist oder nicht. wenn bereits   in dem Pool, gibt nur die   Verweis auf das gleiche Objekt oder erstellen   das neue Objekt im Pool. Es wird   sein weisen viele Verweise auf die gleiche   String-Objekte, wenn jemand ändert sich die   Wert, wirkt es umso   Verweise. So entschied Sonne zu machen,   unveränderlich.

Eigentlich sind die Gründe Zeichenfolge unveränderlich in Java nicht viel mit Sicherheit zu tun. Die zwei Hauptgründe sind die folgenden:

Thead Sicherheit:

Saiten sind extrem weit Objekttyp verwendet. Es ist daher mehr oder weniger garantiert in einer Multi-Threaded-Umgebung verwendet werden. Strings sind unveränderlich, um sicherzustellen, dass es sicher ist, Strings zwischen Threads zu teilen. eine unveränderlichen Saiten stellt sicher, dass, wenn Zeichenfolge aus Thread A zu einem anderen Thread B, Thread B kann nicht unerwartet ändern Zeichenfolge Thread A vorbei.

werden nicht nur helfen, die schon ziemlich komplizierte Aufgabe von Multi-Threaded-Programmierung zu vereinfachen, aber es hilft auch bei der Leistung von Multi-Threaded-Anwendungen. Der Zugriff auf veränderbare Objekte müssen irgendwie synchronisiert werden, wenn sie von mehreren Threads zugegriffen werden kann, um sicherzustellen, dass ein Thread versucht nicht, den Wert des Objekts zu lesen, während es von einem anderen Thread geändert wird. Die richtige Synchronisation ist sowohl hart als richtig für den Programmierer und teuer zur Laufzeit zu tun. Unveränderliche Objekte können nicht geändert werden und daher die Synchronisation nicht brauchen.

Performance:

Während String interning erwähnt wurde, ist es nur einen kleinen Gewinn in der Speichereffizienz für Java-Programme. Nur Stringliterale sind interniert. Das bedeutet, dass nur die Saiten, die gleich in der Quellcode sind wird die gleiche String-Objekt teilen. Wenn Ihr Programm dynamisch Zeichenfolge erstellt, die gleich sind, werden sie in verschiedenen Objekten dargestellt werden.

Noch wichtiger ist, unveränderlichen Strings ihnen erlauben, ihre internen Daten zu teilen. Für viele String-Operationen, bedeutet dies, dass die zugrunde liegende Array von Zeichen nicht kopiert werden muss. Zum Beispiel, sagen Sie die fünf ersten Zeichen von String zu nehmen. In Java, würden Sie ruft myString.substring (0,5). In diesem Fall tut, was die Methode substring () ist einfach ein neues String-Objekt zu erstellen, die myString zugrunde liegenden char [] teilt, aber wer weiß, dass es bei Index 0 beginnt und endet mit dem Index 5 dieses char []. Um in grafischer Form zu setzen diese, würden Sie mit dem folgenden Ende:

 |               myString                  |
 v                                         v
"The quick brown fox jumps over the lazy dog"   <-- shared char[]
 ^   ^
 |   |  myString.substring(0,5)

Das macht diese Art von Operationen extrem billig, und O (1), da die Operation weder abhängig von der Länge der ursprünglichen Zeichenfolge, noch auf der Länge des Teils wir extrahieren müssen. Dieses Verhalten hat auch einige Speicher Vorteile, da viele Strings können die zugrunde liegenden char [] teilen.

Themen Sicherheit und Leistung. Wenn eine Zeichenfolge nicht geändert werden kann, ist es sicher und schnell einen Verweis um unter mehreren Threads zu übergeben. Wenn Strings wandelbar waren, würden Sie immer alle Bytes des Strings auf eine neue Instanz kopieren müssen, oder die Synchronisierung bereitzustellen. Eine typische Anwendung wird eine Zeichenfolge 100 Mal für jedes Mal lesen, die Zeichenfolge geändert werden muss. Siehe Wikipedia auf Unveränderlichkeit .

Man sollte sich wirklich fragen: „Warum sollte X wandelbar sein?“ Es ist besser, Unveränderlichkeit auf Standard, wegen der Vorteile bereits von Princess Fluff . Es sollte eine Ausnahme sein, dass etwas wandelbar.

die meisten des aktuellen Programmiersprachen Standard Veränderlichkeit, aber hoffentlich in Zukunft leider der Standard mehr auf immutablity ist (siehe eine Wunschliste für den nächsten Mainstream Programming language ).

Ein Faktor ist, dass, wenn Strings wandelbar waren, Objekte Strings speichern müßten vorsichtig sein, zu speichern Kopien, damit ihre internen Datenänderung ohne vorherige Ankündigung. Da Strings eine ziemlich primitive Art wie Zahlen sind, ist es schön, wenn man sie behandeln, als ob sie von Wert übergeben wurden, auch wenn sie als Referenz übergeben werden (die auch auf Speicher zu sparen hilft).

Wow! Ich kann die Fehlinformation hier glauben. Strings unveränderlich zu sein hat nichts mit Sicherheit. Wenn jemand bereits Zugriff auf die Objekte in einer laufenden Anwendung hat (was angenommen werden müsste, wenn man gegen jemanden zu schützen versuchen, ‚Hacking‘ einen String in Ihrer App), würden sie sicherlich für Hacker eine viele andere Möglichkeiten zur Verfügung stehen.

Es ist eine ganz neue Idee, dass die Unveränderlichkeit des String adressiert Threadingprobleme. Hmmm ... Ich habe ein Objekt, das von zwei verschiedenen Threads geändert wird. Wie behebe ich das? synchronisieren Zugriff auf das Objekt? Naawww ... lassen Sie uns nicht zulassen, dass jemand das Objekt überhaupt ändern - das wird alle unsere chaotisch Concurrency Probleme beheben! In der Tat, lassen Sie uns alle unveränderliche Objekte machen, und dann können wir die synchonized Konstrukts aus der Java-Sprache entfernt.

Der wahre Grund (wies darauf hin, durch andere oben) ist die Speicheroptimierung. Es ist durchaus üblich, in jeder Anwendung für die gleiche Stringliteral wiederholt verwendet werden. Es ist so weit verbreitet, in der Tat, dass vor Jahrzehnten viele Compiler die Optimierung der Speicherung nur eine einzige Instanz eines Zeichenfolgenliterals gemacht. Der Nachteil dieser Optimierung ist, dass Laufzeitcode, die ein Zeichenfolgenliteral stellt ein Problem ändert, weil es die Instanz für alle anderen Codes zu modifizieren, dass es teilt. Zum Beispiel wäre es für eine Funktion irgendwo in einer Anwendung nicht gut, die Stringliteral „Hund“ auf „Katze“ zu ändern. Ein printf ( „Hund“) würde in „cat“ führt auf diesen geschrieben werden. Aus diesem Grund besteht der Schutz vor Code, um eine Art und Weise benötigt, die Zeichenfolge versucht Literale zu ändern (d. H., Machen sie unveränderlich). Einige Compiler (mit Unterstützung der OS) würden dies durch Stringliteral in ein spezielles Nur-Lese-Speichersegment platzieren, die einen Speicherfehler, wenn ein Schreibversuch verursachen würden gemacht wurde.

In Java wird dies als interning bekannt. Die Java-Compiler hier folgen nur eine Standard-Speicher-Optimierung von Compilern seit Jahrzehnten. Und das gleiche Problem dieser Stringliterale Adresse zur Laufzeit geändert werden, Java macht einfach die String-Klasse unveränderlich (i. E, gibt Ihnen keine Setter, die Sie erlauben würden, den String Inhalt zu ändern). Strings würde nicht unveränderlich sein, wenn der Stringliterale interning traten nicht auf.

String ist kein primitiver Typ, aber Sie wollen es normalerweise mit dem Wert Semantik verwenden, das heißt wie ein Wert.

Ein Wert ist etwas, das Sie vertrauen können nicht hinter dem Rücken ändern. Wenn Sie schreiben: String str = someExpr(); Sie wollen es nicht ändern, wenn Sie mit str etwas tun.

String als Objekt natürlich Semantik Zeiger hat, um Wertsemantik zu erhalten und braucht es unveränderlich zu sein.

Ich weiß, dass dies eine Beule, aber ... Sind sie wirklich unveränderlich? Betrachten Sie die folgende.

public static unsafe void MutableReplaceIndex(string s, char c, int i)
{
    fixed (char* ptr = s)
    {
        *((char*)(ptr + i)) = c;
    }
}

...

string s = "abc";
MutableReplaceIndex(s, '1', 0);
MutableReplaceIndex(s, '2', 1);
MutableReplaceIndex(s, '3', 2);
Console.WriteLine(s); // Prints 1 2 3

Sie könnten sogar eine Erweiterungsmethode machen.

public static class Extensions
{
    public static unsafe void MutableReplaceIndex(this string s, char c, int i)
    {
        fixed (char* ptr = s)
        {
            *((char*)(ptr + i)) = c;
        }
    }
}

Welche der folgenden Arbeit macht

s.MutableReplaceIndex('1', 0);
s.MutableReplaceIndex('2', 1);
s.MutableReplaceIndex('3', 2);

Fazit: Sie sind in einem unveränderlichen Zustand, der durch den Compiler bekannt ist. Von couse die oben gilt nur für .NET Strings als Java keine Zeiger haben. Jedoch kann eine Zeichenfolge vollständig wandelbar sein Zeiger in C #. Es ist nicht, wie Zeiger sollen verwendet werden, haben praktische Nutzung oder sicher verwendet werden; es ist jedoch möglich, so dass die ganze „wandelbar“ -Regel zu biegen. Sie können in der Regel nicht einen Index direkt eine Zeichenkette ändern, und dies ist der einzige Weg. Es gibt eine Möglichkeit, dass dies durch disallowing Zeiger Instanzen von Strings oder das Erstellen einer Kopie, wenn ein String ist, auf verhindert werden könnte, aber weder ist getan, die Strings in C # macht nicht völlig unveränderlich.

Für die meisten Zwecke ein "string" wird (verwendet / behandelt wie / Gedanke / angenommen werden), um eine sinnvolle Atomeinheit, wie eine Zahl .

fragen, warum die einzelnen Zeichen eines Strings nicht wandelbar sind, ist daher wie zu fragen, warum die einzelnen Bits einer ganzen Zahl sind nicht wandelbar.

Sie sollten wissen, warum. Man denke nur an sie.

Ich hasse es zu sagen, aber leider sind diskutieren wir das, weil unsere Sprache saugt, und wir versuchen, ein einziges Wort zu verwenden, string , zu beschreiben, ein Komplex, kontextuell gelegen Konzept oder Objektklasse.

Wir führen Berechnungen und Vergleiche mit „Strings“ ähnlich, wie wir mit Zahlen zu tun. Wenn Strings (oder ganze Zahlen) wandelbar waren, würden wir spezielle Code schreiben müssen, um ihre Werte in unveränderliche lokale Formen, um zu sperren jede Art von Berechnung zuverlässig auszuführen. Daher ist es am besten, eine Zeichenfolge wie eine numerische Kennung zu denken, aber statt des Seins 16, 32 oder 64 Bit lang, könnte es Hunderte von Bits lang sein.

Wenn jemand sagt „string“, wir alle denken, verschiedene Dinge. Diejenigen, die denken, es einfach als eine Reihe von Zeichen, ohne bestimmten Zweck im Auge, werden natürlich entsetzt, dass jemand nur entschieden , dass sie nicht in der Lage sein sollten, um diese Zeichen zu manipulieren. Aber die „string“ Klasse ist nicht nur ein Array von Zeichen. Es ist ein STRING, kein char[]. Es gibt einige grundlegende Annahmen über das Konzept, das wir als „String“ beziehen sich, und es kann in der Regel als sinnvoll, unteilbare Einheit von codierten Daten wie eine Reihe beschrieben. Wenn die Leute über „Manipulation von Strings“ sprechen, vielleicht sind sie wirklich über die Manipulation sprechen Zeichen bauen Strings , und ein String ist so groß. Man denke nur ein wenig über das, was das Wort „string“ wirklich bedeutet.

Betrachten wir für einen Moment, wie es sein würde, wenn Strings waren wandelbar. Die folgende API-Funktion in der Rückkehr Informationen für einen anderen Benutzer, wenn die wandelbar username Zeichenfolge absichtlich oder unabsichtlich von einem anderen Thread geändert, wenn diese Funktion es dazu verleitet werden könnte, wird mit:

string GetPersonalInfo( string username, string password )
{
    string stored_password = DBQuery.GetPasswordFor( username );
    if (password == stored_password)
    {
        //another thread modifies the mutable 'username' string
        return DBQuery.GetPersonalInfoFor( username );
    }
}

Sicherheit ist nicht nur über ‚Zugangskontrolle‘, es geht auch darum, ‚Sicherheit‘ und ‚Gewährleistung Korrektheit‘. Wenn eine Methode nicht einfach geschrieben werden kann und hing von einer einfachen Berechnung oder Vergleich zuverlässig auszuführen, dann sicher es ist, es nicht zu nennen, aber es wäre sicher nicht in Frage stellt die Programmiersprache selbst.

Unveränderlichkeit ist nicht so eng gebunden an Sicherheit. Dafür zumindest in .NET, erhalten Sie die Secure Klasse.

Es ist ein Trade-off. Strings gehen in den String-Pool und wenn Sie mehrere identische Strings erstellen sie teilen den gleichen Speicher. Die Designer rechneten diese Speichertechnik Spar gut für den gemeinsamen Fall funktionieren würde, da Programme sind in der Regel viel die gleichen Saiten schleifen über.

Der Nachteil ist, dass Verkettungen eine Menge extra Strings, die nur Übergänge sind und nur Müll werden, tatsächlich Gedächtnisleistung zu beeinträchtigen. Sie haben String und Stringbuilder (in Java, Stringbuilder auch in .NET ist) zu verwenden, um Speicher in diesen Fällen zu erhalten.

Die Entscheidung Zeichenfolge wandelbar in C ++ haben viele Probleme verursacht, finden Sie in diesem ausgezeichneten Artikel von Kelvin Henney über Mad Cow Disease .

COW = Kopieren auf Schreiben.

Strings in Java sind nicht wirklich unveränderlich, können Sie ihren Wert der mit Reflexion ändern und oder das Laden von Klassen. Sie sollten nicht auf diese Eigenschaft für Sicherheit in Abhängigkeit. Beispiele finden Sie unter: Zaubertrick In Java

Unveränderlichkeit ist gut. Siehe Effective Java. Wenn Sie jedes Mal eine Zeichenfolge zu kopieren hatte man es herumgereicht, dann wäre das eine Menge von fehleranfälligen Code sein. Sie haben auch Verwirrung darüber, welche Änderungen, die Referenzen beeinflussen. Auf die gleiche Art und Weise, die Integer sein unveränderlich hat wie int zu verhalten, haben Strings als unveränderlich zu verhalten sich wie Primitiven zu handeln. In C ++ von Wert Strings vorbei tut dies ohne ausdrückliche Erwähnung in dem Quellcode.

Es gibt eine Ausnahme für fast fast jede Regel:

using System;
using System.Runtime.InteropServices;

namespace Guess
{
    class Program
    {
        static void Main(string[] args)
        {
            const string str = "ABC";

            Console.WriteLine(str);
            Console.WriteLine(str.GetHashCode());

            var handle = GCHandle.Alloc(str, GCHandleType.Pinned);

            try
            {
                Marshal.WriteInt16(handle.AddrOfPinnedObject(), 4, 'Z');

                Console.WriteLine(str);
                Console.WriteLine(str.GetHashCode());
            }
            finally
            {
                handle.Free();
            }
        }
    }
}

Es ist weitgehend aus Sicherheitsgründen. Es ist viel schwieriger, ein System zu sichern, wenn Sie nicht vertrauen können, dass die Saiten manipulationssicher sind.

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