Javaプログラミング学習の記録です。
Javaプログラミングの基本的な開発環境
開発環境として通常は統合開発環境 IDE(Integrated Development Environment)ツールを使用しますが、ここではJavaプログラミングの基礎を学習するため、JDK(Java Development Kit)単体を使用します。
Javaのソフトウェア構成
Javaのソフトウェア構成は以下の通りです。
JDK:Javaプログラムを開発するのに必要なものが一通り含まれた開発キット
javac (コンパイラ):ソースコード(.java)からバイトコード(.class)へ変換するプログラム
java (インタープリタ):Java のプログラムは、クラスファイルのバイトコードを読み込み実行
クラスライブラリ(Java プログラムから利用できる様々な API ):よく使用する機能をクラスとしてまとめたもの
javadoc:ソースコードを解析して、自動的にドキュメントを生成するツール
その他ツール、サンプルプログラム
JRE ( Java Runtime Environment ):Javaのプログラムを実行するためのソフト
コアクラスライブラリ
JVM(Java Virtual Machine):どのOSでもJavaのプログラムが実行できる仮想マシン
環境のセットアップとJavaプログラムの実行
環境のセットアップは以下の通りです。
- JDK をダウンロード(無償)して、インストールします。
- インストール後にJDK に用意されているコンパイラ等のコマンドを実行するために、Pathを設定します。
例)Path = C:\Program Files\Java\jdk1.X.X_XX\bin
(インストールするJDKによっては自動で設定するものもあります。)
Javaプログラムの実行手順は以下の通りです。
- ソースコードの作成
ファイル名.java
ファイル名はクラス名にする必要があります。
- コンパイル
javac ファイル名1.java ファイル名2.java ・・・
複数クラスをコンパイルするには半角スペースで区切ります。
- 実行
java クラス名
mainメソッドが含まれているクラス名を指定する必要があります。
クラスファイルの配置
現在のクラスパス(プログラム実行フォルダ)を基準として、パッケージ階層に応じたフォルダ階層を作成し、その中に必要なクラスファイルを配置(保存)しておく必要があります。
ちなみにパッケージとは、クラスをグループ分けして体系的に分類し管理するしくみです。クラスをパッケージに所属させるには、クラスのソースコードの先頭にpackage文を記述します。
package文の構文は以下のとおりです。
パッケージの名前は、.(ピリオド)で区切って複数個記述することができます。
パッケージの名前は、クラスファイルが格納されたフォルダと同じでなければいけません。
例)クラスパスがC:\java_work でパッケージ名がpkg01.pkg02でクラス名がSample の場合、C:\java_work\pkg01\pkg02フォルダにSample.classファイルを配置します。
異なるパッケージに所属しているクラスを使用するには、パッケージ名[.パッケージ名].クラス名 のように完全修飾クラス名 FQCN(Full Qualified class name)を指定する必要があります。このような場合、import文を使用することにより、FQCN入力の手間を省くことができます。
import文の構文は以下のとおりです。
import文はソースコードの先頭で、なおかつpackage文より後に記述します。
また、パッケージに含まれているすべてのクラスを使用する場合は、クラス名の代わりに * を記述します。
JARファイルとは
JARは、Java ARchive の略語で、Javaのパッケージやアプレットを配布するための形式です。jarコマンドによって、.jar という拡張子を持ったファイルに圧縮したり、解凍することができます。package文を使ってクラスファイルを作成した場合、そのクラスファイルを格納したディレクトリをjarコマンドで圧縮することによりファイル容量を節約でき、他のコンピュータ等に配布しやすくなります。
jarコマンドで JARファイル を作成する場合は、以下のように指定します。
jarコマンドでJARファイルにどのようなファイルが含まれているかを確認する場合は、以下のように指定します。
jarコマンドで JARファイルを解凍する場合は、以下のように指定します。
解凍するとMETA-INFフォルダが作成され、このフォルダの中にはMANIFEST.MFというファイルが含まれています。これは、パッケージの内容を Java の実行環境が解釈するためのファイルの一つです。オプションの指定や環境等によって、他にもファイルが作られます。
JARファイルを解凍せずに、コンパイルや実行をする方法もあります。
APIとは
Javaにあらかじめ添付されている多数のクラス群をAPI(Application Programming Interface)といいます。
APIは通常 java.やjavax.で始まるパッケージ名を使用しています。その中の java.lang パッケージに属するクラスは自動的にインポートされます。
Javaが提供しているAPIクラスは、インタネット上の「APIリファレンス」で調べることができます。
Javaプログラミングの基本文法
他のプログラムと異なるところを中心にまとめています。
コメント文
コメント文とは、プログラムのコンパイル時と実行時に無視される注釈です。
単一行コメントの場合
複数行コメントの場合
変数
変数とは、データを格納する箱のようなもので、変数名とデータ型の2つを必ず指定する必要があります。
変数宣言の構文は以下の通りです。
同じデータ型の変数を宣言するには ,(カンマ)で区切ります。
代表的なデータ型は次のものがあります。
基本データ型(数値、論理値や文字を扱う型)
- 整数:int
- 小数:double
- 真偽:boolean
- 文字列:String
参照型(クラス、インタフェース、配列などの参照値を扱う型)
データ型変換
大きいデータ型の変数に小さいデータ型の変数を代入する際、自動的にデータ型が変換され代入されます。
小さいデータ型の変数に大きいデータ型の変数を代入する際、キャストすることで代入できますが、情報の欠落が発生します。
キャストの構文は、変換先のデータ型を()で囲います。
浮動小数点型から整数型にキャストすると小数点以下は切り捨てられます。
配列
配列とは、同じデータ型の変数を連続して並べたもので、インデックス(0から始まる添え字)を使用して要素(個々の値)にアクセスします。
配列変数宣言の構文は以下の通りです。
配列の要素を作成し代入するには、new演算子を使用します。配列変数の宣言と同時に行うには次の構文になります。
配列の要素数を取得する構文は以下の通りです。
配列の要素は自動的に初期化されます。(数値型の場合は 0)
配列の作成と初期値の代入をまとめて行う構文は以下の通りです。
上記を省略して記述する方法もあります。
配列の要素を配列にすることで2次元以上の配列を記述できます。
このような2次元以上の配列を多次元配列と言います。
2次元配列を宣言する構文は以下の通りです。
2次元配列の要素にアクセスする構文は次の通りです。
制御構文
分岐構文
IF文
ステートメント;
}
ステートメント1;
} else {
ステートメント2;
}
ステートメント1;
} else if(式2) {
ステートメント2;
} else {
ステートメント3;
}
IF文のネスト
if(式2) {
ステートメント;
}
}
switch文
case 値1:
ステートメント1;
braek;
case 値2:
ステートメント2;
braek;
default:
ステートメント3;
}
繰り返し構文
while文(前置判定)
ステートメント;
}
do-while文(後置判定)
ステートメント;
} while(式);
for文
ステートメント;
}
例)配列の全要素を使用
配列変数名[i]を使用した処理;
}
拡張for文
任意の変数名を使用した処理;
}
例)配列の全要素を使用
配列変数名[i]を使用した処理;
}
文字列比較
文字列の比較には、Stringクラスに用意された equals というメソッドを使用します。
構文は次の通りです。
オブジェクト指向を理解するための基礎知識
オブジェクト指向とはソフトウェアを開発する際に用いる部品化の考え方をいいます。部品化の規則として現実世界に出てくる登場人物の単位でプログラムをクラス分けします。つまり、現実世界の登場人物とその振る舞いをコンピュータ内の仮想世界で再現することです。
クラスとは
手続き型のプログラムとオブジェクト指向型のプログラムの違いではじめに戸惑うのが、クラスという概念だと思います。よく言われるのがクラスは、オブジェクトの設計図あるいは金型であるという考え方です。このことからもプログラム動作時に実際に動くのはオブジェクトであって、その大元のある設計図であるクラスが動くわけではありません。また、オブジェクトという用語もあいまいでクラスのことを言うこともあります。そのため、プログラムで実際に動作するものをインスタンスと呼び、そのインスタンスを生み出しているものがクラスです。
まとめると、元になるものがクラス(オブジェクト)で、実体がインスタンス(オブジェクト)です。
設計図となるクラスを定義する構文は以下の通りです。
[修飾子] class クラス名 [extends 親クラス名] [implements インタフェース名] {
[フィールド]
[コンストラクタ]
[メソッド]
- フィールド:「属性」 変数や定数を宣言
- コンストラクタ:インスタンス化の際に自動的に呼び出される処理(初期化処理)
- メソッド:「振る舞い」 処理を定義
また、クラスからインスタンスを作るにはnew 演算子を使い、構文は次の通りです。
- 括弧の中には、コンストラクタへ渡す引数を記述
- new 演算子は、インスタンスの参照値を返し、インスタンスを識別
- 変数はインスタンスの基になったクラスをデータ型として宣言
メソッドとは
メソッドはプログラムの「振る舞い」を定義したもので、つまり処理内容を記述したものになります。また、機能単位で記述したメソッドは、プログラムのメンテナンス性を良くしたり、同じ処理を1つのメソッドにまとめることで作業効率を上げることができます。
メソッドを使用するには次の2つのステップが必要です。
>メソッドを定義する
>定義したメソッドを呼び出す
メソッドを定義する構文は以下の通りです。
<戻り値がない場合>
[修飾子] void メソッド名 ([引数リスト]) {
処理内容;
・・・
}
<戻り値を返す場合>
[修飾子] 戻り値のデータ型 メソッド名 ([引数リスト]) {
処理内容;
・・・
return 戻り値;
定義したメソッドを呼び出す構文は次の通りです。
プログラムはmainメソッドから動き始めるため、プログラムの中に複数のメソッドが定義されていてもその記述されている順序には特に意味はありません。
なお、メソッド内で宣言した変数はローカル変数といい、ほかのメソッドからは使用できません。つまり、異なるメソッドに属するローカル変数はお互いに独立していて無関係です。
フィールドとは
フィールドはプログラムの「属性」(オブジェクトが保持するデータ)を定義したもので、クラスブロック内に宣言された変数のことです。
変数を宣言するには以下の通りです。
- int xa; ←データ型と変数名のみ
- int yb = 10; ←変数に初期値を設定できる
- final int zc = 100; ←finalを付けると値を書き換えることのできない定数になる
インスタンス化した際のフィールドを利用する場合は、
インスタンスの変数名.フィールド名
と記述します。
コンストラクタとは
コンストラクタを使うとフィールドの初期値を自動設定することができます。
えっ、「フィールドの変数に初期値を設定できる」とあったけど、これと何が違うの?と思ったかもしれませんが、後で違いを説明しています。
コンストラクタはメソッドの特殊なもので次の制約があります。
- コンストラクタの名前はクラスと同じ名前でなければいけない
- コンストラクタは戻り値を返すことができないので、戻り値のデータ型を宣言できない(void もダメ)
- コンストラクタはインスタンス生成時に自動的に呼び出されるもので、開発者が直接呼び出すことができない
コンストラクタの構文は以下の通りです。
[修飾子] クラス名 ([引数リスト]) {
自動的に実行する処理内容
・・・
コンストラクタとは、インスタンス生成直後に自動で実行される処理を定義することができるものです。したがって、コンストラクタを使うことで、フィールドの初期値をインスタンス生成時に指定することができます。
これに対して、フィールドの初期値はインスタンスが生成されたときに自動的に初期値が代入され、その後にコンストラクタでこの値を上書きすることもできます。
つまり、フィールドで初期化する場合は、すべてのインスタンスに対して同じ初期値が設定されますが、コンストラクタで初期化する場合は、インスタンスごとに異なる初期値を設定できます。
Javaでは、本来すべてのクラスは最低1つ以上のコンストラクタを持っている必要がありますが、特例として、クラスに1つもコンストラクタが定義されていない場合に限って、引数なし処理内容なしのデフォルトコンストラクタがコンパイル時に自動的に追加されるようになっています。
サンプルソースコード1
ここで、サンプルソースコードにてクラス、メソッド、フィールド、コンストラクタを確認します。
public class Person {
// フィールドの定義
private String name;
private int age = 20;
// コンストラクタの定義
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// メソッドの定義
public void sayHello() {
System.out.println("こんにちは。 " + name + " です。 " + age + " 歳です。");
}
// mainメソッド
public static void main(String[] args) {
// インスタンスの生成
Person person1 = new Person("ミケ", 25);
Person person2 = new Person("タマ", 30);
// メソッドの呼び出し
person1.sayHello();
person2.sayHello();
}
}
このプログラムでは、Personというクラスを定義しています。このクラスには、nameと初期値20が入ったageという2つのフィールドがあります。また、Personクラスには、sayHelloというメソッドがあります。sayHelloメソッドは、nameとageの値を出力するシンプルなメソッドです。
Personクラスには、publicで宣言されたコンストラクタがあります。このコンストラクタはnameとageの値を受け取り、それらの値をフィールドに設定する役割を持ちます。
mainメソッドでは、Personクラスのインスタンスを2つ生成し、それぞれにsayHelloメソッドを呼び出しています。person1インスタンスでは、nameに"ミケ"、ageに25を設定し、person2インスタンスでは、nameに"タマ"、ageに30を設定しています。ageはフィールドの初期値20を上書きしています。sayHelloメソッドでは、nameとageの値を出力するため、person1インスタンスでは「こんにちは。 ミケ です。 25 歳です。」、person2インスタンスでは「こんにちは。 タマ です。 30 歳です。」と出力されます。
オーバーロードとは
オーバーロードとは、同じ名前のメソッドを複数個宣言することを言います。同じ名前のメソッドでも、引数の数や型が違えば、異なるメソッドとして認識されます。ただし、戻り値の型だけが異なるものは定義できません。
これと似たような用語にオーバーライドがありますので、間違わないようにしましょう。
以下がオーバーロードのサンプルソースです。
public class Calculator {
// int型の引数を2つ受け取って足し算を行うメソッド
public int add(int n1, int n2) {
return n1 + n2;
}
// double型の引数を2つ受け取って足し算を行うメソッド
public double add(double n1, double n2) {
return n1 + n2;
}
// int型の引数3つを受け取って足し算を行うメソッド
public int add(int n1, int n2, int n3) {
return n1 + n2 + n3;
}
}
この例では、Calculatorクラスに3つのメソッドがあります。最初の2つのメソッドは、引数の型が異なるため、同じメソッド名addを使用しています。3番目のメソッドは、引数の数が異なるため、同じメソッド名を使用します。
オーバーロードにより、コードの重複を減らすことができ、プログラムの保守性を向上させることができます。
カプセル化とは
Javaの学習をすると決まって出てくるのが、オブジェクト指向の3大機能「カプセル化」「継承」「多態性」です。その中の1つである「カプセル化」とは、クラス、フィールドやメソッドを一部の相手からは利用禁止にする機能で、アクセス制御の設定ができます。クラス、フィールドやメソッドの頭に[修飾子]がついていますが、この中の一部のものをアクセス修飾子と言い、これを使用して設定します。
フィールドやメソッドに対するアクセス修飾子の代表的なものとして以下があります。
- private : 自分自身のクラスのみアクセスを許可
- public : すべてのクラスにアクセスを許可
- 何も書かない場合は自分と同じパッケージに所属するクラスにアクセスを許可
(パッケージとはクラスファイルを集めてグループ分けしたもの)
クラスに対するアクセス修飾子として以下があります。
- public : すべてのクラスにアクセスを許可
- 何も書かない場合は自分と同じパッケージに所属するクラスにアクセスを許可
アクセス修飾子の基本的な設定の仕方としては以下の通りです。
- フィールドはすべて private
- メソッドはすべて public
- クラスは一般的に public
つまり「カプセル化」とは、外部から使用していいフィールドやメソッドだけを公開(public)して、それ以外のフィールドやメソッドをカプセルの中に閉じ込めて非公開(private)にすることです。これでアクセス制御することにより安全性を高めています。
また、非公開(private)にしたフィールドにアクセスするために、アクセス専用の以下の2つのメソッドを用意します。
- getter メソッド : フィールドの値を読むための「値を返すだけのメソッド」
構文は以下の通りです。
private データ型 フィールド名;
public フィールドのデータ型 getフィールド名() {
return this.フィールド名;
}
- setter メソッド : フィールドに値を書くための「値を書き込むだけのメソッド」
構文は以下の通りです。
private データ型 フィールド名;
public void setフィールド名(フィールドのデータ型 引数名) {
this.フィールド名 = 引数名;
}
これら2つのメソッドを用意することにより、setter メソッドを削除してgetter メソッドだけにすることで、フィールドを外部から読めるが書き換えられないRead Onlyのフィールドにできるし、setter メソッドを使用して外部からフィールドの値を書き換える際にsetter メソッド内に条件式を入れることで、書き換える値のチェックを行うことができます。こうすることにより、フィールドを保護することができます。
以下がカプセル化のサンプルソースです。
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
このコードでは、Personクラスのフィールド(nameとage)がprivateで定義されています。これにより、外部からの直接的なアクセスが制限されます。また、publicなgetterとsetterメソッドが定義されています。これらのメソッドを通じて、外部からフィールドにアクセスすることができます。
例えば、以下のようなコードを記述することで、Personクラスのインスタンスを生成し、フィールドにアクセスすることができます。
System.out.println(person.getName()); // "ミケ"を出力
person.setName("タマ");
System.out.println(person.getName()); // "タマ"を出力
このように、getterとsetterメソッドを介してフィールドにアクセスすることで、外部からの直接的なアクセスを制限し、安全性を高めることができます。
継承とは
オブジェクト指向の3大機能の1つである「継承」とは、すでにあるクラスを継承して、新しいクラスを作成する機能です。これにより、コードの再利用性を高めることができます。
継承元となるクラスを「親クラス(スーパークラス)」といい、継承先の新しく作成されたクラスを「子クラス(サブクラス)」といいます。
継承を使用したクラスの定義は以下の通りです。
[修飾子] class クラス名 extends 親クラス名 {
[親クラスとの差分のフィールド]
[コンストラクタ]
[親クラスとの差分のメソッド]
- 親クラスのフィールドとメソッドは継承するので子クラスに記述することなく使用できます。
- 子クラスに新機能を作成する場合、新しい名前でフィールドやメソッドを作成します。
- 親クラスのメソッドを修正したい場合、親クラスのメソッドと同じ名前でメソッドを作成することにより上書きします。
これをオーバーライドといいます。(フィールドは意図しない動作をすることがあるのでオーバーライドさせないこと。)
継承は、1つのクラス(親クラス)をベースとして複数の子クラスを定義できるし、さらに孫クラスも定義できます。
ただし、Javaでは複数のクラスを親として1つの子クラスを定義すること(多重継承)はできません。
なお、クラス宣言に修飾子 final を付けると継承することができません。
また、メソッド宣言に修飾子 final を付けるとオーバーライドできません。
継承では子クラスと親クラスの間に、is-a の関係になっている必要があります。
is-a の関係とは、「子クラス is-a 親クラス」、つまり、子クラスは親クラスの一種であるという関係が成立しないと継承してはいけません。
これにより、子クラスになるほど特殊かつ具体的なものに「特化」(具体化)していき、親クラスになるほど抽象的であいまいなものに「汎化」(一般化)します。
抽象クラスと抽象メソッド
継承に関連して、親クラスの特殊なものとして修飾子 abstract を付けた抽象クラスがあります。
抽象化した親クラスでは、詳細が未定のままメソッドを記述する必要があり、それを抽象メソッドといいます。
抽象メソッドの構文は以下の通りです。
抽象メソッドは詳細が未定なので { } の定義部分がなく、代わりに ; を付けます。
抽象メソッドを含むクラスは必ず abstract を付けるルールになっていて、このクラスを抽象クラスといいます。
抽象クラスの構文は以下の通りです。
public abstract class クラス名 {
・・・
}
抽象クラスは、new によるインスタンス化ができません。
そのため、継承専用のクラスは抽象クラスとして宣言することにより、誤ってインスタンス化されることはありません。つまり、子クラスで抽象メソッドをオーバーライドする必要があります。
インターフェースとは
親クラスの特殊なものとして抽象クラスがありましたが、さらに抽象度が高い特別なものとしてインタフェースがあります
インタフェースの条件は以下の通りです。
- すべてのメソッドは抽象メソッドである。(「public abstract」は省略)
- 基本的にフィールドを持たない。
- public static final が付いたフィールド、つまり定数宣言はできる。(「public static final」は省略)
インタフェースの構文は以下の通りです。
public interface インタフェース名 {
・・・
}
インタフェースを継承して子クラスを定義する場合は、extens ではなく、implements を使用します。
インターフェースを実装する際の構文は以下の通りです。
public class クラス名 implements インタフェース名1,インタフェース名2,・・・ {
・・・
}
インタフェース内ではメソッドの処理動作をいっさい定義していないため、多重継承が可能です。
クラスがインタフェースを実装することで、そのクラスはインタフェースで定義されたメソッドを必ず実装する必要があります。つまり、インタフェースはクラスが提供しなければならないメソッドを定義するための仕組みともいえます。
なお、インタフェース定義する際、既存のインタフェースを継承することも可能で、その場合は implements ではなく extends を使用します。
多態性とは
オブジェクト指向の3大機能のうち、最後の1つである「多態性」とは、簡単に言えば同じようなものを同じように操作できる機能です。
同じようなものとは、ある親クラスを継承して作成された複数の子クラスのことです。
同じように操作できるというのは、それら複数の子クラスのインスタンスを親クラス型の配列でまとめて扱い、親クラス型の引数や戻り値を利用してまとめて処理できることです。
しかし、まとめて処理しても個々のインスタンスは各子クラスにおける定義に従って異なる処理をしてくれます。
この多態性は、他の機能のように専用の構文がないので、理解しづらい機能と言えます。
そこで以下に多態性のサンプルソースを紹介します。
class Animal {
public void sound() {
System.out.println("動物のなき声");
}
}
class Dog extends Animal {
public void sound() {
System.out.println("ワンワン");
}
}
class Cat extends Animal {
public void sound() {
System.out.println("ニャオ");
}
}
class Goat extends Animal {
public void sound() {
System.out.println("メェー");
}
}
public class Main {
public static void main(String[] args) {
Animal[] anim = new Animal[3];
anim[0] = new Dog();
anim[1] = new Cat();
anim[2] = new Goat();
for (Animal an : anim) {
an.sound();
}
}
}
上記のコードでは、親クラスAnimalを継承する子クラスDogとCatとGoatを作成し、それぞれsound()メソッドをオーバーライドしています。
Mainクラスでは、Dog、Cat、Goatの3つのインスタンスを作成し、親クラス型の配列anim でまとめて扱い、それぞれのsound()メソッドを呼び出しています。
このプログラムを実行すると、以下のように出力されます。
ニャオ
メェー
静的フィールドと静的メソッド
修飾子static を指定したフィールドを静的(クラス)フィールドといい、以下の通りです。
- フィールド変数の実体がクラスに準備
- すべてのインスタンスに共通の情報を保存するためのフィールド
- インスタンス化しなくてもアクセスできる
静的フィールドへのアクセス方法は以下の2つです。
修飾子static を指定したメソッドを静的(クラス)メソッドといい、以下の通りです。
- メソッド自体がクラスに属する
- インスタンス化しなくても呼び出せる
- 同一クラス内のstaticが付いていないフィールドやメソッドは使用できない
静的メソッドへのアクセス方法は以下の2つです。