1. ホーム
  2. ジャワ

Javaで考える読書ノート

2022-02-26 17:31:57

Javaで考える読書ノート(IOと並行処理を除く)

1.1. あるクラスのprivateメソッドはすべて暗黙のうちにfinalと指定されており、他のクラスはprivateメソッドを取ることができないので、privateメソッドをオーバーライドすることはできない。privateメソッドをオーバーライドできないので、privateに対応する継承メソッドは無効であり、privateのプロパティは取得できないが、通常はpublicで対応したget、 setメソッドを持っていて、継承後のサブクラスがそのメソッドを呼んで親のプロパティに到達できるようにしている。

1.2. finalクラスは継承を禁止しているので、finalクラスのメソッドはすべて暗黙のうちにfinalと指定され、オーバーライドできないし、finalキーワードをつけることもできるが、あまり意味がない。

1.3. javaのすべてのメソッドは、動的バインディングによってポリモーフィックです。

1.4. メソッドが静的な場合、その動作はポリモーフィックではありません。つまり、コンストラクタはポリモーフィックではなく、static宣言が暗黙のうちになされていることを除けば、実際にはstaticなのです。

2.1. メソッドのスコープ内(他のクラスのスコープ内ではない)に完全なクラスを作成し、これをローカル内部クラスと呼びます(実は命名はローカル変数と同じで、メソッド内をローカル変数と呼びます)。

3.1. セットの中では、HashSetが最も高速に要素を取得できます。保存順序が重要な場合は、比較結果の昇順でオブジェクトを保存するTreeSetや、追加された順にオブジェクトを保存するLinkedHashSetを使用するとよいでしょう。

3.2. リストでは、基本的なarrayListは要素へのランダムアクセスは得意ですが、途中の要素の挿入や削除は遅くなります。LinkedListはarrayListの逆で、途中の挿入や削除は速く、ランダムアクセスは遅くなります。

3.3. ListIteratorはより強力なイテレータですが、その名前からListクラスでしか使用されないことがわかります。普通のIteratorはhasNext()だけで、前にしか進めませんが、ListIteratorはhasNext()とhasPrevious()で、両方向に動けます。

3.4. Queueは待ち行列で、典型的には先入れ先出しのコンテナ、つまり、片方に入ってもう片方から出てくる、取り出す順番は入れる順番と同じである。

3.5、javaのコンテナは、実は4種類ある。Map、List、Set、Queueの4種類で、各コンテナタイプの性能特性の違いについて詳しく説明します。

3.5.1、リスト。要素の繰り返しが可能。

ArrayList:下層は配列でサポートされています。

LinkedList: 双方向リンクテーブルで実装され、各オブジェクトには、チェーン内の前と次の要素への参照とともにデータが格納される。

そのため、テーブルの要素を頻繁に挿入したり削除したりする場合は、LinkedListの方が適しています。

3.5.2、セット 要素は一意でなければならず、重複は許されない。重複はequals()メソッドで判断されるので、setに追加される要素はequals()メソッドを定義してオブジェクトの一意性を確保する必要がある。

HashSet。高速に検索するために設計されたセットで、要素は一般的に使用されるhashCode()メソッドを定義しなければなりません。

ツリーセット。順序を保持するセットで、基礎に(バランスの取れた)木構造を持ち、それを使ってセットから順序のある列を取り出すことができます。sortedSet は TreeSet の唯一の実装です。

LinkedHashSet: HashSetのクエリ速度を持ち、要素の順序を維持するためにチェーンを使用する(この順序は挿入順)ので、イテレータを使用して反復したとき、結果は挿入順であり、要素はhashCode()メソッドを定義する必要があります。

マップ:キーと値のペアを保持し、キーは繰り返すことはできません、ユニークですが、1つだけnullであることができます、値は繰り返すことができる、nullにすることができます。

HashMap:ハッシュテーブル(現在ではHashTableに代わっている)をベースにしたmapの実装である。キーと値のペアを挿入したり問い合わせたりする際のオーバーヘッドは固定で、容量や負荷率をコンストラクタで設定し、コンテナの性能をチューニングすることができる。通常は彼を使う。

