|
今後、皆さんがJava言語を操る上で身に付けておかなければならない概念の一つに、クラス型とプリミティブ型の違いが挙げられます。下記を見て下さい。
char ch = 'あ';
String str = "あ";
この二つは、
System.out.println( ch );
System.out.println( str );
を実行した時は、全く同じ結果となります。つまり、ともにコンソールに「あ」と出力されると言うことです。しかしこの2つには大きな違いがあります。それはchar型はプリミティブ型であり、String型はクラス型だと言うことです。もっと簡単に表現するとString型はメソッドが実行できるが、char型はメソッドを持たないということです。これはいったいどういうことでしょうか?プリミティブと言うのは原始的という意味です。直訳で考えてみるとプリミティブ型は原始型となります。実は、プリミティブ型はJavaにおいて値を扱うための基本的な型であり、Javaの言語仕様の一部をなしています。そのためプリミティブ型は下記のように8つの型から構成されており、言語仕様が拡張されない限りは増えたり、減ったりしません。
|
型
|
表現力
|
| byte |
1バイト整数(-128〜127) |
| short |
2バイト整数(-32768〜32767) |
| int |
4バイト整数(-2147483648〜2147483647) |
| long |
8バイト整数(-9223372036854775808〜9223372036854775807) |
| float |
4バイト単精度浮動小数点数 |
| double |
8バイト倍精度浮動小数点数 |
| char |
2バイト文字データ(\u0000〜\uffff) |
| boolean |
論理値 trueまたはfalse |
これらプリミティブな型は「値」しか扱えません、つまりメソッドなどの機能は所有しておらず上の表の表現力の範囲内の値を持つだけなのです。一方でクラス型は「値」だけではなく、「機能」を持っています。例えるならば、プリミティブ型は「友達の電話番号をメモした紙切れ」でクラス型は「友達の電話番号を記録した携帯電話」と表現することができます。
つまり、紙切れは電話番号を参照することしかできませんが、携帯電話はメモリー内にある電話番号の情報を利用してワンタッチで電話をかけることができます。このとき紙切れは「値」しか持っていませんが、携帯電話は「値」と「機能」を持っています。具体的には、char型はプリミティブ型なので値だけですが、String型はクラス型なので値と機能を持っているのです。
ではプリミティブ型とクラス型の変数を見分けるにはどうしたらよいのでしょうか?
プリミティブ型で宣言されているかいないか
これしかありません。あたりまえのようですが、厳密にはこれしかありません。ただ、他人のソースコードを参照している時(これを「ソースを追う」と表現します。)に宣言部を見つけ出すのは大変な場合があります。その時は、下記のようにメソッドが利用されていたり、フィールドが参照されていればクラス型と見分けたりできます。
a.test();//メソッドが実行されているのでaはクラス型
int i = b.value;//valueというフィールドが参照されているのでbはクラス型
そして、プリミティブ型は下記のように直接変数に値を代入することができます。
int i = 1;
char ch = 'a';
boolean bool = true;
ところが通常クラス型は値を直接セットすることはできません。かならずクラス型のオブジェクトを作った後に、そのオブジェクトに対して値をセットしなければなりません。電話番号の例えを用いると、紙切れには直接書き込めますが、携帯電話には直接書き込むことはできません。携帯電話に電話番号を記録するには、まず携帯電話の本体(オブジェクト)を準備して、さらに電話番号を入力しなければなりません。この時オブジェクトのことをインスタンス(instance:クラスの実体)と呼びます。このインスタンスを生成するためにはnewという予約語を利用してクラス名と同じ名前で呼び出すメソッドのようなもの(コンストラクタ)を実行します。
Integer integer = new Integer( 100 );//Integerクラスはintに対応するラッパークラス
上記はInteger型のクラス型変数としてintegerを宣言し、そこに初期値にint型の100の値を持つIntegerクラスのインスタンスを代入しています。この時「Integer(
100 )」の部分がコンストラクタです。コンストラクタは1種類ではなく、引数があるものやないもの、複数あるものなどが存在します。そしてこのinteger変数は下記のようにメソッドを実行することができます。
long l = integer.longValue();//integer変数が内部に持つint型の値をlong型として返すメソッドを実行
ここで、一つ考え直してみましょう。既に気づかれた方もいるかもしれません.String型の宣言文をもう一度参照してみましょう。
String str = "あ";
最初にString型はクラス型だと言いました。ところがこの式にはnewやコンストラクタの実行部分がありません。いったいこれはどういうことでしょうか?実はJavaにおいてクラス型唯一の例外がString型なのです。つまりString型はダブルクォーテーションで囲った文字列(リテラルと呼びます)を変数に対して直接代入することができるのです。しかし、ここで注意して欲しいのが決してインスタンスが存在しないと言うわけではないのです。先ほど話したリテラル自体がString型のインスタンスとして機能するのです。つまり下記のようなことが可能であると言うことです。
String str2 = ”はじめましてこんにちわ”.substring(1);//substringは引数文字目以降の文字列を切り出してString型で返す
*つまりstr2には「じめましてこんにちわ」の文字が入る
もちろん、他のクラス型と同じようにnewすることもできます。
String str = new String("はじめまして");
しかし、頻繁に利用する文字列をいちいちnewして利用するのはプログラミングを行う上で非常に手間がかかるのでString型のみ代入のような方法を取ることが例外として認められたのです。
では、下記のようにクラス型において変数の宣言のみしてインスタンスを代入しない場合はどうなるのでしょうか?
String str;
プリミティブ型の初期値に関しては配列の説明のこちらを参照してください。じつはクラス型の初期値は、null(ぬる)という特殊な状態になっています。nullとはインスタンスが存在しない状態のことです。プリミティブ型は値が入っていますが、クラス型は値をセットするためのオブジェクトが入っていました。つまりインスタンスが存在しなければ値をセットすることも、機能を実行することもできません。この状態をnullと呼びます。またnull値はクラス型の初期化にも用います。
String str = null;
では、このようにnull状態のクラス型の変数でメソッドを実行したりしたらどうなるのでしょうか?下記を見て下さい。
String str = null;
str.toString();
上記のようなコードはコンパイルエラーにはなりませんが、実行時にtoStringメソッドの部分でNullPointerException(ヌルポインターイクセプション)と呼ばれる例外エラーが発生します。NullPointerExceptionとは、インスタンスが存在しないのにメソッドを実行したり、フィールドを参照したりすると発生します。例外エラーに対する対処はJavaの例外処理機構で詳しく説明しますので、とりあえずはインスタンスの存在しないクラス型の変数はメソッドを実行できないと覚えてください。
|