String s = new String ("xyz") ¿Cuántas instancias se crean?

Citar

Problema: código Java

Cadena s = nueva Cadena ("xyz");

¿Cuántos objetos de cadena se han creado?

No hay una respuesta razonable a esta pregunta en sí.

Citar

Respuesta: Dos (uno es "xyz" y el otro es el objeto de referencia que apunta a "xyz")
(Bueno, esta respuesta tiene muchas quejas ... todos tómelo con calma)

¿Qué pasa con este problema? No define el significado de "creado".
¿Qué es "creado"? ¿Cuándo fue creado?
Y cuando este fragmento de código Java se ejecute realmente, ¿realmente "creará dos instancias de String"?

Si se trata de una pregunta de entrevista, puede pedirle al entrevistador que aclare la definición de "creado" en persona y luego responder en consecuencia. En este momento, lo más probable es que el entrevistador deje que el entrevistado se explique por sí mismo, entonces es fácil de manejar y se lo mostrará al entrevistador.

Si es una prueba escrita, no hay oportunidad de pedir aclaraciones. Sin embargo, la mayoría de los lugares donde surge este tipo de problemas no son muy buenos. Tal vez la persona que escribió la pregunta copió la pregunta de varios libros, luego puede mezclarla escribiendo la respuesta incorrecta de acuerdo con el libro.

================================================ =====

Cambiemos a otra pregunta para hacer:

Código Java

String s = new String ("xyz"); ¿
Cuántas instancias de String están involucradas en tiempo de ejecución?

Una respuesta razonable es:

Respuesta: Dos, una es la instancia correspondiente a la cadena literal "xyz" y reside (interno) en un grupo constante de cadenas compartido globalmente, la otra es creada e inicializada por una nueva Cadena (Cadena), Ejemplo con el mismo contenido que "xyz "

"¡2020 los últimos conceptos básicos de Java y tutoriales en vídeo detallados y rutas de aprendizaje! "
Esta es una respuesta razonable que se puede dar de acuerdo con las disposiciones relevantes de la especificación del lenguaje Java. Teniendo en cuenta que la especificación del lenguaje Java establece claramente:

Especificación del lenguaje Java, tercera edición 写道

El lenguaje de programación Java normalmente se compila con el conjunto de instrucciones codificadas por bytes y el formato binario definido en The Java Virtual Machine Specification, Segunda edición (Addison-Wesley, 1999).

Es decir, se estipula que el lenguaje Java generalmente se compila en el archivo Class definido por la especificación de la máquina virtual Java, pero no estipula "must", dejando espacio para implementar el lenguaje Java sin utilizar la JVM.
Teniendo en cuenta la especificación de la máquina virtual Java, el tipo de constante CONSTANT_String_info involucrado en este código es de hecho la única constante de cadena "xyz". CONSTANT_String_info es un tipo constante que se utiliza para expresar el valor de las expresiones constantes de tipo String (incluidos los literales de cadena) en el lenguaje Java. Si solo considera este nivel, esta respuesta no es un problema.
Por tanto, esta solución puede considerarse razonable.

Vale la pena señalar que "en tiempo de ejecución" en la pregunta incluye tanto la fase de carga de la clase como el tiempo de ejecución del fragmento de código en sí. La relación entre este detalle y la pregunta original dada por el cartel original se discutirá a continuación.

Cuando se encuentre con este tipo de problema, primero debe pensar en consultar las especificaciones relacionadas, aquí están las especificaciones del lenguaje Java y las especificaciones de la máquina virtual Java, así como el JavaDoc de algunas API relacionadas. A muchas personas les gusta tomar "razonable" como un eslogan. Las normas se utilizan para definir varias "razones" - "¿Por qué XXX significa YYY?" "¡Porque está definido en la norma!" - invencible.

Las definiciones relacionadas en la Especificación de máquina virtual Java son las siguientes:

La especificación de la máquina virtual Java, segunda edición 写道

2.3 Literales

Un literal es la representación del código fuente de un valor de un tipo primitivo (§2.4.1), el tipo de cadena (§2.4.8) o el tipo nulo (§2.4). Los literales de cadena y, de manera más general, las cadenas que son valores de expresiones constantes se "internan" para compartir instancias únicas, utilizando el método String.intern.

El tipo nulo tiene un valor, la referencia nula, denotado por el literal nulo. El tipo booleano tiene dos valores, denotados por los literales verdadero y falso.

2.4.8 La cadena de clases

Las instancias de la clase String representan secuencias de caracteres Unicode (§2.1). Un objeto String tiene un valor constante e invariable. Los literales de cadena (§2.3) son referencias a instancias de la clase String.

2.17.6 Creación de nuevas instancias de clase

Una nueva instancia de clase se crea explícitamente cuando ocurre una de las siguientes situaciones:

