Sojiro’s Blog

This is nothing much.

パーフェクトJava読書メモ Chapter 7 インターフェース

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

クラスとインターフェースの違い

  • インターフェースは雛形としての役割を持たない
  • インターフェースは実体化できず、型定義に特化している
  • インターフェースの継承の目的は多様性のみ(クラスの拡張継承には実装コードの共有という側面もある)

インターフェース宣言

1
2
3
[修飾子] interface インターフェース名 {
    メンバ宣言
}

インターフェースの修飾子

modifier description
public グローバルにアクセス可(書かないとパッケージ内に限定)
abstract インターフェースは暗黙的に abstract なので書かなくても同じ
strictfp インターフェース内に記述した浮動小数点演算を厳密に評価
アノテーション 省略

インターフェースのメンバ

  • 抽象メソッド(実装なし)
  • default メソッド
  • static メソッド
  • 定数フィールド
  • static なネストしたクラス
  • static なネストしたインターフェース

メソッド宣言

modifier description
public 暗黙的に public なので書かなくても同じ
default デフォルトメソッド
static static メソッド
abstract 暗黙的に abstract なので書かなくても同じ
  • default メソッドはインスタンスメソッド
  • インターフェースを継承した具象クラスのインスタンスメソッドになる
  • フィールド変数は暗黙的に public static final

インターフェースと実装クラス

インターフェース継承

1
2
3
[修飾子] class クラス名 implements インターフェース名 {
    クラス本体
}

複数のインターフェースを同時に継承することができる

1
2
3
[修飾子] class クラス名 implements インターフェース名, インターフェース名 {
    クラス本体
}
  • クラスの拡張継承とインターフェースの継承を同時にできる
  • このとき implementsextends より後に書く
1
2
3
[修飾子] class クラス名 extends 親クラス名 implements インターフェース名 {
    クラス本体
}
  • インターフェースを継承したクラスはインターフェースの抽象メソッドをすべてオーバーライドする必要がある
  • インターフェースから継承したメソッドのアクセス制御は public 修飾子がないとコンパイルエラー
  • メソッドのオーバーライドはクラスの拡張継承と同様に行う

ネストしたインターフェース

  • クラス内のネストしたインターフェース
    • public protected 無指定 private のいずれかを指定する
  • インターフェース内のネストしたインターフェース
    • 常に public
  • インターフェース内のネストしたクラス
    • 常に public かつ static
  • ネストして宣言されたインターフェースは常に static

インターフェース自体の拡張継承

  • インターフェースも拡張継承できる
  • インターフェースは複数の親インターフェースを指定可能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface Parent {
    void print();
}

interface Child extends Parent {
    // void print() を継承
}

interface Father {
    void print();
}

interface Mother {
    void exec();
}

interface Child2 extends Father, Mother {
    // void print(), void exec() を継承
}

参照

改訂2版 パーフェクトJava

パーフェクトJava読書メモ Chapter 6 コレクションと配列

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

コレクションフレームワーク

—– Hash Table Array Tree Linked List Hash Table + Linked List
set HashSet TreeSet LinkedHashSet
list ArrayList LinkedList
deque ArrayDeque LinkedList
map HashMap TreeMap LinkedHashMap

コレクション型オブジェクトの生成

1
コレクションのインターフェース型<要素の型> 変数 = new コレクションのインターフェースを実装した具象クラス<>([コンストラクタの引数]);
1
List<String> list = new ArrayList<>();
  • 上記例で ArrayList<>ArrayList<String> の略
1
List<int> list = new ArrayList<>();
  • 要素の型に基本型は指定できない

リスト

リストの具象クラス

  • ArrayList
  • LinkedList

