web-dev-qa-db-fra.com

Que fait réellement le «nouveau» mot clé en Java et dois-je éviter de créer de nouveaux objets?

Je me suis inscrit il y a quelques instants, bien que j'utilise beaucoup ce site depuis que j'ai commencé la programmation informatique, que j'apprends moi-même et que je considère comme un petit hobby.

J'ai cherché des questions similaires, mais en fait, je n'ai pas trouvé la réponse que je cherchais. Maintenant, sachant que, dans Java (c'est le langage avec lequel on m'a suggéré de commencer), il est considéré comme une bonne pratique de programmation de déclarer et d'instancier des variables selon vos besoins, veuillez considérer ce qui suit lignes:

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

Supposons maintenant que j'invoque myMethod (), disons, 10 fois lors de l'exécution de mon programme, comment cela fonctionne-t-il? Un nouvel objet est-il créé à chaque fois? La variable myObject est-elle réallouée à chaque fois? Le compliant ignore-t-il cela comme du code car il voit que l'objet a déjà été créé et que la variable myObject a déjà été affectée à cet objet? En un mot: dois-je écrire du code comme ça uniquement si je prévois d'invoquer cette méthode une seule fois? Je sais ... honte à moi d'avoir posé une question aussi stupide, mais donnez-moi s'il vous plaît une chance! Merci d'avance!

--------------------------- édité ---------------------- -------

Alors maintenant, suis-je censé modifier ce message après avoir obtenu de nouvelles réponses? btw ... ça alors c'était rapide, merci beaucoup! Et wow, ça m'a beaucoup dérouté, je suppose que c'est dû au fait que je m'apprends donc ... Quoi qu'il en soit, n'est-il pas inutile de créer un objet new AnotherClass Pour le myObject variable à chaque fois? Je veux dire, si je veux utiliser la variable myObject dans tout mon programme, ne devrais-je pas la déclarer une fois pour toutes? peut-être dans une autre méthode, que je vais invoquer une seule fois? Parce que d'après ce que je comprends, chaque fois que j'invoque myMethod() un nouvel objet est en train de créer, remplaçant ainsi les propres propriétés de myObject aka variables ou est-ce que je dis simplement des bêtises?

--------------------------- édité ---------------------- -------

Mes doutes sont venus après avoir lu ce code sur un site Web dont je ne me souviens pas en ce moment:

    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);
        }
    }
}

Je veux dire, pourquoi ne pas déclarer FileWriter, FileReader, BufferedReader et BufferedWriter en haut de la classe comme ils l'ont fait pour les autres variables? et pourquoi ne pas les initialiser aussi peut-être dans le constructeur? Pourquoi le faire à chaque appel de la méthode plutôt que d'utiliser peut-être la même variable d'instance?

31
zeme

Oui, si vous avez appelé myMethod() 10 fois, cela créera 10 objets uniques et séparés.

Le mot-clé new fait exactement ce qu'il dit sur l'étain, il crée un tout nouvel objet, qu'il existe déjà ou non. Il crée un nouvel objet et remplit la référence à cet objet à l'intérieur de la variable qui lui a été donnée, écrasant toute valeur précédente (objet) de la variable contenue.

La variable myObject est-elle réallouée à chaque fois?

Encore une fois, oui, il serait réalloué avec un nouvel objet à chaque appel de la méthode. Une remarque intéressante à ce sujet serait que la variable ne serait pas "vraiment" réallouée lorsque vous définissez la variable dans le corps de la méthode elle-même, donc à chaque fois que la méthode se termine, elle supprimera les variables qui ont été définies dans son champ d'application. . Donc, ce qu'il fait est de créer 10 variables individuelles et d'attribuer 10 objets individuels, bien que, comme je l'ai dit, les autres auraient dû être supprimées automatiquement afin de ne pas utiliser de mémoire supplémentaire.

En un mot: dois-je écrire du code comme ça uniquement si je prévois d'invoquer cette méthode une seule fois?

Eh bien, comme je l'ai dit, dans l'exemple ci-dessus, chaque objet serait détruit à la fin de l'exécution de la méthode (en supposant que vous n'avez pas assigné la référence d'objet à une variable en dehors de la portée de la méthode), donc dans votre exemple, vous pourriez facilement appeler la méthode autant de fois que vous le souhaitez mais chaque fois ne sera en aucun cas connecté aux appels précédents.

Je me rends compte que ma façon d'écrire peut être déroutante, donc si vous voulez que je clarifie quelque chose, il suffit de demander.

Réponse mise à jour pour refléter la question modifiée

'pourquoi ne pas déclarer FileWriter, FileReader, BufferedReader et BufferedWriter en haut de la classe comme ils l'ont fait pour les autres variables?'

D'accord, je suppose que vous comprenez que les variables ne sont pas réellement appelées FileWriter, FileReader, BufferedReader et BufferedWriter, mais c'est plutôt le type de variable. Leurs noms sont fw, fr, br et bw. Si vous ne comprenez pas ce que je veux dire, demandez. À partir de maintenant, je ferai référence aux variables par les noms que vous avez faits pour faciliter la lecture, après tout fw signifie simplement FileWriter de toute façon, il ne devrait donc pas y avoir trop de confusion.