La evaluación de una expresión de creación de instancia de clase crea una nueva instancia de la clase cuyo nombre aparece en la expresión.
La invocación del método newInstance de la clase Class crea una nueva instancia de la clase representada por el objeto Class para el que se invocó el método.

Una nueva instancia de clase puede crearse implícitamente en las siguientes situaciones:

La carga de una clase o interfaz que contiene un literal String puede crear un nuevo objeto String (§2.4.8) para representar ese literal. Esto puede no ocurrir si el objeto String ya se ha creado para representar una ocurrencia anterior de ese literal, o si el método String.intern se ha invocado en un objeto String que representa la misma cadena que el literal.
La ejecución de un operador de concatenación de cadenas que no es parte de una expresión constante a veces crea un nuevo objeto String para representar el resultado. Los operadores de concatenación de cadenas también pueden crear objetos contenedores temporales para un valor de un tipo primitivo (§2.4.1).

Cada una de estas situaciones identifica un constructor particular que se llamará con argumentos específicos (posiblemente ninguno) como parte del proceso de creación de la instancia de clase.

5.1 El grupo de constantes de tiempo de ejecución

...

● Un literal de cadena (§2.3) se deriva de una estructura CONSTANT_String_info (§4.4.3) en la representación binaria de una clase o interfaz. La estructura CONSTANT_String_info da la secuencia de caracteres Unicode que constituyen la cadena literal.

● El lenguaje de programación Java requiere que los literales de cadena idénticos (es decir, los literales que contienen la misma secuencia de caracteres) deben hacer referencia a la misma instancia de la clase String. Además, si se llama al método String.intern en cualquier cadena, el resultado es una referencia a la misma instancia de clase que se devolvería si esa cadena apareciera como literal. Por lo tanto,
Java 代码

("A" + "b" + "c"). Pasante () == "abc"

debe tener el valor verdadero.

● Para derivar un literal de cadena, la máquina virtual Java examina la secuencia de caracteres dada por la estructura CONSTANT_String_info.

○ Si el método String.intern se ha llamado previamente en una instancia de la clase String que contiene una secuencia de caracteres Unicode idéntica a la dada por la estructura CONSTANT_String_info, entonces el resultado de la derivación literal de cadena es una referencia a esa misma instancia de la clase String.

○ De lo contrario, se crea una nueva instancia de la clase String que contiene la secuencia de caracteres Unicode dada por la estructura CONSTANT_String_info; esa instancia de clase es el resultado de una derivación literal de cadena. Finalmente, se invoca el método interno de la nueva instancia de String.

...

Las estructuras restantes en la tabla constant_pool de la representación binaria de una clase o interfaz, las estructuras CONSTANT_NameAndType_info (§4.4.6) y CONSTANT_Utf8_info (§4.4.7) solo se usan indirectamente al derivar referencias simbólicas a clases, interfaces, métodos y campos y al derivar cadenas literales.

Piense en el JDK de Sun como una implementación de referencia (RI). El JavaDoc para String.intern () es:

JavaDoc escribe

pasante de String público ()

Devuelve una representación canónica del objeto de cadena.

La clase String mantiene un grupo de cadenas, inicialmente vacío, de forma privada.

Cuando se invoca el método interno, si el grupo ya contiene una cadena igual a este objeto String según lo determinado por el método equals (Object), se devuelve la cadena del grupo. De lo contrario, este objeto String se agrega al grupo y se devuelve una referencia a este objeto String.

De ello se deduce que para dos cadenas cualesquiera s y t, s.intern () == t.intern () es verdadero si y solo si s.equals (t) es verdadero.

Todas las cadenas literales y las expresiones constantes con valores de cadena están internas. Los literales de cadena se definen en §3.10.5 de la Especificación del lenguaje Java

Devuelve:
una cadena que tiene el mismo contenido que esta cadena, pero se garantiza que proviene de un grupo de cadenas únicas.

================================================ =====

Haz otra pregunta:

String s = new String ("xyz"); ¿
Cuántas variables de tipo String están involucradas en la declaración del usuario?

La respuesta también es muy simple:

Respuesta: Uno es String s.

Cambie la pregunta a la siguiente versión y la respuesta es la misma:

String s = null;
involucra varias variables de tipo String declaradas por el usuario?

Las variables en Java son variables. Las variables de tipo de referencia son solo referencias a una instancia de objeto o nula, no la instancia en sí. El número de variables declaradas no está necesariamente relacionado con el número de instancias creadas, como decir:

String s1 = "a";
String s2 = s1.concat ("");
String s3 = null;
new String (s1);
Este código involucrará 3 variables de tipo String,
1, s1, apuntando a la siguiente instancia de String 1
2 . s2, apunta a lo mismo que s1
3, s3, el valor es nulo, no apunta a ninguna instancia

Y 3 instancias de String,

1. La instancia de cadena de la constante de cadena residente correspondiente al literal "a"

2. Instancia de cadena de la constante de cadena residente correspondiente al literal ""

