“new”关键字在Java中实际上做了什么,我应该避免创建新对象吗?更新了答案以反映编辑后的问题问题更新后

2022-09-01 09:01:20

我不久前注册了,尽管自从我开始学习计算机编程以来,我一直在充分利用这个网站,我一直在自学并考虑我的一点爱好。

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

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

现在,假设我在运行程序时调用myMethod(),比如说,在运行我的程序时调用10次,这是如何工作的?是否每次都会创建新对象?是否每次都重新分配 myObject 变量?编译器是否跳过了类似代码的内容,因为它看到对象已经创建,并且变量 myObject 已分配给此类对象?简而言之:只有当我计划只调用该方法一次时,我是否应该编写这样的代码?我知道。。。我问了这么愚蠢的问题,这让我感到羞耻,但请给我一个机会!提前致谢!

---------------------------编辑-----------------------------

那么现在我应该在得到新答案后编辑这篇文章吗?顺便说一句...天哪,这很快,非常感谢!哇,这让我感到困惑,很多,我想这是由于我一直在自学的事实......无论如何,每次都为变量创建一个对象不是没用吗?我的意思是,如果我想在整个程序中使用myObject变量,我不应该一劳永逸地声明它吗?也许在另一种方法中,我只调用一次?因为据我所知,每次我调用一个新对象都在创建,从而覆盖myObject自己的属性(即变量),或者我只是在胡说八道?new AnotherClassmyObjectmyMethod()

---------------------------编辑-----------------------------

在从一些我现在不记得的网站上阅读此代码后,我的怀疑来了:

    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,就像它们对其他变量所做的那样?为什么不在构造函数中初始化它们呢?为什么每次调用方法时都这样做,而不是使用相同的实例变量?


答案 1

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

关键字完全符合它在锡上所说的内容,它创建了一个全新的对象,无论一个是否已经存在。它创建一个新对象,并将对该对象的引用填充到给定的变量中,覆盖该变量保存的任何先前值(对象)。new

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

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

简而言之:只有当我计划只调用该方法一次时,我是否应该编写这样的代码?

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

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

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

“为什么不在类的顶部声明FileWriter,FileReader,BufferedReader和BufferedWriter,就像它们对其他变量所做的那样?”

好的,我假设你明白变量实际上并不被称为、、、和,而是变量类型。它们的名称是 、 、 和 。如果你不明白我的意思,就问问。从现在开始,我将通过您为使阅读更容易而使用的名称来引用变量,毕竟无论如何都只是代表,因此不应该有太多的混淆。FileWriterFileReaderBufferedReaderBufferedWriterfwfrbrbwfwFileWriter

这个问题的关键隐藏在变量本身的名称中。请注意它们是如何结束的,或者这可以给我们一个关于它们用途的微妙线索。显然,并且在某种程度上与输出有关。通过查看代码,我们发现我们的怀疑是正确的,并且除了在方法中之外,这些变量不会出现。因此,如果变量没有在代码中的其他任何地方使用,那么在使用它们的方法中定义和初始化它们是合乎逻辑的,它不仅使代码更易于阅读,因为我们“知道”这些变量仅与该方法相关,而且还具有在方法执行结束时删除这些变量的好处, 因此,不会留下只非常短暂使用的变量。根据这些规则,我们可以说 和 也是如此。ReaderWriterFileWriterBufferedWriterwriteText(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!

因为类型根本不匹配。

有意义?


答案 2

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

简而言之:只有当我计划只调用该方法一次时,我是否应该编写这样的代码?

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

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

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

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

使用 时不会发生这种情况。可以保证它将创建一个新实例。它可以使用FactoryMethods实现(不是编译器跳过代码行,而是阻止创建新对象)。例如,Integer 类实现:如果您尝试获取 和 之间的整数,则在使用其 Factory Method 值Of 时,它将始终返回相同的实例(不会创建新对象)。new-128127

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

 assert(five==otherFive);//true

当然,使用 won 不会返回相同的实例,但始终返回一个新实例new

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

 assert(five==otherFive);//false

问题更新后

关于您添加的代码,真的没什么可说的。但是,如果您看一下,您会注意到两种方法。根据它的名字,一次似乎写,另一次似乎读。该行为特定于每种方法,因此该方法不关心用于读取的对象。而且该方法不关心用于写入的对象。因此,将一个可用于该方法是没有意义的,依此类推。writeFilereadFilefileReaderwriteFile

回到您最初的问题,是的,每次调用该方法时,此操作都会实例化一个新对象。这并不重要。最好是问自己“为什么该方法可以访问实例?readFileFileWriter