クラスとは

  • javaのクラスは大きく2つ存在する。
  • 実行用クラスと設計図クラス。
    • 実行用クラス:main()メソッドを含んだクラス。
    • 設計図クラス:実行用クラス、またはそれ以外のクラスで読み込んで使用される。

クラスをもとにメモリ上にデータというもの(オブジェクト)を作ることをインスタンス化と呼ぶ。
また、生成されたモノのことをインスタンスと呼ぶ。
データ型の参照型の1種。
クラスを一言で表すと、オリジナルのデータ型(参照型)を作るための設計図のこと。つまり、クラス=データ型。

クラスに定義するもの

クラスには、属性(メンバ変数、またはフィールド)とメソッドを定義する。属性は、データを管理するためのもの。メソッドは処理を行うもの。
クラス内に定義するそれぞれをメンバと呼ぶ。

オブジェクト指向とは

簡単に言えば、クラスを組み合わせてインスタンスというモノ(オブジェクト)を生成していく考え方のこと。
この考えに沿ってプログラムを書くことで、デバッグや機能追加が楽になる。また、ソースコードがわかりやすくなる。

例)Animalクラスとそのサブクラス(子クラス)であるCatクラスを生成する。この場合、Catクラスという設計図をもとにCat型のオブジェクトであるsiroやmike、kokoといったインスタンスを複製していくことができる。

ローカル変数(メソッド変数)

  • 以下で説明するメンバ変数
  • メンバ変数とは異なり、メソッドやコンストラクタ内で定義する変数。
  • メソッドやコンストラクタ内でデータを管理したい場合に用いられる。
  • メソッド内、またはif文内などローカル変数を定義した場所によって、参照に制限がある。

※用はこれまで、main()メソッド内で定義してきた変数のことをローカル変数と呼ぶ。

メンバ変数(属性、またはフィールド)

  • 属性は、主にメンバ変数と呼ばれる。
  • クラス直下に定義する変数のこと。
  • インスタンスごとやクラス全体で値を管理したい場合に用いられる。
  • インスタンス化、またはクラスごと呼び出す方法によって、どこからでも参照可能。(アクセス修飾子で参照範囲を制限できる)
  • メンバ変数には2種類ある。
メンバ変数 説明
インスタンス変数 インスタンスごとに値を変更できる変数。
static変数 同じクラスをもとに生成した、全てのインスタンスに共通する値を持つ変数。クラス変数とも呼ばれる。

属性の定義と参照

①インスタンス変数

  • 定義:[修飾子] データ型 インスタンス変数名;
  • 参照:インスタンス.インスタンス変数;

インスタンス変数に格納される値は、インスタンスごとに異なるため、クラスには変数宣言のみを定義することが一般的。変数の初期化をしても問題ない。
※修飾子:メンバ変数、メソッドの公開範囲を指定するもの。(未指定でも可。その場合、デフォルトの公開範囲として扱われる。)

参照する際は、必ずnewでインスタンスを生成してからでないと参照できない。

②static変数

  • 定義:[修飾子] static データ型 インスタンス変数名;
  • 参照:インスタンス.static変数;または、クラス.static変数

static変数は、同じクラスをもとに生成した、全てのインスタンスで共通の値を扱うことができる。
そのため、変数は初期化して定義することが一般的。

参照する際は、newでインスタンスを生成してからか、インスタンスを生成せずに直接クラス名を先頭に指定する形のどちらでも参照することができる。
(インスタンスごとではなく、共有できるものなので、staticメンバ専用の別のまとまった1つの領域が生成されるため、直接クラス名を指定して呼び出すことができる。)

メソッド

  • メソッドは、メンバ変数と同様に2種類ある。
メソッド 説明
インスタンスメソッド インスタンスごとに値を変更できる変数。
staticメソッド 全てのインスタンスに共通する値を持つ変数。クラスメソッドとも呼ばれる。
No. 注意点
1 戻り値の型は必ず指定する必要がある。戻り値がない場合はvoidを指定する。指定しない場合は、コンパイルエラー。
2 戻り値は、1つしか値を返すことができない。複数はコンパイルエラー。
3 引数は、複数の値を受け取ることができる。

①インスタンスメソッド()

  • 定義:[修飾子] 戻り値の型 メソッド名(引数){};
  • 参照:インスタンス.インスタンスメソッド();

