¿Qué hace realmente la palabra clave ‘nueva’ en Java, y debería evitar crear objetos nuevos?

Me inscribí hace unos momentos, aunque he estado haciendo un gran uso de este sitio desde que comencé la progtwigción de computadoras, que me he enseñado a mí mismo y considero un pequeño pasatiempo mío.

Busqué preguntas similares, pero de hecho no pude encontrar la respuesta que estaba buscando. Ahora, sabiendo que en Java (ese es el lenguaje con el que se me sugirió comenzar), se considera una buena práctica de progtwigción declarar y crear instancias de variables a medida que las necesita, por favor considere las siguientes líneas:

class MyClass { void myMethod() { AnotherClass myObject = new AnotherClass(); myObject.doStuff(); } } 

Ahora, supongamos que invoco myMethod (), por ejemplo, 10 veces mientras ejecuto mi progtwig, ¿cómo funciona eso? Es un nuevo objeto crear todo el tiempo? ¿La variable myObject se reasigna cada vez? ¿El comstackdor omite ese tipo de código cuando ve que el objeto ya se ha creado y la variable myObject ya se ha asignado a dicho objeto? En pocas palabras: ¿debería escribir código como ese solo si planeo invocar ese método solo una vez? Lo sé … lástima de mí por hacer una pregunta tan estúpida, pero por favor dame una oportunidad! ¡Gracias por adelantado!

————————— editado ———————- ——-

Entonces, ¿ahora se supone que debo editar esta publicación después de obtener nuevas respuestas? por cierto … ¡Dios mío, fue rápido, muchas gracias! Y wow eso me confundió mucho, creo que eso se debe al hecho de que he estado enseñándome a mí mismo así que … De todos modos, ¿no es inútil crear un new AnotherClass Objeto new AnotherClass para la variable new AnotherClass todo el tiempo? Quiero decir, si quiero usar la variable myObject en mi progtwig, ¿no debería declararlo de una vez por todas? tal vez en otro método, ¿que voy a invocar solo una vez? Porque, por lo que yo entiendo, cada vez que invoco myMethod() un nuevo objeto está creando, anulando así las propiedades de propiedad de myMethod() aka variables o ¿estoy hablando tonterías?

————————— editado ———————- ——-

Mis dudas surgieron después de leer este código de un sitio web que no recuerdo en este momento:

  public class DataBase { private static String buf, retString = "\n"; private static File file = new File("test.txt"); public static void readText(JTextArea area) { try { FileReader fr = new FileReader (file); BufferedReader br = new BufferedReader(fr); while ((buf = br.readLine()) != null) { area.append(buf); area.append(retString); } br.close(); fr.close(); } catch (IOException e) { System.out.println("Exception: " + e); } } public static void writeText(JTextArea area) { try { FileWriter fw = new FileWriter (file); BufferedWriter bw = new BufferedWriter(fw); bw.write(area.getText()); bw.close(); fw.close(); } catch (IOException e) { System.out.println("Exception: " + e); } } } 

Quiero decir, ¿por qué no declarar FileWriter, FileReader, BufferedReader y BufferedWriter en la parte superior de la clase como lo hicieron para las otras variables? y ¿por qué no inicializarlos también en el constructor? ¿Por qué hacerlo cada vez que se llama al método en lugar de usar quizás la misma variable de instancia?

Sí, si llamó a myMethod() 10 veces, creará 10 objetos únicos y separados.

La new palabra clave hace exactamente lo que dice en la lata, crea un nuevo objeto, independientemente de si ya existe uno. Crea un nuevo objeto y rellena la referencia a ese objeto dentro de la variable que se le ha asignado, sobrescribiendo cualquier valor anterior (objeto) que contenga la variable.

¿La variable myObject se reasigna cada vez?

De nuevo, sí, se volvería a asignar con un nuevo objeto cada vez que se llamara al método. Una nota interesante acerca de esto sería que la variable no sería “reasignada” realmente ya que está definiendo la variable dentro del propio cuerpo del método, de modo que cada vez que el método finalice eliminará las variables que se definieron dentro de su ‘scope’ . Entonces, lo que realmente hace es crear 10 variables individuales y asignar 10 objetos individuales, aunque como dije, los otros deberían haber sido eliminados automáticamente para que no usen memoria adicional.

