Sojiro’s Blog

This is nothing much.

パーフェクトJava読書メモ Chapter 5 クラス

Javaを使うために改訂2版 パーフェクトJavaを読んだメモ

オブジェクトの生成

Javaのオブジェクト生成方法は以下の5つ

procedure description
new 基本的な生成手段
String リテラル及び結合演算式 文字列固有の手段
オートボクシング 数値クラス固有の手段
リフレクション フレームワークなどが下位に隠蔽すべき手段
clone メソッド Object クラスに実装された手段

ファクトリパターン

new 式をファクトリメソッドに隠蔽することでオブジェクトの生成をコンストラクタから分離する

1
2
3
4
5
6
7
8
9
10
class Sample {
    // コンストラクタへのアクセスを制限
    private Sample() {}

    // ファクトリメソッド
    static Sample getInstance() {
        Sample sample = new Sample();
        return sample;
    }
}
  • オブジェクトプーリング
    • オブジェクトの生成にコストがかかる場合、キャッシュされた生成済みオブジェクトを使う
  • シングルトンパターン
    • オブジェクトの数を1つに制限する

クラス宣言

新しいクラスを定義するには次のように予約語 class を使う

1
2
3
4
5
6
7
8
[修飾子] class クラス名 {
    メンバ宣言
      - フィールド宣言
      - メソッド宣言
      - ネストしたクラス宣言及びネストしたインターフェース宣言
    コンストラクタ宣言
    初期化ブロック
}

クラスの修飾子

  • クラス宣言時に指定できる修飾子は以下の5つ
  • 複数同時指定可能(ただし finalabstract を同時に指定するとコンパイルエラー)
modifier description
public グローバルにアクセス可(書かないとパッケージ内に限定)
final 継承禁止
abstract 抽象クラス
strictfp クラス内に記述した浮動小数点演算を厳密に評価
アノテーション 省略

フィールド

フィールド宣言の修飾子

modifier description
public アクセス制御
protected アクセス制御
private アクセス制御
final フィールド変数への再代入禁止
static クラスフィールド
transient シリアライズの対象外
volatile スレッド間で変数の値を同期
  • publicprotectedprivate は同時に指定不可
  • finalvolatile は同時に指定不可

フィールド変数のスコープ

  • 宣言の位置にかかわらず同一クラス内のすべてのコンストラクタとメソッドから使える
  • コンストラクタ内、メソッド内以外の場所では宣言した行以降がスコープとなる
1
2
3
4
5
6
7
8
9
10
11
12
class Sample {
    // コンパイルエラー
    private final String s2 = s;

    // コンパイルエラー
    {
        System.out.println(s);
    }

    // フィールド宣言
    private final String s = "sample";
}

this参照

  • this 参照は明示的な宣言なしに使える、該当クラスのオブジェクトを参照する参照型変数
  • クラス内での変数名はフィールド変数よりローカル変数、パラメータ変数が優先される
  • これらの場合にフィールド変数を使う場合は this 参照を明示する
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Sample {
    private final String s = "sample";

    void method (String s) {
        s; // パラメータ変数
        this.s; // フィールド変数
    }

    void method2 () {
        String s = "test 2";
        s; // ローカル変数
        this.s; // フィールド変数
    }
}

メソッド

メソッド宣言の修飾子

modifier description
public アクセス制御
protected アクセス制御
private アクセス制御
abstract 抽象メソッド
final オーバーライド不可
static クラスメソッド
synchronized 同期のためのロック獲得
native ネイティヴメソッド
strictfp 浮動小数点演算を厳密に評価
  • publicprotectedprivate は同時に指定不可
  • abstractstaticfinalsynchronizednativestrictfp と同時に指定不可

同一クラス内でのメソッドのスコープ

  • クラス内でメソッド宣言より前で有効
  • クラス内で this 参照を使った呼び出しも可能

他のクラスからのメソッド呼び出し

  • アクセス制御が許せば可能
  • オブジェクト参照に . メソッドアクセス修飾子を適用する

