Java SE 11 Silver 学習 クラス／インタフェース


## はじめに
- [Oracle Certified Java Programmer, Silver SE 11 認定資格](https://www.oracle.com/jp/education/certification/1z0-815-jpn-48261-ja.html)の受験にあたり、Java11について学習／検証した内容を資料としてまとめる。
- 本ドキュメントは、[出題範囲](https://www.oracle.com/jp/education/certification/1z0-815-jpn-48261-ja.html#skill)のうち、以下出題範囲に対応する調査を行った結果となる。
    - クラスの宣言とインスタンスの使用
    - メソッドの作成と使用
    - カプセル化の適用
    - 継承による実装の再利用
    - インタフェースによる抽象化

## 本ドキュメントで解説する内容
- パッケージ
- クラス
    - コンストラクタ
    - フィールド
    - メソッド
        - ローカル変数
        - オーバーライド
        - オーバーロード
        - エントリポイント
- インナークラス
- インタフェース
- 例外

### パッケージ
- Javaのソースファイルを目的に合わせてグループ化した単位。
  - パッケージ名の衝突を防ぐ目的で、パッケージ名は「所属組織のドメイン名の逆順」＋「組織内規約によるパッケージ名」が推奨されている。
  - (参考: [The Java® Language Specification Java SE 11 Edition](https://docs.oracle.com/javase/specs/jls/se11/html/jls-6.html#d5e8350)）
    ```java:パッケージ定義の例
    package org.openjdk.compiler.source.tree;
    ```
**TIPS**
- パッケージの目的は「名前空間の提供」「アクセス制御の提供」「クラスの分類」となる。
- パッケージ宣言を定義しないソースファイルは、「デフォルトパッケージ」に属するものとして扱われる。
    - 「明示的にパッケージ宣言したクラス」からは「デフォルトパッケージに属するクラス」は呼び出しできない(パッケージがimport不可)。
        ```java:test/P.java
        package test;
        public class P {
          public P() { System.out.println("Define package"); }
        }
        ```
        ```java:NP.java
        public class NP {
          public NP() { System.out.println("No Define package"); }
        }
        ```
        ```java:test/B.java
        package test;
        public class B {
          public static void main(String[] args) {
            new P(); //OK
            new NP(); // コンパイルエラー(「デフォルトパッケージに属するクラス」は呼び出し不可)
          }
        }
        ```


### クラス
- クラスとは、オブジェクトが持つ属性(フィールド)や処理(メソッド)を定義したもの。

**TIPS**
- 初期化子の実行タイミングは以下の通り。
    1. クラスロード時
        1. static変数の宣言及び初期化
        2. static初期化子 (複数存在する場合、上から順に実行される)
    2. インスタンス生成時
        1. インスタンス変数の宣言及び初期化
        1. 初期化子 (複数存在する場合、上から順に実行される)
        1. コンストラクタ

```java
public class Main {
  static int s = 1;
  static { System.out.println(s++ + ": static初期化子(1)"); }
  static { System.out.println(s++ + ": static初期化子(2)"); }
  int f = 1;
  { System.out.println(s++ + "," + f++ + ": 初期化子(1)"); }
  { System.out.println(s++ + "," + f++ + ": 初期化子(2)"); }

  public Main() {
    System.out.println(s++ + "," + f++ + ": コンストラクタ");
  }

  public static void main(String[] args) {
    System.out.println("[インスタンス生成]");
    new Main();
  }
```

```text:実行結果
$ java Main.java
1: static初期化子(1)
2: static初期化子(2)
[インスタンス生成]
3,1: 初期化子(1)
4,2: 初期化子(2)
5,3: コンストラクタ
```


### コンストラクタ
- クラスのインスタンスを生成する際に実行される戻り値を持たないメソッド。

**TIPS**
- コンストラクタが定義されないクラスでは、デフォルトコンストラクタ(引数なし／処理なし／可視性はクラスの可視性と同一)が自動的に提供される。
    ```java: デフォルトコンストラクタのイメージ
    public Main(){
        super();
    }
    ```
- コンストラクタでは、処理の先頭にて`super`（スーパークラスのコンストラクタ）、または`this`（自クラスの別のコンストラクタ）を1回だけ実行する必要がある。
    - `super`/`this`が呼ばれない場合、暗黙的に処理の先頭で`super()`が呼ばれたものとして扱われる(正常処理)。
    - `super`/`this`が2回以上呼ばれる場合はコンパイルエラーが発生する。
    - `super`/`this`の前に処理が存在する場合はコンパイルエラーが発生する。

### フィールド
- クラスの属性を表現するために、フィールドとして以下の変数を定義できる。
  - インスタンス変数: クラスインスタンスに紐付く変数
  - static変数： クラスに紐付く変数

**TIPS**
- フィールドの変数が初期化されない場合、数値プリミティブ型の場合は`0`、`boolean`の場合は`false`、参照型の場合は`null`の値で初期化される。
- 親インスタンスのフィールドは `super.フィールド名` で参照できる。
- final属性が付与されたフィールドの変数には、変数宣言、初期化子、コンストラクタのうち1カ所以上で値を代入する必要がある(代入しない場合はコンパイルエラー)
- 親インスタンスと子インスタンスに同じフィールド名が定義された場合、変数を宣言した型のフィールドにアクセスされる。
  ```java
  class A {　public int a = 10;　}
  class B extends A { public int a = 20; }

  public class C {
    public static void main(String[] args) {
      A a1 = new A();
      A a2 = new B();
      B b = new B();
      System.out.println(a1.a); //10 (変数の型宣言(A)のフィールド)
      System.out.println(a2.a); //10 (変数の型宣言(A)のフィールド)
      System.out.println(b.a);  //20 (変数の型宣言(B)のフィールド)
    }
  }
  ```

### メソッド
- 処理を一連でまとめて記述する機能で、クラスの振る舞いを定義することが出来る。

**TIPS**
- 親インスタンスのメソッドは `super.メソッド名([引数])` で参照できる。
- メソッドを実行する場合において、以下の優先順位で実行対象のメソッドが決定される。
    1. 変数の型宣言クラスにおける互換性のあるメソッドのうち最も厳密なメソッドに対してオーバーライドされたメソッド
    2. 変数の型宣言クラスにおける互換性のあるメソッドのうち最も厳密なメソッド
    3. 変数の型宣言クラスのスーパークラスにおける互換性のあるメソッドのうち最も厳密なメソッドに対してオーバーライドされたメソッド
    4. 変数の型宣言クラスのスーパークラスにおける互換性のあるメソッドのうち最も厳密なメソッド

```java
  public static void main(String[] args) {
    A a1 = new A();
    A a2 = new B();
    A a3 = new C();
    B b1 = new B();
    B b2 = new C();
    C c = new C();

    a1.out(new ArrayList<>()); // A-Collection(2.型宣言クラスのメソッド)
    a2.out(new ArrayList<>()); // A-Collection(2.型宣言クラスのメソッド)
    a3.out(new ArrayList<>()); // C-Collection(1.型宣言クラスのメソッドのオーバライド)
    b1.out(new ArrayList<>()); // A-Collection(4.型宣言クラスのスーパークラスのメソッド)
    b2.out(new ArrayList<>()); // C-Collection(3.型宣言クラスのスーパークラスのメソッドのオーバライド)
    c.out(new ArrayList<>());  // C-List(2.型宣言クラスのメソッドのうち最も厳密なもの)
  }

  public static class A {
    public void out(Collection c) { System.out.println("A-Collection"); }
  }
  public static class B extends A {}
  public static class C extends B {
    public void out(Collection c) { System.out.println("C-Collection"); }
    public void out(List c)       { System.out.println("C-List"); }
  }
```


### ローカル変数
 - メソッドで宣言された変数はローカル変数として、メソッド内のみ利用できる。

**TIPS**
- ローカル変数は自動的な初期化は行われない。初期化されていないローカル変数を参照するとコンパイルエラーが発生する。
- ローカル変数の宣言時に値を代入(`var 変数名 = 値;`)することで、代入される値からデータ型を決定する機能(Java10以降)
- ラムダ式や配列初期化式など、型を推測できない場合は利用できない。
- varで宣言された変数は、コンパイル時に静的型付けされるため別の型の値は代入不可。
- varで宣言された変数は、静的型付けされた型として利用できる(メソッドをコールするなど)。

```java
var num = 10L; // long
var str = "abc".substring(2,3); // String
var lambda = () -> {}; // コンパイルエラー
var array = {"ABC", "DEF"}; // コンパイルエラー

for (var s: String[] args) {
  System.out.println(s.charAt(0));
}
```



### オーバーライド
- スーパークラスのメソッド定義に加えてサブクラスに新しいメソッド定義を追加すること。

**TIPS**
- メソッドをオーバーライドする条件は以下となる。
  - シグニチャ(メソッド名＋引数リスト)が同一であること。
  - 戻り値がスーパークラスのメソッドの型と同一か、その型を継承した型であること(共変戻り値)
  - throws句で指定できる例外はスーパークラスのメソッドの例外と同一かその例外を継承した例外であること。またはthrows句を指定しないこと。
  - アクセス修飾子がスーパークラスのメソッドと同一か、より緩いこと。
    - スーパークラスがpublicの場合： publicを指定可能
    - スーパークラスがprotectedの場合： protected, publicを指定可能
    - スーパークラスがdefaultの場合： default, protected, publicを指定可能
    - スーパークラスがprivateの場合： オーバライド不可

|スーパークラス＼サブクラス|public|protected|default|private|
|:--|:--|:--|:--|:--|
|public|override|コンパイルエラー|コンパイルエラー|コンパイルエラー|
|protected|override|override|コンパイルエラー|コンパイルエラー|
|default|同一パッケージ: override<br>別パッケージ: 別メソッド|同一パッケージ: override<br>別パッケージ: 別メソッド|同一パッケージ: override<br>別パッケージ: 別メソッド|同一パッケージ: コンパイルエラー<br>別パッケージ: 別メソッド|
|private|別メソッド|別メソッド|別メソッド|別メソッド|

※ 別メソッドの場合、オーバーライドではない別のメソッドとして定義される。その場合、`@Override`アノテーションが付加されているとコンパイルエラーとなる。

### オーバーロード
- クラスにおいて、同一のメソッド名を持ち引数が異なる複数のメソッドを定義すること。

**TIPS**
- オーバーロードメソッドを定義する条件は以下となる。
  - メソッド名が同一であること。
  - 引数の数、型、順番のいずれかが異なること。
  - 同一のアクセス修飾子を持つこと。
- オーバーロードされたメソッドの引数に、変数の型宣言と実体の型が異なるクラスが指定された場合、変数の型宣言が指定されたものとして振る舞う。
  ```java
    public static void main(String[] args) {
      A a1 = new B();
      A a2 = new C();
      B b1 = new B();
      B b2 = new C();
      C c  = new C();
      m(a1); //A
      m(a2); //A
      m(b1); //B
      m(b2); //B
      m(c);  //C
    }
    public static void m(A a) { System.out.println("A"); }
    public static void m(B b) { System.out.println("B"); }
    public static void m(C c) { System.out.println("C"); }
  }

  interface A {}
  class B implements A {}
  class C extends B {}
  ```
- オーバーロードされたメソッドの優先順位は以下の通り。

例: プリミティブ型(int)
- 可変長引数(int...)と (Integer...)、(Number...)、(Object...)のいずれかを両方定義した場合、優先度が判定できずコンパイルエラーとなる。

|優先順位|型|位置付け|
|:----|:----|:---|
|1|int|一致|
|2|long, float, double|暗黙の型変換|
|3|Integer|ボクシング|
|4|Number, Object|ボクシングの親クラス|
|5|int..., Integer..., Number..., Object...|可変長引数|

<details><summary>検証コード</summary><div>

```java
public class Main {
  public static void main(String[] args) {
    int primitiveInt = 0;
    new Main.IntCheck01().m(primitiveInt);
    new Main.IntCheck02().m(primitiveInt);
    new Main.IntCheck03().m(primitiveInt);
    new Main.IntCheck04().m(primitiveInt);
    new Main.IntCheck05().m(primitiveInt);
    new Main.IntCheck06().m(primitiveInt);
    new Main.IntCheck07().m(primitiveInt);
    new Main.IntCheck08().m(primitiveInt);
    new Main.IntCheck09().m(primitiveInt);
    new Main.IntCheck10().m(primitiveInt);
    new Main.IntCheck11().m(primitiveInt);
  }

  static class IntCheck01 {
    public void m(int i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(long i)       { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer i)    { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck02 {
    public void m(long i)       { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer i)    { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck03 {
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer i)    { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck04 {
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer i)    { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck05 {
    public void m(Integer i)    { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck06 {
    public void m(Number i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck07 {
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck08 {
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    //public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    //public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    //public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck09 {
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck10 {
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntCheck11 {
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
}
```　

```
</div></details>


<details><summary>実行結果</summary><div>

```console
int arg0
long arg0
float arg0
double arg0
java.lang.Integer arg0
java.lang.Number arg0
java.lang.Object arg0
int... arg0
java.lang.Integer... arg0
java.lang.Number... arg0
java.lang.Object... arg0
```

</div></details>



例: ラッパークラス(Integer)
- 可変長引数(int...)と (Integer...)、(Number...)、(Object...)のいずれかを両方定義した場合、優先度が判定できずコンパイルエラーとなる。

|優先順位|型|位置付け|
|:----|:----|:---|
|1|Integer|一致|
|2|Number, Object|親クラス|
|3|int|アンボクシング|
|4|long, float, double|アンボクシング＋暗黙の型変換|
|5|int..., Integer..., Number..., Object...|可変長引数|

<details><summary>検証コード</summary><div>

```java
public class Main {
  public static void main(String[] args) {
    Integer wrapperInt = 0;
    new Main.IntegerCheck01().m(wrapperInt);
    new Main.IntegerCheck02().m(wrapperInt);
    new Main.IntegerCheck03().m(wrapperInt);
    new Main.IntegerCheck04().m(wrapperInt);
    new Main.IntegerCheck05().m(wrapperInt);
    new Main.IntegerCheck06().m(wrapperInt);
    new Main.IntegerCheck07().m(wrapperInt);
    new Main.IntegerCheck08().m(wrapperInt);
    new Main.IntegerCheck09().m(wrapperInt);
    new Main.IntegerCheck10().m(wrapperInt);
    new Main.IntegerCheck11().m(wrapperInt);
  }

  static class IntegerCheck01 {
    public void m(Integer i)    { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(long i)       { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck02 {
    public void m(Number i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(long i)       { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck03 {
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(long i)       { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck04 {
    public void m(int i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(long i)       { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck05 {
    public void m(long i)       { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck06 {
    public void m(float i)      { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck07 {
    public void m(double i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck08 {
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    //public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    //public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    //public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck09 {
    public void m(Integer... i) { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck10 {
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerCheck11 {
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
}

```　

```

</div></details>


<details><summary>実行結果</summary><div>

```console
java.lang.Integer arg0
java.lang.Number arg0
java.lang.Object arg0
int arg0
long arg0
float arg0
double arg0
int... arg0
java.lang.Integer... arg0
java.lang.Number... arg0
java.lang.Object... arg0
```

</div></details>


例: プリミティブ配列型(int[])
- 可変長引数(int...)と (Integer...)を両方定義した場合、優先度が判定できずコンパイルエラーとなる。

|優先順位|型|位置付け|
|:----|:----|:---|
|1|int[], int...|一致|
|2|Object|基底クラス|
|3|Object...|基底クラスの可変長引数|

<details><summary>検証コード</summary><div>

```java
public class Main {
  public static void main(String[] args) {
    int[] primitiveIntArray = {};
    new Main.IntArrayCheck01().m(primitiveIntArray);
    new Main.IntArrayCheck02().m(primitiveIntArray);
    new Main.IntArrayCheck03().m(primitiveIntArray);
    new Main.IntArrayCheck04().m(primitiveIntArray);
  }

  static class IntArrayCheck01 {
    public void m(int[] i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntArrayCheck02 {
//    public void m(int[] i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(int... i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
}
  static class IntArrayCheck03 {
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntArrayCheck04 {
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
}

```　

```

</div></details>


<details><summary>実行結果</summary><div>

```console
int[] arg0
int... arg0
java.lang.Object arg0
java.lang.Object... arg0
```

</div></details>

例: ラッパークラス配列型(Integer[])
- 同一の参照型(Integer, Number, Objectなど)の可変長引数と 配列を両方定義した場合、優先度が判定できずコンパイルエラーとなる。

|優先順位|型|位置付け|
|:----|:----|:---|
|1|Integer[], Integer...|一致|
|2|Number[], Number..., Object[], Object...|親クラスの配列|
|3|Object|基底クラス|


<details><summary>検証コード</summary><div>

```java
public class Main {
  public static void main(String[] args) {
    Integer[] wrapperIntArray = {};
    new Main.IntegerArrayCheck01().m(wrapperIntArray);
    new Main.IntegerArrayCheck02().m(wrapperIntArray);
    new Main.IntegerArrayCheck03().m(wrapperIntArray);
    new Main.IntegerArrayCheck04().m(wrapperIntArray);
    new Main.IntegerArrayCheck05().m(wrapperIntArray);
    new Main.IntegerArrayCheck06().m(wrapperIntArray);
    new Main.IntegerArrayCheck07().m(wrapperIntArray);

  }

  static class IntegerArrayCheck01 {
    public void m(Integer[] i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Integer... i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerArrayCheck02 {
//    public void m(Integer[] i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Integer... i)        { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerArrayCheck03 {
    public void m(Number[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };

  }
  static class IntegerArrayCheck04 {
//    public void m(Number[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Number... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerArrayCheck05 {
    public void m(Object[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
//    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };

  }
  static class IntegerArrayCheck06 {
//    public void m(Object[] i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object... i)  { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
  static class IntegerArrayCheck07 {
    public void m(Object i)     { System.out.println(new Object(){}.getClass().getEnclosingMethod().getParameters()[0]); };
  }
}

```　

```

</div></details>


<details><summary>実行結果</summary><div>

```console
Main.java:15: 警告:最終パラメータの不正確な引数型を持った可変引数メソッドの非可変引数呼出し。
    new Main.IntegerArrayCheck06().m(wrapperIntArray);
                                     ^
  可変引数呼出しに関してはObjectにキャストします。
  非可変引数呼出しに関してはObject[]にキャストしてこの警告を出さないようにします
警告1個
java.lang.Integer[] arg0
java.lang.Integer... arg0
java.lang.Number[] arg0
java.lang.Number... arg0
java.lang.Object[] arg0
java.lang.Object... arg0
java.lang.Object arg0
```

</div></details>


### エントリポイント
- javaアプリケーションのエントリーポイント（プログラムの実行を開始する場所)として、mainメソッドを定義できる。

**TIPS**
- エントリーポイントとして利用可能なmainメソッドは下記のどちらかの構文で定義する。
    ```java
    public static void main(String[] args)
    ```
    ```java
    public static void main(String... args)
    ```
- ソースコードにおいて、mainメソッドは複数存在しても問題ない（プログラム実行時はエントリポイントを持つクラスを指定して実行するため)

- 引数
  - 区切り文字は空白
  - ダブルクォートで括ることでスペースを含む文字列を引数として扱うことができる。
  - ダブルクォートそのものは文字として扱われない(\でエスケープすることで文字として扱える)。
    ```console
    $ java Main a b c   // args = ["a",     "b",   "c"]
    $ java Main "a b" c // args = ["a b",   "c"]
    $ java Main \"a b\" c // args = ["\"a", "b\"", "c"]
    ```

### インナークラス
- クラスの内部に定義するクラスをインナークラスと呼ぶ(内部クラス、ネストクラスとも呼ばれる)。
- メソッドの内部に定義するクラスをローカルクラスと呼ぶ。

**TIPS**
- インナークラスからは、アウタークラスの変数・メソッドにアクセスができる。
- staticインナークラスの場合、アウタークラスのstatic要素(変数、メソッド)にはアクセス可能だが、インスタンス要素(変数、メソッド)にはアクセス不可。

```java
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class Outer {
  int outerVariable = 10;
  static int outerStaticVariable = 20;

  public void outerMethod() {}
  public static void outerStaticMethod() {}

  public static void main(String[] args) {
    Outer.Inner inner = new Outer().new Inner();
    Outer.StaticInner sInner = new Outer.StaticInner();
  }

  class Inner {
    Inner() {
      System.out.println(++outerVariable); //11
      System.out.println(++outerStaticVariable); //21
      outerMethod();
      outerStaticMethod();
    }
  }
  static class StaticInner {
    int innerStaticVariable = 30; // staticクラスのフィールドには暗黙的にstaticが付与される

    StaticInner() {
      System.out.println(outerVariable); //コンパイルエラー(staticインナークラスからアウタークラスのインスタンス変数にはアクセス不可)
      System.out.println(++outerStaticVariable); // 22
      System.out.println(++innerStaticVariable); // 32
      outerMethod();　//コンパイルエラー(staticインナークラスからアウタークラスのインスタンスメソッドにはアクセス不可)
      outerStaticMethod();
    }
  }
}

```

### インタフェース
- クラスが実装しなければならない動作を記述するために使用される抽象型。
- 定数(public static final)およびメソッドのみ定義できる。

**TIPS**
- アクセス修飾子は publicのみ指定可能(省略時はpublicが指定されたものとして扱われる)
- クラスにおいて、クラスの多重継承は不可だが、インタフェースの多重実装は可能。
  ```java
  class X extends A, B (){} // コンパイルエラー
  class X implements A, B () {} // OK
  ```
- サブインタフェースは、スーパーインタフェースを多重継承できる。
  ```java
  interface C extends A, B (){} // OK
  ```
- default句を指定することで、デフォルト実装を定義可能(java8以降)
- default実装が複数存在する場合、再定義が必要となる。再定義しないとコンパイルエラーとなる。
```java
interface A { default void hoge() { System.out.println("A"); } }
interface B { default void hoge() { System.out.println("B"); } }

//インタフェース拡張
interface C extends A,B { } // コンパイルエラー(適用すべきdefaultが確定できない)
interface D extends A,B { void hoge(); } // OK
interface E extends A,B { default void hoge(); } // OK

//クラス実装
class X implements A,B { } // コンパイルエラー(適用すべきdefaultが確定できない)
class Y implements A,B { void hoge(){ System.out.println("Y"); } } // OK
```
- インタフェースを実装／継承したクラス、サブインタフェースにおいて、直接実装／継承するスーパーインタフェースのdefault実装を`インタフェース名.super.メソッド()`で呼び出しできる。
```java
  public class X implements A {
    public void output() {
        System.out.println(A.super.getNum());
    }
  }

  interface A {
     default public int getNum() { return 10; }
  }
```

### 例外
- 例外は「検査例外」「非検査例外」「エラー」に分類できる。
  - 検査例外(Exception)はtry-catchしているか、throws句で宣言する必要がある。
  - 非検査例外(RuntimeException)はtry-catchおよびthrows句での宣言は不要だが、処理自体は可能。
  - エラー(Error)は例外処理することは求められないが、try-catchすることは可能。

#### try-catch-filally構文
- try-catch-filallyの出現順序は変更不可。
  - catch/finallyのいずれかの定義は必須となる。
  - catchブロックは複数記述でき、先頭から順に評価される。
  - 到達不可能なcatch(先発例外のサブクラス)が存在する場合、コンパイルエラーとなる。
- tryブロックまたはcatchブロックでreturnが呼ばれた場合、finallyブロックが実行されたのちにメソッドが終了する。
    - tryブロックとfilannyブロックの両方でreturnで値を返却する場合、finallyブロックの値が返却される。
        ```java
          public static void main(String[] args) {
            System.out.println(test()); //30
          }
        
          public static int test() {
            try {
              return 10;
            } finally {
              return 30;
            }
          }
        ```
    - catchブロックとfilannyブロックの両方でreturnで値を返却する場合、finallyブロックの値が返却される。
        ```java
          public static void main(String[] args) {
            System.out.println(test()); //30
          }
        
          public static int test() {
            try {
              throw new RuntimeException();
            } catch (RuntimeException e) {
              return 20;
            } finally {
              return 30;
            }
          }
        ```


#### try-with-resource構文
- tryブロックで宣言したリソース変数は、tryブロックのスコープのみ利用可能(catchブロック/finallyブロックでは利用不可(コンパイルエラー))
    - tryブロック終了時にリソースが解放される。
    - catchブロックおよびfinallyブロックは省略可能(tryブロックのみ定義することが可能)。
- 以下の順で処理される。
    1. リソースの宣言（リソースが複数定義されている場合、左から順に処理される)
    1. tryブロックの処理
  1. リソースの解放（リソースが複数定義されている場合、右から順に処理される)
  2. catchブロックの処理
  3. finallyブロックの処理
```java
public class Main {
  public static void main(String[] args) {
    try (var a = new ClosableImpl("A");
         var b = new ClosableImpl("B")) {
          System.out.println("[try part]");
      throw new RuntimeException();
    } catch (RuntimeException e) {
      System.out.println("[catch part]");
    } finally {
      System.out.println("[finally part]");
    }
  }

  static class ClosableImpl implements AutoCloseable {
    private String name;
    public ClosableImpl(String name) {
      this.name = name;
      System.out.println(this.name + " opened");
    }
    @Override
    public void close() {
      System.out.println(this.name + " closed");
    }
  }
```


```console:実行結果
A opened
B opened
[try part]
B closed
A closed
[catch part]
[finally part]
```