参照に対する勘違いを起こす理由として、Java のメソッドには「オブジェクトを操作するメソッド」と「オブジェクトを生成するメソッド」の 2 タイプあり、最もよく使われる String クラスに「オブジェクトを生成するメソッド」しかないことが挙げられると思います。
結論から言ってしまうと、String クラスに用意されているメソッドは、どのメソッドを使ってもオブジェクトそのものの値は変わりません。
言い換えると、オブジェクトそのものの値を変えることができません。そのようなメソッドが存在しないからです。
String クラスにはオブジェクトの値を元にした新しいオブジェクトを生成するメソッドしか存在しません。
先のサンプルAの test1 メソッドの内容を変えたサンプルCを例とします。
サンプルC
public class SampleC
{
public static void main(String[] args)
{
String s1 = "main メソッド実行";
SampleC sampleC = new SampleC();
sampleC.test1(s1);
System.out.println(s1); ・・・4
}
public void test1(String s2)
{
s2.toUpperCase(); ・・・1
s2.replaceAll("main", "test1"); ・・・2
s2.concat("?"); ・・・3
}
}
このサンプルCを実行した時、
4の部分でコンソールには
> java SampleC
main メソッド実行
と出力されます。
test1 メソッド内では変数:s2 の String オブジェクトのメソッドを実行しています。
1 の toUpperCase() は文字列を大文字に変換するメソッド、2 の replaceAll(String, String) は文字列内にある第1引数の文字を第2引数の文字に置き換えるメソッド、3 の concat(String) は引数に指定した文字列を末尾に追加するメソッドです。
しかし、このどれを実行しても、変数:s2 が参照している String オブジェクト(これは変数:s1 が参照しているオブジェクトと同じ)は変わりません。
それはこれらのメソッドが「オブジェクトを生成する」タイプのメソッドだからです。
これに対しサンプルBで使用した Vector#add(Object) メソッドは「オブジェクトを操作するメソッド」なので、元のオブジェクトの内容が変更されます。
メソッドの結果として生成されたオブジェクトは戻り値として返りますので、変数で受けてやる必要があります。
public void test1(String s2)
{
String s3 = s2.toUpperCase();
String s4 = s2.replaceAll("main", "test1"");
String s5 = s2.concat("?");
}
こうすると、変数:s3, s4, s5 のそれぞれに結果オブジェクト(の参照)が格納されます。
具体的には変数:s3 には "MAIN メソッド実行"、変数:s4 には "test1 メソッド実行"、変数:s5 には "main メソッド実行?" が入ります。
この時、もちろん変数:s2 が参照しているオブジェクトは変更されません。
この String クラスの「オブジェクトを生成するメソッド」に慣れてしまい、
"メソッドを実行しても元のオブジェクトは変わらない"
= "渡した元のオブジェクトは操作されない"
= "参照渡しってどういうこと?"
という混乱に繋がっているのだと思います。
そして、たまに使用する「オブジェクトを操作するメソッド」のせいで、渡したオブジェクトが変わる時と変わらない時があり、さらに混乱してしまいます。
Java には「オブジェクトを操作するメソッド」や、渡されたバッファに値を格納するようなメソッドが少ないです。
ほとんどがこの「オブジェクトを生成するメソッド」です。
ポインタ=参照渡しを意識させないための工夫でしょうが、これだけでは限界があったのか結果的に「オブジェクトを操作するメソッド」が存在することによって混乱を招いていると思います。
使用するクラスのメソッドが「オブジェクトを生成するメソッド」なのか、「オブジェクトを操作するメソッド」なのか − 意識して使用していくことで、混乱は防げると思います。
私は Sun の人間ではありませんし、VM やメモリをハックしたわけでもありませんので、記載された内容は厳密には事実と異なる部分があるかもしれません。
ですが、「納得できて理解できればいいな」という考えで公開しています。この点はご了承ください。