La clé de cette question est cachée dans les noms des variables elles-mêmes. Remarquez comment ils se terminent par Reader ou Writer cela peut nous donner un indice subtil sur leurs utilisations. Il est clair que FileWriter et BufferedWriter sont en quelque sorte liés à la sortie. En examinant le code, nous voyons que nos soupçons étaient exacts et qu'à aucun moment autre que dans la méthode writeText(JTextArea area) ces variables n'apparaissent. Donc, si la variable n'est utilisée nulle part ailleurs dans le code, il serait logique de les définir et de les initialiser dans la méthode dans laquelle elles sont utilisées, non seulement cela rend le code plus facile à lire car nous "connaissons" ensuite ces variables ne sont liés qu'à cette méthode, mais présentent également l'avantage de supprimer ces variables à la fin de l'exécution de la méthode, ce qui ne laisse pas de variables existantes qui n'ont été utilisées que très brièvement. Par ces règles, nous pouvons dire qu'il en va de même pour FileReader et BufferedReader.

Observez cet exemple sur la portée variable. (Regardez les commentaires que j'ai ajoutés au code)

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

À partir de cet exemple, il devient plus clair pourquoi les variables sont définies dans les méthodes plutôt que comme variables d'instance et initialisées dans le constructeur. Il permet un code beaucoup plus propre et plus lisible.

Pourquoi le faire à chaque appel de la méthode plutôt que d'utiliser peut-être la même variable d'instance?

Eh bien, cette question concerne les types de variables. Nous n'avons pas pu réutiliser une seule variable pour toutes les informations car les types auraient dû être différents.

Si nous prenons toutes les variables du code

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

Maintenant, nous savons que nous ne pouvons pas placer une valeur qui n'est pas du même type que la variable dans cette variable donc quelque chose comme

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

Parce que les types ne correspondent tout simplement pas.

Ça a du sens?

23
linuscash

Oui, un nouvel objet est créé à chaque fois. La référence à chaque myObject est allouée dans la pile.

En un mot: dois-je écrire du code comme ça uniquement si je prévois d'invoquer cette méthode une seule fois?

Si vous voulez que myObject disparaisse une fois l'exécution de la méthode terminée, alors oui. Si pour une raison quelconque, vous devez conserver une référence, vous pouvez le déclarer en tant que membre de la classe.

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

De cette façon, il sera toujours créé à chaque fois que vous appelez myMethod(), mais il existera toujours une fois que myMethod sera terminé. Cela peut être pratique ou non, selon la situation.

Le compliant ignore-t-il cela comme du code car il voit que l'objet a déjà été créé et que la variable myObject a déjà été affectée à cet objet?

Cela n'arrivera pas lors de l'utilisation de new. Il est garanti qu'il créera une nouvelle instance. Il peut être implémenté en utilisant FactoryMethods (pas le compilateur en sautant les lignes de code, mais en empêchant la création d'un nouvel objet) . Par exemple, la classe Integer implémente ceci: Si vous essayez d'obtenir un entier entre -128 Et 127, Il retournera toujours la même instance (ne créera pas de nouvelle objet) lors de l'utilisation de sa méthode d'usine valueOf

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

 assert(five==otherFive);//true

Bien sûr, utiliser new ne retournera pas la même instance, mais toujours une nouvelle

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

 assert(five==otherFive);//false

après la mise à jour de la question

Il n'y a vraiment pas grand-chose à dire sur le code que vous avez ajouté. Cependant, si vous y jetez un coup d'œil, vous remarquerez deux méthodes. Sur la base de ses noms, une fois semble écrire, l'autre semble lire. Ce comportement est spécifique à chaque méthode, donc la méthode qui writeFile ne se soucie pas des objets utilisés pour la lecture. Et la méthode readFile ne se soucie pas des objets utilisés pour écrire. Il n'y a donc aucun sens à rendre un fileReader disponible pour la méthode writeFile, et ainsi de suite.

Pour revenir à votre question d'origine, oui, cela instancie un nouvel objet à chaque appel de la méthode. Ce n'est pas important. Il est préférable de se demander "pourquoi la méthode readFile a-t-elle accès à une instance FileWriter?

4
Tom

Supposons maintenant que j'invoque myMethod (), disons, 10 fois pendant l'exécution de mon programme, comment cela fonctionne-t-il? Un nouvel objet est-il créé à chaque fois?

Oui!

La réutilisation ou la non-réutilisation d'un objet instancié dépend de la conception et de la situation. Il y a des cas où il est préférable de réutiliser des objets, auquel cas vous pouvez créer un champ de classe pour conserver la référence, et il y a des cas où il est préférable de créer un nouvel objet à chaque fois (regardez l'immuabilité, par exemple).

2
Bala R

si vous appelez 10 fois, il y aura 10 cadres de méthode dans votre pile Java, chaque cadre agira sur une nouvelle action () et quand le cadre sera terminé, il sera libéré.

enter image description here

2
user not found

Chaque fois que vous appelez la méthode myMethod, le code s'exécute par le haut sans mémoire sur ce qu'il a fait lors des exécutions précédentes (sauf, bien sûr, si vous avez modifié certains champs de l'objet MyClass. signifie que chaque fois que vous exécutez la méthode, vous allez créer un nouvel objet AnotherClass et le stocker dans myObject. Plus généralement, chaque exécution d'une méthode exécutera le code à partir du haut et ne évitez de recalculer les valeurs même si elles auraient pu être mises en cache à partir d'itérations précédentes, sauf si vous stockez explicitement les valeurs quelque part.

Si ce n'est pas ce que vous voulez, et que vous souhaitez plutôt stocker l'objet que vous avez alloué afin que lors d'itérations futures vous puissiez le référencer à nouveau, vous pouvez le stocker dans une variable d'instance de la classe.

0
templatetypedef

Un nouvel objet est créé à chaque appel de la méthode. Si le contrôle atteint une ligne de code avec un "nouvel" opérateur dessus, alors un objet va être créé; il n'y a pas de mise en cache dans les coulisses ou d'autre magie.

0