Perché la classe String è dichiarata definitiva in Java?


141

Da quando ho saputo che la classe java.lang.Stringè stata dichiarata come definitiva in Java, mi chiedevo perché. Allora non ho trovato alcuna risposta, ma questo post: Come creare una replica della classe String in Java? mi ha ricordato la mia domanda.

Certo, String offre tutte le funzionalità di cui abbia mai avuto bisogno, e non ho mai pensato a nessuna operazione che richiederebbe un'estensione della classe String, ma non saprai mai di cosa potrebbe aver bisogno qualcuno!

Quindi, qualcuno sa qual era l'intento dei designer quando hanno deciso di renderlo definitivo?


Grazie a tutti per le risposte, in particolare TrueWill, Bruno Reis e Thilo! Vorrei poter scegliere più di una risposta come la migliore, ma purtroppo ...!
Alex Ntousias,

1
Considera anche la proliferazione dei progetti "Oh, ho solo bisogno di qualche altro metodo di utilità su String" che si aprirà - tutti i quali non potrebbero usare le altre stringhe perché erano una classe diversa.
Thorbjørn Ravn Andersen,

Grazie per questa risposta è molto utile. abbiamo due fatti ora. Una stringa è una classe Final ed è immutabile perché non può essere modificata ma può essere riferita a un altro oggetto. ma che dire di: - String a = new String ("test1"); quindi, s = "test2"; Se String è un oggetto di classe Final, come può essere modificato? Come posso usare l'oggetto finale modificato. Per favore, fammi sapere se ho erroneamente chiesto qualcosa.
Suresh Sharma,

Puoi vedere questo buon articolo .
Aniket Thakur,

4
Una cosa che per fortuna abbiamo evitato in Java è che "ognuno ha la propria sottoclasse di String con molti metodi extra, e nessuno di questi è compatibile tra loro".
Thorbjørn Ravn Andersen,

Risposte:


88

È molto utile implementare le stringhe come oggetti immutabili . Dovresti leggere l' immutabilità per capirne di più.

Un vantaggio di oggetti immutabili è quello

Puoi condividere i duplicati indicandoli a una singola istanza.

(da qui ).

Se String non fosse definitivo, potresti creare una sottoclasse e avere due stringhe simili quando "viste come stringhe", ma che in realtà sono diverse.


70
A meno che non ci sia una connessione tra le classi finali e gli oggetti immutabili che non vedo, non vedo come la tua risposta si collega alla domanda.
sepp2k,

9
Perché se non è definitivo puoi passare un StringChild a un metodo come parametro String e potrebbe essere mutabile (perché cambia lo stato di una classe figlio).
helios,

4
Wow! Downvotes? Non capisci in che modo la sottoclasse si riferisce all'immutabilità? Gradirei una spiegazione su quale sia il problema.
Bruno Reis,

7
@Bruno, re: downvotes: non ti ho votato in basso, ma potresti aggiungere una frase su come prevenire le sottoclassi rafforzi l'immutabilità. In questo momento, è una specie di mezza risposta.
Thilo,

12
@BrunoReis - ha trovato un bell'articolo a cui potresti collegarti che ha un'intervista a James Gosling (creatore di Java) in cui parla brevemente di questo argomento qui . Ecco un frammento interessante: "Una delle cose che ha costretto le stringhe a essere immutabili è stata la sicurezza. Hai un metodo di apertura dei file. Gli passi una stringa. E poi sta facendo tutti i tipi di controlli di autenticazione prima che riesca a fare il sistema operativo Se riesci a fare qualcosa che ha effettivamente mutato la stringa, dopo il controllo di sicurezza e prima della chiamata del sistema operativo, poi boom, sei in ... "
Anurag

60

Questo è un bell'articolo che delinea due ragioni già menzionate nelle risposte sopra:

  1. Sicurezza : il sistema può distribuire parti sensibili di informazioni di sola lettura senza preoccuparsi che vengano modificate
  2. Prestazioni : i dati immutabili sono molto utili per rendere le cose sicure per i thread.

E questo è probabilmente il commento più dettagliato in quell'articolo. Ha a che fare con il pool di stringhe in Java e problemi di sicurezza. Riguarda come decidere cosa va nel pool di stringhe. Supponendo che entrambe le stringhe siano uguali se la loro sequenza di caratteri è la stessa, allora abbiamo una condizione di competizione su chi arriva prima e insieme ai problemi di sicurezza. In caso contrario, il pool di stringhe conterrà stringhe ridondanti, perdendo così il vantaggio di averlo in primo luogo. Leggilo per te, vero?


