Diferencias en la pila JAVA
Autor:Eve Cole
Fecha de actualización:2009-11-30 17:08:19
-
1. La pila y el montón son lugares utilizados por Java para almacenar datos en Ram. A diferencia de C++, Java administra automáticamente la pila y el montón, y los programadores no pueden configurar directamente la pila o el montón.
2. La ventaja de la pila es que la velocidad de acceso es más rápida que la del montón, solo superada por los registros ubicados directamente en la CPU. Pero la desventaja es que se debe determinar el tamaño y la vida útil de los datos almacenados en la pila y hay una falta de flexibilidad. Además, los datos de la pila se pueden compartir; consulte el punto 3 para obtener más detalles. La ventaja del montón es que puede asignar dinámicamente el tamaño de la memoria y no es necesario informar al compilador de antemano la vida útil. El recolector de basura de Java recopilará automáticamente los datos que ya no se utilizan. Pero la desventaja es que debido a la necesidad de asignar memoria dinámicamente en tiempo de ejecución, la velocidad de acceso es lenta.
3. Hay dos tipos de datos en Java.
Uno son los tipos primitivos, hay 8 tipos en total, a saber, int, short, long, byte, float, double, boolean, char (tenga en cuenta que no existe un tipo básico de cadena). Este tipo de definición se define en la forma int a = 3; long b = 255L y se denomina variable automática. Vale la pena señalar que las variables automáticas almacenan valores literales, no instancias de clases, es decir, no son referencias a clases. Aquí no hay clases. Por ejemplo, int a = 3; donde a es una referencia que apunta al tipo int y que apunta al valor literal 3. Los datos de estos valores literales se conocen en tamaño y vida útil (estos valores literales se definen de forma fija en un determinado bloque de programa y los valores de campo desaparecen después de que sale el bloque de programa, por razones de velocidad). existen en la pila.
Además, la pila tiene una característica especial muy importante: los datos almacenados en la pila se pueden compartir. Supongamos que también definimos:
int a = 3;
int b = 3;
El compilador primero procesa int a = 3; primero crea una referencia a la variable a en la pila y luego busca una dirección con un valor literal de 3. Si no la encuentra, abre una dirección para almacenar el literal. valor de 3, y luego a apunta a la dirección de 3. A continuación, se procesa int b = 3; después de crear la variable de referencia de b, dado que ya hay un valor literal de 3 en la pila, b apunta directamente a la dirección de 3. De esta manera, existe una situación en la que a y b apuntan a 3 al mismo tiempo.
Es importante señalar que la referencia de este valor literal es diferente de la referencia del objeto de clase. Suponga que las referencias de dos objetos de clase apuntan al mismo objeto al mismo tiempo. Si una variable de referencia de objeto modifica el estado interno del objeto, la otra variable de referencia de objeto reflejará inmediatamente el cambio. Por el contrario, modificar el valor de una referencia literal no hace que el valor de otra referencia al literal también cambie. Como en el ejemplo anterior, después de definir los valores de a y b, establecemos a = 4, entonces b no será igual a 4, pero seguirá siendo igual a 3; Dentro del compilador, cuando encuentre a = 4;, volverá a buscar si hay un valor literal de 4 en la pila. De lo contrario, volverá a abrir una dirección para almacenar el valor de 4 si ya existe. , apuntará directamente a esta dirección. Por lo tanto, los cambios en el valor de a no afectarán el valor de b.
El otro son los datos de clases contenedoras, como Integer, String, Double y otras clases que envuelven los tipos de datos básicos correspondientes. Todos estos tipos de datos existen en el montón. Java usa la declaración new () para decirle explícitamente al compilador que se creará dinámicamente según sea necesario en tiempo de ejecución, por lo que es más flexible, pero la desventaja es que lleva más tiempo. 4. La cadena es un tipo de datos de contenedor especial. Es decir, se puede crear en forma de String str = new String( "abc" ); o se puede crear en forma de String str = "abc" (a modo de comparación, antes de JDK 5.0, nunca se había creado; visto Integer i = 3; expresión, porque las clases y los valores literales no se pueden usar indistintamente, excepto String. En JDK 5.0, esta expresión es posible porque el compilador realiza la conversión de Integer i = new Integer (3). fondo) . El primero es un proceso de creación de clases estandarizado, es decir, en Java, todo es un objeto y los objetos son instancias de clases, todas creadas en forma de nuevo (). Algunas clases en Java, como la clase DateFormat, pueden devolver una clase recién creada a través del método getInstance() de la clase, lo que parece violar este principio. No precisamente. Esta clase usa el patrón singleton para devolver una instancia de la clase, pero esta instancia se crea dentro de la clase a través de new () y getInstance() oculta este detalle desde el exterior. Entonces, ¿por qué en String str = "abc"; la instancia no se crea a través de new ()? ¿Viola el principio anterior? En realidad no.
5. Acerca del funcionamiento interno de String str = "abc". Java convierte internamente esta declaración en los siguientes pasos:
(1) Primero defina una variable de referencia de objeto llamada str para la clase String: String str;
(2) Busque en la pila para ver si hay una dirección con un valor de "abc". Si no, abra una dirección con un valor literal de "abc", luego cree un nuevo objeto o de la clase String y agregue el caracteres de o El valor de la cadena apunta a esta dirección y el objeto al que se hace referencia o se registra junto a esta dirección en la pila. Si ya existe una dirección con el valor "abc", se encuentra el objeto o y se devuelve la dirección de o.
(3) Apunte str a la dirección del objeto o.
Vale la pena señalar que generalmente los valores de cadena en la clase String se almacenan directamente. Pero al igual que String str = "abc"; en este caso, el valor de la cadena guarda una referencia a los datos almacenados en la pila.
Para ilustrar mejor este problema, podemos verificarlo a través de los siguientes códigos.
Cadena cadena1 = "abc";
Cadena cadena2 = "abc";
System.out.println(str1==str2); //verdadero;
Tenga en cuenta que aquí no usamos str1.equals(str2), porque esto comparará si los valores de las dos cadenas son iguales. == signo, según las instrucciones del JDK, solo devuelve verdadero cuando ambas referencias apuntan al mismo objeto. Lo que queremos ver aquí es si str1 y str2 apuntan al mismo objeto.
Los resultados muestran que la JVM creó dos referencias str1 y str2, pero solo creó un objeto y ambas referencias apuntaban a este objeto.
Vayamos un paso más allá y cambiemos el código anterior a:
Cadena cadena1 = "abc";
Cadena cadena2 = "abc";
cadena1 = "bcd";
System.out.println(str1 + "," + str2 //bcd, abc);
System.out.println(str1==str2); //falso
Esto significa que el cambio en la asignación da como resultado un cambio en la referencia del objeto de clase y str1 apunta a otro objeto nuevo. Y str2 todavía apunta al objeto original. En el ejemplo anterior, cuando cambiamos el valor de str1 a "bcd", la JVM encontró que no había ninguna dirección para almacenar este valor en la pila, por lo que abrió esta dirección y creó un nuevo objeto cuyo valor de cadena apunta a esta dirección. .
De hecho, la clase String está diseñada para ser inmutable. Si desea cambiar su valor, puede hacerlo, pero la JVM crea silenciosamente un nuevo objeto basado en el nuevo valor en tiempo de ejecución y luego devuelve la dirección de este objeto a una referencia a la clase original. Aunque este proceso de creación está completamente automatizado, después de todo lleva más tiempo. En un entorno sensible a los requisitos de tiempo, tendrá ciertos efectos adversos.
Modifique el código original nuevamente:
Cadena cadena1 = "abc";
Cadena cadena2 = "abc";
cadena1 = "bcd";
Cadena cadena3 = cadena1;
System.out.println(str3); //bcd
Cadena str4 = "bcd";
System.out.println(str1 == str4); //true
La referencia al objeto str3 apunta directamente al objeto señalado por str1 (tenga en cuenta que str3 no crea un nuevo objeto). Después de que str1 cambie su valor, cree una cadena de referencia str4 y apunte al nuevo objeto creado porque str1 modificó el valor. Se puede encontrar que str4 no creó un nuevo objeto esta vez, por lo que logró compartir datos en la pila nuevamente.
Miremos el siguiente código nuevamente.
Cadena str1 = nueva cadena ("abc");
Cadena cadena2 = "abc";
System.out.println(str1==str2); //falso
Se crean dos referencias. Se crean dos objetos. Las dos referencias apuntan a dos objetos diferentes respectivamente.
Cadena cadena1 = "abc";
Cadena str2 = nueva cadena ("abc");
System.out.println(str1==str2); //falso
Se crean dos referencias. Se crean dos objetos. Las dos referencias apuntan a dos objetos diferentes respectivamente.
Los dos fragmentos de código anteriores ilustran que siempre que se use new () para crear un nuevo objeto, se creará en el montón y su valor de cadena se almacenará por separado, incluso si es el mismo que los datos en la pila. , no se compartirá con los datos de la pila.
6. El valor de la clase contenedora del tipo de datos no se puede modificar. No solo el valor de la clase String no se puede modificar, sino que todas las clases contenedoras de tipos de datos no pueden cambiar sus valores internos. 7. Conclusión y sugerencias:
(1) Cuando definimos una clase usando un formato como String str = "abc";, siempre damos por sentado que creamos un objeto str de la clase String. ¡Preocúpate por las trampas! ¡Es posible que el objeto no haya sido creado! Lo único seguro es que se crea una referencia a la clase String. En cuanto a si esta referencia apunta a un nuevo objeto, debe considerarse de acuerdo con el contexto, a menos que cree explícitamente un nuevo objeto a través del método new (). Por lo tanto, una afirmación más precisa es que creamos una variable de referencia str que apunta a un objeto de la clase String. Esta variable de referencia de objeto apunta a una clase String con un valor de "abc". Una comprensión clara de esto es muy útil para eliminar errores difíciles de encontrar en el programa.
(2) El uso de String str = "abc" puede mejorar la velocidad de ejecución del programa hasta cierto punto, porque la JVM determinará automáticamente si es necesario crear un nuevo objeto en función de la situación real de los datos en la pila. . En cuanto al código String str = new String("abc");, siempre se crean nuevos objetos en el montón, independientemente de si sus valores de cadena son iguales o si es necesario crear nuevos objetos, lo que aumenta la carga. en el programa. Esta idea debería ser la idea del modo Flyweight, pero se desconoce si la implementación interna de JDK aplica este modo.
(3) Al comparar si los valores en la clase de empaquetado son iguales, use el método igual () cuando pruebe si las referencias de dos clases de empaquetado apuntan al mismo objeto, use ==.
(4) Debido a la naturaleza inmutable de la clase String, cuando la variable String necesita cambiar su valor con frecuencia, debería considerar usar la clase StringBuffer para mejorar la eficiencia del programa.