ArrayList

  • 良い点
    • インデックスを指定して要素を読み出す速度が速い[get]
    • インデックスを指定して要素を書き換える速度が速い[set]
    • 先頭から順にすべての要素をなめる処理が速い
  • 悪い点
    • 要素の挿入が遅いことがある[add]
      • 先頭に近い位置への挿入は遅い
      • 末尾に近い位置への挿入は速いときもあるが遅いときもある
    • 要素の削除が遅いことがある[remove]
      • 先頭に近い位置の削除ほど遅い
      • 末尾に近い位置の削除ほど速い
      • 最末尾の削除は高速
    • 条件に合致した要素の検索があまり速くない[contains, indexOf, lastIndexOf]
  • 要素の順序をもつので、順序が入れ替わる要素が多くなる処理は遅い
  • 連続したメモリの確保ができない場合、末尾への挿入は遅くなる

LinkedList

  • 良い点
    • 要素の挿入が速い[add]
    • 要素の削除が速い[remove]
  • 悪い点
    • インデックスを指定して要素を読み出す速度はあまり速くない[get]
    • インデックスを指定して要素を書き換える速度はあまり速くない[set]
    • 条件に合致した要素を検索する処理の速度はあまり速くない[contains, indexOf, lastIndexOf]
  • 要素にたどり着くまでリンクをたどるのでリストの真ん中に近い要素ほどアクセスに時間がかかる

マップ

1
2
Map<String, Integer> map = new HashMap<>();
Map<Integer, List<String>> map = new TreeMap<>();
  • Map インターフェースはキーと値の両方の型を <> の中に指定する
  • List インターフェース同様 <> に指定できるのは参照型のみ

マップの具象クラス

  • HashMap
  • LinkedHashMap
  • TreeMap

HashMap

HashMapの内部動作

  • HashMap は内部に配列を確保する。これをハッシュ表と呼ぶ。
  • ハッシュ表の初期サイズは HashMap のコンストラクタで指定する。
  • キーと値のペアを HashMap に追加[put]すると HashMap は内部でキーをハッシュ関数に通す。
  • ハッシュ関数の出力がハッシュ表のインデックスになる。
  • 得られたインデックスの要素としてキーと値のペアを格納する。

LinkedHashMap

LinkedHashMapの内部動作

  • HashMap を拡張継承している
  • ハッシュ表に加えて、 LinkedList と同じリンクリストを内部に保持する
  • 要素を追加すると内部でハッシュ表とリンクリストの両方に要素を追加する
1
LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
  • 上記コンストラクタの accessOrdertrue を指定するとリンクリストの順序が要素にアクセスした順になる

TreeMap

二分探索木の一種である赤黒木と呼ばれるアルゴリズムによるツリー構造で要素をもつ

NavigableMap

TreeMap がキーの順序をもつことを利用したインターフェース

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Sample {
    public static void main(String... args) {
        NavigableMap<String, String> map = new TreeMap<>();
        map.put("kanayama", "one");
        map.put("kawai", "four");
        map.put("kushibiki", "five");

        Map.Entry<String, String> entry = map.ceilingEntry("kana");
        if (entry != null) {
            entry.getKey() + ": " + entry.getValue();  // "kanayama: one"
        }
    }
}

セット

  • セットは数学の集合の概念
  • 要素の重複を許さない
  • Set インターフェースを実装した具象クラスは以下の3つ
    • HashSet
    • LinkedHashSet
    • TreeSet
  • 要素の重複を許さない点が Map のキーの性質と重なるため、具象クラスも Map と重なる

スタック、キュー、デック

  • スタック: 最新要素から常に取り出し
  • キュー: 最古要素から常に取り出し
  • デック: 上記どちらにも対応
  • スタック構造は Deque クラス(Stack クラスは古い)
  • キュー構造は Queue クラス

イテレータ

拡張for構文

1
2
3
for (要素型 ループ変数 : コレクション変数) {
    forループが回るたびにループ変数が要素オブジェクトを順に参照
}

イテレータ

  • イテレーションを抽象化したオブジェクト
  • Iterator インターフェースは以下のメソッドをもつ
    • boolean hasNext();
    • E next();
    • void remove();
  • ListIterator インターフェースは Iterator を継承している
  • ListIterator は上記に加えて以下の以下のメソッドをもつ
    • boolean hasPrevious();
    • E previous();

