【Java Silver対策】値渡しと参照渡しについて学習しよう【例題にも挑戦!】

資格学習

第6章「インスタンスとメソッド」内の「値渡しと参照渡し」を学習しよう

第6章ではインスタンスとメソッドについて学習しますが、今回はその中でも「値渡しと参照渡し」について詳しく学習します。

前編はこちら。中編はこちら。後編はこちら

今回も引き続き解説の参考と例題の引用は【黒本】と呼ばれる「徹底攻略Java SE 11 Silver問題集」から行っていきます。

値渡しと参照渡し

値渡しとは、関数やメソッドにデータを渡すときに、そのデータのコピーを渡す方法です。コピーが作られるので、関数やメソッドの中でそのデータが変更されても、元のデータには影響がありません

参照渡しとは、関数やメソッドにデータを渡すときに、そのデータ自体ではなく、データの「参照」(アドレスやポインタ)を渡す方法です。参照が渡されるので、関数やメソッドの中でそのデータが変更されると、元のデータも変更されます

具体的にコードを見てみます。

//値渡し
public class ValueExample {
    public static void main(String[] args) {
        int a = 5;
        System.out.println("Before: " + a); // aは5
        changeValue(a);
        System.out.println("After: " + a);  // aは変わらず5
    }

    public static void changeValue(int x) {
        x = 10;
    }
}

この例では、aという整数がchangeValueメソッドに渡されますが、渡されるのはaのコピーです。したがって、xが10に変更されても、元のaには影響がなく、出力結果は「Before: 5」と「After: 5」となります。

//参照渡し
public class ReferenceExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        System.out.println("Before: " + numbers[0]); // numbers[0]は1
        changeArray(numbers);
        System.out.println("After: " + numbers[0]);  // numbers[0]が10に変更される
    }

    public static void changeArray(int[] array) {
        array[0] = 10;
    }
}

この例では、numbersという配列がchangeArrayメソッドに渡されますが、渡されるのはnumbers配列の参照です。したがって、array[0]が10に変更されると、元のnumbers[0]も変更され、出力結果は「Before: 1」と「After: 10」となります。

まとめると、以下のようになります。

値渡しと参照渡し
  • 値渡しはデータのコピーを渡すので、関数やメソッドの中でそのデータが変更されても元のデータには影響がありません。
  • 参照渡しはデータの参照を渡すので、関数やメソッドの中でそのデータが変更されると元のデータも変更されます。
モナ
モナ

値渡しと参照渡しの違いについて理解を深めるため、例題を解いてみましょう。

次のプログラムを確認してください。

1. public class Sample {
2.    int num;
3.    public Sample(int num) { 
4.       this.num = num; 
5.    }
6. }

このクラス利用する以下のプログラムを、コンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)

1. public class Main {   
2.    public static void main(String[] args) { 
3.        Sample s = new Sample(10);
4.        modify(s.num);   
5.        System.out.println(s.num);   
6.    }    
7.    private static void modify(int num) {    
8.        num * = 2;
9.    }
10. }             

A. 10が表示される。
B. 20が表示される。
C. コンパイルエラーが発生する。
D. 実行時に例外がスローされる。 

「徹底攻略 Java SE 11 Silver 問題集」より抜粋
ねこ奈
ねこ奈

何となく「値渡し」のパターンに似ている気がするのにゃ。

値渡しの場合、データのコピーになるっていうから正解自体は選択肢Aなんだろうけど、理由がよく分からないのにゃ。

モナ
モナ

確かに正解は選択肢Aなのですが、なぜ実行結果が10になるのか詳しく解説します。

プログラムの流れの解説

Sampleクラスのオブジェクト作成

Sample s = new Sample(10);

ここで、Sampleクラスのコンストラクタが呼び出されます。コンストラクタは、numという引数を受け取ります。この場合、10が渡され、this.num = num;の行で、Sampleオブジェクトのnumフィールドに10が設定されます。

modifyメソッドの呼び出し

modify(s.num);

ここで、modifyメソッドが呼び出されます。引数として渡されるのは、s.numの値です。s.numの現在の値は10です。

modifyメソッドの実行

private static void modify(int num) {
    num *= 2;
}

modifyメソッドの中で、numが2倍にされます。しかし、ここで重要なのは、modifyメソッドが引数を値渡しで受け取るということです。つまり、numの値である10がメソッドに渡され、そのコピーがnumというローカル変数に代入されます。メソッドの中でnumを変更しても、元のSampleオブジェクトのnumフィールドには影響を与えません。

