前回に引き続き参照型のなかでも、配列について確認していきます。
前回の記事はこちら。
配列の正体
配列は一つの変数名で複数のデータを管理する仕組みでしたね。
実は、配列の正体はオブジェクトなのです。
言い換えると、配列は参照型です。
紛らわしいことに、配列の要素は普通の変数でも参照型でも構いません。
【配列】
public class ArraySample01{ public static void main(String[ ] args){ int[ ] a = new int[3]; a[0] = 9; a[1] = 4; a[2] = 5; for(int i = 0; i < 3; i++) System.out.println(a[i]); } }
実行結果
9 4 5
注目したいのは配列の宣言の部分です。
int[ ] a = new int[3];
aというのは配列名で、int型のデータではありません。
int型のデータになるのはa[0]、 a[1]、 a[2]といったところです。
では、aとは何でしょうか?
宣言のところをよく見てみてください。
Humanクラスなどと比較してみましょう。
int[ ] a = new int[3]; Human taro = new Human("太郎", 30, 175.5);
Humanクラスと比べると、「int[ ]クラスというクラスがあり、そのオブジェクトaを宣言した」と読むことができます。
実はまったくこの通りなのです。
int[ ]という、一見変わった名前のクラスとして扱うことができます。
もちろんchar型の配列ならchar[ ]クラス、double型の配列ならdouble[ ]クラスがあると考えて問題ありません。
配列の正体はオブジェクトなのです。
【配列の要素がオブジェクトの場合1】
public class ArraySample02{ public static void main(String[ ] args){ String[ ] a = new String[3]; a[0] = "鈴木 隆弘"; a[1] = "加藤 博一"; a[2] = "高木 豊"; for(int i = 0; i < 3; i++) System.out.println(a[i]); } }
実行結果
鈴木 隆弘 加藤 博一 高木 豊
配列の各要素には変数が入りますが、あくまでも参照値のみがインスタンスとして保持されます。
ただ単にそれが繰り返されるだけなので、あまり難しく考える必要はありません。
【配列の要素がオブジェクトの場合2】
public class MyObj{ private int data; public MyObj(int x){ data = x; } public int getData( ){ return data; } }
public class ArraySample03{ public static void main(String[ ] args){ MyObj o[ ] = new MyObj[4]; o[0] = new MyObj(7); o[1] = new MyObj(4); o[2] = new MyObj(3); o[3] = new MyObj(8); for(int i = 0; i < o.length; i++) System.out.println(o[i] + "の値は" + o[i].getData( ) + "です"); } }
実行結果
MyObj@15db9742の値は7です MyObj@6d06d69cの値は4です MyObj@7852e922の値は3です MyObj@4e25154fの値は8です
配列の要素が4つあります。
4つすべてがオブジェクト、つまり参照型です。
そのためそれぞれハッシュ値を持ちます。
これは1つ前の、Stringを要素に持っている配列と特に変わりません。
それぞれが持っているデータはgetData( )メソッドを呼ばないと取得できず、要素名はオブジェクトのハッシュ値を表します。
ポイント!
配列の参照値は[I@15db9742などのように出力されたはずです。
@マークの右側はハッシュ値、左側はクラス名のはずですが、[Iとは何でしょうか。
[は配列を意味し、Iは配列の要素がint型であることを示します。
他の方の配列も作り、@マークの左側がどうなるか確認してみましょう。
多次元配列
これは前項の「オブジェクトの配列」とまったく変わらないのですが、「配列の配列」を作ることによって多次元の配列を実現することができます。
【多次元配列を使う】
public class ArraySample04{ public static void main(String[ ] args){ int[ ][ ] a = new int[3][ ]; a[0] = new int[2]; a[1] = new int[4]; a[2] = new int[1]; a[0][0] = 6; a[0][1] = 7; a[1][0] = 1; a[1][1] = 3; a[1][2] = 8; a[1][3] = 9; a[2][0] = 4; for(int i = 0; i < a.length; i++){ for(int j = 0; j < a[i].length; j++){ System.out.println(a[i][j]); } } } }
実行結果
6 7 1 3 8 9 4
まず、3行目の段階で配列が1つ作られます。newを実行したのはまだ1回です。
配列の各要素も配列ですが、まだインスタンス化されていない状態です。
そのため、5行目~7行目でそれぞれをインスタンス化します。つまりnewを呼びます。
それでようやく全体が完成します。そのイメージは以下の通りです。
配列はオブジェクトなので、「配列の配列」は前項の「オブジェクトの配列」と同じです。
オブジェクトのところでもふれたように、ただ繰り返していくだけです。
三次元以上の配列も同様に作っていくことができます。
コマンドライン引数
main( )メソッドには引数があります。
見てのとおり、Stringオブジェクトの配列を受け取れるようになっています。
public static void main(String[ ] args){ }
では、main( )メソッドへの引数はどうやって渡すのでしょうか?
これはJavaVMを起動するとき、つまりクラスを実行するときに指定します。
Sampleというクラスを実行するとき、「実行の構成」を選択し、引数を指定します。
abc def ghi
などと文字列を追加できます。
スペースで区切ることによって、それぞれが文字列オブジェクトとして扱われます。
これを「コマンドライン引数」と言います。
では、これを使ったプログラムを見てみましょう。
【コマンドライン引数の利用】
public class Param01{ public static void main(String[ ] args){ System.out.println("先に入ったデータ : " + args[0]); System.out.println("後に入ったデータ : " + args[1]); } }
パッケージエクスプローラから「Param01.java」を選択し、[実行]メニューから[実行構成]を選択します。
[メイン]タブを選択し、[メイン・クラス]の[検索]をクリックして、実行したいクラス名を選択します。
[引数]タブを選択し、[プログラムの引数]に「abc def ghi」と入力し、実行をクリックします。
実行結果
先に入ったデータ : abc 後に入ったデータ : def
この例では、文字列abcがargs[0]、defがargs[1]、ghiがargs[2]に入ります。
プログラムの方ではargs[2]を無視していますが、引数としては渡されています。
逆に、引数が足りないとこのような例外を発生します。
(mainではない)普通のメソッドへ引数を渡す場合は、引数リストが間違っているとEclipseでソースコードを入力時にエラーが発生します。
コマンドラインの場合は実行時に自由に引数を書けるので、プログラム中で使っている引数を渡さないとこのような例外が発生します。
そこで、一般的にはmain( )メソッドの中で最初に引数が渡されているかどうかチェックをします。
public class Param02{ public static void main(String[ ] args){ if(args.length < 2){ System.out.println("使用方法 : java Param02 [引数1] [引数2]"); return; } System.out.println("先に入ったデータ : " + args[0]); System.out.println("後に入ったデータ : " + args[1]); } }
実行時にコマンドライン引数を指定せずに「Param02」を実行します。
実行結果
使用方法 : java Param02 [引数1] [引数2]
このプログラムでは、コマンドライン引数を2つ使用しています。
使用するはずの引数が得られないと困るので、main( )メソッドが実行された時点でコマンドライン引数が渡されているかどうかチェックします。
要素が2つ未満の場合は使い方が間違っているので、ユーザにそれを教えてプログラムは終了しています。
このように、値をチェックするというのはいろいろな場面で行うことがあります。
値が渡されているかどうか、だけではなく値が入っているかどうか、というチェックをする場合もあります。
ポイント!ハードコーディング
コマンドライン引数を利用することで、「ハードコーディング」を減らすことができます。
ハードコーディングとは何か?
順を追って説明しましょう。
プログラムの中では様々なデータが扱われています。例えば、以下のSample.javaというソースコードの中ではnumberという変数やnameというオブジェクトです。
このとき、違う値を使いたくなったときはどうしたら良いでしょうか?
public class Sample{ public static void main(String[ ] args){ int number = 17; String name = "斎藤 明夫"; System.out.println(number); System.out.println(name); } }
その場合は当然内容を書き換えてコンパイルするわけですが、「コンパイルをしなおす」というのは「今まで作ったアプリケーションはもう使えないので新しく作りなおした」という意味を持ちます。このように、扱うデータが決まってしまっている、何をするか決まっていて変えられない、このようなことを「ハードコーディング」と言います。
一般的に、ハードコーディングはできるだけ避けるべきです。
プログラム中ではどんなデータを扱うか勝手に決めず、実行するたびに入力したりファイルから読み込んだりするのが一般的なデータの扱い方です。
まとめ
- 配列の正体
配列はオブジェクトなので、同様に参照型です。
- 多次元配列
多次元配列は「配列の配列」を作ることによって実現できます。
- コマンドライン引数
main( )メソッドへの引数は、クラスを実行するときにスペースで区切って指定します。
参考図書
独学で挫折しそうになったら、オンラインプログラミングスクール