セットに対するイテレーション

1
2
3
4
5
6
7
8
9
// Set<Integer> set
for (Integer n : set) {
    sum += n;
}

for (Iterator<Integer> it = set.iterator(); it.hasNext(); ) {
    Integer n = it.next();
    sum += n;
}

マップに対するイテレーション

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Map<String, String> map

// 拡張 for でキーを繰り返す
for (String key : map.keySet()) {
    System.out.println(key);
}

// イテレータで値を繰り返す
for (Iterator<String> it = map.values().iterator(); it.hasNext(); ) {
    String val = it.next();
    System.out.println(val);
}

// 拡張 for でキーと値を繰り返す
for (Map.Entry<String, String> entry : map.entrySet()) {
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

リストを逆順になめる

1
2
3
4
5
// List<Integer> list
for (ListIterator<Integer> it = list.listIterator(list.size()); it.hasPrevious(); ) {
    Integer i = it.previous();
    System.out.println(i);
}

配列

1
2
3
int[] arr = new int[3];

int[] arr = { 0, 1, 2 };

多次元配列の初期化

1
2
3
4
5
6
7
8
9
10
StringBuilder[][][] arr = {
    {
        { new StringBuilder("000"), new StringBuilder("001") },
        { new StringBuilder("010"), new StringBuilder("011") },
    },
    {
        { new StringBuilder("100"), new StringBuilder("101") },
        { new StringBuilder("110"), new StringBuilder("111") },
    },
};

参照

改訂2版 パーフェクトJava

パーフェクト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

パーフェクトJava読書メモ Chapter 4 変数とオブジェクト

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

変数とオブジェクト

  • Javaの変数は以下の二つに分類される
    • 基本型変数
    • 参照型変数
  • オブジェクトはある体系にそってデータを表すモノであり、名前を持たない

変数

参照型変数

  • C言語のポインタ型変数の値がメモリ上のアドレス値そのものであるのに対し、Javaの参照型変数の値はオブジェクトの位置情報を指し示す抽象的な「何か」である
  • 参照型変数は名前を持ち、オブジェクトを参照することで扱いやすく橋渡しする
  • 参照型変数自体に型があり以下の3種類、これは参照しているオブジェクトの型とは別
    • クラス型
    • 配列型
    • インターフェース型

基本型変数

  • 基本型変数は値をそのまま保持する
  • 基本型の種類は以下の8つ
    • boolean
    • byte
    • char
    • short
    • int
    • long
    • float
    • double

変数の宣言

  • 変数を使うには最初に変数を宣言する
  • 変数宣言は最初に変数の型を書き、続けて変数名を書く
1
StringBuilder sb;
  • 基本型変数の宣言時も型名を変数名の前に書く
1
int i;
  • 同じスコープで同名の変数は宣言できない
1
2
3
4
void method(int i) {
    int i;
}
// コンパイルエラー

変数の初期化

  • 変数は宣言時に初期化できる
1
int i = 1;
  • 初期化しない場合の変数のデフォルト値は変数の型と種類に依存する

変数の修飾子

  • 変数の宣言時に修飾子を付けることができる
  • 変数に使える修飾子は以下
    • private
    • protected
    • public
    • transient
    • final
    • static
    • volatile

オブジェクト

オブジェクトの生成

  • new の後にクラス名を書き、 () で引数を指定する
  • 引数の定義はクラスごとに決まっている
1
new StringBuilder("012");

参照型変数への代入

  • 生成したオブジェクトは参照型変数へ参照を渡して扱う
1
StringBuilder sb = new StringBuilder();
  • あくまで変数が扱っているのはオブジェクトへの参照なので、以下の例では2つの変数は同じオブジェクトへの参照を持つ
1
2
3
4
5
6
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = sb;
sb.append("012");
sb2.append("345");
// sb: "0123445"
// sb2: "012345"

基本型変数への代入

  • 基本型変数へ代入されるのはオブジェクトの参照ではなく値そのもの
  • 値そのものをコピーして代入する
1
2
3
4
5
6
int i = 42;
int j = i + 1;
int K = i;
// i: 42
// j: 43
// k: 42

null参照

  • null は「何も参照していない」ということを表す
1
StringBuilder sb = null;

nullチェック

  • 参照先がなく、 null を持つ参照型変数にたいする演算は NullPointerException
  • Object クラスに null をチェックするためのメソッドがある
method description
equals 引数のどちらかが null でも使える比較
toString null の場合の文字列を指定可能
isNull 引数が null のとき真
notNull 引数が null でないとき真
requireNonNull 引数が null だと即座に NullPointerException

Optional型

  • Optional 型は任意のオブジェクトをくるんで null かもしれない状態を表現する
  • 基本型変数のための Optional 型として以下の 3 つがある
    • OptionalInt
    • OptionalLong
    • OptionalDouble
  • Optional オブジェクトは of または ofNullable メソッドを使って任意のオブジェクトから生成できる
1
2
3
4
StringBuilder sb;
Optional<StringBuilder> osb = Optional.of(sb);
// or
// Oprional<StringBuilder> osb = Optional.ofNullable(sb);
  • get メソッドで Optional オブジェクトでくるんだオブジェクトを取得
1
2
Optional<StringBuilder> osb;
StringBuilder sb = osb.get();
  • くるんだオブジェクトが null でなければそのまま呼び出し、 null であれば引数のオブジェクトを返す例
1
StringBuilder sb = osb.orElse(new StringBuilder("none"));

変数を介さないオブジェクトの操作

  • 変数に参照を格納しなくてもオブジェクトを操作できる
1
int len = new StringBuilder("012").length();
  • 文字列リテラルでも
1
int len = "abc".length();
  • ドット演算子でメソッド呼び出しをつなげることをメソッドチェインという
1
2
3
StringBuilder sb = new StringBuilder();
int len = sb.append("012").append("345").length();
// len: 6

変数と型

  • オブジェクトの型はクラスで定義されている
  • 変数の型とオブジェクトの型が一致もしくはオブジェクトの型が変数の型の下位型であるとき変数にオブジェクトの参照を代入可能
  • 変数が参照するオブジェクトに対して行える操作は変数の型できまる
  • 下位型のオブジェクトは上位型の持つメソッドを持つことが保証されている
    • ただし実態が同一とは限らない

変数の詳細

変数の種類

type description
ローカル変数 メソッドもしくはコンストラクタ内で宣言される。メソッドやコンストラクタが終わると消滅。
パラメータ変数 メソッドもしくはコンストラクタに引数として渡る。メソッドやコンストラクタが終わると消滅。
インスタンスフィールド変数 クラスの構成要素
クラスフィールド変数 クラスの構成要素

変数のデフォルト値

type default value
参照型 null
boolean false
char “¥u0000”
byte, short, int, Long 0
float, double +0.0

変数のスコープ

ローカル変数

  • 変数を宣言した行からメソッドもしくはコンストラクタが終わるまで

ブロックスコープ

  • {} で囲ったブロックに閉じたスコープ
  • 変数を宣言した行からブロックが終わるまで

シャドーイング

  • 同一のスコープ内で同じ変数を2度以上宣言

パラメータ変数のスコープ

  • メソッド及びコンストラクタの中

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

  • フィールド変数のスコープはクラス内
  • 宣言した行の位置は無関係

オブジェクトの寿命

オブジェクトへの参照が外れる条件 * ローカル変数及びパラメータ変数のスコープが外れたとき * オブジェクトが消滅したとき * クラスが消滅し、クラスフィールド変数の参照が外れたとき * 変数に別のオブジェクトの参照もしくは null が再代入されたとき * 変数を介さない操作がされていた場合、式の評価が終わったとき

final変数と不変オブジェクト

  • final 修飾子を指定した変数を final 変数と呼ぶ
  • final 変数は再代入不可
  • final 修飾子が禁止するのは変数自体の値の変更であって、変数が参照するオブジェクト自体の変更ではない

参照

改訂2版 パーフェクトJava

パーフェクトJava読書メモ Chapter 3 数値①

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

整数型

以下の5種類

name bit length min max
byte 8 -128 127
char 16 0 65535
short 16 -32768 32767
int 32 -2147483648 2147483647
long 64 -9223372036854775808 9223372036854775807

bit値

符号あり整数で最上位ビットが1の値は負の値となる

4bit長の符号なし整数

type value bit
min 0 0000
max 15 1111

4bit長の符号あり整数

type value bit
min bit 0 0000
max value 7 0111
min value -8 1000
max bit -1 1111

桁あふれ

整数の加算はbit値を進める処理であり、

整数の減算はbit値を戻す処理である

したがってbitの境界値をまたいで整数の加算減算を行うと予期しない結果となる場合がある

1
2
3
4
5
int i = Interger.MAX_VALUE;
// i: 2147483647

i++
// i: -2147483648

整数リテラル

  • L または l で終わる整数リテラルの型は long
  • それ以外の整数リテラルは int
  • int 型リテラル値を bytecharshort の型の変数に代入する場合、値が代入する先の型の範囲内であれば自動的に型が変換されて代入される
    • 代入する先の型の範囲を超えている場合はコンパイルエラーとなる

基数

  • 0b から始めると 2 進数
  • 0 から始めると 8 進数
  • 0x から始めると 16 進数

整数の演算

四則演算

演算子 演算
+
-
*
/

注意点

  • 大きな正の整数の和における桁あふれ
  • 小さな負の整数の差における桁あふれ
  • 絶対値の大きな整数同士の積の桁あふれ
  • 商の結果は切り捨て
  • 0 による割り算は ArithmeticException

剰余

  • 剰余演算子は %
  • 0 による剰余演算は ArithmeticException

符号反転

単項演算子 - は通常単純に符号を反転させる

1
2
int n = 10;
// -n: -10

しかしここにも桁あふれの問題があるので注意

1
2
int n = -2147483648;
// -n: -2147483648

仕様書によると

-x equals (~x)+1

  • ~ は bit 反転演算子
  • -2147483648 は bit に変換すると 100…0(0 が 31 個つづく)
  • これを反転させると 011…1(1 が 31 個つづく)となり、これは 2147483647 を表す
  • 2147483647 + 1 は桁あふれが起こり、 -2147483648 となる

インクリメント・デクリメント

  • ++ でインクリメント
  • -- でデクリメント

キャスト

() 付きで型を書くことで強制的に型変換する

1
2
int i = 1;
short si = (short)i;

代入先の型に収まらないとあふれたbitが切り捨てられる

1
2
short si = (short)65536;
// si: 0
  • 65536 は bit に変換すると 100…0 (0 が 16 個つづく)
  • short 型は 16 bit なので先頭の 1 があふれる

ブーリアン(真偽値)

ブーリアン型が取りうる値は truefalse のみ

※ ド・モルガンの法則の説明等

参照

パーフェクトJava読書メモ Chapter 2 文字と文字列

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

文字列

“(ダブルクォート)で囲んで文字数リテラル

String クラス

  • 文字列リテラルから String オブジェクトが自動生成
  • 配列のように1文字ごとにindexが振られる

StringBuilder クラス

  • オブジェクトに対して破壊的
  • String オブジェクトは read only

文字列の結合

  • +=StringBuilder の関係
  • join メソッド

文字列の比較

  • == 演算子じゃなくて equals メソッド
    • == 比較は同一のオブジェクトへの参照かどうかの比較
  • 同じ文字列リテラルは同じ String オブジェクト
  • ただし StringStringBuilder では文字列の内容が同じでも違うオブジェクト
    • そんなときは contentEquals メソッド
  • StringBuilder 同士の文字列の内容比較もできないので、 toStringString に変換してから contentEquals 使うこと

文字列と数値の変換

数値から文字列への変換

valueOf メソッド

1
2
String s = String.valueOf(255);
// s: "255"

toString メソッドで10進数以外に直接変換する

1
2
String s = Integer.toString(255, 16);
// s: "ff"

文字列から数値への変換

parseInt メソッド

1
2
int i = Integer.parseInt("255");
// i: 255

基数を与えて10進数以外の処理

1
2
int i = Integer.parseint("ff", 16);
// i: 255

文字

  • ‘(シングルクォート)で囲んで文字リテラル
  • Javaの世界ではUTF-16
    • 文字を16bitの数値で表す char
  • 文字リテラルは char 型の数値
  • String オブジェクトは文字( char 型)の配列

バイト

  • 歴史的に文字とバイトは同一視されがち
  • Javaではバイトを文字と区別する
  • 8bit長の byte
  • バイト列は byte 型の配列

バイト列と文字列の変換

バイト列から文字列への変換

byte の配列を渡して String オブジェクトを生成

1
2
3
byte[] bytes = new byte[]{0x61, 0x62, 0x63};
String s = new String(bytes);
// s: abc

文字列からバイト列への変換

getBytes メソッドを使う

1
2
String s = "abc";
byte[] bytes = s.getBytes();

参照

改訂2版 パーフェクトJava

Android StudioからGAE for Javaアプリケーションをdeployするのに必要なFacet

こちらの記事を参考にGAE for JavaアプリケーションをAndroid Studio + Gradleでセットアップし、サンプルアプリケーションを開発してみました。

早速GAEにdeployしてみようと、メニューバーの Build から Deploy Module to App Engine... を選択してdeployを実行…ところがタスクが走らずうんともすんとも言わないので調べてみました。

結論

以下の設定を app.iml ファイルに追記する

1
2
3
4
5
6
7
8
9
10
11
<facet type="android-gradle" name="Android-Gradle">
  <configuration>
    <option name="GRADLE_PROJECT_PATH" value=":app" />
  </configuration>
</facet>
<facet type="java-gradle" name="Java-Gradle">
  <configuration>
     <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
     <option name="BUILDABLE" value="true" />
  </configuration>
</facet>

こちらは少々特殊な方法でmoduleを作成しているのでFacetの設定が不十分となってしまった模様。

Facetとは

FacetはIntelliJ IDEAに用意された機能で、使用するフレームワークや言語に合わせたFacetを設定することでIntelliJ IDEAが必要なコンポーネントのダウンロードや各種補完機能の設定などを行ってくれるもの。

Android StudioはIntelliJ IDEAをベースとして開発されたIDEなのでFacetの機能を継承している。

参照

DockerでRails環境を構築した手順

2016/07/10 修正: docker-compose.ymlnginx.conf の一部を修正しました

とある経緯で Docker を使って Rails の環境をセットアップした手順です。

setting files

まずは用意した file から。

docker-compose.yml

docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# not use datastore container now
#datastore:
#  build: Dockerfiles/datastore


mysql:
  image: mysql:5.6.26
  environment:
    MYSQL_ROOT_PASSWORD: 'pass'
  ports:
    - '3306:3306'
#  volumes_from:
#    - datastore

nginx:
  build: Dockerfiles/nginx
  ports:
    - '80:80'
#  volumes_from:
#    - datastore
  links:
    - web

web:
  build: .

  command: bundle exec unicorn -c config/unicorn.rb

#  volumes_from:
#    - datastore
  volumes:
    - .:/usr/src/app
  ports:
    - '3000:3000'
  links:
    - mysql
  environment:
    RAILS_ENV: development
    MYSQL_ROOT_PASSWORD: 'pass'
    DATABASE_URL: mysql2://root:pass@mysql:3306
    SECRET_KEY_BASE: hogehoge

Dockerfile

Dockerfile
1
FROM rails:onbuild

Dockerfiles/nginx/Dockerfile

Dockerfile
1
2
3
FROM nginx:1.7.9

COPY nginx.conf /etc/nginx/nginx.conf

Dockerfiles/nginx/nginx.conf

nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
  worker_connections 1024; # increase if you have lots of clients
  accept_mutex off; # "on" if nginx worker_processes > 1
}

