• ベストアンサー

参照型変数の比較について

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 と、出力されると思われます。 以上、わかりにくい箇所があると思われますが、 ご教授の程お願い致します。

  • KGM
  • お礼率44% (66/148)
  • Java
  • 回答数6
  • ありがとう数3

質問者が選んだベストアンサー

  • ベストアンサー
noname#24040
noname#24040
回答No.5

>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のみ) この辺は、数をこなせば自然とわかってくると思います。いろんなサイトを見てみましょう。

KGM
質問者

お礼

このコメントは、、詳しくて理解することができました。 あとは、数をこなせばできると思います。 本当にありがとうございました。

その他の回答 (5)

  • bnosuke-x
  • ベストアンサー率39% (43/110)
回答No.6

ソースの中に"Hello"と書いてある物は、値としては「Stringへの参照」になるので、比較が成り立つのです。 C言語において、プログラム中に書いた文字列リテラルは 「char の1次元配列相当の場所が確保され、文字列がそこに格納されていて、その先頭アドレスを指すポインタ(定数)」であったように、 Javaにおいて、プログラム中に書いた文字列リテラルは 「Stringのインスタンスが生成されており、それへの参照(定数)」となります。 ソースの上では「文字列」ですが、それは人間向けの見た目であって、 実際に動くときには「参照(定数)」なんです。 final の String 変数と同等と考えればいいでしょう。 ついでに s1 == ”Hello” と言うことなら、s1にできることは全て”Hello”にもできるということではないですか?(代入を除く) "Hello".length() も "Hello".hashCode() も "Hello".compareToIgnoreCase(s3) も可能と言うことです。 試してないですけどね。

  • root139
  • ベストアンサー率60% (488/809)
回答No.4

■ 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"

参考URL:
http://www.nextindex.net/java/String.html
KGM
質問者

お礼

ご丁寧な説明ありがとうございます。 2まで説明をしてくれて、助かりました。

  • Bonjin
  • ベストアンサー率43% (418/971)
回答No.3

>「オブジェクトの参照と文字列を比較している」と感じてしまうので、trueになるのが理解できずじまいです。 文字列リテラルは全てStringのインスタンスです。なので、 String s1 = new String("Hello!Java"); と書くと、String#String(String)のコンストラクタが呼ばれますし、 System.out.println("Hello!Java".substring(6)); というような書き方もできます。

KGM
質問者

お礼

参考になりました^^ String型だけは手強くて、、、まだまだ慣れないとダメデスね。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

#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に基づくオープンソースとなったとはいえ ネット上に自由にアクセスできる形では公開されていないように思います。

KGM
質問者

補足

2については、新しいオブジェクトへの参照が代入され s1とs2は独立したものとなるのが理解できたと思います。 しかし、1については、まだ理解できないです。 s1 == "Hello"; 式を見る限り 「オブジェクトの参照と文字列を比較している」 と感じてしまうので、trueになるのが理解できずじまいです。 特殊が理解できないのが、、、痛いところです。

noname#24040
noname#24040
回答No.1

実際に動かしていないので見たかんじで書きます。 >「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 オブジェクトの不変性 ・オブジェクト参照変数 だと思います。がんばってください。

KGM
質問者

補足

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になるのが理解できずじまいです。

