Je suis un développeur Java de longue date et enfin, après la majeure, j'ai le temps de l'étudier décemment afin de passer l'examen de certification ... Une chose qui m'a toujours dérangé, c'est que String est "final". Je le comprends quand une lecture sur les problèmes de sécurité et les choses connexes ... Mais, sérieusement, quelqu'un a-t-il un vrai exemple de cela?
Par exemple, que se passerait-il si String n'était pas définitif? Comme si ce n'était pas en Ruby. Je n'ai entendu aucune plainte de la part de la communauté Ruby ... Et je connais les StringUtils et les classes connexes que vous devez soit implémenter vous-même, soit rechercher sur le Web pour simplement implémenter ce comportement (4 lignes de code) vous suis disposé à.
java.io.File
n'était pas le casfinal
. Oh, ça ne l'est pas. Bugger.Réponses:
La raison principale est la vitesse: les
final
classes ne peuvent pas être étendues, ce qui a permis au JIT de faire toutes sortes d'optimisations lors de la gestion des chaînes - il n'est jamais nécessaire de vérifier les méthodes substituées.Une autre raison est la sécurité des threads: les Immutables sont toujours thread-safe car un thread doit les construire complètement avant de pouvoir être transmis à quelqu'un d'autre - et après la construction, ils ne peuvent plus être modifiés.
De plus, les inventeurs du runtime Java ont toujours voulu pécher par excès de sécurité. Pouvoir étendre String (quelque chose que je fais souvent dans Groovy parce que c'est si pratique) peut ouvrir toute une boîte de vers si vous ne savez pas ce que vous faites.
la source
final
que pour vérifier rapidement qu'une méthode n'est pas remplacée lors de la compilation du code.final
mot clé de la classe. Le tableau de caractères qu'il contient est final, ce qui le rend immuable. Rendre la classe elle-même finale ferme la boucle comme le mentionne @abl car une sous-classe mutable ne peut pas être introduite.Il y a une autre raison pour laquelle la
String
classe Java doit être définitive: elle est importante pour la sécurité dans certains scénarios. Si laString
classe n'était pas définitive, le code suivant serait vulnérable aux attaques subtiles d'un appelant malveillant:L'attaque: un appelant malveillant pourrait créer une sous-classe de String,
EvilString
oùEvilString.startsWith()
renvoie toujours true mais où la valeur deEvilString
est quelque chose de mal (par exemple,javascript:alert('xss')
). En raison du sous-classement, cela échapperait au contrôle de sécurité. Cette attaque est connue sous le nom de vulnérabilité TOCTTOU (time-of-check-to-time-of-use): entre le moment où le contrôle est effectué (que l'url commence par http / https) et le moment où la valeur est utilisée. (pour construire l'extrait html), la valeur effective peut changer. S'ilString
n'était pas définitif, les risques TOCTTOU seraient omniprésents.Donc, si ce
String
n'est pas définitif, il devient difficile d'écrire du code sécurisé si vous ne pouvez pas faire confiance à votre appelant. Bien sûr, c'est exactement la position dans laquelle se trouvent les bibliothèques Java: elles peuvent être invoquées par des applets non fiables, de sorte qu'elles n'osent pas faire confiance à leur appelant. Cela signifie qu'il serait déraisonnablement difficile d'écrire du code de bibliothèque sécurisé, s'ilString
n'était pas définitif.la source
char[]
intérieur de la chaîne, mais cela nécessite de la chance dans le timing.char[]
à l'intérieur de la chaîne; par conséquent, ils ne peuvent pas exploiter la vulnérabilité TOCTTOU.String
n'étaient pas définitives. Aussi longtemps que ce modèle de menace est pertinent, il devient important de se défendre contre. Tant qu'il est important pour les applets et autres codes non fiables, cela est sans doute suffisant pour justifier la finalisationString
, même s'il n'est pas nécessaire pour les applications de confiance. (Dans tous les cas, les applications de confiance peuvent déjà contourner toutes les mesures de sécurité, qu'elles soient finales ou non.)Sinon, les applications java multithread seraient un gâchis (encore pire que ce qui est réellement).
IMHO Le principal avantage des chaînes finales (immuables) est qu'elles sont intrinsèquement thread-safe: elles ne nécessitent aucune synchronisation (l'écriture de ce code est parfois assez triviale mais plus que moins éloignée de cela). Si le était mutable, garantir des choses comme les boîtes à outils Windows multithread serait très difficile, et ces choses sont strictement nécessaires.
la source
final
les classes n'ont rien à voir avec la mutabilité de la classe.Un autre avantage d'
String
être final est qu'il garantit des résultats prévisibles, tout comme l'immuabilité.String
, bien qu'une classe, est destinée à être traitée dans quelques scénarios, comme un type de valeur (le compilateur le prend même en charge viaString blah = "test"
). Par conséquent, si vous créez une chaîne avec une certaine valeur, vous vous attendez à ce qu'elle ait un certain comportement hérité de toute chaîne, semblable aux attentes que vous auriez avec des entiers.Réfléchissez à ceci: que se passe-t-il si vous sous-classe
java.lang.String
et surfait sur les méthodesequals
ouhashCode
? Soudain, deux «tests» de cordes ne pouvaient plus s'égaler.Imaginez le chaos si je pouvais faire ça:
une autre classe:
la source
Contexte
Il y a trois endroits où final peut apparaître en Java:
Faire une finale de classe empêche tout sous-classement de la classe. La finalisation d'une méthode empêche les sous-classes de la méthode de la remplacer. Rendre un champ final empêche qu'il ne soit modifié ultérieurement.
Idées fausses
Des optimisations se produisent autour des méthodes et des champs finaux.
Une dernière méthode facilite l'optimisation de HotSpot via l'inlining. Cependant, HotSpot le fait même si la méthode n'est pas définitive car elle fonctionne en supposant qu'elle n'a pas été remplacée jusqu'à preuve du contraire. En savoir plus sur SO
Une variable finale peut être optimisée de manière agressive, et plus à ce sujet peut être lue dans la section JLS 17.5.3 .
Cependant, avec cette compréhension, il faut savoir qu'aucune de ces optimisations ne concerne la finalisation d'une classe . Il n'y a aucun gain de performance en faisant une finale de classe.
L'aspect final d'une classe n'a rien à voir non plus avec l'immuabilité. On peut avoir une classe immuable (telle que BigInteger ) qui n'est pas finale, ou une classe qui est mutable et finale (telle que StringBuilder ). La décision de savoir si une classe doit être finale est une question de conception.
Conception finale
Les chaînes sont l'un des types de données les plus utilisés. Ils se trouvent comme clés de cartes, ils stockent des noms d'utilisateur et des mots de passe, ils sont ce que vous lisez à partir d'un clavier ou d'un champ sur une page Web. Les cordes sont partout .
Plans
La première chose à considérer de ce qui se passerait si vous pouviez sous-classer String est de réaliser que quelqu'un pourrait construire une classe String mutable qui semblerait autrement être une String. Cela gâcherait Maps partout.
Considérez ce code hypothétique:
C'est un problème avec l'utilisation d'une clé mutable en général avec une carte, mais ce que j'essaie de comprendre, c'est que soudainement un certain nombre de choses à propos de la rupture de la carte. L'entrée n'est plus au bon endroit sur la carte. Dans un HashMap, la valeur de hachage a (aurait dû) changé et donc ce n'est plus à la bonne entrée. Dans TreeMap, l'arborescence est maintenant cassée car l'un des nœuds est du mauvais côté.
Étant donné que l'utilisation d'une chaîne pour ces clés est si courante, ce comportement doit être évité en rendant la chaîne finale.
Vous pourriez être intéressé à lire Pourquoi String est-il immuable en Java? pour en savoir plus sur la nature immuable de Strings.
Cordes néfastes
Il existe un certain nombre d'options néfastes pour les chaînes. Considérez si j'ai fait une chaîne qui retournait toujours vrai quand égal était appelé ... et passé cela dans une vérification de mot de passe? Ou fait en sorte que les affectations à MyString envoient une copie de la chaîne à une adresse e-mail?
Ce sont des possibilités très réelles lorsque vous avez la possibilité de sous-classer String.
Optimisations de chaîne Java.lang
Alors qu'avant, j'ai mentionné que la finale ne rend pas la chaîne plus rapide. Cependant, la classe String (et les autres classes
java.lang
) utilise fréquemment la protection au niveau du package des champs et des méthodes pour permettre aux autresjava.lang
classes de pouvoir bricoler avec les internes plutôt que de passer par l'API publique pour String tout le temps. Fonctions comme getChars sans vérification de plage ou lastIndexOf qui est utilisé par StringBuffer ou le constructeur qui partage le tableau sous-jacent (notez que c'est une chose Java 6 qui a été modifiée en raison de problèmes de mémoire).Si quelqu'un créait une sous-classe de String, il ne serait pas en mesure de partager ces optimisations (sauf si cela en faisait partie
java.lang
aussi, mais c'est un package scellé ).Il est plus difficile de concevoir quelque chose pour l'extension
Concevoir quelque chose pour être extensible est difficile . Cela signifie que vous devez exposer des parties de vos composants internes pour que quelque chose d'autre puisse être modifié.
Une fuite de mémoire ne pouvait pas avoir sa fuite de mémoire fixée. Ces parties devraient avoir été exposées à des sous-classes et changer ce code signifierait alors que les sous-classes se briseraient.
Java se targue de la compatibilité descendante et en ouvrant les classes de base à l'extension, on perd une partie de cette capacité à réparer les choses tout en conservant la calculabilité avec les sous-classes tierces.
Checkstyle a une règle qu'il applique (qui me frustre vraiment lors de l'écriture de code interne) appelée "DesignForExtension" qui impose que chaque classe soit:
Le rationnel pour lequel est:
Autoriser l'extension des classes d'implémentation signifie que les sous-classes peuvent éventuellement corrompre l'état de la classe à partir de laquelle elles sont basées et faire en sorte que diverses garanties que la superclasse donne ne soient pas valides. Pour quelque chose d' aussi complexe que la chaîne, il est presque certain que le changement de partie se casser quelque chose.
Développeur hurbis
Sa part d'être développeur. Considérez la probabilité que chaque développeur crée sa propre sous-classe String avec sa propre collection d' utilitaires . Mais maintenant, ces sous-classes ne peuvent pas être librement attribuées les unes aux autres.
Cette voie mène à la folie. Casting à String partout et vérifier si la String est une instance de votre classe String ou si non, créer une nouvelle String basée sur celle-ci et ... juste, non. Non.
Je suis sûr que vous pouvez écrire une bonne classe String ... mais laisser l' écriture des implémentations de chaînes multiples à ces fous qui écrivent C ++ et doivent traiter
std::string
etchar*
et quelque chose de stimuler et sChaîne , et tout le reste .Java String Magic
Il y a plusieurs choses magiques que Java fait avec les chaînes. Ceux-ci facilitent la tâche d'un programmeur mais introduisent certaines incohérences dans le langage. Autoriser des sous-classes sur String nécessiterait une réflexion très importante sur la façon de gérer ces choses magiques:
Littéraux de chaîne (JLS 3.10.5 )
Avoir du code qui permet de faire:
Cela ne doit pas être confondu avec la boxe des types numériques comme Integer. Vous ne pouvez pas le faire
1.toString()
, mais vous pouvez le faire"foo".concat(bar)
.L'
+
opérateur (JLS 15.18.1 )Aucun autre type de référence en Java ne permet d'utiliser un opérateur dessus. La chaîne est spéciale. L'opérateur de concaténation de chaînes fonctionne également au niveau du compilateur de sorte que cela
"foo" + "bar"
se produit"foobar"
lors de la compilation plutôt qu'au moment de l'exécution.Conversion de chaînes (JLS 5.1.11 )
Tous les objets peuvent être convertis en chaînes simplement en les utilisant dans un contexte de chaîne.
Interning de chaînes ( JavaDoc )
La classe String a accès au pool de chaînes qui lui permet d'avoir des représentations canoniques de l'objet qui est rempli au type de compilation avec les littéraux String.
Autoriser une sous-classe de chaîne signifierait que ces bits avec la chaîne qui facilitent la programmation deviendraient très difficiles ou impossibles à faire lorsque d'autres types de chaînes sont possibles.
la source
Si String n'était pas définitif, chaque coffre à outils de programmeur contiendrait son propre "String avec juste quelques belles méthodes d'assistance". Chacun d'eux serait incompatible avec tous les autres coffres à outils.
J'ai considéré que c'était une restriction idiote. Aujourd'hui, je suis convaincu que cette décision a été très judicieuse. Il garde les cordes comme des cordes.
la source
final
en Java n'est pas la même chose qu'enfinal
C #. Dans ce contexte, c'est la même chose que C #readonly
. Voir stackoverflow.com/questions/1327544/…public final class String
.