パーフェクトJava読書メモ chapter 5 クラス
Javaを使うために改訂2版 パーフェクトJavaを読んだメモ
オブジェクトの生成
Javaのオブジェクト生成方法は以下の5つ
procedure | description |
---|---|
new 式 |
基本的な生成手段 |
String リテラル及び結合演算式 |
文字列固有の手段 |
オートボクシング | 数値クラス固有の手段 |
リフレクション | フレームワークなどが下位に隠蔽すべき手段 |
clone メソッド |
Object クラスに実装された手段 |
ファクトリパターン
new
式をファクトリメソッドに隠蔽することでオブジェクトの生成をコンストラクタから分離する
class Sample {
// コンストラクタへのアクセスを制限
private Sample() {}
// ファクトリメソッド
static Sample getInstance() {
Sample sample = new Sample();
return sample;
}
}
- オブジェクトプーリング
- オブジェクトの生成にコストがかかる場合、キャッシュされた生成済みオブジェクトを使う
- シングルトンパターン
- オブジェクトの数を1つに制限する
クラス宣言
新しいクラスを定義するには次のように予約語 class
を使う
[修飾子] class クラス名 {
メンバ宣言
- フィールド宣言
- メソッド宣言
- ネストしたクラス宣言及びネストしたインターフェース宣言
コンストラクタ宣言
初期化ブロック
}
クラスの修飾子
- クラス宣言時に指定できる修飾子は以下の5つ
- 複数同時指定可能(ただし
final
とabstract
を同時に指定するとコンパイルエラー)
modifier | description |
---|---|
public |
グローバルにアクセス可(書かないとパッケージ内に限定) |
final |
継承禁止 |
abstract |
抽象クラス |
strictfp |
クラス内に記述した浮動小数点演算を厳密に評価 |
アノテーション | 省略 |
フィールド
フィールド宣言の修飾子
modifier | description |
---|---|
public |
アクセス制御 |
protected |
アクセス制御 |
private |
アクセス制御 |
final |
フィールド変数への再代入禁止 |
static |
クラスフィールド |
transient |
シリアライズの対象外 |
volatile |
スレッド間で変数の値を同期 |
public
、protected
、private
は同時に指定不可final
、volatile
は同時に指定不可
フィールド変数のスコープ
- 宣言の位置にかかわらず同一クラス内のすべてのコンストラクタとメソッドから使える
- コンストラクタ内、メソッド内以外の場所では宣言した行以降がスコープとなる
class Sample {
// コンパイルエラー
private final String s2 = s;
// コンパイルエラー
{
System.out.println(s);
}
// フィールド宣言
private final String s = "sample";
}
this参照
this
参照は明示的な宣言なしに使える、該当クラスのオブジェクトを参照する参照型変数- クラス内での変数名はフィールド変数よりローカル変数、パラメータ変数が優先される
- これらの場合にフィールド変数を使う場合は
this
参照を明示する
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 |
浮動小数点演算を厳密に評価 |
public
、protected
、private
は同時に指定不可abstract
はstatic
、final
、synchronized
、native
、strictfp
と同時に指定不可
同一クラス内でのメソッドのスコープ
- クラス内でメソッド宣言より前で有効
- クラス内で
this
参照を使った呼び出しも可能
他のクラスからのメソッド呼び出し
- アクセス制御が許せば可能
- オブジェクト参照に
.
メソッドアクセス修飾子を適用する
引数
- メソッド定義のパラメータ変数を 仮引数
- メソッド呼び出し時に渡すパラメータを 実引数
- メソッドが呼ばれると仮引数に実引数が代入される(call by value)
可変長引数
- メソッド宣言の引数定義でパラメータ変数の型に
...
を書く - 任意の数の実引数で呼び出すことができる
- このような引数を可変長引数という
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");
- 内部的には配列として引数が渡る
// 上記クラス定義と等価
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
呼び出しでコンストラクタの共通化ができる
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
で呼び出す
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;
}
}
デフォルトコンストラクタ
- コンストラクタ宣言が書かれないクラスにはデフォルトコンストラクタが自動生成される
- デフォルトコンストラクタの引数はなし、中身が空
オブジェクト初期化の順序
- フィールド変数にデフォルト値代入
- フィールド変数宣言時の初期化、初期化ブロックを上から順に実行
- コンストラクタ呼び出し
staticメンバ
static
修飾子がついたフィールド変数やメソッド- クラスメンバという
static
がつかないフィールド変数、メソッドはインスタンスメンバ- クラスメンバは実体がクラスにしかなく、オブジェクトはコピーを持たない
- インスタンスフィールドとクラスフィールドは同じ名前空間(同名はつけられない)
継承
- 継承には実装の継承と振る舞いの継承の2つがある
- 振る舞いの継承は後出の「インターフェース」
- 実装の継承としての拡張継承は、あらかじめ意図して設計されたクラスからのみ行うべき
拡張継承の構文
- クラス宣言時に
extends
を使って継承する extends
のないクラス宣言は暗黙にjava.lang.Object
を継承する- Java のすべてのクラスは必ず
java.lang.Object
を直接的または間接的に継承する
[修飾子] class クラス名 extends 基底クラス名 {
クラス本体
}
- 継承したクラスで継承元と同名のフィールド変数を宣言すると継承元のフィールド変数を隠蔽する
- 継承元にあるメソッドと同じシグネチャのメソッドを定義するとメソッドをオーバーライドする
- メソッドをオーバーライドする条件
- 同じメソッド名
- 引数の数と型がすべて一致
- 返り値の型が一致、もしくは継承型
throws
説の例外型が一致もしくは継承した例外型- アクセス制御が一致もしくはより緩い
@Overrideアノテーション
@Override
というアノテーションをつけるとオーバーライドのミスに気づける
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
節などのブロック内で定義するクラス - ローカル内部クラスはブロックの外からはアクセスできない
- クラスの実装をブロック内に隠蔽するときに使う
匿名クラス
- 匿名クラスにはクラス名がない
- オブジェクト生成は以下の構文で行う
new 基底型(実引数) {
メソッド宣言とフィールド宣言の差分実装
}
- 匿名クラスにはクラス名がないため、基底型名を
new
演算子に渡し、基底型との差分実装を書き足す - 匿名クラスを使う利点
- コンストラクタが不要
- オブジェクト作成が1つだけ