• ベストアンサー

参照渡し

javaでintやStringって参照渡しって可能なのでしょうか? プリミティブタイプ以外は参照渡しとあるサイトに書いてあったのですが Stringは何故、値渡しなのでしょうか。 宜しくお願いします。

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

  • ベストアンサー
  • UKY
  • ベストアンサー率50% (604/1207)
回答No.7

> あと補足なんですがインスタンスが確保されているものに > a = null;とかするのは問題あるでしょうか? > なんとなくメモリリークが起きる気がするのですが、、 問題ありません。メモリリークも起きません。 確かに、文字列を連結したりするたびに新しい文字列のインスタンスが生成されていきますが、Javaでは、不要になったインスタンスのメモリは、自動的に「ガーベジコレクタ」によって開放されます。 つまり、不要なインスタンスを破棄・処分するためのコードをいちいちプログラマが書く必要は無いのです。 ある変数に代入されているインスタンスが不要になったら、その変数にnullを代入しておくだけで、あとは適当なタイミングにガーベジコレクタがメモリを開放してくれます。(メモリの開放はnull代入の直後とは限りません)

sha-girl
質問者

お礼

再度にわたるご回答、感謝しております。

その他の回答 (6)

  • Sephy
  • ベストアンサー率35% (7/20)
回答No.6

(1) String a; a = "aaa"; (2) String a = new String(); a = "aaa";  (1)と(2)の動作の違いについて。 (1)  String型の参照を格納する変数aを宣言。(つまりStringオブジェクトはまだ生成されていない。)  aに"aaa"というStringオブジェクトの参照を代入する。  メモリでいうならば「参照用のa」「"aaa"という内容のStringオブジェクト」の分だけ確保されます。 (2)  String型の参照を格納する変数aを宣言し、そのaに新しく生成(new)したStringオブジェクトの参照を代入する。  aに"aaa"というStringオブジェクトの参照を代入する。(これはaの参照先を"aaa"に書き換えただけで、上でnewしたStringオブジェクトに文字列"aaa"を代入したわけではありません。)  メモリでいうならば「参照用のa」「newしたStringオブジェクト」「"aaa"という内容のStringオブジェクト」の分だけ確保されます。  Stringであろうとオブジェクトは全て例外なく参照渡しです。Javaでは配列はオブジェクトなので配列も参照渡しです。このことを利用したのが#1さんの >ちょっとずるいですが、 >要素数1の配列にすればできます。 です。 >Stringというのは特別なクラスなのでしょうか?  Java内部の仕様に組み込まれた特別なクラスですが、プログラマはいちいちそのことを意識しなくてもいいと思います。  意識しなければならないのは、String同士を連結して代入する際、新しいオブジェクトが生成されるという点です。(#4参照)  このことを知らないと今回のteststrのように文字列の「内容」を別のメソッドで書き換えたつもりが、実は書き換わっていない、というバグに繋がる可能性があります。  「頻繁に内容が変更される文字列」を扱うのでしたらStringBufferを使用することをお勧めします。=や+での操作はできなくなりますが、メモリやパフォーマンスの面でStringBufferの方が上回ります。Stringは「固定長の文字列」を扱うのに便利なクラスであると心得てください。

sha-girl
質問者

お礼

ご回答ありがとうございます。 大変勉強になりました。

  • UKY
  • ベストアンサー率50% (604/1207)
回答No.5

プリミティブ型でもオブジェクト型でも、変数を宣言しただけでは、まだ実体は存在しないことに注意してください。 変数の中に実体が存在するのは、代入を行ってからです。(ただし、オブジェクト型変数にnullを代入した場合は、実体は実質的に存在しないことになりますが) > Cのポインタ渡しとは違うのでしょうか 「参照渡し」は、「ポインタ渡し」と同じことです。ただ、Javaではポインタとは言わずに参照という言葉を使うのです。 > String a = new String();とString a; > ではメモリ管理が変わるのでしょうか メモリ管理が異なるというより、処理の内容全体が異なります。 String a; というのは、変数を宣言しただけです。 まだ代入していないので、変数の中身を取り出すことはできません。(この点C言語とは若干異なります) String a = new String(); では、大きく分けて三つの処理が行われます。 一つ目は、上と同じく、変数領域の確保です。 二つ目は、新しいStringのインスタンス(実体)の生成です。 三つ目は、生成した文字列インスタンスを代入することです。(厳密には、代入されるのはインスタンスそのものではなくてインスタンスへの参照です) 上と異なる点は、代入《までも》行われる点です。つまり、このあと変数の中身を取り出すことができます。 最後に、 a = a + "bbb"; についてですが、これは、 「二つの文字列をつなげて、新しい文字列インスタンスを作り、それを代入する」 ということが行われます。 (厳密な処理内容は、4番目の回答でSephyさんがおっしゃっている通りですが) 注意する点は、もともと変数aに入っていた文字列インスタンスの中身が書き換えられるのではなく、まったく新しい文字列インスタンスが生成してそれが代入されるという点です。 > 関数teststrのString aの実体はコピーが渡されるているのでしょうか (Stringはオブジェクト型なので)渡されるのはあくまでも参照です。そして、変数の中身も参照です。 ただし、teststrメソッドの中で、 a = a + "bbb"; としても、これはteststrメソッドの中で使われている変数aに文字列を代入し直しただけです。 つまり、teststrメソッドの変数aの中身は、"aaa"への参照から"aaabbb"への参照に代わりますが(もちろんこの二つの文字列は別なインスタンスです)、mainメソッドの変数aの中身は、aaa"への参照が代入されたままだということです。