LinkedHashMap:HashMapの上にKey-Valueペアの挿入順序を保持する。アクセスはhashMapより少し遅いが、チェーンテーブルを使って内部順序を維持するため、反復アクセスはかえって高速になる。

TreeMap:赤黒木の実装に基づき、キーやキーバリューペアを見るときに、ソートされる(順序はComparableまたはComparatorで決定)ので、結果は順序付きという特徴があり、サブツリーを返す subMap() メソッドを持つ唯一の地図である。SortedMapは現段階ではTreeMapの唯一の実装である。

ConcurrentHashMap。同期ロックを伴わないスレッドセーフなマップだが、HashTableやCollections.synchronizedMap(Map)をロックで同期させて得られる同期マップよりもパフォーマンスが高い。synchronizedMap(Map)は非同期のマップを引数に取り、その実現のためにロックを頼りに同期したものを返している。

WeakHashMap: マップが指すオブジェクトを解放することができる弱キーマップ。機能問題を解決するために設計されている。

IdentityHashMap。equals() の代わりに == を使用して "keys" を比較するハッシュマップで、特殊な問題を解決するために設計されています。

3.6. Object.hashCode() メソッドはハッシュコードを生成しますが、オーバーライドしない限りデフォルトでオブジェクトのアドレスを使用してハッシュコードを計算します。Object.equals() はオーバーライドしない限りデフォルトで同じアドレスのオブジェクトを比較します。

3.7. ハッシュマップの構造は通常ハッシュバケットと呼ばれるもので、ハッシュテーブルのスロットは通常バケットと呼ばれ、実際のハッシュテーブルの配列はバケットという名前になります。

3.8. hashCode()を設計する際に最も重要なことは、同じオブジェクトでhashCode()が呼ばれるたびに同じ値を返すことであり、もちろんそのオブジェクト内のプロパティは変更されていないはずです。

3.9では、javaコンテナは高速エラー報告メカニズムを使用しており、自分のプロセス以外の変更がないかコンテナをプローブし、他のプロセスがコンテナを変更したことを発見したら、直ちにConcurrentModificationException例外をスローします。高速なエラー報告がどのように機能するかの簡単な例を以下に示します。

パブリックスタティックボイドmain(String[ args]{) <未定義

Collection<String> c = new ArrayList<String>();

Iterator<String> it = c.iterator();

c.add("An Object")を実行します。

try{ <未定義

String s = it.next()。

}catch(ConcurrentModificationExceptionのe){。 <未定義

System.out.println(e)を実行します。

}

}

//output: java.util.ConcurrentModificationException.

上記のプログラムは、コンテナがイテレータを取得した後、別のものがコンテナに入れられたため、実行時に例外を発生させ、catch後に例外メッセージを出力しています。この例外は、プログラムの異なる部分が同じコンテナを変更したときに発生し、コンテナの状態が一貫しなくなる可能性があります。この例では、イテレータがコンテナを取得した後にコンテナを変更する操作(iterator.remove()操作など)もあるため、イテレータを取得した後はコンテナのaddメソッドを呼び出してコンテナを再度変更することはできず、イテレータを取得する前に要素を追加しておくとよいでしょう。

3.10. Hashtableは3.5にも出てきますが、HashMapと似ていて、メソッド名まで似ています。Hashtableは同期化されているが、HashMapの同期化の問題はCollectionsの静的メソッドでも解決できる。Map Collections.synchronizedMap( Map m) このメソッドは同期化されたMapを返し、その下にあるHashMapのすべてのメソッドをカプセル化し、マルチスレッド環境でも下にあるHashMapを安全にすることができます。つまり、もうHashMapよりもHashtableを使う理由はないのです。

4.1、Throwableオブジェクトは2種類に分けられる(Throwableから継承された型を参照)。Errorはコンパイル時やシステムエラー(特殊な場合を除き、一般に気にする必要はない)を示すのに使われ、Exceptionはスローできる基本的な型です。

4.2. finally節は必ず実行されるため、try節でreturnしても、finally節は実行され続ける。

