Что на самом деле делает ключевое слово 'new' в Java, и мне следует избегать создания новых объектов?

java object constructor new-operator

65806 просмотра

6 ответа

Я зарегистрировался несколько минут назад, хотя я очень активно использовал этот сайт с тех пор, как начал заниматься компьютерным программированием, которому я сам учил и считаю своим небольшим увлечением.

Я искал похожие вопросы, но на самом деле я не мог найти ответ, который искал. Теперь, зная, что в Java (это язык, с которого мне предложили начать), считается хорошей практикой программирования объявлять и создавать экземпляры переменных по мере необходимости, пожалуйста, обратите внимание на следующие строки:

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

Теперь предположим, что я запускаю myMethod (), скажем, 10 раз при запуске моей программы, как это работает? Новый объект создается каждый раз? Перемещается ли переменная myObject каждый раз? Не пропускает ли компилятор подобный код, когда видит, что объект уже создан и переменная myObject уже назначена этому объекту? В двух словах: должен ли я писать такой код, только если я планирую вызывать этот метод только один раз? Я знаю ... позор, что я задал такой глупый вопрос, но, пожалуйста, дайте мне шанс! Заранее спасибо!

--------------------------- отредактировано ---------------------- -------

Так что теперь я должен редактировать этот пост после получения новых ответов? кстати ... черт возьми, это было быстро, большое спасибо! И это очень меня смутило, я думаю, это связано с тем, что я учил себя так ... В любом случае, не бесполезно ли каждый раз создавать new AnotherClassобъект для myObjectпеременной? Я имею в виду, если я хочу использовать переменную myObject в моей программе, не должен ли я объявить ее раз и навсегда? может быть, другим способом, который я собираюсь вызвать только один раз? Потому что, насколько я понимаю, каждый раз, когда я вызываю myMethod()новый объект, создается, переопределяя, таким образом, собственные свойства myObject или переменные, или я просто говорю чепуху?

--------------------------- отредактировано ---------------------- -------

Мои сомнения возникли после прочтения этого кода с какого-то сайта, который я сейчас не помню:

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

Я имею в виду, почему бы не объявить FileWriter, FileReader, BufferedReader и BufferedWriter в верхней части класса, как они сделали для других переменных? и почему бы их не инициализировать, может быть, в конструкторе? Зачем делать это каждый раз, когда вызывается метод, а не использовать, возможно, одну и ту же переменную экземпляра?

Автор: zeme Источник Размещён: 12.11.2019 09:41

Ответы (6)


23 плюса

Да, если вы звоните myMethod()10 раз, он создаст 10 уникальных и отдельных объектов.

newКлючевое слово делает именно то , что он говорит по олову, он создает новый объект, независимо от того, существует ли уже один. Он создает новый объект и вставляет ссылку на этот объект в указанную переменную, перезаписывая любое предыдущее значение (объект), содержащееся в переменной.

Перемещается ли переменная myObject каждый раз?

Опять же, да, он будет перераспределяться с новым объектом каждый раз, когда вызывается метод. Интересно отметить, что переменная «на самом деле» не будет перераспределена, поскольку вы определяете переменную внутри самого тела метода, поэтому каждый раз, когда метод завершается, он удаляет переменные, которые были определены в его области видимости. , Так что на самом деле он создает 10 отдельных переменных и назначает 10 отдельных объектов, хотя, как я сказал, остальные должны были быть удалены автоматически, чтобы не использовать дополнительную память.

В двух словах: должен ли я писать такой код, только если я планирую вызывать этот метод только один раз?

Как я уже сказал, в приведенном выше примере каждый объект будет уничтожен в конце выполнения метода (при условии, что вы не присваивали ссылку на объект переменной, выходящей за рамки метода), поэтому в своем примере вы могли бы с радостью вызвать метод столько раз, сколько вы хотите, но каждый раз никоим образом не будет связан с предыдущими вызовами.

Я понимаю, что мой способ написания может сбивать с толку, поэтому, если вы хотите, чтобы я что-то разъяснил, просто спросите.

Обновленный ответ для отражения отредактированного вопроса

«Почему бы не объявить FileWriter, FileReader, BufferedReader и BufferedWriter в верхней части класса, как они сделали для других переменных?»

Хорошо, я полагаю , вы понимаете , что переменные не на самом деле называется FileWriter, FileReader, BufferedReader, и BufferedWriter, а это переменный тип. Их имена fw, fr, br, и bw. Если вы не понимаете, о чем я, просто спросите. Отныне я буду ссылаться на переменные по именам, которые вы делали, чтобы сделать чтение более легким, fwведь в FileWriterлюбом случае это просто означает, что не должно быть слишком много путаницы.

Ключ к этому вопросу скрыт в именах самих переменных. Обратите внимание, как они заканчиваются, Readerили Writerэто может дать нам тонкую подсказку об их использовании. Понятно FileWriterи BufferedWriterкак-то связано с выводом. Просматривая код, мы видим, что наши подозрения были верны и что ни в коем случае, кроме как вwriteText(JTextArea area)метод, эти переменные появляются. Таким образом, если переменная не используется где-либо еще в коде, было бы логично определить и инициализировать их в методе, в котором они используются, и это не только облегчит чтение кода, потому что мы затем «знаем» эти переменные относятся только к этому методу, но также имеет то преимущество, что эти переменные удаляются в конце выполнения метода, тем самым не оставляя существующие переменные, которые использовались очень кратко. По этим правилам мы можем сказать то же самое верно FileReaderи для BufferedReader.

