Java : Object (オブジェクト) - API使用例
Object (Java SE 17 & JDK 17) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。
概要
Objectクラスは、暗黙的にすべてのクラスのベースとなります。
つまり、すべてのクラスは Object クラスを継承していることになります。
配列 も Object です。
プリミティブ型(int, float, booleanなど) は Object ではありません。
final var str = "abcd";
System.out.println(str.equals("abcd")); // true
System.out.println(str.equals("XYZ")); // false
System.out.println(str.hashCode()); // 2987074
// 配列は Arrays.toStringを使うと、見やすい文字列となります。
final int[] array = {1, 2, 3, 4};
System.out.println(array.toString()); // [I@34a1547b
System.out.println(Arrays.toString(array)); // [1, 2, 3, 4]
// プリミティブ型は Object ではないので、hashCode や equals メソッドは使えません。
final int num = 123;
// コンパイルエラー
num.hashCode();
num.equals(123);
wait と notify の注意点
Objectクラスには、現在のスレッドを待機させる wait と復帰するための notify メソッドがあります。
wait によるスレッドの待機は、通常…
- notify の呼び出し
- スレッドの中断(interrupt)
- タイムアウト
により復帰します。
しかし、上記の条件以外でも復帰することがあるのでご注意ください。
それはスプリアス・ウェイクアップと呼ばれていて、まれにしか発生しない少し面倒な仕様です。
詳細は、上記の wait メソッドの仕様をご確認ください。
代わりに CountDownLatch を使うことも検討してみましょう。
もう少し詳細が知りたいかたは「Object.waitの注意点」の記事もご参照ください。
コンストラクタ
Object ()
final var obj = new Object();
System.out.println(obj); // java.lang.Object@84e9fc3
System.out.println(obj.hashCode()); // 139370435
System.out.println(obj.getClass()); // class java.lang.Object
メソッド
protected Object clone ()
独自クラスで clone を使いたい場合は、Cloneableインタフェースの実装が必要です。
使用例については、Cloneableのリンク先の記事をご参照ください。
配列は Cloneable が実装されていると見なされるため clone が可能です。
final String[] array = {"aa", "bb", "cc"};
final String[] cloned = array.clone();
System.out.println(array != cloned); // true
System.out.println(Arrays.toString(array)); // [aa, bb, cc]
System.out.println(Arrays.toString(cloned)); // [aa, bb, cc]
// 要素のcloneは行われません。(シャローコピー)
System.out.println(array[0] == cloned[0]); // true
System.out.println(array[1] == cloned[1]); // true
System.out.println(array[2] == cloned[2]); // true
boolean equals (Object obj)
equals メソッドは、hashCode()メソッドと密接に関連します。
詳細については「Object.hashCode (ハッシュ・コード) とは」の記事でまとめていますので、そちらもご参照ください。
final var obj1 = new Object();
final var obj2 = new Object();
System.out.println(obj1.equals(obj2)); // false
System.out.println(obj1.hashCode()); // 361268035
System.out.println(obj2.hashCode()); // 871160466
// インスタンスを別にするために、意図的に String を new しています。
final var s1 = new String("aaa");
final var s2 = new String("aaa");
final var s3 = new String("XXX");
System.out.println(s1 != s2); // true
System.out.println(s1 != s3); // true
System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // false
System.out.println(s1.hashCode()); // 96321
System.out.println(s2.hashCode()); // 96321
System.out.println(s3.hashCode()); // 87384
配列の equals は、単純にインスタンスが違うかどうかを判定します。
もし配列の要素も考慮して比較したい場合は、Arrays.equals が使えます。
配列の hashCode についても同様で、インスタンスそのもののハッシュ・コードを返します。
もし要素も考慮したハッシュ・コードを取得したい場合は、Arrays.hashCode が使えます。
final int[] array1 = {1, 2, 3, 4};
final int[] array2 = {1, 2, 3, 4};
System.out.println(array1 != array2); // true
System.out.println(array1.equals(array2)); // false
System.out.println(Arrays.equals(array1, array2)); // true
System.out.println(array1.hashCode()); // 2113748097
System.out.println(array2.hashCode()); // 629454893
System.out.println(Arrays.hashCode(array1)); // 955331
System.out.println(Arrays.hashCode(array2)); // 955331
protected void finalize ()
非推奨です。
final Class<?> getClass ()
final var obj = new Object();
System.out.println(obj.getClass()); // class java.lang.Object
final var str = "abcd";
System.out.println(str.getClass()); // class java.lang.String
final int[] array = {1, 2, 3};
System.out.println(array.getClass()); // class [I
final var localTime = LocalTime.of(12, 30);
System.out.println(localTime.getClass()); // class java.time.LocalTime
int hashCode ()
使用例や注意点などは、equals(Object obj) にまとめて記載しました。
そちらのメソッドをご参照ください。
final void notify ()
wait と notify メソッドには注意すべき仕様があります。
概要の wait と notify の注意点に記載しましたので、そちらもご参照ください。
下記例は、スプリアス・ウェイクアップを考慮しない簡易的なものです。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newSingleThreadExecutor();
try {
final var obj = new Object();
final Callable<Void> task = () -> {
try {
System.out.println("task start : %f sec.".formatted(elapsedSec.getAsDouble()));
synchronized (obj) {
obj.wait();
}
} finally {
System.out.println("task end : %f sec.".formatted(elapsedSec.getAsDouble()));
}
return null;
};
executorService.submit(task);
Thread.sleep(2000);
synchronized (obj) {
obj.notify();
}
Thread.sleep(10);
} finally {
executorService.shutdown();
System.out.println("--- shutdown --- : %f sec.".formatted(elapsedSec.getAsDouble()));
}
final var ret = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("termination %b : %f sec.".formatted(ret, elapsedSec.getAsDouble()));
// 結果
// ↓
//task start : 0.002282 sec.
//task end : 2.009446 sec.
//--- shutdown --- : 2.025202 sec.
//termination true : 2.025536 sec.
複数のスレッドで wait していると、notifyではどれか1つが復帰します。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newFixedThreadPool(3);
try {
final var obj = new Object();
final Callable<Void> task = () -> {
try {
System.out.println("task start : %f sec.".formatted(elapsedSec.getAsDouble()));
synchronized (obj) {
obj.wait();
}
} finally {
System.out.println("task end : %f sec.".formatted(elapsedSec.getAsDouble()));
}
return null;
};
for (int i = 0; i < 3; i++) {
executorService.submit(task);
}
// 1つずつ復帰します。
for (int i = 0; i < 3; i++) {
Thread.sleep(2000);
synchronized (obj) {
obj.notify();
}
}
Thread.sleep(10);
} finally {
executorService.shutdown();
System.out.println("--- shutdown --- : %f sec.".formatted(elapsedSec.getAsDouble()));
}
final var ret = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("termination %b : %f sec.".formatted(ret, elapsedSec.getAsDouble()));
// 結果
// ↓
//task start : 0.002332 sec.
//task start : 0.002360 sec.
//task start : 0.002291 sec.
//task end : 2.008064 sec.
//task end : 4.018629 sec.
//task end : 6.026974 sec.
//--- shutdown --- : 6.043236 sec.
//termination true : 6.043966 sec.
final void notifyAll ()
wait と notify メソッドには注意すべき仕様があります。
概要の wait と notify の注意点に記載しましたので、そちらもご参照ください。
下記例は、スプリアス・ウェイクアップを考慮しない簡易的なものです。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newFixedThreadPool(3);
try {
final var obj = new Object();
final Callable<Void> task = () -> {
try {
System.out.println("task start : %f sec.".formatted(elapsedSec.getAsDouble()));
synchronized (obj) {
obj.wait();
}
} finally {
System.out.println("task end : %f sec.".formatted(elapsedSec.getAsDouble()));
}
return null;
};
for (int i = 0; i < 3; i++) {
executorService.submit(task);
}
Thread.sleep(2000);
synchronized (obj) {
obj.notifyAll();
}
Thread.sleep(10);
} finally {
executorService.shutdown();
System.out.println("--- shutdown --- : %f sec.".formatted(elapsedSec.getAsDouble()));
}
final var ret = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("termination %b : %f sec.".formatted(ret, elapsedSec.getAsDouble()));
// 結果
// ↓
//task start : 0.001954 sec.
//task start : 0.001982 sec.
//task start : 0.001954 sec.
//task end : 2.006767 sec.
//task end : 2.006766 sec.
//task end : 2.006766 sec.
//--- shutdown --- : 2.031935 sec.
//termination true : 2.032380 sec.
String toString ()
final var obj = new Object();
System.out.println(obj.toString()); // java.lang.Object@1012015a
final var str = "abcd";
System.out.println(str.toString()); // abcd
final var localTime = LocalTime.of(12, 30);
System.out.println(localTime.toString()); // 12:30
final var path = Path.of("R:", "java-work", "sample.txt");
System.out.println(path.toString()); // R:\java-work\sample.txt
// 配列は Arrays.toStringを使うと、見やすい文字列となります。
final int[] array = {1, 2, 3, 4};
System.out.println(array.toString()); // [I@34a1547b
System.out.println(Arrays.toString(array)); // [1, 2, 3, 4]
final void wait ()
wait と notify メソッドには注意すべき仕様があります。
概要の wait と notify の注意点に記載しましたので、そちらもご参照ください。
下記例は、スプリアス・ウェイクアップを考慮しない簡易的なものです。
notifyで復帰する例です。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newSingleThreadExecutor();
try {
final var obj = new Object();
final Callable<Void> task = () -> {
try {
System.out.println("task start : %f sec.".formatted(elapsedSec.getAsDouble()));
synchronized (obj) {
obj.wait();
}
} finally {
System.out.println("task end : %f sec.".formatted(elapsedSec.getAsDouble()));
}
return null;
};
executorService.submit(task);
Thread.sleep(2000);
synchronized (obj) {
obj.notify();
}
Thread.sleep(10);
} finally {
executorService.shutdown();
System.out.println("--- shutdown --- : %f sec.".formatted(elapsedSec.getAsDouble()));
}
final var ret = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("termination %b : %f sec.".formatted(ret, elapsedSec.getAsDouble()));
// 結果
// ↓
//task start : 0.002232 sec.
//task end : 2.006018 sec.
//--- shutdown --- : 2.021639 sec.
//termination true : 2.022081 sec.
割り込みが発生する例です。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newSingleThreadExecutor();
try {
final var obj = new Object();
final Callable<Void> task = () -> {
try {
System.out.println("task start : %f sec.".formatted(elapsedSec.getAsDouble()));
synchronized (obj) {
obj.wait();
}
} catch (InterruptedException e) {
System.out.println("InterruptedException! : %f sec.".formatted(elapsedSec.getAsDouble()));
} finally {
System.out.println("task end : %f sec.".formatted(elapsedSec.getAsDouble()));
}
return null;
};
final var future = executorService.submit(task);
Thread.sleep(2000);
future.cancel(true);
Thread.sleep(10);
} finally {
executorService.shutdown();
System.out.println("--- shutdown --- : %f sec.".formatted(elapsedSec.getAsDouble()));
}
final var ret = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("termination %b : %f sec.".formatted(ret, elapsedSec.getAsDouble()));
// 結果
// ↓
//task start : 0.002510 sec.
//InterruptedException! : 2.012552 sec.
//task end : 2.012789 sec.
//--- shutdown --- : 2.027705 sec.
//termination true : 2.028265 sec.
final void wait (long timeoutMillis)
このメソッドは wait(timeoutMillis, 0) を呼び出したときと同等となります。
API使用例はそちらをご参照ください。
final void wait (long timeoutMillis, int nanos)
wait と notify メソッドには注意すべき仕様があります。
概要の wait と notify の注意点に記載しましたので、そちらもご参照ください。
タイムアウトが発生しないパターンは、wait()と同じになります。
そちらのメソッドもご参照ください。
タイムアウトが発生する例です。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newSingleThreadExecutor();
try {
final var obj = new Object();
final Callable<Void> task = () -> {
try {
System.out.println("task start : %f sec.".formatted(elapsedSec.getAsDouble()));
synchronized (obj) {
obj.wait(2000, 100);
}
} catch (InterruptedException e) {
System.out.println("InterruptedException! : %f sec.".formatted(elapsedSec.getAsDouble()));
} finally {
System.out.println("task end : %f sec.".formatted(elapsedSec.getAsDouble()));
}
return null;
};
executorService.submit(task);
Thread.sleep(5000);
synchronized (obj) {
obj.notify();
}
Thread.sleep(10);
} finally {
executorService.shutdown();
System.out.println("--- shutdown --- : %f sec.".formatted(elapsedSec.getAsDouble()));
}
final var ret = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("termination %b : %f sec.".formatted(ret, elapsedSec.getAsDouble()));
// 結果
// ↓
//task start : 0.002279 sec.
//task end : 2.004025 sec.
//--- shutdown --- : 5.029167 sec.
//termination true : 5.029502 sec.