②staticメソッド

  • 定義:[修飾子] static 戻り値の型 メソッド名(引数){};
  • 参照:インスタンス.staticメソッド();または、クラス.staticメソッド()

参照する際は、newでインスタンスを生成してからか、インスタンスを生成せずに直接クラス名を先頭に指定する形のどちらでも参照することができる。
(インスタンスごとではなく、共有できるものなので、staticメンバ専用の別のまとまった1つの領域が生成されるため、直接クラス名を指定して呼び出すことができる。)

メソッドの注意点 (thisで解決)

インスタンスメンバ・staticメンバの違い

メンバ インスタンスメンバ staticメンバ
変数 インスタンス変数 static変数
メソッド インスタンスメソッド staticメソッド
違い 説明
呼び出し方 インスタンスメンバは、必ずインスタンスを生成してからでないと使用できない。staticメンバは、直接クラスを指定する方法でも使用することができる。
生成されるメモリ領域 インスタンスメンバは、同じクラスをもとに生成した場合であってもインスタンス単位のオブジェクトになる。つまり、インスタンス単位でメモリ領域を生成する。それに対し、staticメンバは、クラス単位のオブジェクトになる。メモリ領域もクラス単位でインスタンスとは別のstatic用の場所に生成される。また、同じクラスをもとに生成したインスタンスはstaticメンバを共有できる。(メモリ領域が共有される)
同じクラス内でのアクセス 同クラスで定義したインスタンスメンバは、同クラスで定義したstaticメンバにアクセスできる。その逆はできない。コンパイルエラー。理由は、「生成されるメモリ領域が違う」ため。
nullに対する呼び出し nullは、何もない。そのため、インスタンスではないのでインスタンスメンバは呼び出せない。staticメンバはインスタンス化しなくてもクラス(型)が同じ場合、呼び出せる。

同じクラス内で定義したものでも実行時に、インスタンスメンバとstaticメンバで生成される領域が異なるため。
また、statiメンバは、クラスメンバと呼ばれる所以でもある通り、同じクラスをもとに生成したインスタンスであればstaticメンバを共有することができる。

以下では、インスタンスメンバ、staticメンバの定義と呼び出し方の違いの例を示す。

Training.java
class Test{
    String instanceArg = "instance";
    static String staticArg = "static";

    void instanceMethod(){System.out.println(instanceArg);}
    static void staticMethod(){System.out.println(staticArg);}
}

class Training {
    public static void main(String[] args){
        Test t = new Test();

        System.out.println(t.instanceArg);
        t.instanceMethod();
        
        System.out.println(t.staticArg);
        t.staticMethod();
        
        System.out.println(Test.staticArg);
        Test.staticMethod();
    }
}

//出力結果:
//instance
//instance
//static
//static
//static
//static

以下では、同じクラス内でもインスタンスメンバ、staticメンバのアクセスについて示す。
コメントアウトしているmethod3は、staticメソッドから同クラスのインスタンス変数にアクセスしているので、コンパイルエラー。
その逆は成功している。
それぞれで生成されるメモリ領域は異なる。また、インスタンスメンバは、同クラスのstaticメンバを共有できることを忘れずに。

Test.java
class Test {

    int instanceVal = 1;
    static int staticVal = 2;

    void method1(){System.out.println(instanceVal);}
    void method2(){System.out.println(staticVal);}

    //static void method3(){System.out.println(instanceVal);}
    static void method4(){System.out.println(staticVal);}
    static void method5(){
        Training t = new Training();
        System.out.println(t.instanceVal);
    }
}

以下では、nullに対する呼び出しの違いを示す。
だいたい、nullを用いた実行は、コンパイルは通り、実行エラーになる。

Test.java
class Test{
    String instanceVal = "instanceVal";
    static String staticVal = "staticVal";
}

class Training {
    public static void main(String[] args){
        Test t = null; //インスタンスではない

        //System.out.println(t.instanceVal);  実行エラー
        System.out.println(t.staticVal);
    }
}
//出力結果:
//staticVal

ローカル変数・メンバ変数の違い

  • 主な違いは、以下の3つ。