Обратите внимание на этот пример с областью действия переменной. (Посмотрите на комментарии, которые я добавил к коду)

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

Из этого примера становится понятнее, почему переменные определяются в методах, а не в качестве переменных экземпляра и инициализируются в конструкторе. Это позволяет гораздо более чистый код, а также быть более читабельным.

Зачем делать это каждый раз, когда вызывается метод, а не использовать, возможно, одну и ту же переменную экземпляра?

Ну, этот вопрос касается типов переменных. Мы не могли повторно использовать одну переменную для всей информации, так как типы должны были бы быть разными.

Если мы возьмем все переменные из кода

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

Теперь мы знаем, что мы не можем поместить значение, которое отличается от типа переменной, в эту переменную, что-то вроде

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

Потому что типы просто не совпадают.

Есть смысл?

Автор: linuscash Размещён: 11.08.2011 01:33

4 плюса

Да, новый объект создается каждый раз. Ссылка на каждый myObjectразмещается в стеке.

В двух словах: должен ли я писать такой код, только если я планирую вызывать этот метод только один раз?

Если вы хотите myObjectисчезнуть после завершения выполнения метода, тогда да. Если по какой-либо причине вам необходимо сохранить ссылку на него, вы можете объявить его членом класса.

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

Таким образом, он будет создаваться при каждом вызове myMethod(), но он будет существовать после myMethodзавершения. Это может быть удобно или нет, в зависимости от ситуации.

Не пропускает ли компилятор подобный код, когда видит, что объект уже создан и переменная myObject уже назначена этому объекту?

Это не произойдет при использовании new. Гарантируется, что это создаст свежий экземпляр. Это может быть реализовано с использованием FactoryMethods (не пропуская строки кода компилятором, но предотвращая создание нового объекта) . Например, класс Integer реализует это: если вы попытаетесь получить целое число между -128и 127, он всегда будет возвращать один и тот же экземпляр (не будет создавать новый объект) при использовании его метода Factory.valueOf

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

 assert(five==otherFive);//true

Конечно, использование не newбудет возвращать тот же экземпляр, но всегда новый

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

 assert(five==otherFive);//false

после обновления вопроса

Там действительно не так много, чтобы сказать о коде, который вы добавили. Однако, если вы посмотрите, вы заметите два метода. Основываясь на своих именах, один раз, кажется, пишет, другой, кажется, читает. Такое поведение специфично для каждого метода, поэтому метод, writeFileкоторый не заботится об объектах, используемых для чтения. И этот метод readFileне заботится об объектах, используемых для записи. Так что нет смысла делать метод fileReaderдоступным writeFileи так далее.

Возвращаясь к исходному вопросу, да, это создает новый объект каждый раз, когда вызывается метод. Это не важно. Желательно спросить себя: «Почему readFileметод имеет доступ к FileWriterэкземпляру?

Автор: Tom Размещён: 11.08.2011 01:15

2 плюса

Теперь предположим, что я запускаю myMethod (), скажем, 10 раз при запуске моей программы, как это работает? Новый объект создается каждый раз?

Да!

Повторное использование или не повторное использование экземпляра объекта зависит от дизайна и ситуации. Есть случаи, когда лучше повторно использовать объекты, и в этом случае вы можете создать поле класса, чтобы сохранить ссылку, и есть случаи, когда лучше каждый раз создавать новый объект (например, посмотрите на неизменность).

Автор: Bala R Размещён: 11.08.2011 01:15

2 плюса

если вы вызываете 10 раз, в вашем стеке Java будет 10 кадров метода, каждый кадр будет выполнять действие new (), а когда кадр будет завершен, он будет освобожден.

введите описание изображения здесь

Автор: user not found Размещён: 11.08.2011 02:03

0 плюса

Каждый раз, когда вы вызываете myMethodметод, код выполняется сверху без памяти о том, что он делал в предыдущих выполнениях (если, конечно, вы не изменили некоторые поля MyClassобъекта. Это означает, что каждый раз, когда вы запускаете метод, вы будете создавать новый AnotherClassобъект и сохраните его myObject. В более общем смысле, каждое выполнение метода будет запускать код сверху и не будет препятствовать повторному вычислению значений, даже если они могли быть кэшированы из предыдущих итераций, если вы явно не сохраните значения где-либо.

Если это не то, что вам нужно, и вы хотите сохранить выделенный объект, чтобы на последующих итерациях вы могли ссылаться на него снова, вы можете сохранить его в переменной экземпляра класса.

Автор: templatetypedef Размещён: 11.08.2011 01:15

0 плюса

Новый объект создается каждый раз, когда вызывается метод. Если элемент управления достигнет строки кода с оператором «new», то объект будет создан; за кулисами нет никакого кеширования или другой магии.

Автор: Ernest Friedman-Hill Размещён: 11.08.2011 01:15
Вопросы из категории :
32x32