sha-girl
質問者

お礼

>String a; というのは、変数を宣言しただけです。 >まだ代入していないので、変数の中身を取り出すことはできません。(この点C言語とは若干異なります) 確かにString aの時点ではnull状態ですね。 a = "aaa";をはじめて実行したときにインスタンスが確保されるということですね? あと補足なんですがインスタンスが確保されているものに a = null;とかするのは問題あるでしょうか? なんとなくメモリリークが起きる気がするのですが、、

  • Sephy
  • ベストアンサー率35% (7/20)
回答No.4

teststrメソッド内の a = a + "bbb"; のところで混乱しているのだと思います。 Stringの代入 a = a + "bbb"; は、コンパイラによって a = new StringBuffer().append(a).append("bbb").toString(); のように書き換えられます。ここで新しくStringBufferがnewされていることに注目してください。

sha-girl
質問者

お礼

ご回答ありがとうございます。大変参考になりました。 コンパイラが値渡しであるかのように見せかけているのですね? Stringというのは特別なクラスなのでしょうか? 或いは他にも、StringとStringBufferの関係のクラスってあるのでしょうか?

  • tekebon
  • ベストアンサー率62% (36/58)
回答No.3

intやfloatなどはプリミティブタイプですのでその値が直接格納されます。 引数渡しにおいては値のコピーが渡されることになります。 そして、Stringはクラスですので引数渡しは参照渡しになります。 しかしStringはデータを変更できません。 String str="Hello"; str+=" world"; とした場合には、まず最初に「Hello」というデータもつStringオブジェクトを strが参照することになります。 しかし、その後「world」という文字列を追加しようとしても、もとのオブジェクトとは別の 「Hello world」というデータをもつオブジェクトを参照することになります。 よって「一応」参照渡しとなっていますが呼び出し元のオブジェクトを書き換えることが出来ません。 (値を見ることは出来ますが) 文字データを参照で扱う場合にはStringBufferクラスを使います。 プリミティブタイプを参照で扱うためにはIntegerクラスやDoubleクラスなどがあります。

sha-girl
質問者

お礼

ご回答ありがとうございます。 例えば下記この場合 関数teststrのString aの実体はコピーが渡されるているのでしょうか? class test{  public static void main(String[] args) throws NumberFormatException,IOException{   String a;   a = "aaa";   System.out.println(a);   teststr(a);   System.out.println(a);   return;  }    public static void teststr(String a){   a = a + "bbb";   System.out.println(a);  } }

sha-girl
質問者

補足

import java.util.Vector; class test{  public static void main(String[] args) throws NumberFormatException,IOException{   String a;   a = "aaa";   System.out.println(a);   teststr(a);   System.out.println(a);   return;  }    public static void teststr(String a){   a = a + "bbb";   System.out.println(a);  } } ちょっと混乱してきました。 これは参照渡しですよね? 先ほどのプログラムを String a = new String(); に書き換えると、最後のaaaが表示されなくなりました。 ただのString a;でも実体はありますよね? String a = new String();とString a; ではメモリ管理が変わるのでしょうか?

  • UKY
  • ベストアンサー率50% (604/1207)
回答No.2

intやdoubleなどの値型は必ず値渡しです。 Stringや配列などのオブジェクト型は必ず参照渡しです。 > 関数にStringを渡したとき内部的に自分のコピーを作っているということなのでしょうか そのようなことはありません。メソッド(Javaでは関数とは呼びません)に文字列を渡しても、インスタンスが勝手にコピーされるということはありません。なぜなら、参照渡しだからです。 見た目が値渡しであるかのように見えるのは、文字列のインスタンスの内容を書き換えられないからです。

sha-girl
質問者

お礼

ご回答ありがとうございます。ひょっとすると私が「参照渡し」という言葉の定義を勘違いしているのかもしれません。 Cのポインタ渡しとは違うのでしょうか?

  • ranx
  • ベストアンサー率24% (357/1463)
回答No.1

Stringも一応参照渡しだったと思います。 ただ、値を書き換えられないだけです。 値を書き換えたければ、ちょっとずるいですが、 要素数1の配列にすればできます。

sha-girl
質問者

お礼

早速のご回答有難うございます。 ところで「一応参照渡し」というとどういうことでしょう? 関数にStringを渡したとき内部的に自分のコピーを作っている ということなのでしょうか?