http {
  include mime.types;
  default_type application/octet-stream;
  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

  access_log  /var/log/nginx/access.log  main;

  sendfile on;

  tcp_nopush on; # off may be better for *some* Comet/long-poll stuff
  tcp_nodelay off; # on may be better for some Comet/long-poll stuff

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/html text/xml text/css
             text/comma-separated-values
             text/javascript application/x-javascript
             application/atom+xml;

  upstream app_server {
    # for UNIX domain socket setups:
    # server unix:/path/to/.unicorn.sock fail_timeout=0;

    # for TCP setups, point these to your backend servers
    # server 192.168.0.7:8080 fail_timeout=0;
    server web:3000 fail_timeout=0;
  }

  server {
    listen       80;
    server_name  localhost;
    client_max_body_size 4G;
    keepalive_timeout 5;

    # path for static files
    root /usr/your_app/home/system/public;

    try_files $uri/index.html $uri.html $uri @app;

    location @app {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://app_server;
    }

    # Rails error pages
    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /usr/your_app/home/system/public;
    }
  }
}

Setup Tutorial

Download Tools

VirtualBox

https://www.virtualbox.org/wiki/Downloads

please install version >= 5.x

Homebrew

http://brew.sh/index_ja.html

1
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Docker Tools

Docker

1
$ brew install docker