違い ローカル変数 メンバ変数
修飾子 使えない 使える
スコープ メソッド内、メソッド内制御文内のみアクセス可 インスタンス変数の場合、インスタンスを生成することで、クラス内のどこからでもアクセスできる。instance変数の場合は、インスタンスを生成してもしなくてもどこからでもアクセスできる。
変数の初期値 配列でない場合は変数を初期化しなければコンパイルエラー。 変数宣言の時点でもでフォルトで初期値が代入される

以下では、ローカル変数とメンバ変数の上記の違いの例を示す。

  • インスタンス変数:クラスをインスタンス化することで使用できるようになる。ここでは、変数の初期化を行わずとも変数に初期値が代入されていることがわかる。
  • static変数:クラスのインスタンス化してもしなくても使用することができる。以下では、インスタンス化せずにstatic変数を使用している。
  • ローカル変数:アクセス修飾子をつけた場合、コンパイルエラーになる。また、if文ブロック内で定義したローカル変数は、そのif文内でしか参照できない。それ以外の変数は、if文内でも参照できる。
Training.java
class Training {
    int i;
    static int i2 = 10;
    public static void main(String[] args){
        // String i = new String();
        // System.out.println(i);

        int i = 1;
        //public int i = 1; コンパイルエラー
        System.out.println(i);

        Test t_instance = new Test();
        System.out.println(t_instance.i);
        
        System.out.println(Test.i2);
        
        if(i == 1){
            String s = "変数sは、if文ブロック内のみ参照可";
            System.out.println(s);
            System.out.println(i);
            System.out.println(t_instance.i);
            System.out.println(Test.i2);
        }
        //System.out.println(s); コンパイルエラー
    }
}

//出力結果:
//1
//0
//10
//変数sは、if文ブロック内のみ参照可
//1
//0
//10

メンバ変数では、変数宣言時に以下のデータが型別に初期値が生成される。
しかし、ローカル変数の場合は、1つ要素を持った配列インスタンス(newで生成した配列)に限って配列の型に応じた以下の初期値が生成される。
(それ以外はコンパイルエラー)

データ型 初期値
byte, short, int, long 0
float, double 0.0
char ¥u0000
boolean false
参照型 null
class Training {
    int i; //0を格納

    public static void main(String[] args){
        int[] array = new int[1]; //1つ要素を持った配列インスタンスの生成
	System.out.println(array[0]); //0を出力
	System.out.println(array); //配列自体の参照先を出力

	int[] array2 = {}; //空の配列を生成
	//System.out.println(array2[0]); 実行エラー
	System.out.println(array2); //配列自体の参照先を出力

	//String s; コンパイルエラー
	//int i; コンパイルエラー
    }
}
  • 配列インスタンスとは:
    newをつけることでクラスをもとにインスタンスを生成することになる。
    配列やStringは、newが不要、つまりインスタンスを生成しなくても特定の記述(int[] i = {}, String s = "")をすることでそれぞれを生成できる。
    この場合、特定の記述をすることでコンパイラがそれぞれを認識するできるようになっている。

インスタンス化

  • インスタンス化:クラス(設計書)をもとにインスタンスというモノ(オブジェクト)を生成すること。
  • インスタンスを生成するには、newを用いる。
  • データ型 変数名 = new クラス名();

インスタンスを扱うには、newでインスタンスを生成し、それを変数に格納する。
なお、クラスは参照型のデータを作るための設計書なので、インスタンスは、参照型になる。(そのため、変数が所持しているのはインスタンス自体ではなく、そのメモリアドレスになる。)

クラスの定義

  • class クラス名{}
  • クラス名の頭文字は大文字がルール
No. 注意点
1 1ファイルに複数のクラスを定義できる。(アクセス修飾子publicを指定できるのは、1ファイル1クラスのみ。また、publicを指定したクラスは、ファイル名と同名にしなければいけない。)
2 1ファイルに複数のクラスを定義した場合は、コンパイル時、定義したクラス分のクラスファイルが生成される。

クラスの使い方

  • 以下では、1人のBMIを算出するBmiクラスを定義する。
  • メンバ変数には、人の名前。メソッドには、BMIを計算する処理を定義している。
  • メソッドでは、身長と体重を引数で受け取り、BMIの計算結果を返す処理を定義している。
  • クラス=データ型(参照型) であるためインスタンスを格納する変数のデータ型は、クラスと同名の型を指定していることがわかる。
