Java

Java 復習

久しぶりにJavaを使うことになりそうなので、備忘録的に復習したことをまとめました。間違えているところはご指摘いただけると助かります...

Java概要

  • Sun Microsystemsによって1990年代前半に開発された
  • 2010年にSun MicrosystemsはOracleに買収された
  • キャッチコピーは「Write Once, Run Anywhere」
    • コンパイラがソースコード .java ファイルをバイトコード .class に変換し、 JVM がバイトコードを解釈、実行する
  • 当初は1.*とバージョンがつけられていたが現在は6、7、8のように整数で表される
    • 1.0 -> 1.1 -> 2.0 -> 1.3 -> 1.4 -> 5.0 -> 6 -> 7(GAE) -> 8(AWS Lambda)

オブジェクト指向

  • メリット

    • 実際の物事の動きをそのままプログラムに置き換える
    • 機能を追加する際に既存コードへの影響を少なくできる
  • Javaのコードはクラスからなり、クラスはステート(インスタンス変数)と機能(メソッド)からなる

    • クラスからインスタンスをつくる
    • メソッドはインスタンスで共通、インスタンス変数は固有
    • メソッドへのデータの受け渡し(引数)は値渡し
    • メソッドはオーバーロードができる(同じメソッド名で異なる引数)
    • インスタンス変数にはデフォルト値がある(ローカル変数にはない)
  • カプセル化

    • インスタンス変数の不正な操作を防ぐ
    • インスタンス変数を private で宣言し、インスタンスの操作は public 宣言したゲッターメソッド、セッターメソッドから行う
  • 継承

    • クラスがIS-A関係にあるとき継承を行う
      • Cat is an Animal.
      • class Cat extends Animal {}
      • 似たものとしてHas-A関係がある
    • 継承は一方向なのでスーパークラスのメソッドをサブクラスから利用できるが、逆はできない
    • オーバーライドによってスーパークラスのメソッドの上書きができる
    • メリット
      • DRY: 継承は共通のメソッドをスーパークラスにおいて、複数のサブクラスから利用することにより重複排除ができる
      • ポリモーフィズム: メソッドの引数、変数の宣言時に型としてスーパークラスを指定することにより、サブクラスの代入が可能になる
      • 新しいサブクラスを追加しても該当コードの修正無しで利用可能になる
    • 継承利用時の注意
 - サブクラスはスーパークラスより具体的なものにする
      • インスタンス化が必要ないスーパークラスは abstract で抽象クラスにする
      • サブクラスにロジックの実装を強制したい場合、abstract で抽象メソッドにする
        • この場合、抽象クラスにする必要がある
      • メソッドの再利用目的で利用しない、IS-A関係がなりたつときに利用する
      • コンストラクタはスーパークラスが先によばれ、サブクラスのコンストラクタは後によばれる、順序の変更はできない
      • Javaでは2つ以上のクラスを継承する多重継承を禁止している
        • 継承する複数のスーパークラスに同じメソッド名がある場合、どちらを優先するかの問題
        • ただしインターフェースの複数の実装は可能
    • Object は全てのクラスのスーパークラス
      • メソッドとして、equals、hashCode、getClass、toStringをもつ
      • これらは上書き可能で、特にオブジェクト同士の比較(equals, hashCode)や、デバッグ(toString)の際に上書きされる
  • インターフェース

    • 完全な抽象クラス、メソッドはすべて抽象メソッドになる
    • 継承関係(IS-A関係)の複数のクラスに、共通機能をつける場合に利用する
      • ArrayList, HashSetクラスは継承関係はないがどちらも要素に繰り返しアクセスできるIterableのインタフェースを実装している
    • インターフェースをメソッドの引数や返り値の型に指定すると、実装するクラスのインスタンスの代入が可能になる

変数

  • 変数はプリミティブ型(int, double, boolean)と参照型に分けられる
  • 参照型の変数はヒープ領域にあるオブジェクトの参照を格納する
  • プリミティブ型を格納した配列(int[]等)も参照型の変数になる
  • 比較の際に注意が必要で、プリミティブ型の場合、「==」、参照型の場合「equals」(同じオブジェクトレファレンスか知りたい場合は「==」)
  • 変数はキャスト(型変換)ができる
    • intからdoubleへのキャストは可能
    • doubleからintはMathライブラリを利用して小数の扱いを指定
  • プリミティブ型には対応するラッパーオブジェクトがある
    • int型にはIntegerオブジェクト
    • ジェネリクスに指定できるのはオブジェクトなので、 ArrayList<int> はコンパイルエラーになり、 ArrayList<Integer> が正しい型宣言になる
    • 自動でラッパーオブジェクト<->プリミティブ型が変換される
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(1); // int型のデータを入れる
int i = list.get(o); // int型の変数で受け取る
  • Stringの不変性をもつ
    • 文字列をループで連結する場合は、毎回Stringプールに新しいオブジェクトが作成される
    • オブジェクトをつくるオーバーヘッド、Stringプールがガーベジコレクションされない問題点が指摘されている
    • StringBuffer/StringBuilder#appendでの連結が推奨される