Docker Machine

1
$ brew install docker-machine

Docker Compose

1
$ brew install docker-compose

Download Application

1
2
3
$ cd ~/path/to/workspace
$ git clone git@github.com:your/App.git your_app
$ cd your_app

Setup Docker Machine

1
2
3
4
5
6
$ docker-machine create --driver virtualbox dev
...

$ docker-machine ls
NAME   ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER    ERRORS
dev    -        virtualbox   Running   tcp://192.168.99.100:2376           v1.10.0
1
2
3
$ eval "$(docker-machine env dev)"

$ echo 'eval "$(docker-machine env dev)"' >> ~/.bashrc
1
2
3
4
5
$ docker-machine start dev
...

$ docker-machine ip dev
192.168.99.100

Setup Application

Build Docker Images and Start Containers

1
2
3
4
5
6
# ~/path/to/workspace/your_app
$ docker-compose build
...

$ docker-compose up
...

please open another tab

Setup DB

1
2
$ docker-compose run web rake db:create
$ docker-compose run web rake db:migrate

Access Application

1
2
$ docker-machine ip dev
192.168.99.100

access to 192.168.99.100:3000

Plenv で Perl を管理する

Perl をバージョンごと、あるいはプロジェクトごとに管理するためのツールである plenv の導入手順メモ