5.1、Stringオブジェクトは不変であり、StringクラスのメソッドでStringの値を変更しているように見えるものは、実際には全く新しいStringオブジェクトを生成し、元のStringオブジェクトはそのまま残されています。

javaのジェネリックは消去法で実装されています。つまり、ジェネリック型を使用している場合、特定の型情報はすべて消去され、オブジェクトを使用していることだけが分かります。つまり、List<String>とList<Integer>は実行時には実際には同じ型であり、どちらもListというquot; native"の型に消去されます。消去を理解しそれをどう扱うかは、javaジェネリックス学習において最大の難関と言えます。消去を理解しそれをどう扱うかはjavaジェネリックス学習において最大の難関と言えます。以下の通りです。

クラス c1 = new ArrayList<String>().getClass();

クラス c2 = new ArrayList<Integer>().getClass();

System.out.println(c1 == c2);//output: true

同様に消去のため、同じクラスは以下のように同じジェネリックインタフェースの2つのバリエーションを実装することができません。

interface A<T> {}となります。

Bクラスは、A<Integer> を実装しています。

CクラスはBを拡張し、A<String>を実装しています{}。

クラスCはコンパイルできず、A<Integer>とA<String>は消去のため同じ型Aに還元されてしまい、CはインターフェースAの実装を2回重複していることになります。

しかし、面白いことに、Aからジェネリックパラメータを取り除くと、うまくコンパイルできるのです。次のようになる。

interface A {}です。

BクラスはAを実装している{}。

クラスCはBを拡張し、Aを実装しています{}。

クラスCはコンパイル可能です。

6.2. javaのジェネリックの制限の一つ:ジェネリックの型パラメータとして基本型を使用できない、例えばArrayList<int>などは作成できない。

7.1、コンパイラが作成してくれるenumは全てjava.langを継承しています。

7.2. enum の values() メソッドは enum インスタンスの配列を返し、その配列の要素は厳密に enum で宣言された順序のままです。興味深いことに、enum に values() メソッドがなければ、コンパイラは Enum を継承して enum を作成し、values メソッドは親クラスである Enum にあると思うことでしょう。残念ながら、Enum クラスを調べてみると、そのどちらでもなく、Enum にあるわけでもないことがわかります。答えは、values()メソッドは、enumを作成したときにコンパイラが追加した静的メソッドだからです。

7.3. ordinal() メソッドは、宣言時の各列挙体インスタンスの並び順を0から順に並べたint値を返します。

7.4. 対応する enum インスタンスは == で直接比較できますし、equals() や hashcode() メソッドもコンパイラが自動的に提供してくれます。

7.5. また、すべての列挙型は java.lang.Enum を継承し、Java は多重継承をサポートしていないので、あなたの列挙型はもはや他のクラスから継承することができません。

enum EnumA extends ClassB{...}. これは使えません。

7.6. enumのもう一つの非常に興味深い特徴は、enumインスタンスに対してメソッドを書くことで、それぞれのenumインスタンスに独自の異なる振る舞いを与えることができることです。一般的な手順としては、enumで抽象的なメソッドを定義し、それを各インスタンスに対して実装していきます。以下のようになります。

public enum EnumTest{ <未定義

SHI_LI_ONE { <未定義

文字列 getInfo(){ <未定義

return "instance1"。

}

},

SHI_LI_TWO { <未定義

文字列 getInfo(){ <未定義

return "instance2"。

}

};

abstract String getInfo();

}

8.1. switch文はdefault句を使わなくても使える。式の値がすべての値と異なるとき、switch文を終了し、残りの実行を継続する。

9.1. アノテーションはメタデータとも呼ばれ、コードに情報を追加するための正式な方法を提供します。アノテーションは、@interfaceで定義されます。

public @interface Test{}

9.2. アノテーションは、継承をサポートしていません。extendsを使用して@interfaceから継承することはできません。

10.1、静的に変更された変数は、クラス情報とともにメソッド領域に格納されるため、コピーは1つしかなく、これを利用して、マルチスレッドのタスクの区別の目印となるIDを、タスク作成ごとに1を追加するなどの変更ができることは周知のとおりです。