Класс Object, из пакета java.lang, находится на вершине дерева иерархии классов. Каждый класс является потомком, прямым или косвенным, класса Object. Любой использованный или написанный нами класс наследует методы этого класса. Благодаря этому мы можем сослаться на объект, тип которого нам не известен.
Исходный код данного класса из OpenJDK можно посмотреть тут.
Разберемся по порядку с методами этого класса.
|
|
private static native void registerNatives(); |
Обычно, для того, чтобы JVM нашла нативные функции, они должны быть названы определенным образом. Например для java.lang.Object.registerNatives, соответствующая функция на С должна называться Java_java_lang_Object_registerNatives. Используя JNI функцию registerNatives можно именовать наши С функции как захочется.
Код на С :
|
|
static JNINativeMethod methods[] = { {"hashCode", "()I", (void *)&JVM_IHashCode}, {"wait", "(J)V", (void *)&JVM_MonitorWait}, {"notify", "()V", (void *)&JVM_MonitorNotify}, {"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll}, {"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone}, }; JNIEXPORT void JNICALL Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) { (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])); } |
Следует заметить, что Object.getClass нет в этом списке. Это значит что он будет вызываться со стандартным именем Java_java_lang_Object_getClass.
Регистрация нативных функций также полезна если мы встраиваем Java в программу на С и хотим ссылаться на функции своего приложения, а не на функции из общей библиотеки или хотим привязать нативные методы к другим функциям на С.
Далее идет статичный конструктор.
|
|
static { registerNatives(); } |
|
|
public final native Class<?> getClass(); |
Этот метод нельзя переопределить.
Возвращает класс объекта, методы которого можно использовать для получения информации об этом классе, например его имя (getSimpleName()), его суперкласс (getSuperClass()), и реализуемых интерфейсах (getInterfaces()).
Например, следующий метод выведет имя класса объекта:
|
|
void printClassName(Object obj) { System.out.println("The object's" + " class is " + obj.getClass().getSimpleName()); } |
|
|
public native int hashCode(); |
Возвращает хэш-код объекта. Этот метод поддерживается в интересах хэш-таблиц (таких как HashMap, например).
По умолчанию, если два объекта равны, их хэш-коды должны быть также равны. Если переопределить метод equals(), реализация метода hashCode() у Object перестает быть корректной. Поэтому, если мы переопределяем метод equals(), мы должны переопределить метод hashCode().
В доках Oracle написано: нативный метод реализован так, что возвращает адрес объекта в памяти.
В openJDK 7 для HotSpot JVM он основан на генераторе случайных чисел. Смотреть метод get_next_hash.
Но также можно выбрать другой режим, в том числе и адрес в памяти.
С помощью ключа командной строки -XX:hashCode=n (где n от 0 до 5).
0 – Park-Miller RNG (по умолчанию)
1 – f(адрес, глобальное_состояние)
2 – константа 1
3 – последовательный счетчик
4 – адрес объекта
5 – Thread-local Xorshift
|
|
public boolean equals(Object obj) { return (this == obj); } |
Метод сравнивает два объекта и возвращает true, если они равны. Метод equals(), реализованный в классе Object использует тождественный оператор (==), чтобы определить, являются ли два объекта равными. Для примитивных типов данных это корректно. Для объектов метод проверяет только то, ссылки указывают на один и тот-же объект.
Всегда надо переопределять метод equals(), если сравнение тождественным оператором не подходит для объекта.
Переопределив метод equals() обязывает разработчика переопределить метод hashCode().
|
|
protected native Object clone() throws CloneNotSupportedException; |
Если класс или один из его суперклассов реализуют интерфейс Cloneable, то можно использовать метод clone() для создания копии существующего объекта.
Реализация этого метода у Object проверяет, реализует ли объект у которого clone() был вызван интерфейс Cloneable(). Если нет, то выбрасывается CloneNotSupportedException. Если да, то создается объект того же класса, как исходный и его поля инициализируется теми же значениями.
Для некоторых классов стандартное поведение метода clone() работает отлично. Но если объект содержит ссылку на внешний объект, то возможно потребуется переопределить поведение метода clone(). В противном случае изменение внешнего объекта в одном классе приведет к его изменению в другом (полученном клонированием) классе.
|
|
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } |
Возвращает строковое представление объекта. При переопределении метода рекомендуется возвращать краткий но информативный результат. Для класса Object метод toString() возвращает строку, содержащую имя класса, экземпляром которого является объект и шестнадцатеричное представление хэщ-кода объекта, разделенные символом “@”.
Методы notify(), notifyAll(), wait() объекта участвуют в синхронизации независимо запущенных потоков в программе.
|
|
public final native void notify(); |
|
|
public final native void notifyAll(); |
|
|
public final native void wait(long timeout) throws InterruptedException; |
|
|
public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && timeout == 0)) { timeout++; } wait(timeout); } |
|
|
public final void wait() throws InterruptedException { wait(0); } |
|
|
protected void finalize() throws Throwable { } |
Класс Object предоставляет callback метод finalize(), который может вызываться для объекта когда он становится “мусором”. Реализация finalize() у Object не делает ничего. Его можно переопределить для очистки, нарпимер свободных ресурсов.
Метод finalize() может быть вызван системой автоматически, но тот момент когда он будет вызван никак не определен. По этому нельзя пологаться на этот метод, что-бы сделать свою чистку. Следует заметить, что если объект доступен для сборки и в нем переопределен метод finalize(), то он не вызовется сразу, а поместится в очередь, которая обрабатывается специально созданным для этого потоком.
Есть вероятность, что этот метод не будет вызван совсем. Это может произойти в момент, когда объект уже станет доступным для сборщика мусора и программа завершит свою работу.
Краткая выдержка про использование finalize (Джошуа Блох):
1. finalize() можно использовать только в двух случаях:
1.1. Проверка/подчистка ресурсов с логированием
1.2. При работе с нативным кодом, который не критичен к утечке ресурсов
2. finalize() замедляет работу GC по очистке объекта в 430 раз
3. finalize() может быть не вызван
Источник