Training.java
//設計図クラス
class Bmi{
    //インスタンス変数(メンバ変数)
    String name;

    //通常メソッド
    public double calcBmi(double height, double weight){
        height /= 100;
        double result = weight / Math.pow(height, 2);
        return result;
    }
}

//実行用クラス
class Training {
    //main()メソッド
    public static void main(String[] args){
        Bmi human1 = new Bmi(); //インスタンス化
	human1.name = "tanaka";
        System.out.println(human1.name); //インスタンス変数の呼び出し
        System.out.println(human1.calcBmi(170, 70)); //通常メソッドの呼び出し
    }
}
//出力結果:
//tanaka
//24.221453287197235

以下では、human2インスタンスを追加している。
メンバ変数nameにsatoを代入し、別の引数を入力してcalcBmi()メソッドを実行している。
また、メンバ変数はインスタンス変数なので、生成するインスタンス別に値を格納することができる。
このように、1つクラスを作っておけば、簡単に複製することができる。

Training.java
//一部省略

class Training {
    public static void main(String[] args){
        Bmi human1 = new Bmi();
	human1.name = "tanaka";
        System.out.println(human1.name);
        System.out.println(human1.calcBmi(170, 70));

        Bmi human2 = new Bmi(); //インスタンスの追加
        human2.name = "sato";
        System.out.println(human2.name);
        System.out.println(human2.calcBmi(180, 50));
    }
}

//出力結果:
//tanaka
//24.221453287197235
//sato
//15.432098765432098

コンストラクタ

コンストラクタは、メソッドの1種で、インスタンスを生成した時に最初に実行される。
そのため、初期化メソッドとも呼ばれる。

No. 注意点
1 コンストラクタ名をクラスと同名にする
2 戻り値はない(戻り値の型宣言も不要)
3 インスタンス化時に、引数を通じて値を受け取ることができる
4 クラスにコンストラクタを定義しなかった場合、コンパイラによってコンパイル時に空のコンストラクタが定義される。(これをデフォルトコンストラクタと呼ぶ)

※デフォルトコンストラクタは、何もコンストラクタを定義しない場合に限り、コンパイルのタイミングで追加される。

コンストラクタの定義・呼び出し

  • 定義:クラス名(){}
  • 呼び出し:new クラス名();

これまで、newを使ってインスタンスを生成していたがこれは、同時にコンストラクタを実行していたことになる。
コンストラクタを定義することで、インスタンス生成時に初期値を設定することができるというメリットがある。

以下では、コンストラクタの定義と実行の例を示す。
また、以下では、コンストラクタを定義することでメンバ変数(インスタンス変数)へ値を代入する手間がなくなっている。

Training.java
class Bmi{
    String name;

    //コンストラクタの定義
    Bmi(String s){
        name = s;
    }

    public double calcBmi(double height, double weight){
        height /= 100;
        double result = weight / Math.pow(height, 2);
        return result;
    }
}

class Training {
    public static void main(String[] args){
        Bmi human1 = new Bmi("tanaka"); //コンストラクタ実行
        System.out.println(human1.name);
        System.out.println(human1.calcBmi(170, 70));
    
        Bmi human2 = new Bmi("sato"); //コンストラクタ実行
        System.out.println(human2.name);
        System.out.println(human2.calcBmi(180, 50));
    }
}

//出力結果:
//tanaka
//24.221453287197235
//sato
//15.432098765432098

オーバーロード

  • オーバーロード:1つのクラス内に、同名のメソッドやコンストラクタを複数定義すること
  • コンパイラは、コンパイル時に「引数の数」、「引数の並び」、「引数のデータ型」の3つによってメソッドを区別する。同名のものがあっても3つのうちどれかが異なればオーバーロードとみなす。
  • つまり、引数名以外の「引数の数」、「引数の並び」、「引数のデータ型」の3つだけによって別のメソッドとして扱うことができる。
No. 注意点
1 引数の数、並び、データ型の3つで同名のメソッドを区別する。
2 上記3つが同じで戻り値が異なる場合、メソッドが区別されず、オーバーロードとみなされないのでコンパイルエラーになる。
3 上記3つが同じで引数名が異なる場合、既存のメソッドが定義されているということでこんぱいるえらーになる。
4 コンストラクタもオーバーロードすることができる。オーバーロードのルールは上記と同じ。
5 スーパークラスから継承したメソッドでもオーバーロードできる。