結果の表示

System.out.println(s.num);

最後に、s.numの値が表示されます。modifyメソッドの中で変更が行われたのはローカル変数のnumであって、Sampleオブジェクトのnumフィールドはそのままです。したがって、s.numの値は依然として10です。

ねこ奈
ねこ奈

なるほどにゃあ、Sampleオブジェクトのnumとmodifyメソッドのnumは別物ってことなのにゃ。

じゃあ、「参照渡し」のパターンはどうなるのか知りたいのにゃ。

モナ
モナ

それでは同じような問題で「参照渡し」のパターンだとどうなるのか見てみましょう。

次のプログラムを確認してください。

1. public class Sample {
2.    int num;
3.    public Sample(int num) { 
4.       this.num = num; 
5.    }
6. }

このクラス利用する以下のプログラムを、コンパイル、実行したときの結果として、正しいものを選びなさい。(1つ選択)

1. public class Main {   
2.    public static void main(String[] args) { 
3.        Sample s = new Sample(10);
4.        modify(s);   
5.        System.out.println(s.num);   
6.    }    
7.    private static void modify(Sample s) {    
8.        s.num * = 2;
9.    }
10. }             

A. 10が表示される。
B. 20が表示される。
C. コンパイルエラーが発生する。
D. 実行時に例外がスローされる。 

モナ
モナ

先ほどの例題と異なる箇所が分かりやすいように黄色のマーカーを引いています。

「徹底攻略 Java SE 11 Silver 問題集」より抜粋
ねこ奈
ねこ奈

さっきの問題とmodifyのメソッドが異なっているのにゃ!

modify(Sample s)ということは、Sampleオブジェクトそのものを受け取っているということかにゃ。

参照渡しの場合、元のデータも変更されるから、numの値は20に変更されるのにゃ。

だから正解の選択肢はBなのにゃ。

でも、やっぱりイマイチ理解できてないから、モナ解説よろしくにゃ。

モナ
モナ

値渡しと参照渡しを理解しているかどうかがJavaを理解しているかに繋がると思います。

それでは、例題の内容について詳しく解説します。

プログラムの流れの解説

Sampleクラスのオブジェクト作成

Sample s = new Sample(10);

ここで、Sampleクラスのコンストラクタが呼び出されます。内容自体は先ほどの例題と同じなので割愛します。

modifyメソッドの呼び出し

modify(s);

ここで、modifyメソッドが呼び出されます。引数として渡されるのは先ほどと違い、Sampleオブジェクトs自体です。

modifyメソッドの実行

private static void modify(Sample s) {
    s.num *= 2;
}

modifyメソッドの中で、Sampleオブジェクトsのnumフィールドが2倍にされます。ここで重要なのは、modifyメソッドが引数としてSampleオブジェクトそのものを受け取っているということです。Javaでは、オブジェクトそのものを引数として渡す場合は参照渡しといいます。これにより、modifyメソッドの中でオブジェクトのフィールドを変更すると、その変更は元のオブジェクトにも反映されます。

結果の表示

System.out.println(s.num);

最後に、s.numの値が表示されます。modifyメソッドの中で、s.numは2倍されて20になっています。この変更は元のSampleオブジェクトに対して行われたため、s.numの値は20になります。

モナ
モナ

メソッドの引数がプリミティブ型(基本型)なのか、参照型なのかで値渡しなのか参照渡しなのかが分かります。問題を解く際は必ず確認するようにしましょう。

ねこ奈
ねこ奈

JavaSilverの問題に必ず出るよってデイトラの講義内でも言われていたのにゃ。

理解できるようにしっかりと復習するのにゃ!

まとめ 値渡しと参照渡しについてしっかりと理解しておこう

今回は第6章「インスタンスとメソッド」のうち、「値渡しと参照渡し」について解きました。

ハック
ハック

まさかの一項目だけでの記事化です。

値渡しと参照渡しだけで約6千文字の記事ができてしまうくらい、解説に力を入れました。

この項目を理解しているか否かで「インスタンスが理解できているか」が分かってしまいます。

コードを入力しながら動きを確認し、理解を深めていきます。

以上で今回の学習記録を終えます。

ここまでご覧いただきありがとうございました。

コメント

タイトルとURLをコピーしました