JDK: подноготная стандартной библиотеки. Часть 1. Java Core: java.lang.Object (original) (raw)

Previous Entry Flag Next Entry

JDK: подноготная стандартной библиотеки. Часть 1. Java Core: java.lang.Object

JDK - как много в этом звуке для сердца джавера слилось!
Классы Java
Действительно ли так много? Да, 19184 класса в моей JDK 1.6.0 UPDATE 3. Большая часть из них закрыта, но и оставшихся нескольких тысяч открытых классов больше, чем достаточно. Но ведь даже сертифицированному Java программисту нелегко на память перечислить хотя бы полсотни из них!

Почему мы так мало знаем о классах JDK? Причины у всех свои, но думаю, что основным стоппером в использовании малоизвестных классов является незнание того, как именно они могут помочь более эффективно решить поставленную задачу. Обзору особенностей популярных и возможностей менее популярных классов JDK будет посвящен этот цикл статей.

С чего начнем? Конечно, с java core!

java.lang.*

Этот пакет представлен классами обертками, основными классами исключений, классами Object, Class, Math, System, Runtime, Thread, ThreadLocal, String, StringBuffer, StringBuilder.

Все данные в Java делятся на объекты и примитивные типы. Примитивные типы нам неинтересны - их знает даже программист на BASIC. А все объекты являются наследниками java.lang.Object. Если не знаешь, что такое наследование, то тебе скорее всего сюда.

java.lang.Object обладает тем поведением, которым должен обладать любой объект в Java. Что это за поведение?

public boolean equals(Object obj);

Метод выполняет проверку на эквивалентность другому объекту. Реализация по-умолчанию проверяет совпадение ссылок на объект - это работает очень быстро и обладает адекватной семантикой. Твоя реализация может:

Дальше курсив - это возможная реплика абстрактного читателя.
- А зачем это вообще надо? Я вот никогда не использую этот метод. У себя в программе вообще могу написать отдельную функцию, сравнивающую объекты или определить свой метод myequals внутри объекта. Он будет так же хорош для моей задачи.
- equals активно используется контейнерами стандартной библиотеки. Не определил у себя в объекте нормальный equals - лишился возможности использовать объект в контейнере.

public int hashCode();

Метод создает хэш код объекта. По умолчанию хэш код генерируется из адреса объекта. Этот метод нужен только для использования внутри хэш таблиц (java.util.HashSet) . Зачем ради каких то хэш таблиц заводить целый метод для всех(!) объектов языка? Затем, что хэш таблицы и карты используются в языке повсеместно. А если ты еще не используешь их, то мы идём за тобой!

Правило: Если твои объекты совпадают по equals, то у них обязан быть одинаковый hashCode. Если объекты различаются по equals, то рекомендуется сделать для них разный hashCode. Подробнее расскажу в теме о контейнерах.

Правило: всегда переопределяйте equals одновременно с hashCode, если собираетесь использовать объект вместе с хэш таблицами!

protected void finalize()

Очень странный метод, который по стандарту может вызваться при разрушении объекта. А может и не вызываться. Раз он такой непостоянный, то не стоит вообще на него обращать внимания. И даже не пытайтесь закрывать в нем открытые ранее потоки/файлы/сокеты! И не стоит путать его с деструкторами объекта из С++.

Совет: забей на finalize

public final Class getClass();

Метод получает класс объекта. Логично, что его нельзя переопределить, а тем, кому всё же хочется, следует руки поотрывать.

Совет: если вам понадобился getClass(), то, возможно, лучше модифицировать эту часть кода с использованием полиморфизма.

protected native Object clone() throws CloneNotSupportedException;

Метод возвращающий поверхностную копию(en) данного объекта. Используется как конструктор копирования в с++.
По-умолчанию является наследуемым (protected) и в случае, если объект не реализует интерфейс Coneable, кидает исключение CloneNotSupportedException. Вопрос того, почему clone() является protected, да еще и кидает исключения, донимает лучшие умы тысячелетия. Моё объяснение этого феномена таково: clone является protected по той причине, что разработчики jdk дают нам возможность использовать функционал поведения clone по-умолчанию, но при этом не хотят чтобы, опасная и неочевидная семантика поверхностного копирования была по-умолчанию доступна всем пользователям объекта.

У clone есть два серьезных недостатка:
1. clone возвращает Object. Это даёт повод возвращать объекты другого типа. Но за такие вещи нужно наказывать двухнедельными принудительными работами на языке Cobol.
2. Если ты знаешь, что некий наследник класса Object можно клонировать, то ты не вызовишь метод clone иначе, как через reflection api. Например, в твоей нетипизированной коллекции находятся различные объекты. Некоторые из них можно клонировать, т. е. они реализуют интерфейс Cloneable. К какому общему базовому интерфейсу нужно выполнить кастомизацию типа, чтобы вызвать метод clone? По-умолчанию нет такого интерфейса. Сам Cloneable не содержит внутри себя объявления открытого метода clone. Cloneable - пустой интерфейс и по сути является всего лишь меткой.

Более подробно про метод clone.

Совет: хотите заниматься клонированием? Заведите нормальный интерфейс Clone:

interface Clone extends Cloneable {
public T clone() throws CloneNotSupportedException;
}static class Dolly implements Clone {
private String DNA;public Dolly(String s) {
DNA = s;
}public Dolly clone() throws CloneNotSupportedException {
return (Dolly) super.clone();
}
}public static void main(String[] args) throws IOException, CloneNotSupportedException {
Dolly dolly = new Dolly("TTAGGCGGCATTACGG");
ArrayList myObjects = new ArrayList();
myObjects.add(new Integer(123));
myObjects.add(dolly);
Set clones = new HashSet();
for (Object o : myObjects) {
if (o instanceof Clone) {
clones.add(((Clone) o).clone());
}
}
// здесь в clones находится только один объект Dolly
}

Остались методы notify, notifyAll, wait, которые используются для организации многопоточности. О них я постараюсь написать в статье про потоки.

Также есть метод toString, не представляющий никакого интереса для любознательных кроме того, что позволяет выполнять приведение типа к String автоматически:

System.out.println("Dolly's value is "+new Dolly());

Все эти методы пришли к нам из далекого прошлого становления языка Java. Не судите их слишком строго.

Полная версия статьи в блоге Java мастера