Java : Object (オブジェクト) - API使用例

Object (Java SE 17 & JDK 17) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。


概要

Objectクラスは、クラス階層のルートです。 すべてのクラスは、スーパー・クラスとしてObjectを持ちます。 配列を含むすべてのオブジェクトは、このクラスのメソッドを実装します。

クラス構成

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);

waitnotify の注意点

Objectクラスには、現在のスレッドを待機させる wait と復帰するための notify メソッドがあります。

wait によるスレッドの待機は、通常…

  • notify の呼び出し
  • スレッドの中断(interrupt)
  • タイムアウト

により復帰します。

しかし、上記の条件以外でも復帰することがあるのでご注意ください。
それはスプリアス・ウェイクアップと呼ばれていて、まれにしか発生しない少し面倒な仕様です。

public final void wait​(long timeoutMillis, int nanos) throws InterruptedException
...
スレッドは通知、中断、またはタイムアウトすることなく起きることができます。いわゆる「スプリアス・ウェイクアップ」です。 スプリアス・ウェイクアップは、実際にはまれにしか発生しませんが、アプリケーションでは、スレッドが再開されることで発生する可能性がある条件をテストし、条件が満たされない場合は待機を続けて、スプリアス・ウェイクアップから保護しなければいけません。 次の例を参照してください。

詳細は、上記の 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 ()

このObjectの実行時クラスを返します。

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 ()

このオブジェクトのモニターで待機中のスレッドを1つ再開します。

waitnotify メソッドには注意すべき仕様があります。
概要の 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 ()

このオブジェクトのモニターで待機中のすべてのスレッドを再開します。

waitnotify メソッドには注意すべき仕様があります。
概要の 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 ()

現在のスレッドを起動するまで待ちます。通常、notifiedまたはinterruptedです。

waitnotify メソッドには注意すべき仕様があります。
概要の 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)

現在のスレッドは、通常、notifiedまたはinterruptedによって、または一定量のリアルタイムが経過するまで、起動するまで待機します。

このメソッドは wait(timeoutMillis, 0) を呼び出したときと同等となります。
API使用例はそちらをご参照ください。

final void wait (long timeoutMillis, int nanos)

現在のスレッドは、通常、notifiedまたはinterruptedによって、または一定量のリアルタイムが経過するまで、起動するまで待機します。

waitnotify メソッドには注意すべき仕様があります。
概要の 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.

関連記事

ページの先頭へ