質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

6回答

2103閲覧

引数の引渡し

kisaragizinzin7

総合スコア90

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2016/09/20 07:42

コード public class Sample{ public static void main(String[] args){ int k = 10; int x[] = {1, 2, 3, 4, 5}; int x2[] = {6, 7, 8, 9}; display(k, x, x2); change(k, x, x2); display(k, x, x2); } public static void change(int k, int x[], int x2[]){ k = 7; int y[] = {11, 12, 13, 14, 15}; x = y; x2[0] = 100; } public static void display(int k, int x[], int x2[]){ for(int i = 0; i < x.length; i++){ System.out.print(x[i] + " "); } System.out.println(""); System.out.println(k); for(int i = 0; i < x2.length; i++){ System.out.print(x2[i] + " "); } System.out.println(""); } } 実行結果 1 2 3 4 5 10 6 7 8 9 1 2 3 4 5 10 100 7 8 9

上のプログラムはk,x,x2を表示して、値を変更してから再度表示するプログラムです。
changeメソッドでk,x,x2ともに変更したのにx2しか変わっていませんでした。

引数として引き渡す場合
基本データ型はメソッド外部の内容がわからない。
配列やオブジェクトは参照がコピーされるだけなのでメソッド外部の内容がわかる。
ようなことを聞いたのですが、x,x2は配列なのにxは変わってないのにx2は変わりました。

結局何が理由で変更されるのとされないのがあるのか教えっていただきたいです。
よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

changeメソッド内でxが参照している2つの配列を実体A、実体Bとして説明します。
実体A:{1,2,3,4,5}
実体B:{11,12,13,14,15}

changeメソッドに引数が渡され、処理が開始する際の「mainメソッド内のx」と「changeメソッド内のx」が指しているのは以下の通りです。

main.x:実体Aを指す change.x:実体Aを指す

ここで大事なことは、「main.x」と「change.x」は「同じ実体を指しているだけの別の変数」であるということです。
changeメソッド内で実体Bが定義され、x = ychange.xの参照先が実体Bに変わります。
つまり、

main.x:実体Aを指す change.x:実体Bを指す

という状態になります。
変わっているのはchange.xの参照先だけなので、main.xは変わらず実体Aを指し続け、値も変わりません。
また、xが変わらないのにx2が変わるのは、

x = y :change.xの参照先を、y(が指している実体B)に向ける。
x2[0] = 100 :change.x2の参照先はそのままで、参照先の中身(0番目の要素)を変更している。

という違いによるものです。

投稿2016/09/21 01:25

編集2016/09/21 01:27
KaedeKazane

総合スコア408

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kisaragizinzin7

2016/09/27 00:05

回答ありがとうございます! とてもわかりやすく理解できました。 ありがとうございました!
guest

0

ベストアンサー

change(int k, int[] x, int[] x2)xというのは、change()メソッド内のみで利用できる変数で、main()メソッドで宣言されたxの参照を上書きしているわけではありません。

以下の図で説明します。
便宜的に、hoge()メソッド内の変数vhoge.vと表記します。
例えば、質問中のmain()メソッド内のint[]型変数xmain.xとします。
(もちろんコード中で書くとエラーになりますが…)
また、{1, 2, 3, 4}は配列、-><-は変数が配列を指していることを表します。

change()メソッドが呼び出された時点では以下のようになっています。

main.x -> {1, 2, 3, 4, 5} <- change.x main.x2 -> {6, 7, 8, 9} <- change.x2

つづいて、yが宣言されます。

main.x -> {1, 2, 3, 4, 5} <- change.x main.x2 -> {6, 7, 8, 9} <- change.x2 {11, 12, 13, 14, 15} <- change.y

その次の行(x = y)で、change.xは「change.yが指しているもの」を指すように指定します。

main.x -> {1, 2, 3, 4, 5} main.x2 -> {6, 7, 8, 9} <- change.x2 change.x -> {11, 12, 13, 14, 15} <- change.y