L'estensione della stringa avrebbe causato il caos con uguali e stagisti. JavaDoc afferma che è uguale a:

Confronta questa stringa con l'oggetto specificato. Il risultato è vero se e solo se l'argomento non è null ed è un oggetto String che rappresenta la stessa sequenza di caratteri di questo oggetto.

Supponendo che java.lang.Stringnon fosse definitivo, a SafeStringpotrebbe eguagliare a Stringe viceversa; perché rappresenterebbero la stessa sequenza di personaggi.

Cosa succederebbe se ti applicassi interna SafeString- andrebbe SafeStringnel pool di stringhe della JVM? L' ClassLoadere tutti gli oggetti i SafeStringriferimenti in attesa per gli aspiranti quindi ottenere bloccato in posizione per tutta la durata della JVM. Avresti una condizione di gara su chi potrebbe essere il primo a internare una sequenza di personaggi - forse SafeStringvinceresti, forse un String, o forse un SafeStringcarico da un caricatore di classi diverso (quindi una classe diversa).

Se vincessi la gara in piscina, questo sarebbe un vero singleton e le persone potrebbero accedere al tuo intero ambiente (sandbox) attraverso la riflessione e secretKey.intern().getClass().getClassLoader().

Oppure la JVM potrebbe bloccare questo buco assicurandosi che solo oggetti String concreti (e nessuna sottoclasse) siano stati aggiunti al pool.

Se equals è stato implementato in modo tale che SafeString! = StringThen SafeString.intern! = String.intern, E SafeStringdovrebbe essere aggiunto al pool. Il pool diventerebbe quindi un pool <Class, String>anziché anziché <String>e tutto ciò di cui avresti bisogno per entrare nel pool sarebbe un nuovo classloader.


2
Ovviamente il motivo delle prestazioni è un errore: se String fosse un'interfaccia sarei stato in grado di fornire un'implementazione che funziona meglio nella mia applicazione.
Stephan Eggermont,

28

Il motivo assolutamente più importante che String è immutabile o finale è che viene utilizzato dal meccanismo di caricamento della classe e quindi presenta aspetti di sicurezza profondi e fondamentali.

Se String fosse mutabile o non definitivo, una richiesta per caricare "java.io.Writer" avrebbe potuto essere modificata per caricare "mil.vogoon.DiskErasingWriter"

riferimento: Perché String è immutabile in Java


15

String è una classe fondamentale di Java, molte cose si basano sul fatto che funzionano in un certo modo, ad esempio essendo immutabili.

Creare la classe finalimpedisce sottoclassi che potrebbero infrangere questi presupposti.

Si noti che, anche ora, se si utilizza la riflessione, è possibile interrompere le stringhe (modificarne il valore o l'hashcode). La riflessione può essere fermata con un responsabile della sicurezza. In caso Stringcontrario final, tutti potrebbero farlo.

Altre classi non dichiarate finalti consentono di definire sottoclassi un po 'spezzate (potresti avere una Listche si aggiunge alla posizione sbagliata, per esempio) ma almeno la JVM non dipende da quelle per le sue operazioni principali.


6
finalsu una classe non garantisce l'immutabilità. Garantisce solo che gli invarianti di una classe (uno dei quali può essere l'immutabilità) non può essere modificato da una sottoclasse.
Kevin Brock,

1
@Kevin: si. La finale in una classe garantisce che non ci siano sottoclassi. Non ha nulla a che fare con l'immutabilità.
Thilo,

4
Fare un finale di classe non lo rende, di per sé, impossibile. Ma rendere finale una classe immutabile assicura che nessuno crei una sottoclasse che rompe l'immutabilità. Forse le persone che affermano l'immutabilità non erano chiare in cosa intendessero esattamente, ma le loro affermazioni sono corrette se comprese nel contesto.
Jay,

Alcune volte ho letto questa risposta e ho pensato che fosse una risposta okey, poi ho letto hashcode e uguale a "The Effective Java" e ho capito che era un'ottima risposta. Chiunque ha bisogno di spiegazioni, raccomando ieam 8 e iteam 9 dello stesso libro.
Abhishek Singh,

6

