JAVA　基本文法[7] - 複数クラスを用いた開発

API
Javaにあらかじめ添付されている多数のクラス群をAPIという。
APIは通常「java.」や「javax.」で始まるパッケージ名を用いている。
java.langパッケージに属するクラスは自動的にインポートされる
APIに用意されているクラスは、APIリファレンスで調べることが出来る。

---

###### ソースファイルを分割する
１つのソースファイルの中に含まれるメソッドの数が増えると、ソースコード全体を把握することが難しくなる…。
そこで！！Javaでは１つのソースファイルに全てのメソッドを書くのではなく、複数のソースファイルに分割して記述出来るような仕組みを持つ。
「複数のソースファイルに分けて開発する＝複数のクラスに分けて開発する」
１つのプログラムを複数の部品に分けることを「部品化」という。
複数のクラスファイルが入っているフォルダを丸ごと「１つの完成品」とする。

メソッドを複数のクラスに分けて記述するメリット
・「ファイルごとに開発を分担し、それぞれが並行して開発を進められる」
・整理されてわかりやすくなる

```Calc.java
//以下のプログラムを分割しよう！！！
public class Calc {
  public static void main(String[] args) {
    int a = 10; int b = 2;
    int total = tasu(a, b);
    int delta = hiku(a, b);
    System.out.println("足すと" + total + "、引くと" + delta);
  }
  public static int tasu(int a, int b) {
    return (a + b);
  }
  public static int hiku(int a, int b) {
    return (a - b);
  }
}
```
↓分割すると、以下の２つのソースファイルに分割することが出来る。
```Calc.java
//メインメソッドがあるCalcクラス
public class Calc {
  public static void main(String[] args) {
    int a = 10; int b = 2;
    int total = CalcLogic.tasu(a, b);
    int delta = CalcLogic.hiku(a, b);
    System.out.println("足すと" + total + "、引くと" + delta);
  }
}
```
```CalcLogic.java
//計算処理が記述されたCalcLogicクラス
public class CalcLogic {
  public static int tasu(int a, int b) {
    return (a + b);
  }
  public static int hiku(int a, int b) {
    return (a - b);
  }
}
```
---
##### 複数クラスで構成されるプログラム
●複数クラスのコンパイル
分割したプログラムはそれぞれをコンパイルする必要がある！

::: note
複数のソースファイルをjavacコマンドでコンパイルするには次のコマンドを打つ。
javac クラス名①.java クラス名②.java
:::

●複数クラスの入ったフォルダの「プログラムの実行方法」

::: note
複数クラスの入ったフォルダの「プログラムの実行方法」
java　クラス名
:::
「渡された複数のクラスファイルのうち、mainメソッドが含まれているクラスの名前を指定する必要がある」

---
##### パッケージの利用
クラスが増えて分かりにくい…そこで解決してくれる仕組みが「パッケージ」である。
Javaには、「各クラスを『パッケージ』というグループに所属させて、分類、管理できる仕組みを持つ」。
パッケージ名は、Javaの識別子のルールに従っていれば自由に定めてOK。アルファベットは小文字を使用するのが一般的。またドットで区切ったパッケージ名も多くある。（例. calcapp.main）
「パッケージの中にパッケージを入れることが出来ない。更に、パッケージに親子関係や階層関係はない。」

::: note
クラスをパッケージに所属させる
package 所属させたいパッケージ名;
※package文は必ずソースコードの先頭に記述する。
:::
ただ上記の文だけではコンパイルエラーになってしまう。「どのパッケージの〇〇クラスかを明示していない」為である。
その為下記で説明する「所属パッケージ名を添えたクラス名を指定」する必要がある。

●パッケージを含むクラス名を指定する
```Calc.java
//別のパッケージにあるクラスを呼び出す
package calcapp.main;

public class Calc {
  public static void main(String[] args) {
    int a = 10; int b = 2;
    int total = calcapp.logics.CalcLogic.tasu(a, b);
    int delta = calcapp.logics.CalcLogic.hiku(a, b);
    System.out.println("足すと" + total + "、引くと" + delta);
  }
}
```
このように、あるクラスから別パッケージのクラスを利用する場合、「パッケージ名を頭につけた完全なクラス名」を使う必要がある。この完全なクラス名のことを、「完全限定クラス名（FQCN）」という。

::: note
完全限定クラス名（FQCN）
パッケージ名.クラス名
:::

●完全限定クラス名（FQCN）の入力を省略する
import文を扱う。

::: note
完全限定クラス名（FQCN）の入力を省略する宣言
import パッケージ名.クラス名;
※import文は、ソースコードの先頭に記述する。ただ、package文よりは後に！

パッケージの全クラスをインポートする場合は…
import パッケージ名.*;

複数のパッケージのそれぞれの全クラスをインポートする場合は１個ずつ記述する…
import パッケージ名①.&#42;;
import パッケージ名②.&#42;;
:::

●パッケージに属したクラスの実行方法
「Java クラス名」というコマンドを打っても、実行するとエラーになる。
「NoClassDefFoundError」というエラーで直訳すると「クラス定義が見つからない」。
原因は、「起動しようとしているクラスの指定が誤っている」こと。
その為、次のように起動しなければならない。

::: note
パッケージに属したクラスの実行コマンド
java 起動したいクラス完全限定クラス名（FQCN）
:::
これでOKと思いきやこれでもまだエラーになってしまう…