アクセスレベル

  • Public, Private, Default, Protectedがある
  • 一般に使われるのはPublicとPrivate
  • Public: あらゆるコードから利用可能
  • Private: 自クラスからのみ利用可能
  • Default: 自パッケージのクラスからのみ利用可能
  • Protected: 自パッケージのクラスに加えて他パッケージのサブクラスから利用可能

ガーベジコレクション

  • Javaが利用するメモリ領域はスタックとヒープの2つにわけられる
  • メソッド、ローカル変数(メソッド内で宣言される変数)はスタックに格納される
    • メソッドが新しいメソッドを呼ぶとスタックに追加される
    • メソッドが返り値返すとスタックから取り出される
  • オブジェクトはヒープに格納される
  • オブジェクトはすべての参照がなくなるとガーベージコレクションの対象になる

スタティック

  • スタティックメソッドはインスタンスを必要としないメソッド(例: Mathクラスのメソッド)
  • コンストラクタを private にしたクラスをつくるとインスタンスがつくれなくなる
  • スタティックメソッドでは、インスタンス変数や通常のメソッドの利用はできないがスタティック変数は利用できる
    • スタティック変数はクラスのすべてのインスタンスで値が同じになる変数
    • static final で定数を定義できる

例外

  • メソッドの利用者に、特定の条件でメソッドが正常終了できなかったことを知らせるために例外を発生させる
  • 例外はすべてException型を継承するクラスオブジェクト
  • RuntimeExceptionは、不測の事態で発生するというよりプログラムのロジックの不備で発生する
    • Integer#parseIntのNumberFormatExceptionは引数の文字列が数ではないことに起因し、それは呼び出す前のロジックで排除するべき
    • RuntimeExceptionを継承する例外はtry/catchブロック不要(非チェック例外)
  • ポリモーフィズム:catchにExceptionを指定するれば全ての例外をうける
try {} catch(Exception e) {}
  • 非推奨だが、テストのためのコードなどでは簡便なため有効
    • catch文の順序が重要で、サブクラスを上に書くようにする
try {
} catch(Cat e) {
} catch(Animal e) {
  • 例外処理をしない場合は、メソッド宣言時にthrowsを指定し回避できる

IO

  • FileWriterのwriteメソッドは実行のたびにディスクアクセスしパフォーマンスが悪くなる
  • BufferedWriterで書き込みを行うと、一定量メモリにためて、容量がいっぱいになると書き込むため、ディスクアクセスが減る
    • 強制的に書き込みを行う場合はflush()を使う
  • 読み込みようにはBufferedReaderを利用する

スレッド

  • CPUを時間分割して、複数の処理を同時に平行しているようにみせる
  • スレッドを起動するには、Runnableのinterfaceを実装したクラスのオブジェクトをつくる
  • スレッドの状態として実行可能、ブロック(Sleep、IO)、実行中の3つがある
    • それぞれの状態はスレッドスケジューラが管理、決定する
  • 複数のスレッドが同じオブジェクトにアクセスすると問題が起きる
    • オブジェクトのアクセスへのロックは、メソッドにsynchronizedをつけて、同時にアクセスするスレッドを1つに限定できる
    • 利用にはデッドロックに気をつける

データ構造

  • SET: 要素の重複がないことが重要なときに使う
    • HashSet, TreeSet
    • hashCode()とequals()を利用して重複排除するので、必要に応じてオーバライドする
  • List: 要素の並び方が重要なときに使う
    • ArrayList, LinkedList
    • Collections.sortでsortができる
  • Map:キーによって要素を見つけたいときに使う

    • HashMap
  • ポリモーフィズム

    • 下記2つの違いに注意
    • スーパークラスのインスタンスを要素とするコレクションを代入する場合
public void barkAll(ArrayList<Animal> user) {}
  • サブクラスのインスタンスを要素とするコレクションを代入する場合(ワイルドカードを利用)
public void barkAll(ArrayList<? extends Animal> user) {}

パッケージとJAR

  • クラスをパッケージに分けることで、同名のクラスを利用可能にする
  • パッケージ名は慣例はドメインを逆にしたもの
  • クラスファイルはJARにまとめることができる
    • Manifest.txtにMain-Classを指定することで実行可能なJARファイルが作成できる