Javaでのプログラミング学習をしていく中で、初心者の方にはわかりづらい内容の一つとして、変数のデータ型と参照型の違いについてがあります。
今回の記事では、Javaで変数を定義したときにデータ型と参照型とでは、動きにどのような違いがあるのかを解説していきたいと思います。
変数の定義
まず、Javaで変数を定義した際にはどのような動きをしているのかを見ていきましょう。
例として以下の定義文を使用します。
int i = 10;
int型の変数iに10という値を定義しています。
まず、Javaではこのように変数を定義するとPCのメモリ上にデータを配置します。
イメージでいうと次のような感じです。
そして、定義した変数iを呼び出すことによって、この10というデータを指定することができるというわけですね。
これが最もベターな変数定義時の動きです。
次に、各型ごとの定義時の動きの違いを見ていきましょう。
データ型変数
データ型の変数としては、以下のようなものがあります。
型名 | 説明 |
long | 整数型、8バイトの値を定義できる。 |
int | 整数型、4バイトの値を定義できる。 |
short | 整数型、2バイトの値を定義できる。 |
byte | 整数型、1バイトの値を定義できる。 |
double | 浮動小数点型、8バイトの値を定義できる。 |
float | 浮動小数点型、4バイトの値を定義できる。 |
boolean | 真偽型、trueかfalseの1ビットの値を定義できる。 |
char | 文字型、1バイトの文字コードを定義できる。 |
今回、各型の説明については省略します。
注目してほしいのが、変数の型によってサイズが初めから決まっているということです。
例えば、int型では4バイトの数字を定義できるのですが、1という数字を定義しても1000000000という値を定義しても同じ4バイトを使うというわけですね。
当然、定義したい数字が低ければ、int型の変数を使うとメモリを無駄にしてしまうため、byte型などの小さな値用のデータ型が用意されているというわけです。
※ただし、現在プログラムを実行するコンピュータは昔に比べて高性能なものが多く、1,2バイト節約したところで大した意味がないうえに型が異なると手間がかかることも多いため、たいていの場合int型を利用するようになっています。
このように、初めからメモリに配置するサイズを決めて値を格納していくのがデータ型と呼ばれる変数型です。
参照型変数
次に参照型です。
基本的に、先ほど説明したデータ型変数以外の変数は全て参照型です。
今回は、String型を例に説明していきます。
String str = “Hello”;
これを定義したとします。
通常であれば、一番左下に変数の値が定義されるはずですが、参照型の場合は、値は全く別の場所に格納されます。
そして、本来定義されるべき左下の位置には、定義した値が入っている場所を示すアドレスが格納されています。
このアドレスにアクセスすることで、値を取り出すことができるようになっています。
そのため、String型などの変数に入っているのは、”Hello”という文字列ではなく、0x0000001のような文字列が格納されているアドレスが定義されます。
なぜこのような仕様になっているのかというと、String型や配列といった変数の値が可変長であることが挙げられます。
String型は、文字列型であり、文字は1文字1~2バイトというサイズです。
そのため、あらかじめ決まったバイト数でメモリに定義するにはサイズの大きさが変わりすぎます。
そのため、本来の位置にはアドレスのみを格納して、値自体は別の場所に格納することで、可変長の値の定義を可能にしているというわけです。
参照型を扱う際の注意点
メモリの仕様について、解説をしていきましたが、データ型を扱う際はこのメモリの仕様を意識する必要は初心者の時点ではありません。
しかし、参照型では意識するとまでいかなくても理解をしておかなければ不可解な現象が起きてしまうことがあります。
試しに、以下のコードを入力して実行してみましょう。
public class Main {
public static void main(String[] args){
String str1 = “Hello”;
String str2 = “World”;
String str3 = str1 + str2;
String str4 = “HelloWorld”;
System.out.println(“str3=”+str3);
System.out.println(“str4=”+str4);
if(str3==str4){
System.out.println(“テストです。”);
}
}
}
さて、思った通りの結果は出ましたでしょうか。
このコードでは、HelloWorldという文字列を二通りの方法でそれぞれstr3とstr4に定義しています。
そしてこの二つをif文で比較して同じ値なら「テストです。」という結果を出力するという内容になっています。
しかし、結果としては、同じ値のはずなのに「テストです。」は出力されませんでしたよね?
これはなぜか、流れを図にしてみていきましょう。
今回if文に使用した比較演算子==では、単純に変数に格納されたデータを比較します。
そのため、str3とstr4に格納されているアドレスを比較してしまい、同じ文字列なのにif文の処理は実行されなかったというわけです。
もしも、String型を比較したいのなら.equalsメソッドを使用しましょう。
ここでもう一つポイントなのですが、次はこのようなコードを実行してみてください。
public class Main {
public static void main(String[] args){
String str1 = “Hello”;
String str2 = “Hello”;
System.out.println(“str1=”+str1);
System.out.println(“str2=”+str2);
if(str1==str2){
System.out.println(“テストです。”);
}
}
}
先ほどの説明でいけば、今回のようなコードでもif文の中身は処理されないはずですが、「テストです。」と出力されましたよね?
これはなぜかというと、String型だけの特殊な処理で、新しく値を定義した際に、同じ値がすでに定義されていれば、その値のアドレスを流用するという処理がされているためです。
ちょっとややこしいかもしれませんが、参照型にはこのような特徴があります。
この仕組みを理解していないと、==と.equalsの違いなどを理解することが難しくなってしまうので、最低限は理解しておきましょう。
Javaプログラミングでは、サンプル通りにプログラムを実行していくだけでは理解しきるのが難しいところがところどころあります。
これからも、今回のように初心者の方がわかりづらいだろうなと思った部分について、解説をしていきたいと思います。