‘new’关键字在Java中实际上做了什么,我应该避免创建新对象吗?

我刚刚注册了,但是自从我开始学习计算机编程以来,我一直在很好地利用这个网站,我一直在自学并考虑我的一点兴趣。

我确实在寻找类似的问题,但实际上我找不到我想要的答案。 现在,意识到,在Java(这是我建议开始使用的语言)中,根据需要声明和实例化变量被认为是很好的编程习惯,请考虑以下几行:

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

现在,假设我在运行程序时调用了myMethod()10次,那有什么用呢? 每次创建一个新对象吗? 是否每次都重新分配myObject变量? 编译器是否会跳过代码,因为它看到对象已经创建并且变量myObject已经分配给了这样的对象? 简而言之:只有当我打算只调用一次该方法时,我才能编写这样的代码吗? 我知道……因为问我这么愚蠢的问题而感到羞耻,但请给我一个机会! 提前致谢!

—————————编辑———————- ——-

所以现在我应该在得到新答案后编辑这篇文章吗? 顺便说一下…天哪,很快,非常感谢! 哇我很困惑,我想这是因为我一直在教自己……不管怎么说,每次为myObject变量创建一个new AnotherClass对象都没用? 我的意思是,如果我想在整个程序中使用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,就像它们为其他变量所做的那样? 为什么不在构造函数中初始化它们呢? 为什么每次调用方法时都这样做而不是使用相同的实例变量?

是的,如果您调用myMethod() 10次​​,它将创建10个唯一且独立的对象。

new关键字完全按照它在锡上所说的那样,它创建了一个全新的对象,而不管它是否已经存在。 它创建一个新对象,并在给定的变量内填充对该对象的引用,覆盖变量所持有的任何先前值(对象)。

是否每次都重新分配myObject变量?

同样,是的,每次调用该方法时都会重新分配一个新对象。 关于这一点的一个有趣的注意事项是,当你在方法体本身中定义变量时,变量不会“真正”重新分配,因此每次方法结束时它将删除在其范围内定义的变量。 。 所以它实际上做的是创建10个单独的变量并分配10个单独的对象,尽管正如我所说的其他对象应该被自动删除所以它不会使用任何额外的内存。

简而言之:只有当我打算只调用一次该方法时,我才能编写这样的代码吗?

正如我所说,在上面的例子中,每个对象都会在方法执行结束时被销毁(假设你没有将对象引用分配给方法范围之外的变量)所以在你的例子中你可以愉快地调用方法尽可能多次,但每次都不会连接到以前的呼叫。

我意识到我的写作方式可能令人困惑,所以如果你想让我澄清任何事情只是问。

更新答案以反映编辑的问题

‘为什么不像在其他变量中那样在类的顶部声明FileWriter,FileReader,BufferedReader和BufferedWriter?’

好吧,我假设您理解变量实际上并不是FileWriterFileReaderBufferedReaderBufferedWriter ,而是变量类型。 他们的名字是fwfrbrbw 。 如果你不明白我的意思只是问。 从现在开始,我将通过你所做的名称来引用变量,使阅读变得更加简单,毕竟fw FileWriter ,所以不应该有太多的混乱。

这个问题的关键隐藏在变量本身的名称中。 注意它们如何以ReaderWriter结尾,这可以给我们一个关于它们用途的微妙线索。 显然, FileWriterBufferedWriter以某种方式与输出有关。 通过查看代码,我们看到我们的怀疑是正确的,除了在writeText(JTextArea area)方法之外,这些变量不会出现。 因此,如果变量未在代码中的任何其他位置使用,那么在它们所使用的方法中定义和初始化它们将具有逻辑意义,这不仅使代码更容易阅读,因为我们“知道”那些变量只与该方法有关,但也有利于在方法执行结束时删除这些变量,从而不会留下仅仅非常简单地使用的变量。 根据这些规则,我们可以说FileReaderBufferedReader也是如此。

观察有关变量范围的示例。 (看看我添加到代码中的评论)

 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! 

因为类型根本不匹配。

合理?

是的,每次都会创建一个新对象。 每个myObject的引用都在堆栈中分配。

简而言之:只有当我打算只调用一次该方法时,我才能编写这样的代码吗?

如果您希望myObject在方法执行完成后消失,那么是。 如果由于某种原因,您需要保留对它的引用,那么您可以将它声明为类成员。

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

这样,每次调用myMethod()时仍会创建它,但在myMethod完成后它仍然存在。 根据具体情况,这可能很方便,也可能不方便。

编译器是否会跳过代码,因为它看到对象已经创建并且变量myObject已经分配给了这样的对象?

使用new时不会发生这种情况。 它保证会创建一个新的实例。 它可以使用FactoryMethods实现(不是编译器跳过代码行,而是阻止创建新对象) 。 例如, Integer类实现了这一点:如果尝试获取介于-128127之间的整数,则在使用其Factory Method 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实例?

现在,假设我在运行程序时调用了myMethod()10次,那有什么用呢? 每次创建一个新对象吗?

是!

重用或不重用实例化对象取决于设计和情况。 在某些情况下,重用对象会更好,在这种情况下,您可以创建一个类字段来保留引用,并且在某些情况下,每次最好创建一个新对象(例如,查看不变性)。

如果你调用10次,你的java堆栈中将有10个方法框架,每个框架将执行new()动作,当框架完成时,它将释放。

在此处输入图像描述

每次调用myMethod方法时,代码都从顶部执行,没有关于它在先前执行时所做的事情的内存(当然,除非您更改了MyClass对象的某些字段。这意味着每次运行该方法时,将创建一个新的AnotherClass对象并将其存储在myObject 。更一般地说,方法的每次执行都将从顶部运行代码,并且不会避免重新计算值,即使它们可能已经从先前的迭代缓存,除非您明确地将值存储在某处。

如果这不是你想要的,而你想要存储你分配的对象,以便在将来的迭代中你可以再次引用它,你可以将它存储在类的实例变量中。

每次调用方法时都会创建一个新对象。 如果控件到达一行代码并且上面有一个“new”运算符,那么就会创建一个对象; 幕后没有缓存或其他魔法。