- ベストアンサー
参照型変数の比較について
1,2のソースについての質問となります。 ご教授の程お願い致します。 ◇◆1◆◇ class Sample{ public static void main(String args[]){ String s1 = "Hello"; String s2 = "Hello"; String s3 = new String("Hello"); System.out.println(s1 == "Hello"); //(1) } } ◇◆2◆◇ class Sample{ public static void main(String args[]){ String s1 = "Hello!Java"; String s2 = s1; s1 = s1.substring(6); //(2) System.out.println(s1); //(3) System.out.println(s2); //(4) } } ○1の質問 (1)の部分になりますが、 「s1 == "Hello"」の結果がtrueとなる理由が理解しずらいです。 「==」演算子では、「同じオブジェクトを参照しているか」を 比較するものであって、この場合「オブジェクトと文字列」の比較を しているので、falseになると思われます。 仮に、Helloがオブジェクトとして捉えられている場合であっても 「String s3 = new String("Hello");」で作成されたオブジェクトを 参照しているものとなるので、s1と比較をしてもfalseになると 思われます。 ○2の質問 このソースを実行後、 Java Hello!Java と、出力される理由についての質問となります。 (2)で「Java」の文字列を返し、s1に代入されますが、 s2はs1の参照型であるため、「Hello!Java」から「Java」に 変わると思われます。実際には、 Java Java と、出力されると思われます。 以上、わかりにくい箇所があると思われますが、 ご教授の程お願い致します。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
- ベストアンサー
>s1 == "Hello"; >式を見る限り >「オブジェクトの参照と文字列を比較している」 >と感じてしまうので、trueになるのが理解できずじまいです。 >特殊が理解できないのが、、、痛いところです。 すごくはしょって言えば、 Javaは全てオブジェクトです。 s1もオブジェクト、"Hello"という「文字列」もオブジェクトです。 String s1 = "Hello"; String s2 = "Hello"; String s3 = new String("Hello"); でs1にはプールにある"Hello"へのアドレスのようなものが格納されます。s1 == s2 s2 == "Hello" s1 == "Hello" は全てTRUEです。 つまり " " の中が同じならプールに元からある"Hello"が使われる。これがString型の特権。 String型でも new String("Hello") だと毎回違うオブジェクトが性背されます。文字列はしょっちゅう扱うのでそんな効率悪いことはJVMはしない、と私は理解してます。 どこからも参照されなくなったオブジェクトは、 幸いガーベージコレクションが自動で処理してくれます。(Javaのみ) この辺は、数をこなせば自然とわかってくると思います。いろんなサイトを見てみましょう。
その他の回答 (5)
- bnosuke-x
- ベストアンサー率39% (43/110)
ソースの中に"Hello"と書いてある物は、値としては「Stringへの参照」になるので、比較が成り立つのです。 C言語において、プログラム中に書いた文字列リテラルは 「char の1次元配列相当の場所が確保され、文字列がそこに格納されていて、その先頭アドレスを指すポインタ(定数)」であったように、 Javaにおいて、プログラム中に書いた文字列リテラルは 「Stringのインスタンスが生成されており、それへの参照(定数)」となります。 ソースの上では「文字列」ですが、それは人間向けの見た目であって、 実際に動くときには「参照(定数)」なんです。 final の String 変数と同等と考えればいいでしょう。 ついでに s1 == ”Hello” と言うことなら、s1にできることは全て”Hello”にもできるということではないですか?(代入を除く) "Hello".length() も "Hello".hashCode() も "Hello".compareToIgnoreCase(s3) も可能と言うことです。 試してないですけどね。
- root139
- ベストアンサー率60% (488/809)
■ 1に関して Javaのコンパイラは、 "Hello" と記述されている時点で、この文字列値("Hello")を持つStringオブジェクトを生成します。ただし、既に同じ文字列("Hello")が出現していて、そのStringオブジェクトが存在する場合は、それを使います。 ですので、s1、s2、(1)の行の"Hello"は同じオブジェクトへの参照となりますね。 もっと言えば、s3に代入している行のStringのコンストラクタの引数の"Hello"も同じオブジェクトです。(new String(これ→"Hello")) ■ 2に関して 参照型に"="で代入するという事は、その変数が指し示すオブジェクトを設定すると言ってよいと思います。 ですので、(2)では、 「s1の指しているオブジェクトの値の"Hello!Java"の7文字目以降を値に持つオブジェクトを新たに生成して、それを指すようにs1を設定しなおす。」 という感じかと。 図示すると↓こんな感じでしょうか。 [[ String s1 = "Hello!Java"; ]] s1 → "Hello!Java" [[ String s2 = s1; ]] s1 → "Hello!Java" ← s2 [[ s1 = s1.substring(6); ]] "Hello!Java" ← s2 s1 → "Java"
お礼
ご丁寧な説明ありがとうございます。 2まで説明をしてくれて、助かりました。
- Bonjin
- ベストアンサー率43% (418/971)
>「オブジェクトの参照と文字列を比較している」と感じてしまうので、trueになるのが理解できずじまいです。 文字列リテラルは全てStringのインスタンスです。なので、 String s1 = new String("Hello!Java"); と書くと、String#String(String)のコンストラクタが呼ばれますし、 System.out.println("Hello!Java".substring(6)); というような書き方もできます。
お礼
参考になりました^^ String型だけは手強くて、、、まだまだ慣れないとダメデスね。
- sakusaker7
- ベストアンサー率62% (800/1280)
#1 さんの説明にあるとおり、Javaにおいては文字列は特別な扱いを受けています。 String s1 = "Hello"; String s2 = "Hello"; というコード片があったとき、s1とs2は同じ文字列オブジェクトを保持します。 このためs1 == s2 が true となるわけです。 ところがここで、 String s1 = "Hello"; String s3 = new String("Hello"); とすると、今度は s1 == s2 は真になりません。 これは s1 = "Hello" という初期化が特別扱いを受けているのに対して、 s3 = new Strings("Hello")としたときはそうではないためです。 Java言語規定 字句文法 http://www.y-adagio.com/public/standards/tr_javalang/3.doc.htm#100960 の 3.10.5 文字列リテラル を読まれると良いと思います。 substringを実行しても片方にしか影響しないのはJavaにおいては文字列は immutable (変更不可能)なものであるため、何らかの操作を加えたとき 元の文字列そのものが変化するのではなく、コピーをとってそれに 操作を加えた新しい文字列オブジェクトを生成するためです。 この辺の話は、 Java言語仕様 第3版 コンピュータ書籍専門ネット書店 cbook24 http://www.cbook24.com/bm_detail.asp?sku=4894717158 を読んでみるとよいのではないでしょうか? GPLに基づくオープンソースとなったとはいえ ネット上に自由にアクセスできる形では公開されていないように思います。
補足
2については、新しいオブジェクトへの参照が代入され s1とs2は独立したものとなるのが理解できたと思います。 しかし、1については、まだ理解できないです。 s1 == "Hello"; 式を見る限り 「オブジェクトの参照と文字列を比較している」 と感じてしまうので、trueになるのが理解できずじまいです。 特殊が理解できないのが、、、痛いところです。
実際に動かしていないので見たかんじで書きます。 >「s1 == "Hello"」の結果がtrueとなる理由が理解しずらいです。 まず、大前提として、String型は特殊と思ってください。 これは、いろんなサイトに詳しく書いてます。 s1 と s2 は「プール」にある"Hello"を参照します。 s1 == "Hello" は true で正解です。しかし、s1==s3 はfalseです。 プールにある"Hello" とメモリ上のインスタンスを参照するs3は別物。 うーん。説明が下手です。 ためしに、Stringではなくほかのクラスのオブジェクトでもやってみてはどうでしょう。 もう一つの質問について、 String s2 = s1 でs2 の参照先が"Hello!Java"になります。 "Hello!Java"というオブジェクトは不変です。 s1 = s1.substring(6); でs1の参照先が変わります。 s1は新しい"Java"というオブジェクトを参照します。 おそらくこれから学ばなければならないことは、 ・String オブジェクトの不変性 ・オブジェクト参照変数 だと思います。がんばってください。
補足
2については、納得できましたが、若干気になることがあります。 substring()メソッドで、s1は新しいオブジェクトを参照すること になりますが、その際以前あったs1(Hello!Java)は消去される ことになるのでしょうか?? 新しいオブジェクトを参照することになれば、 オブジェクトを参照するs1は二種類(Hello!JavaとJava)となり substring()メソッドにより、「Hello!Java」が消えることに なると思われます。如何でしょうか?? また、1についてはまだ納得できないようです。 >String s1 = "Hello"; >String s2 = "Hello"; この場合、s2で新しいオブジェクトへの参照を生成せずに、 既存にあるもの(s1)を参照することになるのは理解できました。 そのため、s1 == s2 はtrueになります。 おまけに、s1.eqauals(s2)でもtureになります >String s3 = new String("Hello"); この場合では、新しいオブジェクトへの参照を生成します。 s3は独立したものとなります。 そのため、 s1 == s3 s2 == s3 はfalseとなります。 で、、、引っかかるのは s1 == "Hello"がどうしてtrueになるのかがが理解できないです。 式を見る限り 「オブジェクトの参照と文字列を比較している」 と感じてしまうので、trueになるのが理解できずじまいです。
お礼
このコメントは、、詳しくて理解することができました。 あとは、数をこなせばできると思います。 本当にありがとうございました。