Come ha detto Bruno, si tratta di immutabilità. Non si tratta solo di stringhe ma anche di wrapper, ad esempio Double, Integer, Character, ecc. Ci sono molte ragioni per questo:

  • Sicurezza del filo
  • Sicurezza
  • Heap gestito da Java stesso (diversamente dall'heap ordinario che è Garbage Collected in modo diverso)
  • Gestione della memoria

Fondamentalmente così tu, come programmatore, puoi essere sicuro che la tua stringa non verrà mai cambiata. Inoltre, se sai come funziona, puoi migliorare la gestione della memoria. Prova a creare due stringhe identiche una dopo l'altra, ad esempio "ciao". Noterai, se esegui il debug, che hanno ID identici, ciò significa che sono esattamente gli STESSI oggetti. Ciò è dovuto al fatto che Java ti consente di farlo. Questo non sarebbe possibile se le corde fossero muttabili. Possono avere lo stesso io, ecc., Perché non cambieranno mai. Quindi, se mai decidessi di creare 1.000.000 di stringhe "ciao" quello che faresti davvero è creare 1.000.000 di puntatori a "ciao". Oltre ad assegnare qualsiasi funzione sulla stringa, o qualsiasi wrapper per quel motivo, si tradurrebbe nella creazione di un altro oggetto (guarda di nuovo l'ID oggetto - cambierà).

Adizionale finale in Java non significa necessariamente che l'oggetto non può cambiare (è diverso, ad esempio, dal C ++). Significa che l'indirizzo a cui punta non può cambiare, ma è comunque possibile modificarne le proprietà e / o gli attributi. Quindi comprendere la differenza tra immutabilità e finale in alcuni casi potrebbe essere davvero importante.

HTH

Riferimenti:


1
Non credo che le stringhe vadano su un heap diverso o utilizzino una diversa gestione della memoria. Sono sicuramente spazzatura da raccogliere.
Thilo,

2
Inoltre, la parola chiave finale di una classe è completamente diversa dalla parola chiave finale di un campo.
Thilo,

1
Ok, su JVM di Sun, le stringhe che sono internate () possono andare nel perm-gen, che non fa parte dell'heap. Ma questo sicuramente non accade per tutte le stringhe o per tutte le JVM.
Thilo,

2
Non tutte le stringhe vanno in quell'area, ma solo le stringhe che sono state internate. Lo interning è automatico per stringhe letterali. (@Thilo, digitando mentre hai inviato il tuo commento).
Kevin Brock,

Grazie per questa risposta è molto utile. abbiamo due fatti ora. Una stringa è una classe Final ed è immutabile perché non può essere modificata ma può essere riferita a un altro oggetto. ma che dire di: - String a = new String ("test1"); quindi, s = "test2"; Se String è un oggetto di classe Final, come può essere modificato? Come posso usare l'oggetto finale modificato. Per favore, fammi sapere se ho erroneamente chiesto qualcosa.
Suresh Sharma,

2

Potrebbe essere stato per semplificare l'implementazione. Se si progetta una classe che sarà ereditabile dagli utenti della classe, allora si dovrà prendere in considerazione un intero nuovo set di casi d'uso nel proprio progetto. Cosa succede se fanno questo o quello con il campo X previsto? Rendendolo definitivo, possono concentrarsi sul corretto funzionamento dell'interfaccia pubblica e assicurarsi che sia solido.


3
+1 per "progettare l'eredità è difficile". A proposito, questo è molto ben spiegato in "Effective Java" di Bloch.
sleske,

2

Con molti buoni punti già menzionati, vorrei aggiungere un altro motivo per cui String è immutabile in Java è consentire a String di memorizzare nella cache il suo hashcode , essendo immutabile String in Java memorizza nella cache il suo hashcode e non calcola tutti tempo che chiamiamo metodo hashcode di String , che lo rende molto veloce come chiave hashmap da utilizzare in hashmap in Java.

In breve, poiché String è immutabile, nessuno può cambiarne il contenuto una volta creato, il che garantisce che hashCode of String sia lo stesso su più invocazioni.

Se vedi la Stringclasse ha è dichiarato come

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

e la hashcode()funzione è la seguente -

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;
}

Se è già un computer, restituisci il valore.


2

Per essere sicuri che non otteniamo una migliore implementazione. Ovviamente avrebbe dovuto essere un'interfaccia.

[modifica] Ah, ottenendo più voti all'oscuro. La risposta è perfettamente seria. Ho dovuto programmare più volte la stupida implementazione di String, portando a gravi perdite di prestazioni e produttività


2

