Pourquoi la classe String est-elle déclarée finale en Java?

141

À partir du moment où j'ai appris que la classe java.lang.Stringest déclarée comme finale en Java, je me demandais pourquoi. Je n'ai trouvé aucune réponse à l'époque, mais cet article: Comment créer une réplique de la classe String en Java?m'a rappelé ma question.

Bien sûr, String fournit toutes les fonctionnalités dont j'ai jamais eu besoin, et je n'ai jamais pensé à une opération qui nécessiterait une extension de la classe String, mais vous ne saurez toujours jamais ce dont quelqu'un pourrait avoir besoin!

Alors, est-ce que quelqu'un sait quelle était l'intention des concepteurs lorsqu'ils ont décidé de le rendre définitif?

Alex Ntousias
la source
Merci à tous pour vos réponses, en particulier TrueWill, Bruno Reis et Thilo! J'aimerais pouvoir choisir plus d'une réponse comme la meilleure, mais malheureusement ...!
Alex Ntousias
1
Considérez également la prolifération des projets "Oh, j'ai juste besoin de quelques méthodes utilitaires de plus sur String" qui apparaissaient - qui ne pouvaient tous utiliser les chaînes les unes des autres parce qu'elles étaient une classe différente.
Thorbjørn Ravn Andersen
Merci pour cette réponse, c'est très utile. nous avons deux faits maintenant. Une chaîne est une classe finale et est immuable car elle ne peut pas être modifiée mais peut être référée à un autre objet. mais qu'en est-il: - String a = new String ("test1"); alors, s = "test2"; Si String est un objet de classe final, comment peut-il être modifié? Comment puis-je utiliser un objet final modifié. S'il vous plaît laissez-moi si j'ai demandé quelque chose à tort.
Suresh Sharma
Vous pouvez consulter ce bon article .
Aniket Thakur
4
Une chose que nous avons heureusement évitée en Java est que "tout le monde a sa propre sous-classe de String avec beaucoup de méthodes supplémentaires, et aucune de celles-ci n'est compatible les unes avec les autres".
Thorbjørn Ravn Andersen

Réponses:

88

Il est très utile d'avoir des chaînes implémentées en tant qu'objets immuables . Vous devriez lire sur l' immuabilité pour en savoir plus.

L'un des avantages des objets immuables est que

Vous pouvez partager des doublons en les pointant vers une seule instance.

