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

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

Я искал похожие вопросы, но на самом деле я не мог найти ответ, который искал. Теперь, зная, что в 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 в верхней части класса, как они сделали для других переменных? и почему бы их не инициализировать, может быть, в конструкторе? Зачем делать это каждый раз, когда вызывается метод, а не использовать, возможно, одну и ту же переменную экземпляра?

Ответы [ 6 ]

23 голосов
/ 11 августа 2011

Да, если вы позвоните 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!

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

Имеет смысл?

4 голосов
/ 11 августа 2011

Да, новый объект создается каждый раз.Ссылка на каждый 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?

2 голосов
/ 11 августа 2011

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

enter image description here

2 голосов
/ 11 августа 2011

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

Да!

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

0 голосов
/ 11 августа 2011

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

0 голосов
/ 11 августа 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...