A parte le ovvie ragioni suggerite in altre risposte, un pensiero di rendere finale la classe String potrebbe anche essere correlato al sovraccarico delle prestazioni dei metodi virtuali. Ricorda che String è una classe pesante, rendendo questo finale, significa sicuramente nessuna sotto-implementazione, significa nessuna indiretta che chiama mai l'overhead. Ovviamente ora abbiamo cose come invoke virtuale e altre, che fanno sempre questo tipo di ottimizzazione per te.


2

Oltre ai motivi menzionati in altre risposte (sicurezza, immutabilità, prestazioni), va notato che Stringha un supporto linguistico speciale. Puoi scrivere Stringletterali e c'è supporto per l' +operatore. Consentire ai programmatori di effettuare una sottoclasse String, incoraggerebbe hack come:

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.

1

Bene, ho un pensiero diverso, non sono sicuro di essere corretto o no, ma in Java String è l'unico oggetto che può essere trattato come un tipo di dati primitivo e voglio dire che possiamo creare un oggetto String come String name = "java " . Ora come altri tipi di dati primitivi che sono copiati per valore e non copiati per riferimento, String dovrebbe avere lo stesso comportamento, ecco perché String è definitivo. Questo è quello che pensavo. Si prega di ignorare se è completamente illogico.


1
Secondo me String non si comporta mai come un tipo primitivo. Una stringa letterale come "java" è in realtà un oggetto della classe String (è possibile utilizzare l'operatore punto su di essa, immediatamente dopo la citazione di chiusura). Quindi assegnare un valore letterale a una variabile stringa significa semplicemente assegnare i riferimenti agli oggetti come al solito. Ciò che è diverso è che la classe String ha un supporto a livello di linguaggio integrato nel compilatore ... che trasforma le cose tra virgolette doppie in oggetti String e l'operatore + come menzionato sopra.
Georgie,

1

La finalità delle stringhe le difende anche come standard. In C ++ puoi creare sottoclassi di stringhe, così ogni negozio di programmazione potrebbe avere la sua versione di string. Ciò porterebbe alla mancanza di uno standard forte.


1

Diciamo che hai una Employeeclasse che ha un metodo greet. Quando greetviene chiamato il metodo, viene semplicemente stampato Hello everyone!. Questo è il comportamento atteso del greetmetodo

public class Employee {

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

Ora, lascia la GrumpyEmployeesottoclasse Employeee sostituisci il greetmetodo come mostrato di seguito.

public class GrumpyEmployee extends Employee {

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

Ora nel codice qui sotto dai un'occhiata al sayHellometodo. Prende l' Employeeistanza come parametro e chiama il metodo greet sperando che direbbe Hello everyone!Ma quello che otteniamo è Get lost!. Questo cambiamento nel comportamento è dovuto aEmployee 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!"
    }
}

Questa situazione può essere evitata se la Employeeclasse è stata creata final. Ora dipende dalla tua immaginazione la quantità di caos che un programmatore sfacciato potrebbe causare se StringClass non fosse dichiarata come final.


0

JVM sa cosa è immutabile? La risposta è No, il pool costante contiene tutti i campi immutabili ma tutti i campi / oggetti immutabili non sono memorizzati solo nel pool costante. Solo lo implementiamo in modo tale da ottenere l'immutabilità e le sue caratteristiche. CustomString potrebbe essere implementato senza renderlo definitivo utilizzando MarkerInterface che fornirebbe un comportamento speciale di Java per il suo pool, la funzionalità è ancora attesa!


0

La maggior parte delle risposte è legata all'immutabilità, perché un oggetto di tipo String non può essere aggiornato sul posto. Ci sono molte buone discussioni qui e la comunità Java farebbe bene ad adottare l'immutabilità come preside. (Non trattengo il respiro.)

Tuttavia, la domanda del PO riguarda perché è definitiva - perché non può essere estesa. Alcuni qui lo hanno accettato, ma concordo con l'OP che qui c'è un vero divario. Altre lingue consentono agli sviluppatori di creare nuovi tipi nominali per un tipo. Ad esempio in Haskell posso creare i seguenti nuovi tipi che sono identici in fase di esecuzione come testo, ma forniscono sicurezza di bind in fase di compilazione.

newtype AccountCode = AccountCode Text
newtype FundCode = FundCode Text

Vorrei quindi presentare il seguente suggerimento come miglioramento del linguaggio Java:

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

(O forse potremmo iniziare a sbarazzarci di Java)


-1

Se crei una stringa una volta, lo considererà, è un oggetto se vuoi modificarlo, non è possibile, creerà un nuovo oggetto.


Per favore chiarisci la tua risposta.
Marko Popovic,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.