S-JIS[2007-02-28/2022-09-21] 変更履歴
Javaでは、全てのクラスがjava.lang.Objectから派生している。
ここで定義されているメソッドは、継承したクラスがオーバーライドすることを想定しているものがある。
メンバー | 説明 |
---|---|
public String toString() | デフォルトでは、「オブジェクト名+@+ハッシュコード」を返す。 普通はサブクラスでオーバーライドして、分かり易い文字列を返すようにする。 |
public Class getClass() | リフレクションで使用。 |
protected Object clone() | インスタンスの複製を作る。 フィールド(メンバー変数)の複製(ディープコピー)まで作りたい場合は、当メソッドをオーバーライドして、自分で複製処理を加える必要がある。 |
public boolean equals(Object) | オブジェクトが(値として)等しい場合にtrueを返す。 サブクラスでオーバーライドして、そのクラスで想定する比較を行う。 |
public int hashCode() | ハッシュコード値を返す。 equals()を自分で定義した場合は、ハッシュコードも正しく返すようにする必要がある。 |
public void wait() public void notify() |
マルチスレッド(排他処理)で使用。 |
protected void finalize() | ファイナライザー。[/2008-09-14] |
オブジェクトが値として等しいかどうかを判断する。
(細かい条件はJavadoc参照。けど要するに等しければtrueを返せばいいのだ(爆))
→==演算子との違い
(ただし、オーバーライドされていないデフォルトの状態では、実装は「this==otherObject」なので、==演算子と等しい。[2010-01-25])
equals()をオーバーライドして独自の比較を行うようにした場合は、hashCode()も相応の値を返すように実装しなければならない(らしい。ハッシュ値を使わないなら不要な気がしないでもないが)。
ハッシュコード値を返す。
当メソッドは、
ハッシュテーブル(Hashtable)やハッシュマップ(HashMap)で使用される目的で存在しているらしい。
だからハッシュ系のクラスを使わないのであれば、hashCode()を実装する必要はない…とは思うが。
equals()で等価と判断される2つのインスタンスでは、同じハッシュ値を返さないといけない。
逆に非等価の場合は、違うハッシュ値でも同じハッシュ値でもよい。(ただし、値が異なる方がハッシュテーブルでの効率は上がる)
つまり極端な話、常にhashCode()が0を返すような実装をすれば、最低限の要件は満たすわけだ(爆)
ここから導き出される使用方法は、「オブジェクトが等しい(equals()がtrue)ならば、ハッシュコードは等しい」。
数学的(論理的)にはその対偶は正しいので、「ハッシュコードが等しくないならばequals()はtrueでない」
すなわち「ハッシュコードが不一致ならオブジェクトは等しくない」。
ハッシュコードの実装は、これを満たす必要がある。
equals()がどういう実装をするかにも依るだろうが、一般的には内部で保持しているプリミティブ型のメンバーの値が全て等しく、オブジェクト型のメンバーが全てequals()=trueであれば「等しい」と判断すると思う。
つまり、hashCode()が返す値は、メンバーの「プリミティブ型の値」と「オブジェクトのhashCode()の値
」を組み合わせる(加算するとか排他的論理和をとるとか)という事をすればよいと思われる。
(メンバーのオブジェクトがnullの場合は、固定値(例えば0)でいいと思う。HashMapではそんな扱いをしている)
class Sample {
int n;
String s;
public boolean equals(Object obj) { if (obj == null) return false; if (obj instanceof Sample) { Sample o = (Sample)obj; if (this.n != o.n) return false; if (this.s == null) return o.s == null; return this.s.equals(o.s); } return super.equals(obj); }
public int hashCode() { int h = (s==null) ? 0 : s.hashCode(); return n ^ h; }
}
HashMapは、キーのオブジェクトのhashCode()とequals()を使用している。
キーがnullの場合、特別な処理を行う。[/2008-01-20]
(JDK1.4ではNULL用オブジェクトに置き換えて後続の処理を行う。
JDK1.6ではテーブル0番がnull用の場所と決め打ちされていて、その中から探す)
キーがnull以外の場合、ハッシュコードを元に“内部で保持しているテーブル(配列)”のインデックスを算出する。
このテーブルの要素は、エントリー(キーと値の組)がリスト構造になっている。
(したがって、ハッシュコードが分散している方が効率がよい)
エントリーにはハッシュ値も保持しているので、検索時にいちいち再計算したりはしない。
(したがって、格納したキーの内容が外部から変更されてハッシュ値が変わると問題が起きる可能性がある)
検索するときは、まずテーブルのインデックスをキーのハッシュコード値から決定し、その要素内のリストを1つずつサーチしていく。
サーチの際のキーの比較には、まずキーのハッシュコードが一致しているかどうかを判定する。(要件から言って、ハッシュコードが不一致ならオブジェクトは等しくないと判断できるから)
ハッシュコードが等しければ、次に、キーのインスタンスが等しい(==演算を使用)かチェックし、等しければ当然そのエントリーを使用する。
等しくなければキーのequals()を呼んで、値として等しいかどうかチェックしている。
それでも等しくなければ、リストの次のエントリーのサーチに移る。
つまり、最終的にはキーのequals()を呼んで等しいかどうかチェックしているが、途中の比較ではなるべくハッシュコードを使って計算を高速化しようとしている。
(逆に言うと、ハッシュコードだけを使用しているわけではない。名前がHashMapだからハッシュだけと誤解しそうだが)
ついでに。テーブルの各要素はリストなので、サーチ方法は“バカサーチ”だ…。バカサーチであっても、サーチ件数が数個程度しか無いなら変にクイックソートとか使い出すより早いだろうが…しきい値次第か。
そのしきい値を“保持しているエントリー数”が超えたらテーブル(配列)を2倍に拡張しているが、全リストを作り直しているので、数が増えると時間は結構かかりそうだ。
ファイナライザー。[2008-01-19]
オブジェクトがどこからも使用(参照)されなくなって、GCにより破棄される時に呼ばれるメソッド。
デフォルト(Objectクラス内)では何も実装されていない。
ファイナライザーはGC(ガーベッジコレクション)によって破棄される時に呼ばれるので、GCが動かない限り呼ばれない。
アプリケーション(javaコマンド)が終了する際にはGCは呼ばれないようなので、オブジェクトのライフサイクルとしては、コンストラクターは必ず呼ばれるけれどもファイナライザーが必ず呼ばれるとは限らない。
class Fin { int n; Fin(int init) { n = init; System.out.println("Fin#init" + n); } @Override protected void finalize() throws Throwable { System.out.println("Fin#end" + n); super.finalize(); } }
new Fin(1); System.gc(); //GCを強制実行する Thread.sleep(20); new Fin(2);
sleep(20)あり | sleep()なし |
Fin#init1 Fin#end1 Fin#init2 |
Fin#init1 Fin#init2 Fin#end1 |
※GCは別スレッドで実行されるらしいので、sleep()を入れないと実行順は思ったとおりにならなかった。
いずれにしても、最後に作ったオブジェクト(Fin(2)
)のファイナライザーは呼ばれていない。
したがって、finalize()の実装は あくまで「念の為」の予備的な位置づけにすべきだろう。
(main()メソッド内でfinallyを使って最後に強制GCを実行するようにしてみたとしても、System.exit()を使われてしまうとfinallyすら実行されないので完璧とは言えない)
finalize()は様々なスレッドから呼ばれる可能性があるので、マルチスレッドで動いても大丈夫なようにしておかなければならないらしい。[2008-09-14]
(Java言語仕様第3版12.6章『クラス・インスタンスのファイナライズ』)
JDK1.7でObjectsクラスが追加になった。[2013-06-29]
Objectクラスに関するstaticメソッドが集まっている感じ。
メソッド | 説明 |
---|---|
isNull(Object obj) |
nullのときtrueを返す。JDK1.8以降。[2014-03-19] |
nonNull(Object obj) |
null以外のときtrueを返す。JDK1.8以降。[2014-03-19] |
requireNonNullElse(T obj, T defaultObj) |
objがnull以外のときobj、nullのときdefaultObjを返す。Java9以降。[2017-09-23] |
requireNonNullElseGet(T obj,
Supplier<? extends T>
supplier) |
objがnull以外のときobj、nullのときsupplierの結果を返す。Java9以降。[2017-09-23] |
equals(Object a, Object b) |
nullも比較できるequals()。 双方がnullならtrue、片方がnullで他方がnull以外ならfalse。 それ以外はObject#equals()と同じ。 |
deepEquals(Object a, Object b) |
nullも比較できるdeepEquals()。 双方がnullならtrue、片方がnullで他方がnull以外ならfalse。 それ以外はほぼArrays.deepEquals()と同じ。 (Arrays.deepEquals()は引数がObject配列だが、Objects.deepEquals()は配列以外でも渡せる) |
compare(T a, T b, Comparator<? super T> c) |
aとbの双方がnullだったら0を返す。 それ以外は普通にComparator#compare(a,b)を呼び出す。 (したがって、aかbのどちらがnullだった場合の挙動は、Comparator次第。 何も考慮されていないNPEが発生する可能性がある。 Java8ならnullsFirst()やnullsLast()を使うと良いかも。[2015-09-05]) |
hashCode(Object o) |
nullにも対応しているhashCode()。 引数がnullなら0、それ以外ならObject#hashCode()を返す。 |
hash (Object... values) |
引数を使ってハッシュ値を算出する。 Arrays.hashCode(values)と同じ。 |
toString(Object obj) |
String.valueOf(obj)と同じ。 (引数がnullなら "null" 、それ以外はobj.toString()の結果を返す) |
toString(Object obj, String nullDefault) |
objがnullならnullDefault、それ以外はobj.toString()の結果を返す。 |
toIdentityString(Object obj) |
クラス名+"@"+identityHashCodeという文字列を返す。Java19以降。[2022-09-21] |
requireNonNull(T obj) |
nullチェックを行い、nullならNullPointerExceptionを発生させ、null以外ならobjを返す。 返す例外がAssertErrorとかIllegalArgumentExceptionなら使い道があるが、NPEなのでイマイチ。 |
requireNonNull(T obj,
Supplier<String>
messageSupplier) |
“NPEを発生させる際のメッセージを返す”インターフェースを渡す形式。JDK1.8以降。[2014-03-19]T value = Objects.
|
checkIndex(int
index, int length) |
0≦index<lengthのときindexを返す。それ以外の場合はIndexOutOfBoundsExceptionを発生させる。Java9以降。[2017-09-23] |
checkFromToIndex(int fromIndex, int toIndex, int length) |
0≦fromIndex≦toIndex≦lengthのときfromIndexを返す。それ以外の場合はIndexOutOfBoundsExceptionを発生させる。Java9以降。[2017-09-23] |
checkFromIndexSize(int fromIndex, int size, int length) |
0≦fromIndexかつfromIndex+size≦lengthのときfromIndexを返す。それ以外の場合はIndexOutOfBoundsExceptionを発生させる。Java9以降。[2017-09-23] |