●クラス名だけでクラスファイルを探しだすための仕組み
「クラスローダー」：
JVMが内部に持っている機構。
「完全限定名を指定されたら、その名前を持つクラスのクラスファイルをPC内から検索し、JVMに読み込んで利用可能にする」という役割を担っている。
クラスローダーは読み込み対象クラスのFQCNに基づき、クラスパスを基準としてパッケージ階層に従ったフォルダ構成内を探し読み込む。

ここでエラーの原因を説明。
前述で述べたものだと、「JVMは使いたいクラス名を指定しているだけであって、クラスファイルがハードディスクのどこのフォルダにあるのかを、いっさい指定していない点」にある。

クラスローダーは「クラスパス」というヒント情報を使うことで、高速に目的のクラスファイルを探しだす。
クラスパスとは、「クラスローダーがクラスファイルを探す際に。見にいくべきフォルダの場所」のことで、
あらかじめ１つ以上の場所を指定する。（例.c:¥work）

::: note
[クラスパスの指定方法３つ]
❶起動時にjavaコマンドで指定する
javaコマンドでJVMを起動する際に、-cp（-classpath）オプションで指定。
例）「java -cp c:¥work Calc」
❷検索場所をOSに登録しておく
javaコマンドを入力する度に、毎度-cpオプションをしてするのは大変。そこでOSの「環境変数」という設定にクラスパスを登録しておくことが出来る。javaコマンドは、この環境変数を自動的に読み込んでクラスファイルの検索に利用する。環境変数の設定方法はOSによって異なるため詳細は後ほど。
❸特に設定しない
環境変数に指定がなく、-cpオプションの指定もない場合、通常はjavaコマンドが実行されたフォルダがクラスパスとなる。例えば、c:¥workでjavaコマンドを実行すれば、c:¥workがクラスパスに設定される。
:::
::: note
クラスパスで指定できる対象
・フォルダの場所
クラスファイルが置かれているフォルダの場所（絶対パス）を指定。最も一般的。c:¥workだと、workフォルダ内のクラスファイルが検索対象となる。

・クラスファイルが入ったJARファイルやZIPファイル
例）「c:¥work¥jars¥calcapp.jar」

・複数のフォルダ、jar/ZIPファイル、それらの組み合わせ
複数のフォルダ、jar/ZIPファイルをデリミタ文字で区切ってクラスパスに指定できる。
デリミタ文字、Windowsの場合は「;」、MacOSやLinuxは「：」。クラスローダーは、指定された場所を前から順に探していく。
Windows例）「c:¥work;c:¥work¥jars¥calcapp.jar」
MacOSやLinux例）「/var/javadev:/var/javadev/jars/calcapp.jar」
:::

ここまで来たが、まだエラーになる…
エラー「ClassNotFoundException」という表示が…。これは「クラスローダーが目的のクラスファイルを探し出せない」
[クラスファイルの正しい配置]を理解する必要がある。
「現在のクラスパスを基準として、パッケージ階層に対応したフォルダ階層を作り、その中に必要なクラスファイルを配置しておく必要がある。」


::: note alert
プログラムが動作するまでの流れ（これまでのまとめ）
①JVMは起動させるクラス名（calcapp.main.Calc）を受け取る。
②JVMはクラスローダーに対してcalcapp.main.Calcを読み込むよう指示する。
③クラスローダーはクラスパスを確認する。
④クラスローダーは、クラスパスを基準として「calcapp」→「main」とフォルダを降りていき（c:¥work¥calcapp¥mainの中）、そこにCalc.classというファイルを発見する。
⑤クラスローダーは発見したCalc.classを読み込む。
⑥JVMは読み込んだCalcクラスのmainメソッドを実行する。
:::

---
##### 名前空間
●パッケージ使うもう一つのメリットは、担当者同士のクラス名の取り合いを防げること。
内容が異なる別々のクラスで同じ名前を取り合うことを「名前の衝突」という。
「名前空間（使うことができる名前の総量）」は限られており、新しくクラスを作るとそのクラス名は使えなくなり、使えるクラス名は減っていく…。
しかしJavaでは！！！！
「パッケージが異なれば、同じクラス名を使って良い」というルールがある。なぜならクラス名が同一でも、パッケージ名が異なれば「完全限定クラス名が異なるので両者を区別できるから」。

しかし第２問題…パッケージ名がダブっては意味がなくなる。
●パッケージ名自体の衝突を避ける方法
Javaでは、自分（自社）が保有するインターネットドメインが前後逆順にしたものから始まるパッケージ名を用いることを推奨。
例）foo.example.comというインターネットドメインを取得している企業であれば、com.example.fooで始まるパッケージ名を使う。インターネットドメインはそれぞれの組織ごとに世界に１つだけなので、これでパッケージ名の衝突をすることがない。com.example.fooより後は、企業や組織内でパッケージ名が衝突しないように調整を行えばいい。

---
##### Java API
JavaのAPI（Application Programming Interface ）は、Javaでアプリケーションを作成するために必要となる標準機能を提供するインタフェースのことです。

```Main.java
//APIの一例
public class Main {
  public static void main(String[] args) {
    int[] heights = {172, 149, 152, 191, 155};
    java.util.Arrays.sort(heights); //(API)並べ替え命令
    for (int h : heights) {
      System.out.println(h);
    }
  }
}
/*実行結果
149
152
155
172
191
*/
```
・APIは通常「java.」「javax」で始まるパッケージを用いている。
・java.langパッケージに属するクラスは自動的にインポートする
・APIに用意されているクラスは、APIリファレンスで調べることができる。