web-dev-qa-db-ja.com

Javaで 'new'キーワードは実際に何をしますか。新しいオブジェクトの作成を避けるべきですか?

少し前にサインアップしましたが、コンピュータープログラミングを始めて以来、このサイトを大いに活用してきました。

私は同様の質問を探しましたが、実際に私が探していた答えが見つかりませんでした。ここで、Java(これは私が最初に提案した言語です))では、必要に応じて変数を宣言およびインスタンス化するのが良いプログラミング慣行であると考えられています。行:

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

ここで、プログラムの実行中にmyMethod()を10回呼び出した場合、どのように機能するのでしょうか?毎回新しいオブジェクトが作成されますか? myObject変数は毎回再割り当てされますか?コンパイラーは、オブジェクトが既に作成されており、変数myObjectがそのようなオブジェクトにすでに割り当てられていることを確認するため、そのようなコードをスキップしますか?簡単に言うと、そのメソッドを1回だけ呼び出す場合にのみ、そのようなコードを記述する必要がありますか?私は知っている...そのような愚かな質問をするのは恥だが、チャンスをくれ!前もって感謝します!

---------------------------編集---------------------- -------

だから今、私は新しい答えを得た後、この投稿を編集することになっていますか?ところで...すみません、ありがとうございました!そして、それは私をとても混乱させました、私はそれを私が自分自身を教えてきたという事実によると思います...とにかく、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を、他の変数の場合と同様にクラスの最上部で宣言しないのはなぜですか?そして、おそらくコンストラクターでそれらを初期化しないのはなぜですか?同じインスタンス変数を使用するのではなく、メソッドが呼び出されるたびに実行するのはなぜですか?

31
zeme

はい、myMethod()を10回呼び出した場合、10個の一意で個別のオブジェクトが作成されます。

newキーワードは、ブリキに書かれていることを正確に実行し、既に存在するかどうかに関係なく、まったく新しいオブジェクトを作成します。新しいオブジェクトを作成し、与えられた変数内にそのオブジェクトへの参照を詰め込み、変数が保持していた以前の値(オブジェクト)を上書きします。

MyObject変数は毎回再割り当てされますか?

繰り返しますが、はい、メソッドが呼び出されるたびに新しいオブジェクトで再割り当てされます。これに関する興味深い注意点は、メソッド本体自体で変数を定義しているため、変数が「実際に」再割り当てされないため、メソッドが終了するたびに、そのスコープ内で定義された変数が削除されることです。 。したがって、実際に行うことは、10個の個別変数を作成して10個の個別オブジェクトを割り当てることですが、他のメモリは追加メモリを使用しないように自動的に削除する必要があります。

簡単に言うと、そのメソッドを1回だけ呼び出す場合にのみ、そのようなコードを記述する必要がありますか?

さっきも言ったように、上記の例では、各オブジェクトはメソッド実行の最後に破棄されます(メソッドのスコープ外のオブジェクト参照を変数に割り当てなかったと仮定)。そのため、この例では、メソッドを喜んで呼び出すことができます何度でも好きなだけ何度でも、以前の呼び出しに接続されることは決してありません。

私の執筆方法は混乱を招く可能性があることを理解しています。

編集した質問を反映するように回答を更新しました

「FileWriter、FileReader、BufferedReader、BufferedWriterを他の変数のようにクラスの先頭で宣言しないのはなぜですか?」

さて、変数は実際にはFileWriterFileReaderBufferedReader、およびBufferedWriterと呼ばれるのではなく、変数型であると理解していると思います。それらの名前は、fwfrbr、およびbwです。あなたが私が意味することを理解していないなら、ただ尋ねます。これからは、読みやすくするために変数を名前で参照します。結局、fwFileWriterの略であるため、あまり混乱しないようにします。

この質問の鍵は、変数自体の名前の中に隠されています。それらがReaderまたはWriterで終わることに注意してください。これにより、それらの使用に関する微妙な手がかりが得られます。明らかに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!

タイプが単純に一致しないためです。

理にかなっていますか?

23
linuscash

はい、毎回新しいオブジェクトが作成されます。各myObjectへの参照はスタックに割り当てられます。

簡単に言うと、そのメソッドを1回だけ呼び出す場合にのみ、そのようなコードを記述する必要がありますか?

メソッドの実行が完了した後にmyObjectを消したい場合は、はい。何らかの理由で参照を保持する必要がある場合は、クラスメンバーとして宣言できます。

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

この方法では、myMethod()を呼び出すたびに作成されますが、myMethodの完了後も存在します。これは状況に応じて、便利な場合とそうでない場合があります。

コンパイラーは、オブジェクトが既に作成されており、変数myObjectがそのようなオブジェクトにすでに割り当てられていることを確認するため、そのようなコードをスキップしますか?

newを使用している場合、これは起こりません。新しいインスタンスが作成されることが保証されています。 FactoryMethods(コンパイラはコードの行をスキップしないが、新しいオブジェクトの作成を防ぐ)を使用して実装できます。たとえば、 整数クラス はこれを実装します:_-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
_

質問更新後

追加したコードについて実際に言うことはあまりありません。ただし、見てみると、2つの方法がわかります。その名前に基づいて、かつて書いているように見えますが、もう一方は読んでいるようです。この動作は各メソッドに固有であるため、writeFileが読み取りに使用されるオブジェクトを気にしないメソッドです。また、メソッドreadFileは、書き込みに使用されるオブジェクトを気にしません。したがって、fileReaderwriteFileメソッドで使用可能にするなどの意味はありません。

元の質問に戻ると、はい、これはメソッドが呼び出されるたびに新しいオブジェクトをインスタンス化します。大事なことじゃない。 「なぜreadFileメソッドがFileWriterインスタンスにアクセスするのですか?」

4
Tom

ここで、プログラムの実行中にmyMethod()を10回呼び出すと仮定します。これはどのように機能しますか?毎回新しいオブジェクトが作成されますか?

はい!

インスタンス化されたオブジェクトを再利用するかどうかは、設計と状況によって異なります。オブジェクトを再利用した方が良い場合があります。その場合、参照を保持するクラスフィールドを作成できます。また、毎回新しいオブジェクトを作成するのが最適な場合があります(たとえば、不変性を調べます)。

2
Bala R

10回呼び出すと、Javaスタックに10個のメソッドフレームがあり、各フレームはnew()アクションを実行し、フレームが終了すると解放されます。

enter image description here

2
user not found

myMethodメソッドを呼び出すたびに、コードは前の実行で行われたことをメモリなしで上から実行します(もちろん、MyClassオブジェクトの一部のフィールドを変更した場合を除きます。つまり、メソッドを実行するたびに、新しいAnotherClassオブジェクトを作成してmyObjectに保存します。より一般的には、メソッドを実行するたびにコードが上から実行され、値をどこかに明示的に保存しない限り、以前の反復でキャッシュされた可能性がある場合でも、値の再計算を避けます。

これが必要なものではなく、代わりに割り当てたオブジェクトを保存して、将来の反復で再び参照できるようにする場合は、クラスのインスタンス変数に保存できます。

0
templatetypedef

メソッドが呼び出されるたびに、新しいオブジェクトが作成されます。 「new」演算子が設定されたコード行に制御が到達すると、オブジェクトが作成されます。舞台裏のキャッシュやその他の魔法はありません。