plenv をインストールする

homebrew の update

1
2
3
$ brew update
Updated Homebrew from c0fae05 to bfe20af.
No changes to formulae.

先ほど rbenv をインストールする手順 で同じことをやったばかりなので更新なし

plenv と perl-build のインストール

これも どこか で見たような手順

perl-buildplenv のプラグイン

1
2
3
4
$ brew install plenv perl-build
==> Downloading https://github.com/tokuhirom/plenv/archive/2.2.0.tar.gz
==> Downloading from https://codeload.github.com/tokuhirom/plenv/tar.gz/2.2.0
...

plenv init

1
2
$ echo 'eval "$(plenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

plenv init - でやっていることは以下の通り

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ plenv init -
export PATH="/Your/Home/Directory/.plenv/shims:${PATH}"
export PLENV_SHELL=bash
source '/usr/local/Cellar/plenv/2.2.0/libexec/../completions/plenv.bash'

plenv() {
  local command

  command="$1"
  if [ "$#" -gt 0 ]; then
    shift
  fi

  case "$command" in
  rehash|shell)
    eval "`plenv "sh-$command" "$@"`";;
  *)
    command plenv "$command" "$@";;
  esac
}

perl のインストール

インストールできる perl のバージョンを確認

1
2
3
4
5
6
$ plenv install -l
Available versions:
 5.6.0
 5.6.1-TRIAL1
 5.6.1-TRIAL2