このとき「change.xが指している配列」の情報を更新しても、「main.xが指している配列」の情報は更新されないのです。

さて、最後に、change.x2の0番目の要素を変更します。

main.x -> {1, 2, 3, 4, 5} main.x2 -> {100, 7, 8, 9} <- change.x2 change.x -> {11, 12, 13, 14, 15} <- change.y

このとき、「change.x2の指している配列」と「main.x2の指している配列」は同一であるため、「main.x2の指している配列」も更新されています。

これが、main()から見えるxが更新されていない理由と、x2が更新されている理由です。

投稿2016/09/20 08:11

carimatics

総合スコア740

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kisaragizinzin7

2016/09/21 00:44

回答ありがとうございます。 この考え方でいくとmain.kとchange.kも同一でkも7に更新されると思うのですが、kは10のままです。 これはなぜなのでしょうか? すみませんがよろしくお願いします。 change()メソッドが呼び出された時点 main.k -> 10 <- change.k k = 7の時点でmain.kとchange.kは同一
carimatics

2016/09/21 01:22 編集

Javaの変数にはプリミティブ型と参照型の2種類があります。 何がプリミティブ型で何が参照型かは調べればすぐに分かると思うのでここには書きませんが、int型はプリミティブ型です。 プリミティブ型の変数は参照(回答で定義した<-、->)ではなく値を直接持っているので、引数で渡してもmain.kとchange.kが同一の参照を持つことはありません。 従って、change.kを変更してもmain.kの値には影響しません。
kisaragizinzin7

2016/09/26 23:50

そういうことだったんですね! 回答ありがとうございました!
guest

0

Java では配列もオブジェクトです。
オブジェクトの変数は、オブジェクトの参照を保持しています。