(String.concat () es un método interesante, lo devolverá cuando encuentre que el parámetro pasado es una cadena vacía, por lo que no se creará ninguna instancia de String adicional aquí)

3. Una nueva instancia de String creada por new String (String), no hay ninguna variable que apunte a ella.

Volver a las preguntas y "respuestas estándar" citadas al principio

String s = new String ("xyz"); ¿
Cuántos objetos String se han creado?

Respuesta: Dos (uno es "xyz" y el otro es el objeto de referencia que apunta a "xyz")
se demuestran mediante reduccionismo. Suponga que la pregunta es "Se crearon varias instancias de String mientras se ejecutaba este fragmento de código". Si la "respuesta estándar" es correcta, entonces el siguiente fragmento de código debería crear 4 instancias de String cuando se ejecute:

String s1 = new String ("xyz");
String s2 = new String ("xyz");
Alguien saltará inmediatamente y dirá que los literales "xyz" superior e inferior se refieren al mismo objeto String, por lo que no debería ser Se crearon 4 objetos.

¿Cuántos deberían ser?

El proceso de carga de clases en tiempo de ejecución y la ejecución real de un determinado fragmento de código deben discutirse por separado para que sean significativos.

Para ejecutar el fragmento de código en la pregunta, la clase en la que se encuentra debe cargarse primero, y la misma clase solo se cargará una vez como máximo (tenga en cuenta que para la JVM, "la misma clase" no es suficiente para el nombre completo de la clase debe ser el mismo, pero <nombre completo de la clase, definición de cargador de clases> es el mismo par).

De acuerdo con el contenido de la especificación citada anteriormente, una implementación de JVM que cumpla con la especificación debe crear y residir una instancia de String como una constante para corresponder al literal "xyz" durante el proceso de carga de clases; se lleva a cabo específicamente en la resolución etapa de la clase de carga. Esta constante se comparte globalmente y es necesario crear una nueva instancia de String solo si no ha residido antes ninguna cadena con el mismo contenido.

Cuando el fragmento de código en la pregunta original se ejecuta realmente, el código de bytes que la JVM necesita ejecutar es similar a esto:

Código de bytecode de Java:

0: new # 2; // clase java / lang / String
3: dup
4: ldc # 3; // String xyz
6: invokespecial # 4; // Método java / lang / String. "" :( Ljava / lang / String;) V
9: astore_1
Cuántas veces ha aparecido un nuevo java / lang / String en este es el número de objetos String que se crean. En otras palabras, el código de la pregunta original solo creará una nueva instancia de String cada vez que se ejecute.
Aquí, la instrucción ldc simplemente empuja una referencia de un objeto String ("xyz") que se ha creado durante el proceso de carga de clases a la parte superior de la pila de operandos, y no crea un nuevo objeto String.

Entonces, el fragmento de código que se acaba de usar para la reducción:

String s1 = new String ("xyz");
String s2 = new String ("xyz");
Cada ejecución solo creará 2 nuevas instancias de String.


Para evitar que algunos estudiantes se confundan, me gustaría enfatizar nuevamente:

En el lenguaje Java, la expresión "nueva" es responsable de crear una instancia, en la que se llama al constructor para inicializar la instancia; el tipo de valor de retorno del constructor en sí es nulo, no "el constructor devuelve una referencia al recién creado object ", pero el valor de la nueva expresión es una referencia al objeto recién creado.

En consecuencia, en la JVM, la instrucción de código de bytes "nueva" solo es responsable de crear la instancia (incluida la asignación de espacio, establecer el tipo, establecer valores predeterminados para todos los campos, etc.) y presionar la referencia al objeto recién creado al operando Top de la pila. En este momento, la referencia no se puede usar directamente y está en un estado no inicializado; si un método a contiene código que intenta llamar a cualquier método de instancia a través de la referencia en el estado no inicializado, entonces el método a fallará la verificación del código de bytes de JVM. resultado, JVM se negó a ejecutar.

Lo único que se puede hacer con una referencia al estado no inicializado es llamar al constructor de la instancia a través de él, que se representa como un método de inicialización especial "" en el nivel de archivo de clase. La instrucción de llamada real es especial, y los parámetros requeridos deben presionarse secuencialmente en la pila de operandos antes de la llamada real. En el ejemplo de código de bytes anterior, el comando para suprimir los parámetros incluye dos dup y ldc, respectivamente, el parámetro oculto (la referencia de la instancia recién creada, por ejemplo, el constructor es "this") y el primer parámetro real declarado explícitamente (un referencia a la constante "xyz") se inserta en la pila de operandos.

Una vez que el constructor regresa, la referencia de la instancia recién creada se puede usar normalmente.

Dirección de conexión: https://www.iteye.com/blog/rednaxelafx-774673

Supongo que te gusta

Origin blog.csdn.net/weixin_46699878/article/details/110688570
Recomendado
Clasificación