...

最新の安定版であるバージョン 5.22.1 をインストールする

1
2
3
$ plenv install 5.22.1
Installing 5.22.1 as 5.22.1
...

インストールされたバージョンを確認

1
2
3
$ plenv versions
* system (set by /My/Home/Directory/.plenv/version)
  5.22.1

使用する perl を設定

1
2
3
4
$ plenv global 5.22.1
$ plenv versions
  system
* 5.22.1 (set by /My/Home/Directory/.plenv/version)

perl に cpanm をインストールする

現在使用している perl に cpanm をインストールする

1
2
3
4
5
6
$ plenv install-cpanm
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   314    0   314    0     0   2078      0 --:--:-- --:--:-- --:--:--  2079
100  296k  100  296k    0     0   367k      0 --:--:-- --:--:-- --:--:-- 2135k
...

cpanm のパスが変わっていることを確認

1
2
$ which cpanm
/My/Home/Directory/.plenv/shims/cpanm

参照

Rbenv で Ruby を管理する

Ruby をバージョンごと、あるいはプロジェクトごとに管理するためのツールである rbenv の導入手順メモ

rbenv をインストールする

homebrew の update

1
2
3
$ brew update
Updated Homebrew from b369c25 to c0fae05.
...

rbenv と ruby-build のインストール

