コンピュータがプログラムを実行しているとき、 そのプログラムや データは主記憶装置(メモリ) の中に置かれています。 CPUは、メモリの中のプログラムやデータにアクセスして計算を 進めていきます。
コンピュータが Java のプログラムを実行しているとき、たとえば
sp204: ~/pro 5> java Hello
として Hello.class を実行している場合は図1の動作を行なっています。
図1: プログラム実行(中間コードの場合) |
さて、CPUはメモリの中のプログラムやデータにどうやって アクセスしているのでしょうか?
コンピュータ内のメモリは1バイト単位で0から順番に番号が つけられています。 この「メモリにつけられた番号」を「番地」または「アドレス」といいます。
CPUはメモリ中の特定の位置を「番地」で指定してアクセスしているのです。
たとえば 64Kバイトの大きさのメモリであれば、0から ffff(16進数、 10進数だと65535になります)の範囲の「番地」でアクセスします。 また、1Gバイトのメモリであれば、0から 3fffffff(16進数、 10進数だと1073741823になります)の範囲の「番地」でアクセスします。
1バイトよりも大きいデータは、メモリ中の連続した複数のアドレスに 記憶されます。通常、それらのデータについて述べるときは、複数の アドレスのうちの一番小さなアドレスを使って表します。 たとえば、データが4バイトの大きさで、メモリ中の 1000 〜 1003 番地に 記憶されているとき、 「このデータは1000番地(から始まる4バイトに)記憶されている」 という言い方をします。
java言語では、データの型によって
Java言語の基本データ型 (int, double など) では、 データがメモリ中に占める大きさが 厳密に定義されています。 たとえば int 型だと4バイトの大きさ、double型だと 8 バイトの 大きさとなります。 データの型によって1バイト〜8バイトの大きさになりますが、 基本データ型ではいつも決まった大きさのメモリ中にデータを 記憶できることになります。
基本データ型では、「データの値そのもの」を扱います。 変数にもデータの値そのものが直接保持されています。 したがって、
Sample09.java |
Sample09.javaの実行例 |
|
オブジェクトはJavaの基本データ型とは異なり、『参照型』です。
参照型では「データの値」を直接扱うのではなく、「そのデータが 記憶されている場所(メモリ中の番地)」を扱います。 オブジェクトを変数に保持する場合は、そのオブジェクトへの 『参照』を保持することになります。
(注)Java言語の仕様としては、「「参照」は必ずしも「番地」そのものではない」 のですが、大抵のJava処理系では「参照」=「番地」として実装されて いますので、動作の理解のためには「参照」=「番地」と理解しておけば 十分です。
参照型では、変数にデータの参照が保持されています。 したがって、
Sample11.java |
Sample11.javaの実行例 |
|
上で述べたように「クラス」型のデータは参照型でした。 「配列型」はもう一つの参照型といえます。
配列型は厳密には Javaオブジェクト、すなわちクラス型の一種なのですが、 特殊な文法が用意されていて、振舞いも特殊なので別のものと考えることが できます。
変数について学習したときに、「データの型さえ合っていればどんな値でも 入れられるので便利だな。だけど、1つの変数には1個の値しか入れられない のがちょっと不便かも」とか思いませんでしたか?
少し複雑なプログラムを作るようになると、「1つの変数に複数の値を 入れてまとめて管理できればいいのになぁ」と考えるようになります。 これを実現してくれるのが「配列型」なのです。
配列は「同じ型のデータを複数個まとめたもの」といえます。
配列の型は、配列の各要素が保持する値の型に「開き大括弧」 [ と 「閉じ大括弧」 ] という文字を組み合わせたものです。
配列型の変数の宣言
[配列の宣言]
型[] 配列名;
[配列の宣言例]
int[] a;
double[] b;
Java以外の言語(C言語やC++言語など)との互換性を維持するために、 配列の宣言方法はもう一つあります。
配列型の変数の宣言(もう一つの方法)
[配列の宣言]
型 配列名[];
[配列の宣言例]
int a[];
double b[];
Javaで配列データを生成するには、オブジェクトと同じように new 演算子 を用います。配列は他のオブジェクトとは違って初期化する必要はありません ので、括弧の間にパラメータリストを渡すことはしません。その代り、 要素数を正の整数として [ と ]の間に指定します。
int[] a = new int[5]; // 5個のint型データを要素とする配列
double[] b = new double[64]; // 64個のdouble型データを要素とする配列
配列の各要素は、自動的にデフォルト値で初期化されます。 デフォルト値は以下の通りです。
型 | デフォルト値 |
---|---|
boolean | false |
char | '\u0000' |
整数(int, longなど) | 0 |
実数(double, floatなど) | 0.0 |
参照型(オブジェクトや配列) | null |
配列は、厳密にはオブジェクトですので、インスタンス・フィールドを 持っています。lengthというインスタンス・フィールドは「配列の要素数」 すなわち「配列の長さ」を表します。
int[] a = new int[5];
...
System.out.println(a.length); // 5と出力されます。
配列内の個々のデータを要素と呼び、配列名に添字(そえじ、index)を 付けて表します。 添字の有効範囲は 0 から「要素数-1」までであることに注意して 下さい。 1から「要素数」までを添字の範囲であると 勘違いする人が多いのですが、これはです。
配列の参照の時は、添字には定数だけでなく「式」を記述できます。 すなわち変数や計算式、関数呼び出しなどを書くことができます。
配列を使った例として Array01.java を示します。 これは「指定された月は何日間か」を答えるプログラムです。 配列の添字は0から始まるので、「1月の日数」を「0番目の要素」に、 「2月の要素」を「1番目の要素」に、...、「12月の要素」を 「11番目の要素」に記憶しています。
Array01.java |
Array01.javaの実行例 |
|
Array01.javaの実行結果において、月の値としては不適切な 20 が入力されたとき、 「19という添字は範囲を超えている」という Exception が出力されて途中で プログラムの実行が終っていることに注意して下さい。
Java言語では、プログラム実行時に配列にアクセスするときに 添字範囲のチェックを行ないます。 したがって、「負の整数」や「添字範囲を越える大きな整数」 を添字として配列の範囲外の要素にアクセスしようとすると Exception が起きて、プログラムが終了してしまいます。
配列の範囲外にアクセスしないように、 配列のインデックスがおかしな値でないかプログラム内で チェックした方がよいプログラムであると言えます。 不適切な値のときに「入力が間違っています」と表示する ようにArray01.java を変更したプログラムが Array02.java です。
Array02.java |
Array02.javaの実行例 |
|
不適切な値のときに「入力が間違っています」と表示して 正しい値が入力されるまで何度でも入力させるように Array02.java を変更したプログラムが Array03.java です。
Array03.java |
Array03.javaの実行例 |
|
配列は「参照型」です。すなわち、メモリの中でそのデータ用に 確保されているひとまとまりの場所の「先頭の番地」で表されます。 メソッドの引数として配列を渡す場合は、この「先頭の番地」が コピーされて渡されます。 メソッドの中で、個々の要素にアクセスをするとき、 「その番地から始まるデータの何番目」という形式でアクセスされるので、 もしもメソッドの中で配列の要素に代入したとすると、その配列の その要素の値は変わってしまいます。 すなわち、メソッドを呼び出した側でも影響を受ける ことに注意が必要です。
Java言語では、 配列の初期値を { と } の間にカンマで区切って記述することができます。 たとえば
int[] m;
m = new int[12]; // int型を要素とする要素数12個の配列を生成する
m[0]=31; m[1]=28; m[2]=31; m[3]=30;
m[4]=31; m[5]=30; m[6]=31; m[7]=31;
m[8]=30; m[9]=31; m[10]=30; m[11]=31;
という記述は
int[] m = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
と表現してもよいのです。
したがって、Array03.java は、次のように書き換えることができます。
Array05.java |
課題提出〆切は次回の講義の開始時刻です。
提出先 | http://ynitta.com/class/algoA/local/handin/list.php?id=kadai3a |
---|---|
提出ファイル | Salary01.java |
コメント欄 | なし |
アルバイトをしていて、その給料の支払日は月末であるとします。 「月」「日」を入力すると、その日から次の給料日までの日数を 表示するプログラム Salary01.java を書いて下さい。 入力された日が支払日当日の場合は0日と答えるものとします。 また閏年は考慮しなくてよいものとします。 各月が何日あるかを記憶するのに、配列を使うとよいでしょう。
間違った月や日の入力に対しては Exception を起こすプログラムで 構わないものとします。 もちろん、間違った入力に対しては「入力が間違いです」と表示 されるようにしても構いません。
Salary01.java |
Salary01.javaの実行例 |
|
提出先 | http://ynitta.com/class/algoA/local/handin/list.php?id=kadai3b |
---|---|
提出ファイル | NthDay.java |
コメント欄 | なし |
月日を入力すると、その日が「その年の何日目であるか」を 表示するプログラム NthDay.java を作って下さい。 閏年は考慮しなくてよいものとします。
間違った月や日の入力に対しては Exception を起こすプログラムで 構わないものとします。 もちろん、間違った入力に対しては「入力が間違いです」と表示 されるようにしても構いません。
NthDay.java |
NthDay.javaの実行例 |
|
提出先 | http://ynitta.com/class/algoA/local/handin/list.php?id=kadai3c |
---|---|
提出ファイル | RevArray.java |
コメント欄 | なし |
次のプログラムは、入力された複数の整数を逆順に表示するプログラムです。 配列の要素を逆順にするメソッド void rev(int[]) の定義を自分で作成しなさい。
RevArray.java |
RevArray.javaの実行例 |
|
提出先 | http://ynitta.com/class/algoA/local/handin/list.php?id=kadai3d |
---|---|
提出ファイル | Salary02.java |
コメント欄 | なし |
原稿書きのアルバイトをしていて、その給料の支払日は原稿を 提出した月の翌月末であるとします。 原稿を提出した月日を入力すると、その日から実際に給料が 支払われるまでの日数を表示するプログラム Salary02.java を書いて下さい。 閏年は考慮しなくてよいものとします。 12月に提出した原稿の支払日には工夫が必要です。
Salary02.javaの実行例 |
|
提出先 | http://ynitta.com/class/algoA/local/handin/list.php?id=kadai3e |
---|---|
提出ファイル | WaitChristmas.java |
コメント欄 | なし |
月日を入力すると、その日からクリスマス(12月25日)まで あと何日であるかを答えるプログラムを書きなさい。 閏年は考慮しなくてよいものとします。 12月26日以降であれば、次の年のクリスマスまでの日数を 答えること。
WaitChristmas.javaの実行例 |
|