En pocas palabras: ¿debería escribir código como ese solo si planeo invocar ese método solo una vez?

Bien, como dije, en el ejemplo anterior, cada objeto se destruiría al final de la ejecución del método (suponiendo que no hayas asignado la referencia del objeto a una variable fuera del scope del método), así que en tu ejemplo, puedes llamar al método tantas veces como quiera, pero cada vez de ninguna manera estaría conectado a las llamadas anteriores.

Me doy cuenta de que mi forma de escribir puede ser confusa, así que si quieres que aclare algo, solo pregunta.

Respuesta actualizada para reflejar la pregunta editada

‘¿por qué no declarar FileWriter, FileReader, BufferedReader y BufferedWriter en la parte superior de la clase como lo hicieron para las otras variables?’

De acuerdo, supongo que entiendes que las variables no se llaman realmente FileWriter , FileReader , BufferedReader y BufferedWriter , sino que este es el tipo de variable. Sus nombres son fw , fr , br y bw . Si no entiendes lo que quiero decir, solo pregunta. A partir de ahora, me referiré a las variables por los nombres que hizo para facilitar la lectura, después de todo, fw solo significa FileWriter así que no debería haber demasiada confusión.

La clave de esta pregunta está escondida dentro de los nombres de las variables mismas. Observe cómo terminan en Reader o Writer esto puede darnos una pista sutil sobre sus usos. Claramente, FileWriter y BufferedWriter tienen que ver con la salida de alguna manera. Al revisar el código vemos que nuestras sospechas eran correctas y que en ningún otro punto que no sea dentro del writeText(JTextArea area) aparecen estas variables. Entonces, si la variable no se usa en ningún otro lugar dentro del código, tendría sentido lógico definirlas e inicializarlas dentro del método en el que se usan, no solo hace que el código sea más fácil de leer porque entonces “conocemos” esas variables solo están relacionados con ese método, pero también tienen el beneficio de que esas variables se eliminen al final de la ejecución del método, por lo que no dejan variables que solo se usaron muy brevemente. Según estas reglas, podemos decir que lo mismo es cierto para FileReader y BufferedReader .