引数

  • メソッド定義のパラメータ変数を 仮引数
  • メソッド呼び出し時に渡すパラメータを 実引数
  • メソッドが呼ばれると仮引数に実引数が代入される(call by value)

可変長引数

  • メソッド宣言の引数定義でパラメータ変数の型に ... を書く
  • 任意の数の実引数で呼び出すことができる
  • このような引数を可変長引数という
1
2
3
4
5
6
7
8
9
10
11
12
class Sample {
    void exec (String... messages) {
        for (String s : messages) {
            System.out.println(s);
        }
    }
}

Sample sample = new Sample();
sample.exec();
sample.exec("hoge");
sample.exec("hoge", "fuga", "foo");
  • 内部的には配列として引数が渡る
1
2
3
4
5
6
7
8
// 上記クラス定義と等価
class Sample {
    void exec (String[] messages) {
        for (String s : messages) {
            System.out.println(s);
        }
    }
}

返り値

  • return 文を使う
  • return はどこにいくつ書いても良い
  • return の後に実行されるような文を書くとコンパイルエラー
  • void メソッドに return を書くとコンパイルエラー
  • return が返す値が返り値の型に代入できないとコンパイルエラー

メソッド実行の終わり方

  • return 文で抜ける
  • void 型のメソッドを最後まで実行して抜ける
  • 例外を投げて抜ける

メソッドのオーバーロード

  • 同じ引数の並びで同名のメソッドの宣言はコンパイルエラー
  • 引数の型が変わると同名でもコンパイル可能
  • これをメソッドのオーバーロードと呼ぶ

メソッドのシグネチャ

  • クラスの中でメソッドを一意に特定する情報をシグネチャと呼ぶ
  • メソッドのシグネチャは以下の2つ
    • メソッド名
    • 引数の型の並び

再帰呼び出し

  • メソッドが自分自身のメソッドを呼ぶこと
  • 停止条件が必要

コンストラクタ

コンストラクタの宣言

  • オブジェクト生成時に呼ばれる
  • コンストラクタ名はクラス名と一致する
  • 修飾子に書けるのは以下の3つ
    • public
    • protected
    • private
  • コンストラクタはクラス名と同名のメソッドに見えるが言語仕様上は別物
  • コンストラクタ内に return 文を書くとコンパイルエラー
  • オブジェクトの初期化処理はコンストラクタにまとめるべき

this呼び出しとsuper呼び出し

  • this 呼び出しでコンストラクタの共通化ができる
1
2
3
4
5
6
7
8
9
10
11
12
class Sample {
    // フィールド宣言を省略

    Sample (String name, String label) {
        this(name, label, 10); // 下のコンストラクタ呼び出し
    }
    Sample (String name, String label, int level) {
        this.name = name;
        this.label = label;
        this.level = level;
    }
}
  • 継承したクラスから継承元のコンストラクタを super で呼び出す
1
2
3
4
5
6
7
8
class SubSample extends Sample {
    private final String type;

    SubSample (String name, String label, int level, String type) {
        super(name, label, level); // Sample クラスのコンストラクタ呼び出し
        this.type = type;
    }
}

デフォルトコンストラクタ

  • コンストラクタ宣言が書かれないクラスにはデフォルトコンストラクタが自動生成される
  • デフォルトコンストラクタの引数はなし、中身が空

オブジェクト初期化の順序

  1. フィールド変数にデフォルト値代入
  2. フィールド変数宣言時の初期化、初期化ブロックを上から順に実行
  3. コンストラクタ呼び出し

staticメンバ

  • static 修飾子がついたフィールド変数やメソッド
  • クラスメンバという
  • static がつかないフィールド変数、メソッドはインスタンスメンバ
  • クラスメンバは実体がクラスにしかなく、オブジェクトはコピーを持たない
  • インスタンスフィールドとクラスフィールドは同じ名前空間(同名はつけられない)

