Java言語の型は次の2つのカテゴリーに分けられる: プリミティブ型と参照形。
プリミティブ型(基本型)
次の8種類。整数はすべて符号付きで、C言語と違ってunsignedはない。
型 | 格納できる値 | 備考 |
---|---|---|
boolean | 真偽値(trueもしくはfalse) | |
char | 1文字 | Unicodeのコードを表す16ビットの整数。教科書P.12 |
byte | 整数(8ビット) | 教科書P.10 |
short | 整数(16ビット) | 教科書P.10 |
int | 整数(16ビット) | 教科書P.10 |
long | 整数(32ビット) | 教科書P.10 |
float | 実数(32ビット) | 教科書P.11 |
double | 実数(64ビット) | 教科書P.11 |
基本型の変数は、「箱」のイメージで問題ない。変数名は箱についている名前。
では、次のコードを実行してみよう(コード全体)。
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// (1) プリミティブ型の宣言と初期化 int a = 0; int b = 1; System.out.println("a: " + a); System.out.println("b: " + b); // (2) プリミティブ型の代入 b = a; System.out.println("a: " + a); System.out.println("b: " + b); // (3) プリミティブ型の代入 a = 2; System.out.println("a: " + a); System.out.println("b: " + b); |
(1)では、aに0、bに1を代入している。
(2)で代入という操作をすると、aの値がコピーされてbに入る。
(3)で、aに代入するとaの値が変わる。bの値は変わらない。
参照型(クラス型)
参照型はクラス型ともいい、要するに、クラスによる型のこと。クラス型の変数は、クラス(やインターフェース)の実体(インスタンス)を格納する。
例えば、円(通貨じゃなくて図形の方)を表すCircleクラスを定義して、インスタンス化することを考えよう。半径を表すフィールドを持つクラスである。
1 2 3 4 5 6 7 8 9 |
package d00000.lecture10; /** * 円を表すクラス */ public class Circle { /** 半径を表すフィールド */ public int radius; } |
この場合の変数は、プリミティブ型の時と違って、「箱」のイメージではない!「商品タグ」のイメージが適切である。特に代入演算子を使った時の処理について注意が必要だ。実際に実行して、代入がどのような操作なのか確認しよう。
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
// (4) 参照型のインスタンス化とフィールドへの代入 Circle c1 = new Circle(); c1.radius = 0; Circle c2 = new Circle(); c2.radius = 1; System.out.println("c1.radius: " + c1.radius); System.out.println("c2.radius: " + c2.radius); // (5) 参照型の代入 c2 = c1; System.out.println("c1.radius: " + c1.radius); System.out.println("c2.radius: " + c2.radius); // (6) 参照型のフィールドに代入 c1.radius = 2; System.out.println("c1.radius: " + c1.radius); System.out.println("c2.radius: " + c2.radius); |
(4)では、インスタンス化して、フィールドに値を代入している。変数名(インスタンス名)は商品タグのようにインスタンスに紐づいている。
(5)で、代入の操作を行っているが、これは「c2の紐付け先をc1のものと同じにする」という意味になる(C言語がわかっている人向けの説明: ポインタのコピー)。元々c2が紐づいていたインスタンスはどこからも使われていない状態になったので、ガベージコレクタ(教科書P.92)によって、そのうちメモリから消される。
(6) c1のフィールドの値を代入によって変更すると、c2のフィールドも同じになる(というか同じものを指し示している)。
このように、参照型では(初学者にとっては)直感と違う処理が行われるので注意する。この辺りについては「シャローコピー」や「ディープコピー」といった単語で検索してみると理解が深まるだろう。
比較
代入だけではなく、比較についても注意しなければならない。次のコードを実行して見よう(コード全体)。
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
int a = 1; int b = 1; if (a == b) { System.out.println("aとbは同じ"); } else { System.out.println("aとbは違う"); } Circle c1 = new Circle(); c1.radius = 1; Circle c2 = new Circle(); c2.radius = 1; if (c1 == c2) { System.out.println("c1とc2は同じ"); } else { System.out.println("c1とc2は違う"); } |
参照型の比較は、「指し示しているインスタンスが同じか」という比較をしているので、フィールドの値がすべて同じであったとしても指し示しているインスタンスが違えばfalseとなる。参照型は、普通は「==」で比較してはいけない、と覚えておこう(もちろん、同じインスタンスを指し示しているか、という判別が必要なときはこれで良い)。
では、参照型で比較したい場合はどうしたらいいのだろうか?これは、地味にフィールドを比較していくしかない。Circleクラスの場合はフィールドが1つだけなので簡単だが、フィールドがたくさんある場合は大変だ。なので、比較が必要なクラスは比較するためのメソッドを作っておくのが良い。じゃあ、どうやって作るか、利用するか、ということは今後の講義で解説する。
参照型あれこれ
文字列
Javaの文字列はStringクラスのインスタンスだ。つまり参照型だ。だから、比較などでは注意しなければならない。また、文字列は非常によく使われるので、特別扱いされている。
特別扱い1: 普通のクラスはnew演算子でインスタンス化を行うが、文字列のインスタンス化はダブルクォーテーションで囲むことによってインスタンス化することができる!
ちなみに、ダブルクォーテーションを使わず、charの配列から文字列を作ると次のようになる。
1 2 |
char[] helloCharArray = {'こ', 'ん', 'に', 'ち', 'は'}; String hello = new String(helloCharArray); |
特別扱い2: +演算子で結合ができる!(教科書P.13、ちなみに、「演算子のオーバーロード」という機能がある言語では、+演算子の機能を変えることができる)
Javaの文字列は様々なメソッドが用意されているので使ってみよう。
配列
配列も参照型である。
lengthという読み取り専用の特殊なフィールドのようなものがある(教科書P.23)。
例えば、for文でループ回数に使えば、文字列の長さが変わってもその部分のコードを書き換える必要はなくなる。
1 2 3 4 |
char[] aiueo = "あいうえお".toCharArray(); for (int i=0; i<aiueo.length; i++) { System.out.println(aiueo[i]); } |