J'ai fait face à cette question dans une interview. J'ai expliqué que String
est immuable et StringBuffer
est une classe mutable. Je ne sais pas grand-chose sur mutable et immuable et je ne connais pas non plus la réponse exacte. Quels sont les concepts clés pour pouvoir distinguer les deux idées? Y a-t-il un impact sur les performances lors du choix l'un par rapport à l'autre?
Une classe immuable est immuable, comme dans, elle n'est pas modifiable. Vous ne pouvez pas le changer. Si vous avez x :: String, vous pouvez être absolument certain à 100% que x ne changera jamais (ou du moins, si c'est le cas, les gens abusent de la flexibilité de votre langue et vous avez le droit de leur dire où aller ).
Une classe mutable n'est pas immuable. Vous pouvez le changer, d'autres personnes peuvent le changer, et vous ne pouvez pas compter sur le fait qu'il soit le même. Il pourrait être changé dans un autre thread, vous ne pouvez même pas être sûr qu'il soit dans le même état d'une ligne à l'autre, sauf si vous avez un bon verrouillage.
La capacité de modifier l'état de l'objet est le concept clé, mais l'histoire ne s'arrête pas là. Vous voudrez peut-être choisir une classe immuable dans un environnement threadé, car maintenant vous n'avez plus à vous soucier du verrouillage, car personne ne peut y écrire de toute façon. Vous voudrez peut-être choisir des objets immuables dans un grand système, car vous ne pouvez pas être sûr que personne d'autre n'a une poignée sur ce paramètre d'entrée et ne le modifiera pas sous vous la première fois qu'il le pourra. Vous voudrez peut-être choisir des objets immuables car ils permettent de raisonner sur le comportement de votre code en petites unités - comme dans, si tout ce qui concerne une fonction repose sur des données immuables, vous pouvez regarder cette fonction isolément, c'est garanti.
Il y a des impacts sur les performances, mais ce n'est pas à sens unique. Les objets immuables permettent de nombreuses optimisations que vous ne pouvez tout simplement pas faire sur des données modifiables, comme le partage agressif de la mémoire (car bon, il ne peut pas changer) ou l'incrustation plus agressive. Les données mutables ont tendance à augmenter les performances lorsque vous devez apporter de nombreuses modifications aux blocs de mémoire.
Si vous êtes intéressé à voir quelque part où l'immuabilité brille vraiment, jetez un œil à un langage comme Haskell, qui a été conçu à partir de zéro pour l'immuabilité, permettant un support au niveau de la langue pour des choses comme la paresse et beaucoup d'optimisation.
Pour définir correctement les classes mutables et immuables, il faut d'abord définir ce que signifie qu'un objet instance est mutable. Une instance d'objet est modifiable s'il existe des moyens (*) via lesquels l'état encapsulé pourrait être modifié. Une instance d'objet est immuable s'il est impossible de modifier son état. Une classe est immuable si toutes les instances de cette classe sont garanties immuables, quelle que soit la personne qui pourrait y faire référence.
(*) Il existe certaines approches comme Reflection où le code malveillant pourrait modifier à peu près n'importe quoi; la plupart des discussions sur l'immuabilité se concentrent sur les choses que le code peut faire "légitimement", et considèrent ces astuces de réflexion comme illégitimes. En dehors des applications de sécurité, l'objectif normal est de faire fonctionner correctement les objets pour les clients qui se comportent bien; si une classe se comporte bizarrement lorsque les clients font des choses illégitimes, c'est le problème des clients.
De nombreuses discussions sur l'immuabilité ignorent le fait que si toutes les instances d'une classe immuable sont immuables, les instances d'une classe mutable peuvent également être immuables. Certaines discussions sur la mutabilité suggèrent que pour qu'une classe soit "réellement" immuable, tous ses domaines doivent également être des types immuables. Non seulement ce n'est pas vrai, mais dans de nombreux cas, cela représenterait un objectif presque impossible étant donné l'absence de types de tableaux immuables. Ce qui est nécessaire, c'est qu'un objet immuable doit toujours être certain qu'aucune référence à un objet mutable dans lequel il se trouve ne sera jamais exposée à un code qui pourrait le muter. Tout ce que le code extérieur peut légitimement vouloir faire avec l'objet encapsulé doit être fait par des méthodes implémentées par ou au nom de l'objet contenant immuable.
Il serait utile que .NET ou Java dispose de moyens déclaratifs pour spécifier que certains objets, bien qu'ils soient de type mutable, ne doivent jamais être modifiés. Malheureusement, aucune fonctionnalité de ce type n'existe. Malgré cela, un problème majeur La clé pour écrire du code correct et efficace est de savoir quelles références identifient des choses qui sont connues pour être des classes immuables, lesquelles identifient (ce qui doit être) des instances immuables de classes éventuellement mutables, lesquelles identifient des instances mutables non partagées de classes mutables, et lesquelles certains ne correspondent à aucun de ces modèles (généralement parce qu'ils identifient des "entités"). De nombreux types de bogues, dont certains peuvent être très obscurs, peuvent résulter de l'utilisation de l'un des types de référence ci-dessus comme s'il s'agissait d'un autre.
PS - Un cadre peut atteindre l'efficacité et la robustesse de la validation de const sans les tracas de la "constance" de style C++ si
Il existe des types de stockage distincts pour les "références à l'entité", "la référence illimitée à la valeur potentiellement mutable", la "référence en lecture seule à la valeur potentiellement mutable" et la "référence à la valeur immuable"
Les valeurs sont clonables [le clonage pourrait être largement automatisé si les types de références contenues sont distingués]
Les membres peuvent être marqués selon qu'ils doivent être invocables sur des références en lecture seule ou immuables
Chaque instance d'objet comprend un indicateur indiquant si des références illimitées peuvent exister
Toute référence peut être implicitement convertie en une référence en lecture seule; une référence non restreinte peut également être implicitement convertie en une référence immuable en créant un nouveau clone immuable. Une référence en lecture seule peut être implicitement convertie en immuable, créant un nouveau clone immuable si nécessaire. Un nouvel objet mutable peut être cloné à partir de n'importe quelle référence.
Un aspect essentiel de la distinction "entité" contre "valeur" serait qu'un objet n'est une "valeur" que si (1) il est immuable, o (2) il n'a qu'un seul propriétaire, et aucune référence d'aucune sorte n'existe à l'objet qui n'est pas sous le contrôle de ce propriétaire. Essayer de reprendre le contrôle de toutes les références qui pourraient exister à un objet qui a déjà été librement exposé à du code extérieur serait impossible, et un mécanisme strictement imposé par le compilateur serait probablement trop restrictif pour être utile, mais même si les choses fonctionnaient comme C++ "const-correctness" où le code pourrait promettre de se comporter avec une sémantique appropriée même s'il semblait "suspect", mais tout problème qui en résulterait serait la faute du programmeur qui romprait sa promesse, ce qui serait un avantage significatif par rapport au statu quo.
Pas Java, mais je recommanderais de lire les articles d'Eric Lippert sur l'immuabilité . Ce problème n'est pas aussi clair que vous ne le pensez pour Java. Il n'y a pas d'impact sur les performances inhérent au choix de l'un plutôt que sur l'autre, bien qu'il puisse avoir un impact sur les performances en fonction de votre utilisation exacte.
Le concept clé est de savoir si et comment un objet peut être modifié.
mutable - susceptible de changer.
immuable - immuable dans le temps ou impossible à modifier.
Ok, nous avons donc les définitions. Nous savons exactement ce que les mots signifient, mais comment s'appliquent-ils à la programmation? Passons à deux nouvelles définitions.
objet mutable - un objet susceptible de changer.
objet immuable - un objet qui ne change pas dans le temps ou qui ne peut pas être changé.
Nous pouvons utiliser directement la définition d'origine lorsque nous la mettons en termes de programmation orientée objet. La dernière question est de savoir comment utiliser cela dans la pratique?
Un objet immuable doit être initialisé lors de la construction et s'appuyer uniquement sur d'autres objets immuables une fois initialisé.
Je voudrais donner une image plus claire de la compréhension du concept de classe mutable et immuable.
Mutable signifie dont la valeur peut être modifiée en sens inverse est vraie pour Immutable comme déjà dit.
En fait, chaque fois que vous appelez le constructeur d'une classe, nous allouons en fait un espace mémoire de ce type de variable en tas. par exemple
String str1 = new String("john");
String str2 = str1 ;
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
Ici, comme j'ai appelé le constructeur de la classe de chaîne, une mémoire est allouée pour cette variable avec la valeur "john" et son code de référence est retourné à str1 varible. Dans l'instruction très suivante, str2 pointe également vers le même emplacement mémoire. Les deux instructions println afficheront la même valeur indiquant que les deux variables pointent vers le même emplacement mémoire. Maintenant écrivez ce code
str2 = "monk";
System.out.println(str2.hashCode());
Au moment où vous écrivez cette ligne, un nouvel espace mémoire pour les données de type chaîne est créé en tas, sa valeur est définie sur "moine" et sa référence est renvoyée à str2 et donc str2 ne pointe plus vers le même emplacement de mémoire et donc vous obtiendrez une valeur de code de hachage différente dans l'instruction println. La raison en est "String est une classe immuable". Vous ne pouvez donc en aucun cas modifier sa valeur. Pour StringBuffer Class, vous pouvez vérifier sa mutabilité en utilisant ce code
StringBuffer sb = new StringBuffer("mohan");
StringBuffer sb1 = sb.append(" kumar");
System.out.println(sb.hashCode());
System.out.println(sb1.hashCode()+" - "+(sb1==sb)+" - "+sb.hashCode());
Utilisé en termes de primitives (type intégré) et d'objets (type défini par l'utilisateur). Immuable signifie que vous ne pouvez pas modifier la valeur. Mutable signifie que vous pouvez modifier la valeur.
Beaucoup pensent que les variables primitives et les variables d'objet ayant un modificateur final devant elles sont immuables, cependant, ce n'est pas exactement vrai. Final ne signifie presque pas immuable pour les variables. Consultez ce lien pour un exemple de code http://www.siteconsortium.com/h/D0000F.php .