¿Por qué String es inmutable en Java?

1. Introducción

En Java, las cadenas son inmutables. Una pregunta obvia que es bastante frecuente en las entrevistas es "¿Por qué las cadenas están diseñadas como inmutables en Java?"

A James Gosling, el creador de Java, se le preguntó una vez en una entrevista cuándo se deben usar inmutables, a lo que responde:

Usaría un inmutable siempre que pueda.

Además, apoya su argumento al afirmar las características que proporciona la inmutabilidad, como el almacenamiento en caché, la seguridad, la reutilización fácil sin replicación, etc.

En este tutorial, exploraremos más a fondo por qué los diseñadores del lenguaje Java decidieron mantener String inmutable.

2. ¿Qué es un objeto inmutable?

Un objeto inmutable es un objeto cuyo estado interno permanece constante después de haber sido creado por completo . Esto significa que una vez que el objeto ha sido asignado a una variable, no podemos actualizar la referencia ni mutar el estado interno de ninguna manera.

Tenemos un artículo separado que analiza los objetos inmutables en detalle. Para obtener más información, lea el artículo Objetos inmutables en Java.

3. ¿Por qué la cadena es inmutable en Java?

Los beneficios clave de mantener esta clase como inmutable son el almacenamiento en caché, la seguridad, la sincronización y el rendimiento.

Analicemos cómo funcionan estas cosas.

3.1. Presentar a String Pool

La cadena es la estructura de datos más utilizada. El almacenamiento en caché de los literales String y su reutilización ahorra mucho espacio en el montón porque diferentes variables String se refieren al mismo objeto en el grupo String . El grupo de prácticas de cuerdas sirve exactamente para este propósito.

Java String Pool es la región de memoria especial donde la JVM almacena las cadenas . Dado que las cadenas son inmutables en Java, la JVM optimiza la cantidad de memoria asignada para ellas almacenando solo una copia de cada cadena literal en el grupo. Este proceso se denomina pasantía:

String s1 = "Hello World"; String s2 = "Hello World"; assertThat(s1 == s2).isTrue();

Debido a la presencia del grupo String en el ejemplo anterior, dos variables diferentes apuntan al mismo objeto String del grupo, lo que ahorra un recurso de memoria crucial.

Tenemos un artículo separado dedicado a Java String Pool. Para obtener más información, diríjase a ese artículo.

3.2. Seguridad

La cadena se usa ampliamente en aplicaciones Java para almacenar información confidencial como nombres de usuario, contraseñas, URL de conexión, conexiones de red, etc. También es ampliamente utilizada por cargadores de clases JVM mientras cargan clases.

Por lo tanto, proteger la clase String es crucial con respecto a la seguridad de toda la aplicación en general. Por ejemplo, considere este simple fragmento de código:

void criticalMethod(String userName) { // perform security checks if (!isAlphaNumeric(userName)) { throw new SecurityException(); } // do some secondary tasks initializeDatabase(); // critical task connection.executeUpdate("UPDATE Customers SET Status = 'Active' " + " WHERE UserName = '" + userName + "'"); }

En el fragmento de código anterior, digamos que recibimos un objeto String de una fuente no confiable. Inicialmente estamos haciendo todas las comprobaciones de seguridad necesarias para comprobar si la cadena es solo alfanumérica, seguida de algunas operaciones más.

Recuerde que nuestro método de llamador de origen no confiable todavía tiene referencia a este objeto userName .

Si las cadenas fueran mutables, para cuando ejecutamos la actualización, no podemos estar seguros de que la cadena que recibimos, incluso después de realizar las comprobaciones de seguridad, sea segura. El método de llamada no confiable aún tiene la referencia y puede cambiar la Cadena entre verificaciones de integridad. Por lo tanto, nuestra consulta es propensa a inyecciones SQL en este caso. Por lo tanto, las cadenas mutables podrían provocar una degradación de la seguridad con el tiempo.

También podría suceder que el String userName sea ​​visible para otro hilo, que luego podría cambiar su valor después de la verificación de integridad.

En general, la inmutabilidad viene a nuestro rescate en este caso porque es más fácil operar con código sensible cuando los valores no cambian porque hay menos intercalaciones de operaciones que podrían afectar el resultado.

3.3. Sincronización

Ser inmutable automáticamente hace que el hilo de String sea seguro, ya que no se cambiarán cuando se acceda desde varios hilos.

Por lo tanto , los objetos inmutables, en general, se pueden compartir entre varios subprocesos que se ejecutan simultáneamente. También son seguros para subprocesos porque si un subproceso cambia el valor, en lugar de modificar el mismo, se crearía una nueva cadena en el grupo de cadenas . Por lo tanto, las cadenas son seguras para subprocesos múltiples.

3.4. Almacenamiento en caché de hashcode

Dado que los objetos String se usan abundantemente como una estructura de datos, también se usan ampliamente en implementaciones hash como HashMap , HashTable , HashSet , etc. Cuando se opera con estas implementaciones hash, el método hashCode () se llama con bastante frecuencia para el agrupamiento.

La inmutabilidad garantiza a Strings que su valor no cambiará. Entonces, el método hashCode () se anula en la clase String para facilitar el almacenamiento en caché, de modo que el hash se calcula y se almacena en caché durante la primera llamada de hashCode () y se devuelve el mismo valor desde entonces.

Esto, a su vez, mejora el rendimiento de las colecciones que utilizan implementaciones hash cuando se operan con objetos String .

Por otro lado, las cadenas mutables producirían dos códigos hash diferentes en el momento de la inserción y la recuperación si el contenido de la cadena se modificara después de la operación, lo que podría perder el objeto de valor en el mapa .

3.5. Actuación

Como vimos anteriormente, el grupo de cadenas existe porque las cadenas son inmutables. A su vez, mejora el rendimiento al ahorrar memoria dinámica y un acceso más rápido a las implementaciones hash cuando se opera con Strings.

Dado que String es la estructura de datos más utilizada, la mejora del rendimiento de String tiene un efecto considerable en la mejora del rendimiento de toda la aplicación en general.

4. Conclusión

A través de este artículo, podemos concluir que las cadenas son inmutables precisamente de modo que sus referencias pueden tratarse como una variable normal y uno puede pasarlas, entre métodos y subprocesos, sin preocuparse por si el objeto String real al que apunta cambiará.

También aprendimos cuáles podrían ser las otras razones que llevaron a los diseñadores del lenguaje Java a hacer esta clase como inmutable.