継承

  • 継承には実装の継承と振る舞いの継承の2つがある
  • 振る舞いの継承は後出の「インターフェース」
  • 実装の継承としての拡張継承は、あらかじめ意図して設計されたクラスからのみ行うべき

拡張継承の構文

  • クラス宣言時に extends を使って継承する
  • extends のないクラス宣言は暗黙に java.lang.Object を継承する
  • Java のすべてのクラスは必ず java.lang.Object を直接的または間接的に継承する
1
2
3
[修飾子] class クラス名 extends 基底クラス名 {
    クラス本体
}
  • 継承したクラスで継承元と同名のフィールド変数を宣言すると継承元のフィールド変数を隠蔽する
  • 継承元にあるメソッドと同じシグネチャのメソッドを定義するとメソッドをオーバーライドする
  • メソッドをオーバーライドする条件
    • 同じメソッド名
    • 引数の数と型がすべて一致
    • 返り値の型が一致、もしくは継承型
    • throws 説の例外型が一致もしくは継承した例外型
    • アクセス制御が一致もしくはより緩い

@Overrideアノテーション

  • @Override というアノテーションをつけるとオーバーライドのミスに気づける
1
2
3
4
5
6
7
8
9
10
11
12
Class Sample {
    void exec (CharSequence s) {
        System.out.println("sample:exec");
    }
}

Class SubSample extends Sample {
    @Override
    void exec (String s) {
        System.out.println("subSample:exec");
    }
}

super参照

  • オーバーライドされた元メソッドに private 修飾子がついていなければオーバーライドしたメソッド内から super 参照を通じて元のメソッドを呼び出すことができる
  • 隠蔽されたフィールド変数も同様

finalクラス

  • final 修飾子がついたクラスを final クラスと呼ぶ
  • final クラスを継承元にして extends で拡張しようとするとコンパイルエラー

抽象クラスと抽象メソッド

  • 抽象クラスはインスタンス化できないクラス
  • abstract 修飾子をつけてクラス宣言すると抽象クラスになる
  • 抽象クラスは何らかの具象クラスの基底クラスとなる(雛形の役割を担う)
  • メソッド修飾子として abstract をつけると抽象メソッドとなる
  • 抽象メソッドはメソッド本体をもたない(オーバーライド前提)
  • 抽象メソッドを一つでももつとそのクラスは抽象クラス

ネストしたクラス

  • あるクラスの下請けを担うクラスをヘルパークラスという
  • クラス内にクラスを宣言できる
  • ネストしたクラスをメンバクラスと呼ぶ
  • 外側のクラスをエンクロージングクラスと呼ぶ

staticなネストしたクラス

  • static 修飾子がついたネストしたクラス
  • private 修飾子が指定されるとエンクロージングクラスの外から見えなくなる
  • エンクロージングクラスの private フィールドや private メソッドにアクセスできる
  • エンクロージングクラスもネストしたクラスの private フィールドや private メソッドにアクセスできる

内部クラス

  • static なネストしたクラスを内部クラスと呼ぶ
  • 内部クラスのオブジェクトはエンクロージングオブジェクトへの参照を暗黙的にもつ
  • エンクロージングクラスのクラスメソッド内では内部クラスのオブジェクト生成ができない

ローカル内部クラス

  • ローカル内部クラスはメソッド内、コンストラクタ内、初期化ブロック内、 if 節などのブロック内で定義するクラス
  • ローカル内部クラスはブロックの外からはアクセスできない
  • クラスの実装をブロック内に隠蔽するときに使う

匿名クラス

  • 匿名クラスにはクラス名がない
  • オブジェクト生成は以下の構文で行う
1
2
3
new 基底型(実引数) {
    メソッド宣言とフィールド宣言の差分実装
}
  • 匿名クラスにはクラス名がないため、基底型名を new 演算子に渡し、基底型との差分実装を書き足す
  • 匿名クラスを使う利点
    • コンストラクタが不要
    • オブジェクト作成が1つだけ

参照

改訂2版 パーフェクトJava

Comments