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

时间:2011-08-11 01:10:33

标签: java object constructor new-operator

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

我确实在寻找类似的问题,但实际上我找不到我想要的答案。 现在,意识到,在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 个答案:

答案 0 :(得分:22)

是的,如果您拨打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!

因为类型根本不匹配。

有意义吗?

答案 1 :(得分:4)

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

  

简而言之:我应该只在我打算调用的时候编写这样的代码   那种方法只有一次?

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

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

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

  

当编译器看到该对象时,编译器会跳过代码   已经创建了变量myObject   分配给这样的对象?

使用new时不会发生这种情况。它保证会创建一个新的实例。它可以使用FactoryMethods (不是编译器跳过代码行,但阻止创建新对象)来实现。例如,Integer class实现了这一点:如果你试图获得-128127之间的整数,它将在使用其工厂时返回相同的实例(不会创建新对象)方法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 :(得分:2)

  

现在,假设我在运行my时调用了myMethod()10次   程序,这是如何工作的?每次都会创建一个新对象吗?

是!

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

答案 3 :(得分:2)

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

enter image description here

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

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