public static void change(int k, int x[], int x2[]) { k = 7; int y[] = {...}; x = y; // change 引数のx は y を指すが、呼び元(main) のxは変わらない x2[0] = 100; // change 引数 x2 == 呼び元の x2 なので、呼び元のx2[0] が変わる

投稿2016/09/20 08:08

yskz44

総合スコア100

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kisaragizinzin7

2016/09/21 00:52

回答ありがとうございます。 // change 引数のx は y を指すが、呼び元(main) のxは変わらない // change 引数 x2 == 呼び元の x2 なので、呼び元のx2[0] が変わる とありますが、 change 引数 x2 == 呼び元の x2 なのでしたら change 引数 x == 呼び元の xになり、そのchange 引数 x がyを指すので呼び元の x == y にならなのでしょうか? よろしくお願いします。
guest

0

大雑把な基本(プリミティブ型の場合)
仮引数(呼び出された側で受け取ったもの)は実引数(呼び出した側で引き渡したもの)のコピーでしかない。

上記からいえること
仮引数に変更を加えても(それだけでは)実引数に影響はない。

プリミティブ型でない場合(オブジェクトの場合)
オブジェクト(配列含む)を格納した変数は「参照情報」を保持している。「参照情報」も引数渡しの際にコピーされることは上記と同じではある。ただし、「参照の先を変更」した場合は呼び出し型にも影響を与える。もっとも、実引数そのものが変更されるわけではなく、この参照先が変更されるわけである。

ちょっと書き方変えてみました。

投稿2016/09/20 12:00

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kisaragizinzin7

2016/09/27 00:04

回答ありがとうございます。 仮引数と実引数は別物として考えるんですね! ありがとうございました!
guest

0

他の回答者と同じことを、別の言い方で言います。
参照型について念のため書きます。

◆参照型
参照型の本体はメモリーヒープ上に存在する。参照型変数は(暗黙に)メモリーヒープを指している。
参照型変数と、ヒープメモリに存在する実体(配列の値そのもの)は別の領域である。

結局何が理由で変更されるのとされないのがあるのか教えっていただきたいです。

実引数、k, x x2はどれも変わっていません。仮引数にコピー(代入)しただけですから変わりません。
x2[0]が100に変わったのは、参照先のヒープ上の配列の要素[0]が100に置き換えられたから。

◆Javaのメソッド呼び出しは値呼び
メソッドの仮引数は実引数のコピー(値呼び)である。コピーとは変数の代入である。
メソッドの仮引数の有効範囲はメソッドのブロック内である。ブロック退出とともに仮引数は捨てられる。

これを踏まえて、メソッド呼び出しを、ネストしたブロックの処理に置き換えます。
以下のコードは、メソッドを呼ぶのと同じ処理になります。行コメントを読んでください。

Java

1int k = 10; // プリミティブ型の変数はスレッドスタックに割り当てられる。 2int x[] = {1, 2, 3, 4, 5}; // 配列はヒープ上のアドレスm番地に割り当てられたものとする。 3int x2[] = {6, 7, 8, 9}; // 配列はヒープ上のアドレスn番地に割り当てられたものとする。 4 5 6// メソッド呼び出しのつもり 7{ 8 // 以下の変数が引数のつもり 9 int kLocal = k; // kの値10がklocalにコピーされる。 10 int xLoacl[] = x; // xの参照のコピーが渡される。m番地を指している。 11 int x2Local[] = x2; // x2の参照のコピーが渡される。n番地を指している。 12 13 14 kLocal = 7; 15 int y[] = {11, 12, 13, 14, 15}; // 配列はヒープ上のアドレスl番地に割り当てられたものとする。 16 xLoacl = y; // xlocalの参照先、n番地がl番地に変更された。 17 18 x2Local[0] = 100; // n番地のオフセット0:配列要素[0]の値を100に置き換える。 19 // この変更だけが、ヒープの値に影響を与える。 20 21 22 // ブロック退出に伴い、ローカル変数、kLocal, xLoacl[], x2Local[], y[]は消滅する。 23 // y[]とxLoacl[]の参照先の配列{11, 12, 13, 14, 15}はゴミになる。 24} 25// k, x[], x2[]の値はもとのままで変わっていない。k : 10, x[] m番地, x2[] n番地 26// 変わったのは参照先のヒープの内容。n番地の配列要素[0] 27

###参照について(追記)
Q. 参照型変数とプリミティブ型変数の違いはなんですか?
A. 変数は次の種類があります。

・プリミティヴ型変数(値そのものが格納される変数)
・参照型変数(配列とオブジェクトを参照する。参照型変数の中身はint型の値)
・型変数(ジェネリクスで使用する。議論の対象外なので説明を省略します)

参照型変数に格納する整数値は、ヒープ領域に格納されたオブジェクトの本体を指すために使う。
ヒープ領域は大きなメモリ領域で、その中に自由にオブジェクトを割り当てて使える。

Q. 参照型変数の中身を見ることが来ますか?
A. System.identityHashCode(); を使うと見ることができます。2つの参照型変数の値が同じなら、同じオブジェクトを指します。

参照がnullの場合

Java

1int[] a = null; 2int hash = System.identityHashCode(a); 3System.out.println(hash);

a はnull。参照がないので 0 が表示されます。

参照に配列を割り当てる

Java

1a = new int[] {1,2,3,4,5,6}; 2hash = System.identityHashCode(a); 3System.out.println(hash);

配列をnew すると、ヒープに int型 6 個分の領域を割り当てる。
割り当てたint型 6 個を、それぞれ、1, 2, 3, 4, 5, 6 に初期化する。
ヒープの配列への参照値(数値)を参照型変数 a に代入する。

a (nnnnnnn) ———>  配列の本体 | 1 2 3 4 5 6 |

別の参照に参照を代入する

Java

1int[] b = null; 2b = a; 3hash = System.identityHashCode(b); 4System.out.println(hash);

b には a の参照値(nnnnnnn)が代入される。
代入によって b が a そのものを指すわけではない。(重要)
a と b は何の関係もなく、それぞれがヒープの同じ配列を指しているだけ。

参照を比較する

Java

1boolean isSame = a == b; 2System.out.println(isSame); 3 4isSame = System.identityHashCode(a) == System.identityHashCode(b); 5System.out.println(isSame);

a == b は実は、参照値(nnnnnnn)を整数比較しているだけ。

###メソッドの引数の有効範囲(追記)

プリミティヴ型 k が引数の場合

Java

1static void change(int k) { 2 k = 7; 3}

メソッドが呼ばれると、ローカル変数 k がとられる。(k は実引数とはなんの関係もない)
k に実引数の値 10 がコピーされる。
k に 7 を代入する。
メソッド退出時に k を破棄する。このメソッドは何も有意義なことはしない。

参照型 x2[] が引数の場合

Java

1static void change1(int[] x2) { 2 x2[0] = 100; 3}

メソッドが呼ばれると、ローカル変数 x2 がとられる。(x2 は実引数とはなんの関係もない)
x2 に実引数の参照値(nnnnnn)がコピーされる。
参照値が参照する配列の本体の要素0の値は 6。要素0の値を 100 に置き換える。
メソッド退出時に x2 を破棄する。
x2 は破棄されるが、ヒープの配列の本体は変更されたまま。このメソッドは有意義なことをしている。

参照型 x[] が引数の場合

Java

1static void change1(int[] x) { 2 x = new int[] {11,12,13,14,15}; 3}

メソッドが呼ばれると、ローカル変数 x がとられる。(x は実引数とはなんの関係もない)
x に実引数の参照値(nnnnnn)がコピーされる。
配列をnew する。ヒープに int型 5 個分の領域を割り当てる。
割り当てたint型 5 個を、それぞれ、11, 12, 13, 14, 15 に初期化する。
ヒープの配列への参照値(mmmmmm)を参照型変数 x に代入する。
今や x の参照値は、別の配列を指すようになった。
メソッド退出時に x を破棄する。
参照値(mmmmmm)が指す配列の本体を誰も参照しないので、GCの対象になる。
このメソッドは何も有意義なことはしない。

参照型 x[] が引数で勝つ返り値に参照を返す場合

Java

1static int[] change3(int[] x) { 2 return new int[] {11,12,13,14,15}; 3}

メソッドが呼ばれると、ローカル変数 x がとられる。(x は実引数とはなんの関係もない)
x に実引数の参照値(nnnnnn)がコピーされる。
しかし x を使わずに、新たな配列を生成して、その参照をメソッドの返り値として返す。

配列をnew する。ヒープに int型 5 個分の領域を割り当てる。
割り当てたint型 5 個を、それぞれ、11, 12, 13, 14, 15 に初期化する。
ヒープの配列への参照値(mmmmmm)を返り値として返す。
メソッド退出時に x を破棄する。
メソッドの返り値を実引数 x に代入すれば、x の配列が丸ごと入れ替わる。
このメソッドは値を返すことで有意義なことをしている。

説明は以上です。質問は受け付けます。気軽にどうぞ。

投稿2016/09/20 11:26

編集2016/09/21 11:50
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kisaragizinzin7

2016/09/27 00:03

詳しい解説ありがとうございます! 理解できました!
guest

0

言語仕様4.12.5. Initial Values of Variables

Each method parameter (§8.4.1) is initialized to the corresponding argument value provided by the invoker of the method (§15.12).

実引数で仮引数が初期化されます。

changeメソッドでk,x,x2ともに変更した

chageメソッドではx2が参照する要素の1番目の値を変更しています。よって、呼び出し側で宣言している配列x2の要素の内容が変わります。

結局何が理由で変更されるのとされないのがあるのか

配列の要素に代入、オブジェクトに対してフィールドを変えるメソッドを呼び出す場合が代表的でしょうか。以下の2つのメソッドが良い例だと思います。

・Arrays#fill()メソッド
・Collections#addAll()メソッド

投稿2016/09/20 08:06

編集2016/09/20 08:07
java-beginner

総合スコア452

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問