Observe este ejemplo sobre el scope de la variable. (Mira los comentarios que agregué al código)

 public class DataBase { private static String buf, retString = "\n"; // buf & retString - created private static File file = new File("test.txt"); // file - created public static void readText(JTextArea area) { try { FileReader fr = new FileReader (file); // fr (FileReader) - created BufferedReader br = new BufferedReader(fr); // br (BufferedReader) - created while ((buf = br.readLine()) != null) { area.append(buf); area.append(retString); } br.close(); fr.close(); } // fr (FileReader & br (BufferedReader) - destroyed catch (IOException e) { System.out.println("Exception: " + e); } } public static void writeText(JTextArea area) { try { FileWriter fw = new FileWriter (file); // fw (FileWriter) - created BufferedWriter bw = new BufferedWriter(fw); // bw (BufferedWriter) - created bw.write(area.getText()); bw.close(); fw.close(); } // fw & bw - destroyed catch (IOException e) { System.out.println("Exception: " + e); } } } // buf, retString and file - Still exist as long as the object exists 

A partir de este ejemplo, se vuelve más claro en cuanto a por qué las variables se definen en los métodos en lugar de como variables de instancia y se inicializan dentro del constructor. Permite un código mucho más limpio además de ser más readable.

¿Por qué hacerlo cada vez que se llama al método en lugar de usar quizás la misma variable de instancia?

Bueno, esta pregunta tiene que ver con los tipos de variables. No pudimos reutilizar una sola variable para toda la información ya que los tipos habrían sido diferentes.

Si tomamos todas las variables del código

 private static String buf, retString = "\n"; // valid private static File file = new File("test.txt"); // valid FileReader fr = new FileReader (file); // valid BufferedReader br = new BufferedReader(fr); // valid FileWriter fw = new FileWriter (file); // valid BufferedWriter bw = new BufferedWriter(fw); // valid 

Ahora sabemos que no podemos colocar un valor que no sea del mismo tipo que la variable en esa variable, así que algo así como

 FileReader fr = new BufferedReader(fr); // Is not valid! 

Porque los tipos simplemente no coinciden.

¿Tener sentido?

Sí, se crea un nuevo objeto cada vez. La referencia a cada myObject se asigna en la stack.

En pocas palabras: ¿debería escribir código como ese solo si planeo invocar ese método solo una vez?

Si desea que myObject desaparezca después de que se complete la ejecución del método, entonces sí. Si por algún motivo, necesita mantener una referencia, puede declararlo como miembro de la clase.

 class MyClass { AnotherClass myObject; void myMethod() { myObject = new AnotherClass(); myObject.doStuff(); } } 

De esta forma, se creará cada vez que llame a myMethod() , pero seguirá existiendo después de que myMethod finalice. Esto puede ser útil, o no, dependiendo de la situación.

¿El comstackdor omite ese tipo de código cuando ve que el objeto ya se ha creado y la variable myObject ya se ha asignado a dicho objeto?

Esto no sucederá cuando uses new . Se garantiza que creará una nueva instancia. Se puede implementar utilizando FactoryMethods (no el comstackdor salteando líneas de código, pero impidiendo la creación de un nuevo objeto) . Por ejemplo, la clase Integer implementa esto: si intenta obtener un número entero entre -128 y 127 , siempre devolverá la misma instancia (no creará un nuevo objeto) cuando use su valor Factory valueOf

  Integer five = Integer.valueOf("5");//Will always return the same instance. Integer otherFive = Integer.valueOf("5"); assert(five==otherFive);//true 

Por supuesto, usar new no devolverá la misma instancia, pero siempre una nueva

  Integer five = new Integer("5");//Will create a new object each time. Integer otherFive = new Integer("5"); assert(five==otherFive);//false 

después de la actualización de pregunta

Realmente no hay mucho que decir sobre el código que ha agregado. Sin embargo, si echas un vistazo, verás dos métodos. Basado en sus nombres, una vez parece escribir, el otro parece leer. Ese comportamiento es específico para cada método, por lo que el método que writeFile no se preocupa por los objetos utilizados para la lectura. Y el método readFile no se preocupa de los objetos usados ​​para escribir. Por lo tanto, no tiene sentido hacer que un fileReader esté disponible para el método writeFile , y así sucesivamente.

Volviendo a su pregunta original, sí, esto ejemplifica un nuevo objeto cada vez que se llama al método. No es importante. Es preferible preguntarse “¿por qué el método readFile tiene acceso a una instancia de FileWriter ?

Ahora, supongamos que invoco myMethod (), por ejemplo, 10 veces mientras ejecuto mi progtwig, ¿cómo funciona eso? Es un nuevo objeto crear todo el tiempo?

¡Sí!

Reutilizar o no reutilizar un objeto instanciado depende del diseño y la situación. Hay casos en los que es mejor reutilizar objetos, en cuyo caso puede crear un campo de clase para conservar la referencia, y hay casos en los que es mejor crear un nuevo objeto cada vez (mire la inmutabilidad, por ejemplo).

si invocas 10 veces, habrá 10 marcos de métodos en tu stack de Java, cada marco actuará como nueva () acción y cuando el marco termine, se liberará.

enter image description here

Cada vez que llamas al método myMethod , el código se ejecuta desde la parte superior sin memoria sobre lo que hizo en las ejecuciones previas (a menos que, por supuesto, hayas cambiado algunos campos del objeto MyClass . Esto significa que cada vez que ejecutas el método, creará un nuevo objeto AnotherClass y lo almacenará en myObject . De manera más general, cada ejecución de un método ejecutará el código desde la parte superior y no evitará volver a calcular los valores incluso si podrían haber sido almacenados en caché de iteraciones previas a menos que almacene los valores explícitamente en algún lugar .

Si esto no es lo que desea, y en su lugar desea almacenar el objeto que asignó para que en iteraciones futuras pueda hacer referencia de nuevo, puede almacenarlo en una variable de instancia de la clase.

Se crea un objeto nuevo cada vez que se llama al método. Si el control llega a una línea de código con un operador “nuevo”, entonces se creará un objeto; no hay escondite detrás de escena ni otra magia.