40過ぎのおっさんが勉強してみるブログ

会社の帰りの電車で勉強したことをメモしていきます

Effective Javaを読んでみる (2章)

今更ですが、Effective Java第2版を読んでいってみたいと思います。ただ読むだけなのもアレなので、書いてある項目ごとに簡単なまとめをつけてみます。

2章 オブジェクトの生成と消滅

(1章は「はじめに」なので2章から始まります)

項目1 コンストラクタの代わりにstaticファクトリーメソッドを検討する

  • ファクトリーメソッドには名前を付けられる
    • コンストラクタでは引数の型が同じものを複数作れない。
    • 引数の型が似たコンストラクタを複数作った場合に区別を付けづらい。
  • コンストラクタでは必ずオブジェクトが生成されるが、ファクトリーメソッドではオブジェクトを生成するかどうかを選択できる
  • ファクトリーメソッドでは、そのクラスの型だけでなく、サブタイプの型のインスタンスを返すことも可能
  • 短所:他のstaticメソッドと区別をつけづらい
    • ファクトリーメソッド特有の命名をすることで区別をつける。

項目2 数多くのコンストラクタパラメータに直面した時にはビルダーを検討する

  • パラメータが多い場合については、コンストラクタでもファクトリーメソッドでも対応できない。
  • テレスコーピングコンストラクタ・パターン
    • new Class(int, int, int, int, int) など
    • 書くのも読むのも困難
  • JavaBeansパターン
    • デフォルトコンストラクタ+setterたくさん
      • 読みやすくはある
    • 生成途中で、インスタンスの状態を不変に保てない
  • Class.newInstance()は論外
  • ビルダーパターンを使う NutritionFacts cocacola = new NutritionFacts(240, 8).calories(100).sodium(35).carbohydrate(27).build()
    • ポイント -コンストラクタは必須パラメータだけ
      • オプションのパラメータを続けて設定
      • 最後にbuild()を実行
    • インスタンスの状態を不変に保てる&読みやすい

項目3 privateのコンストラクタかenum型でシングルトン特性を強制する

  • Java5以降では、シングルトンを使う場合は要素が1つだけのenumを作る。
    • プライベートコンストラクタだと、シリアライズへの対応とか色々面倒

項目4 privateのコンストラクタでインスタンス化不可能を強制する

  • ユーティリティクラスがインスタンス化されることを防ぐため、コンストラクタをprivateで作っておく。
    • これが無いと、デフォルトコンストラクタでインスタンス化されてしまう。

項目5 不必要なオブジェクトの生成を避ける

  • 呼び出し回数が多い処理の中では、時間がかかるオブジェクトの生成(Calendar.getInstance()など)を避ける。
    • 1回だけ別のところで呼び出しておく
  • オートボクシングでもオブジェクトの生成が起きる。
    • longとLongを書き間違えるなどで無意識にやってしまうことがある
  • 生成コストの高いものに対してオブジェクトプールを自作するのは基本的にNG。DB接続のようにものすごく生成コストが高い場合のみ行う。
  • 防御的コピー(項目39)でオブジェクトを生成するのは必須。

項目6 廃れたオブジェクト参照を取り除く

  • メモリリーク(オブジェクトへの参照が残ったままでGCされない)に注意する。
  • 配列などを使って独自のメモリ管理をしている場合
    • 使わなくなった領域をnullクリアする
  • キャッシュを実装している場合
    • WeakHashMapを使うと便利。
      • キーへの参照がなくなると、値のオブジェクトを自動で削除してくれる
  • クライアントがリスナーやコールバックを登録したが、明示的に解除を行わない場合
    • この場合もWeakHashMapが便利。

項目7 ファイナライザを避ける

  • 重要な資源(ファイル・DBコネクションなど)をクローズする目的でfinalizeを使ってはいけない
    • finalizeは実行されるタイミングが不明。ずっと実行されないことすらある。
  • finalizeを使っていいのは次の2つの場合だけ。
    • クライアントがクローズを忘れた場合に備える安全ネットとして
      • finalizeの中で、「クローズを忘れているよ」と警告を出すべき
    • 重要でないネイティブ資源を開放するため(?)
      • (重要でないネイティブ資源って具体的に何?例が書いてない)
  • publicで、かつfinalでないクラスにfinalizeを入れるときはファイナライザガーディアンを使うこと。
    • 普通にfinalizeを書くと、呼ばれない可能性がある