次のJavaコードセグメントは、AP Computer Science模擬試験からのものです。
String s1 = "ab";
String s2 = s1;
s1 = s1 + "c";
System.out.println(s1 + " " + s2);
このコードの出力は、BlueJでは「abc ab」です。ただし、可能な選択肢の1つは「abc abc」です。答えは、Javaがプリミティブ型のように(値によって)文字列参照を設定するのか、オブジェクトのように(参照によって)設定するのかによって異なります。
これをさらに説明するために、プリミティブ型の例を見てみましょう。
int s1 = 1;
int s2 = s1; // copies value, not reference
s1 = 42;
System.out.println(s1 + " " + s2); // prints "1 42"
しかし、残高を保持するBankAccount objectsがあったとしましょう。
BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
BankAccount b2 = b1; // reference to the same object
b1.setBalance(0);
System.out.println(b1.getBalance() + " " + s2.getBalance()); // prints "0 0"
文字列の場合、どちらがそうであるかわかりません。それらは技術的にはオブジェクトですが、私のコンパイラは、変数を相互に設定するときにプリミティブ型のように扱います。
Javaがプリミティブ型のような文字列変数を渡す場合、答えは「abc ab」です。ただし、Javaが他のオブジェクトへの参照のように文字列変数を扱う場合、答えは「abc abc」になります
どちらが正しい答えだと思いますか?
Java文字列は不変であるため、実際に再割り当てを行うと、変数は文字列の値を変更するのではなく、文字列の新しいインスタンスを指すようになります。
String s1 = "ab";
String s2 = s1;
s1 = s1 + "c";
System.out.println(s1 + " " + s2);
2行目では、s1 == s2 AND s1.equals(s2)です。 3行目の連結の後、s1は不変の値「abc」を持つ別のインスタンスを参照するため、s1 == s2でもs1.equals(s2)でもありません。
BankAccountとStringの違いは、Stringは不変であることです。 「setValue()」や「setContent()」などはありません。銀行口座の同等の例は次のようになります。
BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
BankAccount b2 = b1; // reference to the same object
b1 = new BankAccount(0);
System.out.println(b1.getBalance() + " " + s2.getBalance()); // prints "0 500"
したがって、このように考えると(実際にはコンパイラが行うことではなく、機能的に同等です)、文字列連結シナリオは次のようになります。
String s1 = "ab";
String s2 = s1;
s1 = new String("abc");
System.out.println(s1 + " " + s2); //prints "abc ab"
Stringがプリミティブのように扱われるか、オブジェクトのように扱われるかは関係ありません。
文字列の例では、2つの文字列を連結すると新しい文字列インスタンスが生成されますがs1に割り当てられます。変数s2はまだ変更されていない(!)古いStringインスタンスを参照しています。
BankAccountに残高を設定するメソッドがあり、新しいBankAccountを返すとすると、例は次のようになります。
BankAccount b1 = new BankAccount(500); // 500 is initial balance parameter
BankAccount b2 = b1; // reference to the same object
b1 = b1.createNewAccountWithBalance(0); // create and reference a new object
System.out.println(b1.getBalance() + " " + b2.getBalance()); // prints "0 500"
実際、Stringはクラスであり、参照によって割り当て/渡されます。しかし、混乱しているのはステートメントです。
_String s = "abc";
_
これは、文字列が( '_int x = 10;
_'のような)特権であることを示唆しています。しかし、それは単なるショートカットであり、ステートメント 'String s = "abc";'です。実際には「String s = new String( "abc" );
」としてコンパイルされます「_Integer x = 10;
_」が「Integer x = new Integer( 10 );
」としてコンパイルされるのと同じように
このメカニズムは「ボクシング」と呼ばれます。
そしてさらに混乱します:クラス 'Integer
'とプリミティブ 'int
'がありますが、Stringには同等のプリミティブがありません(ただし_char[]
_は近くにあります)
シエデハーン
Javaでは、String
オブジェクトが割り当てられて渡されます参照により;この点が他のオブジェクトとまったく同じように動作します。
ただし、String
sはimmutableです。新しいオブジェクトを作成せずに既存の文字列の値を変更する操作はありません。たとえば、これは、s1 = s1 + "c"
が新しいオブジェクトを作成し、s1
に格納されている参照をこの新しいオブジェクトへの参照に置き換えることを意味します。
String
isクラスなので、String
変数is参照。しかし、Javaには特別な処理と構文があるため、例のようなことができるのはそのためです。
たとえば、 http://download.Oracle.com/javase/tutorial/Java/nutsandbolts/datatypes.html 。
Java.lang.Stringはプリミティブではなくオブジェクトです。
最初の例でコードが行ったことは次のとおりです。
しかし、参照または価値についてのあなたの質問に答えるために、それは参照によるものです。
アサーション
Javaが文字列変数を他のオブジェクトへの参照のように扱う場合、答えは「abc abc」になります。
間違っている。 Javaは、他のオブジェクトへの参照のように文字列変数を扱います。 Strings はオブジェクトですが、答えはそれでも「abc ab」です。
問題は、代入演算子が行うことではありません。割り当て演算子は、例のどの場合でもStringオブジェクトへの参照を割り当てます。
問題は、連結演算子( '+')が行うことです。新しいStringオブジェクトを作成します。他の人が言ったように、これはStringオブジェクトが不変であるために必要ですが、それはオペレーターの動作の問題であり、単にStringが不変だからではありません。連結演算子は、Stringオブジェクトが変更可能であっても、新しいObjectを返す可能性があります。
対照的に、2番目の例では、b1.setBalance(0)は新しいオブジェクトを作成せず、既存のオブジェクトを変更します。
int s1 = 1;
int s2 = s1; // copies value, not reference
s1 = 42;
System.out.println(s1 + " " + s2); // prints "1 42"
印刷しない"1 42"
だが "42 1"
。各個別の線を考慮に入れます。最初にs1が1を割り当て、次にs2がs1を割り当てます。これは今までのところ1です(Javaが3番目の線をまだ表示していないとします)。次に= Javaは3行目を見て、すぐにs1を42に変更します。その後、Javaは、これまでにわかっていることを出力するように指示されました。つまり、s1は42とs2です。 1(古いs1)です。
文字列についても同じことが起こります。
String s1 = "ab";
String s2 = s1;
s1 = s1 + "c";
System.out.println(s1 + " " + s2);// prints "abc ab".
Fort Stringは必ずしもs1を変更する必要はありませんが、s1はヒープメモリ内の新しいStringオブジェクトを参照しますが、古い「ab」オブジェクトはまだそこにあり、s2の新しい参照があります。