関連するQ&A

  • Javaの参照型変数についての質問です。

    SUN教科書Javaアソシエイツ P209 問5-7より 次のプログラムの空欄(1)に入れて実行すると、出力結果がtrueになる コードはどれですか。4つ選択して下さい。 class Sample{ public static void main(String[] args){ String s1 = "Hello"; String s2 = "Hello"; String s3 = new String("Hello"); System.out.println(【  (1)  】) } } A. s1 == s2 B. s1 == s3 C. s2 == s3 D. s1 == "Hello" E. s3 == "Hello" F. s1.equals(s3) G. s2.equals(s3) 以上が問題です。正解の選択肢はA,D,F,Gです。 正解については理解出来ています。しかし、Eの選択肢もtrueに なると思うのですがどうしてfalseなのでしょうか。 s1とs2が文字列Helloを参照しているのは分かります。 しかし、String s3 = new String("Hello");で、 s3も文字列Helloのオブジェクトを生成しているのですから、 s3もHelloを参照していることになり、s3 == "Hello"も trueになると思うのですが…。 アドバイス、よろしくお願い致します。

    • ベストアンサー
    • Java
  • Javaの参照型変数の比較についての質問です。

    SUN教科書Javaアソシエイツ P209 問5-8より 出力結果を問う問題です。 class A{String s = ”ABC”;} class Sample{ public static void main(String[] args){ A a1 = new A(); A a2 = new A(); if(a1 == a2){ System.out.println("a1 == a2"); } if(a1.s == a2.s){ System.out.println("a1.s == a2.s"); } if(a1.equals(a2)){ System.println("a1 equals a2"); } if((a1.s).equals(a2.s)){ System.out.println("a1.s equals a2.s"); } } } 【解答】 a1.s == a2.s a1.s equals a2.s 「a1.s」と「a2.s」の記述、つまり表記が意味している内容が良く分かりません。 a1とa2が別のオブジェクトであることは理解出来ています。 ですから、(a1 == a2)と(a1.equals(a2))がfalseとなるのも分かります。 ですが、「a1.s」の「.s」とは何なのでしょうか。 解説には「a1.sとa2.sはAクラスのオブジェクトが保持する同一のStringオブジェクトを 参照しています。」とあります。Aクラスを生成した時にその中にString型クラスsが 作れているということでしょうか。 アドバイスをよろしくお願い致します。

    • ベストアンサー
    • Java
  • javaの基本的な質問

    下記を実行すると、上から順番にメソッドが実行され、 「Hello Java  Hello Java」 と表示されるような気がしてしまうのですが… なぜ、一度しか表示されないのでしょうか。 class Main { public static void main(String[] args) { hello(); } public static void hello() { System.out.println("Hello Java"); } }

    • ベストアンサー
    • Java
  • javaの参照渡しの問題

    javaの参照渡しの問題 javaの初心者で問題集で勉強しているのですが、 以下の問題の参照渡しの違いがわかりません。 ------------------------------------------- ソース1 class Samplea{ public void method(){ int b[] = new int[2]; set(b); System.out.println(b[0]); } public void set(int[] b){ b[0] = 30; } } class Sample{ public static void main(String srgs[]){ Samplea x = new Samplea(); x.method(); } } 実行結果 30 ------------------------------------------- ソース2 public class Test{ public static void main(String args[]){ String s1= "Hello"; String s2= "Good-Bye"; change(s1,s2); System.out.println(s1); } static void change(String s1,String s2){ s1 += ",Tom"; s1 = s2; } } 実行結果 Hello ------------------------------------------- 配列とString型は両方とも参照型なので ソース1の実行結果が「30」ならば、 ソース2の実行結果は「Good-Bye」になると思いました。 どなたか教えていただけないでしょうか。 宜しくお願いいたします。

    • ベストアンサー
    • Java
  • この文章あっていますか?

    public class HelloWorld { public static void main(String[] args) { String message; message = "Hello Java World !"; System.out.println(message); } } これが正解文なのですが public class HelloWorld { public static void main(String[] args) { String message; message = "Hello java World"; System.out.ptintln(message); } } この文のどこが間違っていますか?ぜんぜんわからないので誰か教えてください。

    • ベストアンサー
    • Java
  • オブジェクトの参照渡しについて

    メソッドの引数にオブジェクトを指定した場合、 それは「参照渡し」だと理解していました。 でも以下のプログラムでは、指定したオブジェクトに変化がありません。 public static void main(String[] args) { BigDecimal b = new BigDecimal(1); System.out.println(b); test(b); System.out.println(b); } public static void test(BigDecimal b) { b = new BigDecimal(2); } 出力は以下です。 ------------------------- 1 1 ------------------------- どういうことでしょうか? 正しい知識をお教えください。 よろしくお願いします。

    • ベストアンサー
    • Java
  • public class HelloFriendsFor2 {

    public class HelloFriendsFor2 { public static void main(String[] args) { System.out.print("Hello "); for (String name : args) { System.out.print(name); System.out.print(" "); } System.out.println(); } } } for文の結果を見るためどうしたらいいですか? 本ではこうやって書いてたんですけど c:\javac HelloFriendsFor2.java c:\java HelloFriendsFor2 HI HELLO NICE TO MEET YOU netbeansで見る方法はないですか?

    • ベストアンサー
    • Java
  • 参照・値渡しについて

    下記プログラム(ソース1)を実行すると「1」という値が出力されます。しかし、2行目をprivate static int a;とすると「0」という値が出力されます。 オブジェクトは参照渡しで基本データ型は値渡しと思ったのですが、2行目の値をString型で行ったところ全く変更されていない値が出力されました。(ソース2) 一体どういうことでしょうか? 回答のほどよろしくお願い致します。 (ソース1) public class X{ private static int a[] = new int[1]; public static void main(String []args){ modify(a); System.out.println(a[0]); } public static void modify(int a[]){ a[0]++; } } (ソース2) public class X{ static String a = "a"; public static void main(String []args){ modify(a); System.out.println(a); } public static void modify(String a){ a = "b"; } }

    • ベストアンサー
    • Java
  • javaの問題わからなくって質問します

    内容はEclipse上でプロジェクトフォルダを作りひとつは別のjavaファイルを動かすのともう一つはそのお同じフォルダから動かされるjavaプログラムです。 説明不足というかまだ触れてそんなにたってないのでソースを載せます class SampleManager { public static void main(String[] args){ exec obj= new exec(samlple1.java); obj.say(); } } class exec{ String phrase; exec(String phrase){ this.phrase=sample2.java; } void say(){ System.out.println(phrase); } } /////// public class samlple1 { public static String java; public static void exec(String[] args) {//execをした理由はSampleManagerにあるクラスexecを呼び出れるため System.out.println("Hello,World!")//この二つの出力結果を表示したい; System.out.println("こんにちは!"); } } ひとつのフォルダに上記のソースいれてSampleManager.java からsample.javaの出力結果の HelloWorld! こんにちは! を出力したいですがエラーはなく例外処理もありませんけど何故か出力結果はnullが返ってしまいます なぜでしょうか? アドバイスをお願いします

  • クラスの参照(同じファイル内 vs 別々のファイルの場合)

    二つの.javaファイルがあります。 <ABC.java> class ABC { public static void main(String args[]) { A obj = new A(); obj.hello(); } } --------------------------------------------- <A.java> class A{ void hello() { System.out.println("Hello from A"); } } --------------------------------------------- ABC.javaファイルでコンパイルすると、「 シンボルを解釈処理できません。」というエラーメッセージが出ます。でも、ABC.javaファイルの中に、class Aを続けて書くとコンパイルできます。なぜ、別々のファイルにすると参照できないのでしょうか? (おなじフォルダ内に入れています) 教えてください。よろしくお願いします。

    • ベストアンサー
    • Java