Что на самом деле делает ключевое слово "новое" в Java и следует ли избегать создания новых объектов?
Автор вопроса: zemeОтветов: 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 вверху класса, как это было сделано для других переменных? и почему бы не инициализировать их, может быть, в конструкторе? Зачем делать это каждый раз при вызове метода, а не использовать, возможно, одну и ту же переменную экземпляра?
Глупых вопросов не бывает... Tom 11.08.2011
Итак, теперь я должен редактировать этот пост после того, как получу новые ответы? Не всегда, только если есть обновление исходного вопроса или что-то, что остается неясным. Tom 11.08.2011
Я спросил это, потому что я не могу идти в ногу! Вы довольно часто редактируете свои ответы, что, безусловно, здорово, просто я не ожидал такого отличного ответа и не знаю, к какому сообщению прокомментировать или оставить отзыв! zeme 11.08.2011
если вы хотите использовать один и тот же экземпляр каждый раз, вам нужно где-то его сохранить. обычно для этого и нужны члены экземпляра. andrew cooke 11.08.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!
Потому что типы просто не совпадают.
Есть смысл?
Ответ 2 · рейтинг 4 · Tom
Да, каждый раз создается новый объект. Ссылка на каждый myObject размещается в стеке.
Если вы хотите, чтобы myObject исчезло после завершения выполнения метода, то да. Если по какой-то причине вам нужно сохранить ссылку на него, вы можете объявить его как член класса.
class MyClass {
AnotherClass myObject;
void myMethod() {
myObject = new AnotherClass();
myObject.doStuff();
}
}
Таким образом, он все еще будет создаваться каждый раз, когда вы вызываете myMethod(), но он все еще будет существовать после завершения myMethod. Это может быть удобно или нет, в зависимости от ситуации.
Пропускает ли компилятор подобный код, поскольку видит, что объект уже создан и переменная myObject уже присвоена такому объекту?
Этого не произойдет при использовании new. Гарантируется, что он создаст новый экземпляр. Его можно реализовать с помощью FactoryMethods(компилятор не пропускает строки кода, но предотвращает создание нового объекта). Например, класс Integer реализует это: если вы попытаетесь получить целое число между -128 и 127, оно всегда будет возвращать один и тот же экземпляр (не будет создавать новый объект) при использовании своего фабричного метода 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?
Ответ 3 · рейтинг 2 · Bala R
Теперь предположим, что я вызываю myMethod(), скажем, 10 раз во время выполнения моей программы, как это работает? Каждый раз создается новый объект?
Да!
Повторное использование или отказ от повторного использования созданного объекта зависит от дизайна и ситуации. Есть случаи, когда лучше повторно использовать объекты, и в этом случае вы можете создать поле класса для хранения ссылки, а есть случаи, когда лучше каждый раз создавать новый объект (например, изучить неизменность).
Ответ 4 · рейтинг 2 · dongjk
если вы вызываете 10 раз, в вашем java-стеке будет 10 фреймов методов, каждый фрейм будет выполнять действие new(), и когда кадр завершится, он будет выпущен.
Ответ 5 · templatetypedef
Каждый раз, когда вы вызываете метод myMethod, код выполняется сверху без памяти о том, что он делал при предыдущих выполнениях (если, конечно, вы не меняли некоторые поля объекта MyClass. Это означает, что каждый раз, когда вы запускаете метод, вы создаст новый объект AnotherClass и сохранит его в myObject. В более общем случае каждое выполнение метода будет запускать код сверху и не будет избегать повторного вычисления значений, даже если они могли быть кэшированы с предыдущих итераций, если вы явно не сохраните значения где-то .
Если это не то, что вам нужно, и вместо этого вы хотите сохранить выделенный вами объект, чтобы в будущих итерациях вы могли снова ссылаться на него, вы можете сохранить его в переменной экземпляра класса.
Ответ 6 · Ernest Friedman-Hill
Каждый раз при вызове метода создается новый объект. Если управление достигает строки кода с "новым" оператором, то будет создан объект; нет никакого закулисного кэширования или другой магии.