27
public class StackOverFlow {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<String>();
        al.add("A");
        al.add("B");
        markAsNull(al);
        System.out.println("ArrayList elements are "+al);

        String str = "Hello";
        markStringAsNull(str);
        System.out.println("str "+ str);
    }
    private static void markAsNull(ArrayList<String> str){
        str.add("C");
        str= null;
    }
    private static void markStringAsNull(String str){
        str = str + "Append me";
        str = null;
    }
}

これは以下を出力します:

ArrayList elements are [A, B, C]
str Hello

の場合ArrayList、追加された要素が取得されます。Stringメソッド呼び出しの場合、渡される文字列には影響しません。JVMは正確に何をしているのですか?誰か詳しく説明してくれませんか?

4

5 に答える 5

21

Arraylist 文字列オブジェクトの場合、追加された要素が取得されます。String の場合、メソッド呼び出しは、渡される String には影響しません。

Javaが値渡しであり、Stringsが不変であるために発生します

電話すると

markAsNull(ArrayList<String> str)

名前による新しい参照strは、 が指す同じものに対して作成ArrayListされalます。そのadd上の要素strが同じオブジェクトに追加されると。後で入れますstrnull、オブジェクトには新しい値が追加され、 によってポイントされa1ます。

電話すると

markStringAsNull(String str)
{
    str = str + "Append me";
    // ...
}

この行は、指定された文字列を追加しstr = str + "Append me";て新しいオブジェクトを作成し、それを に割り当てます。しかし、これは実際の文字列への参照にすぎず、新しく作成された文字列を指しています。(不変のため) 元の文字列は変更されません。Stringstr

于 2013-04-08T05:33:00.373 に答える
5

markXAsNullメソッドは、ローカル参照を に設定していますnull。これは、その場所に格納されている実際の値には影響しません。メソッドには値へのmain独自の参照がまだあり、printlnそれらを使用して呼び出すことができます。

また、文字列の連結を行うときtoString()はオブジェクトで呼び出されているため、ArrayList は括弧内の値のリストとして出力されます。

于 2013-04-08T05:28:42.170 に答える
5

本から: SCJP - Sun Certified Programmer for Java 6 Study Guide (Katty Sierra - Bert Bates) Chapter 3 Objective 7.3 - Passing Variables Into Methods

Java は、実際には、単一の VM 内で実行されるすべての変数に対して値渡しです。値渡しとは、変数値渡しという意味です。つまり、変数のコピーによる受け渡しです。

値渡しの要点: 呼び出されたメソッドは呼び出し元の変数を変更できませんが、オブジェクト参照変数の場合、呼び出されたメソッドは変数が参照するオブジェクトを変更できます。変数の変更とオブジェクトの変更の違いは何ですか? オブジェクト参照の場合、呼び出されたメソッドが呼び出し元の元の参照変数を再割り当てして、別のオブジェクトまたは null を参照できないことを意味します。たとえば、次のコード フラグメントでは、

void bar() {
Foo f = new Foo();
doStuff(f);
}
void doStuff(Foo g) {
g.setName("Boo");
g = new Foo();
}

g を再割り当てしても、f は再割り当てされません! bar() メソッドの最後で、2 つの Foo オブジェクトが作成されています。1 つはローカル変数 f によって参照され、もう 1 つはローカル (引数) 変数 g によって参照されます。doStuff() メソッドには参照変数のコピーがあるため、たとえば setName() メソッドを呼び出すなどして、元の Foo オブジェクトにアクセスする方法があります。ただし、doStuff() メソッドには f 参照変数を取得する方法がありません。そのため、doStuff() は f が参照するオブジェクト内の値を変更できますが、doStuff() は f の実際の内容 (ビット パターン) を変更できません。つまり、doStuff() は f が参照するオブジェクトの状態を変更できますが、f に別のオブジェクトを参照させることはできません!

于 2013-04-08T05:41:03.373 に答える
2

Java は値の受け渡しの概念に従います (Java には参照による受け渡しはありません)。したがって、文字列を関数に渡すと、その文字列への「参照のコピー」が関数に送信されます。そのため、関数で変数を null に設定しても、呼び出し元に戻ると、元の値のみが参照されます。そのため、元の文字列は効果がありません。

Arraylist の場合、参照のコピーは元の Arraylist を参照します (文字列の場合も同様です)。ただし、ArrayList に何かを追加すると、元のオブジェクトが参照されるため、その効果を確認できます。( メソッド arraylist=new ArrayList() で試してみてください。元の arraylist はそのまま残ります)。

文字列の場合は、

str=str + "abc";

Java は、文字列 "xyzabc" (たとえば、str="xyz") への参照を持つ新しい String オブジェクトを作成し、"xyz" はガベージ コレクションの対象となります。しかし、「xyz」にはそれを参照する変数がまだあるため、それ (元の文字列) はガベージ コレクションされません。しかし、関数呼び出しが「xyzabc」を超えるとすぐに、ガベージ コレクションが実行されます。

議論の要約は、参照が同じオブジェクトを参照している限り、関数で変更を加えることができますが、参照を変更しようとすると ( str=str+"abc") 、メソッドで新しいオブジェクトを参照しています。元のオブジェクトがそのまま残るようにします。

于 2013-04-08T05:29:41.357 に答える