rbenv と同時に ruby-build もインストールする

ruby-buildrbenv のプラグインで rbenv install コマンドを提供する

1
2
3
$ brew install rbenv ruby-build
==> Installing dependencies for rbenv: autoconf, pkg-config, openssl, ruby-build
...

rbenv init

1
2
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile

rbenv init - でやっていることは以下の通り

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ rbenv init -
export PATH="/Your/Home/Directory/.rbenv/shims:${PATH}"
export RBENV_SHELL=bash
source '/usr/local/Cellar/rbenv/1.0.0/libexec/../completions/rbenv.bash'
command rbenv rehash 2>/dev/null
rbenv() {
  local command
  command="$1"
  if [ "$#" -gt 0 ]; then
    shift
  fi

  case "$command" in
  rehash|shell)
    eval "$(rbenv "sh-$command" "$@")";;
  *)
    command rbenv "$command" "$@";;
  esac
}

ruby のインストール

インストールできる ruby のバージョンを確認

1
2
3
4
5
6
$ rbenv install -l
Available versions:
  1.8.6-p383
  1.8.6-p420
  1.8.7-p249
...

バージョン 2.2.0 をインストールする

1
2
3
4
5
$ rbenv install 2.2.0
Downloading ruby-2.2.0.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.0.tar.bz2
Installing ruby-2.2.0...
...

インストールされたバージョンを確認

1
2
3
$ rbenv versions
* system (set by /My/Home/Directory/.rbenv/version)
  2.2.0

使用する ruby を設定

1
2
3
4
$ rbenv global 2.2.0
$ rbenv versions
  system
* 2.2.0 (set by /My/Home/Directory/.rbenv/version)

ブログを管理しているディレクトリは system ruby にしておく

1
2
3
4
5
6
7
8
9
$ cd git/blog/
$ rbenv local system
$ rbenv versions
* system (set by /My/Home/Directory/git/blog/.ruby-version)
  2.2.0
$ cd
$ rbenv versions
  system
* 2.2.0 (set by /My/Home/Directory/.rbenv/version)

参照