簡體   English   中英

'new'關鍵字在Java中實際上做了什么,我應該避免創建新對象嗎?

[英]What does the 'new' keyword actually do in Java, and should I avoid creating new objects?

我剛剛注冊了一段時間,但是自從我開始學習計算機編程以來,我一直在很好地利用這個網站,我一直在自學並考慮我的一點興趣。

我確實在尋找類似的問題,但實際上我找不到我想要的答案。 現在,意識到,在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”運算符,那么就會創建一個對象; 沒有幕后緩存或其他魔法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM