【Java】値渡しと参照渡しについて

f:id:hira98:20190516183027p:plain

【結論】

  • Javaではメソッドコール時に値を渡す方法として、値渡しと参照渡しの2種類の方法がある。

  • 値渡しの場合、メソッドの呼び先で変数を更新しても、呼び元の変数には反映されない。

  • 参照渡しの場合、メソッドの呼び先で変数を更新すると、呼び元の変数にも反映される。

  • これらを理解しており、以下のソースを読み解ける方はこの記事を読む必要がない。

【目次】

引数の渡し方

結論で、引数の渡し方には値渡しと参照渡しがあると書きました。

しかし、渡すデータの種類に応じてJavaコンパイラが処理を分岐しているわけではありません。

メソッドを呼び出した時のデータの受け渡しは、あくまでも単純なコピーです。

メソッドを呼び出す際に引数保存用のエリアを確保し、そこに呼び元が引数として渡したいエリアの値をコピーしているだけです。

値をコピーしているだけにも関わらず、値渡しと参照渡しと言う呼称がある理由は、引数として指定する変数へのデータの格納方法が違うからです。

どのような違いがあるかについては、以降で説明します。

基本データ型

基本データ型とは、変数宣言時に確保するメモリ領域の大きさが決まっている型の事をいいます。

Javaでは基本データ型として次のような型が用意されています。

型名 サイズ(bit) 補足
boolean 1 true or false
byte 8 -128〜127
char 16 Unicode文字 ¥u0000~¥uFFFF
short 16 -32,768〜32,767
int 32 -2,147,483,648 〜 2,147,483,647
float 32 単精度浮動小数点数
long 64 -9,223,372,036,854,775,808 〜 9,223,372,036
double 64 倍精度浮動小数点数

確保するメモリ領域が決まっているため、次のように変数を宣言すると、

int a = 10;

コンパイラでは次のように解釈されます。

  • 手順1)

    int aより、32bitのメモリ領域を確保する。

  • 手順2)

    = 10;より手順1)で確保したメモリ領域に10を格納する。

ここで注目すべきは、手順1)で確保した変数aには値が格納されているということです。

よって、次のようにメソッドを呼び出すと、変数aに格納された値がコピーされます。

int a = 10;
hoge(a);

呼び先と呼び元で異なるメモリ領域に対して読み書きを行なっているため、呼び先での変更は呼び元には反映されません。

このような特徴があるため、基本データ型で宣言された変数を引数として渡す場合を値渡しと呼びます。

参照型

参照型とは、変数宣言時に確保するメモリ領域が決まっておらず、処理に応じて動的に変化する型のことをいいます。

Javaでは基本データ型の配列だったり、クラスから生成されたインスタンス変数を宣言すると参照型になります。

確保するメモリ領域が不定のため、次のように宣言すると。

int num1[] = new int[]{1, 2, 3};
// ↑は↓のように書いてもいい。
int num2[] = {1, 2, 3};
// このように、同じ処理をより短い記述で書くための言語仕様を、シンタックスシュガー(糖衣構文)という。

コンパイラでは次のように解釈されます。

  • 手順1) int num1[]よりint型の配列を宣言しているため 、参照型と判断し、配列のデータが格納されたエリアの先頭アドレスを格納するためのエリアを確保します。

  • 手順2) 次に、new int[]{1, 2, 3}を解析します。newはメモリ確保するためのJavaの命令です。以降のint[]{1,2,3}よりint型のエリアを3つ連続で確保し、各エリアに1,2,3を初期値として代入すると解釈されます。

  • 手順3) 最後に、手順1で確保したメモリ領域に、手順2で確保した配列の先頭アドレスを格納します。

ここで注目すべきは、手順1)で確保した変数numには実際の値は入っておらず、コンパイラが配列格納用に確保したメモリエリアのアドレスが格納されているということです。

よって、次のようにメソッドを呼び出すと、変数numに格納されたメモリアドレスが渡されます。

int num[] = new int[]{1, 2, 3};
hoge(num);

メソッドの呼び先では、呼び元から渡された変数numが参照しているメモリ領域に対して、読み書きを行います。メソッドの呼び元と呼び先で同じメモリ領域にアクセスしているため、呼び先での変更は呼び元にも反映されます。

このような特徴があるため、参照型で宣言された変数を引数として渡す場合は参照渡しと呼びます。

参考情報

https://www.seshop.com/product/detail/18868

→最近の僕のJavaに関する情報源は紫本だけです。

https://ryoutaku-jo.hatenablog.com/

→ブログの書き方について参考になりました。

最後に

スクールでRubyを学んでいた時に、「Rubyでは値渡しと参照渡しの違いってどうやって判断するんですかね?」と質問したら、「ハッ?値渡し?参照渡し?」となって僕も「ハッ😨?」となったのを思い出しました😅

そんなスクールに通って得た情報で一番価値があったのは、脳みそに「ブログジェネレーター」のプラグインが搭載された駆け出しエンジニアの存在を知れた事でした。