(d' ici ).

Si String n'était pas final, vous pourriez créer une sous-classe et avoir deux chaînes qui se ressemblent lorsqu'elles sont "vues comme des chaînes", mais qui sont en fait différentes.

Bruno Reis
la source
70
À moins qu'il y ait un lien entre les classes finales et les objets immuables que je ne vois pas, je ne vois pas comment votre réponse se rapporte à la question.
sepp2k
9
Parce que s'il n'est pas définitif, vous pouvez passer un StringChild à une méthode en tant que paramètre String, et il pourrait être mutable (car un changement d'état de classe enfant).
helios
4
Hou la la! Des votes négatifs? Ne comprenez-vous pas comment le sous-classement se rapporte à l'immuabilité? J'apprécierais une explication sur quel est le problème.
Bruno Reis
7
@Bruno, re: downvotes: Je ne vous ai pas voté contre, mais vous pouvez ajouter une phrase expliquant comment la prévention des sous-classes renforce l'immuabilité. À l'heure actuelle, c'est une sorte de demi-réponse.
Thilo
12
@BrunoReis - a trouvé un bel article vers lequel vous pourriez créer un lien et qui contient une interview avec James Gosling (créateur de Java) où il parle brièvement de ce sujet ici . Voici un extrait intéressant: "L'une des choses qui a forcé Strings à être immuable était la sécurité. Vous avez une méthode d'ouverture de fichier. Vous lui transmettez une chaîne. Et puis il effectue toutes sortes de vérifications d'authentification avant de commencer à faire le système d'exploitation. Si vous parvenez à faire quelque chose qui a muté efficacement la chaîne, après le contrôle de sécurité et avant l'appel du système d'exploitation, alors boum, vous êtes ... "
Anurag
60

C'est un bel article qui décrit deux raisons déjà mentionnées sur les réponses ci-dessus:

  1. Sécurité : le système peut distribuer des bits sensibles d'informations en lecture seule sans se soucier de leur altération
  2. Performances : les données immuables sont très utiles pour rendre les choses thread-safe.

Et c'est probablement le commentaire le plus détaillé de cet article. Cela a à voir avec le pool de chaînes en Java et les problèmes de sécurité. Il s'agit de savoir comment décider de ce qui entre dans le pool de cordes. En supposant que les deux chaînes sont égales si leur séquence de caractères est la même, alors nous avons une condition de concurrence sur qui y arrive en premier et avec des problèmes de sécurité. Sinon, le pool de chaînes contiendra des chaînes redondantes, perdant ainsi l'avantage de l'avoir en premier lieu. Lisez-le par vous-même, d'accord?


L'extension de String ferait des ravages avec les égaux et les stagiaires. JavaDoc dit égal:

Compare cette chaîne à l'objet spécifié. Le résultat est vrai si et seulement si l'argument n'est pas nul et est un objet String qui représente la même séquence de caractères que cet objet.

En supposant que ce java.lang.Stringn'était pas définitif, a SafeStringpourrait égaler a String, et vice versa; parce qu'ils représenteraient la même séquence de caractères.

Que se passerait-il si vous postuliez internà un SafeString- irait SafeString-il dans le pool de chaînes de la JVM? Les ClassLoaderobjets et tous les objets auxquels les SafeStringréférences sont conservées seraient alors verrouillés pendant toute la durée de vie de la machine virtuelle Java. Vous obtiendrez une condition de concurrence sur qui pourrait être le premier à interner une séquence de personnages - peut-être que vous SafeStringgagneriez, peut-être un String, ou peut-être un SafeStringchargé par un chargeur de classe différent (donc une classe différente).

Si vous gagniez la course dans la piscine, ce serait un vrai singleton et les gens pourraient accéder à l'ensemble de votre environnement (bac à sable) par réflexion et secretKey.intern().getClass().getClassLoader() .

Ou la JVM pourrait bloquer ce trou en s'assurant que seuls des objets String concrets (et aucune sous-classe) étaient ajoutés au pool.

Si égal a été implémenté de telle sorte que SafeString! = StringAlors SafeString.intern! = String.intern, Et SafeStringdevrait être ajouté au pool. La piscine deviendrait alors une piscine <Class, String>au lieu de<String> et tout ce dont vous auriez besoin pour entrer dans le pool serait un nouveau chargeur de classe.

Anurag
la source
2
Bien sûr, la raison des performances est une erreur: si String avait été une interface, j'aurais été en mesure de fournir une implémentation qui fonctionne mieux dans mon application.
Stephan Eggermont
28

La raison absolument la plus importante pour laquelle String est immuable ou définitif est qu'il est utilisé par le mécanisme de chargement de classe, et a donc des aspects de sécurité profonds et fondamentaux.

Si String avait été mutable ou non définitif, une demande de chargement de "java.io.Writer" aurait pu être modifiée pour charger "mil.vogoon.DiskErasingWriter"

reference: Pourquoi String est immuable en Java

Jackob
la source
15

String est une classe très fondamentale en Java, beaucoup de choses dépendent de son fonctionnement d'une certaine manière, par exemple étant immuable.

Faire la classe final empêche les sous-classes qui pourraient casser ces hypothèses.

Notez que, même maintenant, si vous utilisez la réflexion, vous pouvez casser les chaînes (modifier leur valeur ou leur hashcode). La réflexion peut être arrêtée avec un gestionnaire de sécurité. Si ce Stringn'était pasfinal , tout le monde pouvait le faire.

D'autres classes qui ne sont pas déclarées finalvous permettent de définir des sous-classes quelque peu cassées (vous pourriez avoir un Listqui s'ajoute à la mauvaise position, par exemple) mais au moins la JVM ne dépend pas de celles-ci pour ses opérations de base.

Thilo
la source
6
finalsur une classe ne garantit pas l'immuabilité. Cela garantit simplement que les invariants d'une classe (dont l'un peut être l'immuabilité) ne peuvent pas être modifiés par une sous-classe.
Kevin Brock
1
@Kevin: oui. Final sur une classe garantit qu'il n'y a pas de sous-classes. N'a rien à voir avec l'immuabilité.
Thilo
4
Rendre un cours final ne le rend pas en soi immuable. Mais rendre une classe immuable définitive garantit que personne ne crée une sous-classe qui rompt l'immuabilité. Peut-être que les gens qui parlent de l'immuabilité ne savaient pas exactement ce qu'ils voulaient dire, mais leurs déclarations sont correctes lorsqu'elles sont comprises dans leur contexte.
Jay
Il y a quelques temps, j'ai lu cette réponse et j'ai pensé que c'était une bonne réponse, puis j'ai lu hashcode & equals de «The Effective Java» et j'ai réalisé que c'était une très bonne réponse. Tout le monde a besoin d'explications, je recommande ieam 8 et iteam 9 du même livre.
Abhishek Singh
6

Comme l'a dit Bruno, il s'agit d'immuabilité. Il ne s'agit pas seulement de chaînes de caractères, mais aussi de tout wrapper, par exemple Double, Integer, Character, etc. Il y a plusieurs raisons à cela:

  • Sécurité du fil
  • Sécurité
  • Heap qui est géré par Java lui-même (différemment du tas ordinaire qui est Garbage Collected de manière différente)
  • Gestion de la mémoire

En gros, cela vous permet, en tant que programmeur, d'être sûr que votre chaîne ne sera jamais modifiée. Cela aussi, si vous savez comment cela fonctionne, peut améliorer la gestion de la mémoire. Essayez de créer deux chaînes identiques l'une après l'autre, par exemple "bonjour". Vous remarquerez, si vous déboguez, qu'ils ont des ID identiques, cela signifie qu'ils sont exactement LES MÊMES objets. Cela est dû au fait que Java vous permet de le faire. Ce ne serait pas possible si les chaînes étaient muttables. Ils peuvent avoir la même chose que moi, etc., car ils ne changeront jamais. Donc, si jamais vous décidez de créer 1 000 000 chaînes "bonjour", ce que vous feriez vraiment est de créer 1 000 000 pointeurs vers "bonjour". De plus, allouer n'importe quelle fonction sur une chaîne, ou n'importe quel wrapper pour cette raison, entraînerait la création d'un autre objet (regardez à nouveau l'ID de l'objet - cela changera).

De plus, la finale en Java n'est pas nécessairement signifie que l'objet ne peut pas changer (il est différent par exemple de C ++). Cela signifie que l'adresse vers laquelle il pointe ne peut pas changer, mais vous pouvez toujours modifier ses propriétés et / ou ses attributs. Donc, comprendre la différence entre l'immuabilité et le final dans certains cas peut être vraiment important.

HTH

Références:

Artur
la source
1
Je ne pense pas que les chaînes passent à un autre tas ou utilisent une gestion de la mémoire différente. Ils peuvent certainement être ramassés.
Thilo
2
De plus, le mot-clé final d'une classe est complètement différent du mot-clé final d'un champ.
Thilo
1
D'accord, sur la JVM de Sun, les chaînes intern () peuvent entrer dans le perm-gen, qui ne fait pas partie du tas. Mais cela ne se produit certainement pas pour toutes les chaînes ou toutes les JVM.
Thilo
2
Toutes les chaînes ne vont pas dans cette zone, juste les chaînes qui ont été internées. L'interne est automatique pour les chaînes littérales. (@Thilo, en tapant lorsque vous avez soumis votre commentaire).
Kevin Brock
Merci pour cette réponse, c'est très utile. nous avons deux faits maintenant. Une chaîne est une classe finale et est immuable car elle ne peut pas être modifiée mais peut être référée à un autre objet. mais qu'en est-il: - String a = new String ("test1"); alors, s = "test2"; Si String est un objet de classe final, comment peut-il être modifié? Comment puis-je utiliser un objet final modifié. S'il vous plaît laissez-moi si j'ai demandé quelque chose à tort.
Suresh Sharma
2

C'était peut-être pour simplifier la mise en œuvre. Si vous concevez une classe qui sera héritable par les utilisateurs de la classe, vous avez alors un tout nouvel ensemble de cas d'utilisation à prendre en compte dans votre conception. Que se passe-t-il s'ils font ceci ou cela avec le champ X protégé? En le rendant définitif, ils peuvent se concentrer sur le bon fonctionnement de l'interface publique et s'assurer qu'elle est solide.

AaronLS
la source
3
+1 pour "concevoir pour l'héritage est difficile". BTW, c'est très bien expliqué dans "Effective Java" de Bloch.
sleske
2

Avec beaucoup de bons points déjà mentionnés, j'aimerais en ajouter un autre - l'une des raisons pour lesquelles String est immuable en Java est de permettre à String de mettre en cache son hashcode , étant immuable String en Java met en cache son hashcode, et ne calcule pas tous heure à laquelle nous appelons la méthode hashcode de String , ce qui la rend très rapide en tant que clé hashmap à utiliser dans hashmap en Java.

En bref, étant donné que String est immuable, personne ne peut modifier son contenu une fois créé, ce qui garantit que le hashCode de String sera le même lors d'appels multiples.

Si vous voyez que la Stringclasse a est déclarée comme

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

et la hashcode()fonction est la suivante -

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

S'il s'agit déjà d'un ordinateur, renvoyez simplement la valeur.

Aniket Thakur
la source
2

Pour nous assurer que nous n'obtenons pas une meilleure mise en œuvre. Cela aurait bien sûr dû être une interface.

[modifier] Ah, obtenir des votes plus ignorants. La réponse est parfaitement sérieuse. J'ai dû programmer plusieurs fois mon chemin autour de l'implémentation stupide de String, entraînant une perte de performances et de productivité sévère

Stéphan Eggermont
la source
2

Outre les raisons évidentes suggérées dans d'autres réponses, l'idée de rendre la classe String finale pourrait également être liée à la surcharge de performance des méthodes virtuelles. N'oubliez pas que String est une classe lourde, ce qui rend cette finale, signifie pas de sous-implémentation à coup sûr, signifie pas de surcharge d'appel d'indirection. Bien sûr, nous avons maintenant des choses comme l'invocation virtuelle et d'autres, qui font toujours ce type d'optimisation pour vous.

Abhishek Singh
la source
2

En plus des raisons mentionnées dans d'autres réponses (sécurité, immuabilité, performances), il convient de noter qu'il Stringdispose d'un support linguistique spécial. Vous pouvez écrire des Stringlittéraux et il existe un support pour l' +opérateur. Permettre aux programmeurs de se sous String-classer encouragerait les hacks tels que:

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
la source
1

Eh bien, j'ai une pensée différente, je ne sais pas si j'ai raison ou non, mais en Java, la chaîne est le seul objet qui peut être traité comme un type de données primitif, je veux dire que nous pouvons créer un objet String comme String name = "java " . Maintenant, comme d'autres types de données primitifs qui sont copiés par valeur et non par référence, String devrait avoir le même comportement, c'est pourquoi String est final. C'est ce que je pensais. Veuillez ignorer si c'est complètement illogique.

webdev
la source
1
À mon avis, String ne se comporte jamais comme un type primitif. Une chaîne littérale comme "java" est en fait un objet de la classe String (vous pouvez utiliser l'opérateur point dessus, immédiatement après le guillemet fermant). Ainsi, attribuer un littéral à une variable de chaîne consiste simplement à affecter des références d'objet comme d'habitude. Ce qui est différent, c'est que la classe String a une prise en charge au niveau du langage intégrée au compilateur ... transformant les choses entre guillemets en objets String, et l'opérateur + comme mentionné ci-dessus.
Georgie
1

La finalité des cordes les défend également comme un standard. En C ++, vous pouvez créer des sous-classes de string, de sorte que chaque boutique de programmation puisse avoir sa propre version de string. Cela conduirait à un manque de norme solide.

ncmathsadist
la source
1

Disons que vous avez une Employeeclasse qui a une méthode greet. Lorsque la greetméthode est appelée, elle s'imprime simplement Hello everyone!. Voilà donc le comportement attendu de la greetméthode

public class Employee {

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

Maintenant, laissez la GrumpyEmployeesous - classe Employeeet la greetméthode de substitution comme indiqué ci-dessous.

public class GrumpyEmployee extends Employee {

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

Maintenant, dans le code ci-dessous, jetez un œil à la sayHellométhode. Il prend une Employeeinstance comme paramètre et appelle la méthode greet en espérant qu'il dirait Hello everyone!Mais ce que nous obtenons est Get lost!. Ce changement de comportement est dû àEmployee 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!"
    }
}

Cette situation peut être évitée si le Employeecours a été fait final. Maintenant, c'est à votre imagination la quantité de chaos qu'un programmeur effronté pourrait causer si StringClass n'était pas déclaré comme final.

Andy
la source
0

JVM sait-il ce qui est immuable? La réponse est non, le pool de constantes contient tous les champs immuables mais tous les champs / objets immuables ne sont pas stockés uniquement dans le pool de constantes. Seulement, nous l'implémentons de manière à atteindre l'immuabilité et ses caractéristiques. CustomString pourrait être implémenté sans le rendre définitif en utilisant MarkerInterface qui fournirait un comportement spécial java pour son pooling, la fonctionnalité est toujours attendue!

salut.nitish
la source
0

La plupart des réponses sont liées à l'immuabilité - pourquoi un objet de type String ne peut pas être mis à jour sur place. Il y a beaucoup de bonnes discussions ici, et la communauté Java ferait bien d'adopter l'immuabilité comme principe. (Ne retenant pas mon souffle.)

Cependant, la question du PO est de savoir pourquoi il est définitif - pourquoi ne peut-il pas être prolongé. Certains ici l'ont fait, mais je suis d'accord avec le PO pour dire qu'il y a un réel écart ici. Un autre langage permet aux développeurs de créer de nouveaux types nominaux pour un type. Par exemple, dans Haskell, je peux créer les nouveaux types suivants qui sont identiques au moment de l'exécution en tant que texte, mais fournissent la sécurité de liaison au moment de la compilation.

newtype AccountCode = AccountCode Text
newtype FundCode = FundCode Text

Je proposerais donc la suggestion suivante comme une amélioration du langage 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

(Ou peut-être pourrions-nous commencer à nous déconnecter de Java)

dsmith
la source
-1

Si vous créez une chaîne une seule fois, il considérera que, c'est un objet si vous voulez modifier cela, ce n'est pas possible, cela créera un nouvel objet.

Suresh
la source
Veuillez clarifier votre réponse.
Marko Popovic