【オーバーロードされたメソッドを呼び出す際の優先順位】
完全一致>暗黙型変換>Boxing>可変長引数

以下ではオーバーロードの例を示す。
また、コメントアウトしているコンパイルエラーの例では、戻り値だけが異なる場合、変数名だけが異なる場合をしてしている。

Training.java
class Test{

    void test(){System.out.println("test1");}
    void test(String s){System.out.println("test2");}
    void test(int i){System.out.println("test3");}
    void test(String s, int i){System.out.println("test4");}
    //String test(String s, int i){System.out.println("test5");} コンパイルエラー
    //void test(String s2, int i2){System.out.println("test6");} コンパイルエラー
}

class Training {
    public static void main(String[] args){
        Test t = new Test();

        t.test();
        t.test("test");
        t.test(111);
        t.test("test", 111);
    }
}

//出力結果:
//test1
//test2
//test3
//test4

可変長引数

  • 可変長引数:メソッドの引数を配列として扱い、可変に引数を指定できるようにした引数のこと
  • 定義:public static void main(String... args){}
No. 注意点
1 データ型の後に...をつける。
2 他の引数を定義する場合、可変長引数は最後に記述すること。
3 オーバーロード時、可変長引数を定義したメソッドが後回しにされる。引数の方を優先。
4 main()メソッドで用いても問題ない。可変長引数の実体は配列なので。(String[] = String...
5 可変超引数にnullを渡した場合は、実行エラーになる。(null自体を渡したいのか、nullを格納した配列を渡したいのかが判断できないため)
6 オーバーロードの時、配列を受け取るメソッドがある場合は注意。

以下では、可変長引数を用いた例を示す。
可変長引数は、1つの配列として扱われていることがわかる。
また、可変長引数に値を渡さなかった場合はでもブロック内で、空の配列を出力できることもわかる。

※配列なので、array.length ()は不要

Training.java
class Test{

    void method(String s, int... a){
        System.out.println(s + "size:" + a.length);
        for(int i : a){
            System.out.println("第二引数の値" + i);
        }
    }
}

class Training {
    public static void main(String[] args){
        Test t = new Test();

        t.method("1回目");
        t.method("2回目", 1);
        t.method("3回目", 1, 2);
        t.method("4回目", 1, 2, 3);
    }
}

//出力結果:
//1回目size:0 ←空の配列を受け取っていることがわかる。
//2回目size:1
//第二引数の値1
//3回目size:2
//第二引数の値1
//第二引数の値2
//4回目size:3
//第二引数の値1
//第二引数の値2
//第二引数の値3

以下では、オーバーロード時の優先順を示す。
可変長引数よりも引数のメソッドが優先されていることわかる。

Training.java
class Test{

    void method(int... a){
        System.out.println("可変長引数が優先");
    }

    void method(int i){
        System.out.println("引数が優先");
    }
}

class Training {
    public static void main(String[] args){
        Test t = new Test();

        t.method(1);
    }
}

//出力結果:
//引数が優先

ただし、以下のように指定した場合、コンパイルエラーになる。
可変長引数は配列を受け取るため、引数のデータ型、引数の数、引数の並びが同じものとして判断されるため。

Training.java
void method(int... a){
    System.out.println("可変長引数が優先");
}
void method(int i[]){
    System.out.println("引数が優先");
}

staticイニシャライザ

  • staticイニシャライザ:クラスファイルが読み込まれたタイミングで実行されるもの。
  • 定義:static {}
No. 注意点
1 static変数と同様に、クラス直下に定義する。
2 main()メソッドの前やコンストラクタの前に処理が実行される。

以下では、staticイニシャライザを用いた例を示す。
main()メソッド、コンストラクタより最初に実行されていることがわかる。

Training.java
class Test{
    // ③
    static {
        System.out.println("staticイニシャライザ(Test)");
    }
    // ④
    Test() {
            System.out.println("コンストラクタ");
    }
}

class Training {
    //①
    static {
        System.out.println("staticイニシャライザ");
    }
    //②
    public static void main(String[] args){
        System.out.println("mainメソッド");

        Test t = new Test();
    }
}
//出力結果:
//staticイニシャライザ
//mainメソッド
//staticイニシャライザ(Test)
//コンストラクタ