Что такое класс object java
Класс Object
В Java есть специальный суперкласс Object и все классы являются его подклассами. Поэтому ссылочная переменная класса Object может ссылаться на объект любого другого класса. Так как массивы являются тоже классами, то переменная класса Object может ссылаться и на любой массив.
В таком виде объект обычно не используют. Чтобы с объектом что-то сделать, нужно выполнить приведение типов.
У класса есть несколько важных методов.
Методы getClass(), notify(), notifyAll(), wait() являются финальными и их нельзя переопределять.
Метод hashCode()
Для вычисления хеш-кода в классе String применяется следующий алгоритм.
У любого объекта имется хеш-код, определяемый по умолчанию, который вычисляется по адресу памяти, занимаемой объектом.
Значение хеш-кода возвращает целочисленное значение, в том числ и отрицательное.
Если в вашем классе переопределяется метод equals(), то следует переопределить и метод hashCode().
Метод toString()
Очень важный метод, возвращающий значение объекта в виде символьной строки.
Очень часто при использовании метода toString() для получения описания объекта можно получить набор бессмысленных символов, например, [I@421199e8. На самом деле в них есть смысл, доступный специалистом. Он сразу может сказать, что мы имеем дело с одномерным массивом (одна квадратная скобка), который имеет тип int (символ I). Остальные символы тоже что-то означают, но вам знать это не обязательно.
Если же вам нужно научное объяснение, то метод работает по следующему алгоритму (из документации).
Обычно принято переопределять метод, чтобы он выводил результат в читаемом виде.
Все методы класса Object, метод toString()
— Сегодня мы будем изучать класс Object.
Ты уже сталкивался с ним, и знаешь, что Object – базовый класс для всех классов. У него практически нет никаких данных, но есть несколько методов.
— А зачем ему методы? Разве кто-то создает его объекты?
— Посмотри на ситуацию с такой стороны – методы, которые есть у Object’а, есть вообще у всех классов. Т.е. разработчики Java отобрали несколько методов, которые, по их мнению, должны быть у всех классов и добавили их в класс Object.
А в сочетании с полиморфизмом – возможностью переопределять методы класса Object в классах-наследниках – это порождает очень мощный инструмент.
Давай посмотрим, что же это за методы:
Метод | Описание |
---|---|
Возвращает строковое представление объекта. | |
Пара методов, которые используются для сравнения объектов. | |
Возвращает специальный объект, который описывает текущий класс. | |
Методы для контроля доступа к объекту из различных нитей. Управление синхронизацией нитей. | |
Метод позволяет «освободить» родные не-Java ресурсы: закрыть файлы, потоки и т.д. | |
Метод позволяет клонировать объект: создает дубликат объекта. |
Методы можно разбить на 6 групп. С некоторыми ты уже знаком, а с остальными мы познакомимся в ближайших лекциях.
— Что-то не вижу тут ничего полезного.
— Амиго! Если бы эти методы были не важны, их бы не добавили абсолютно всем объектам! Так что советую тебе более внимательно разобраться – что это такое и зачем они нужны. Если тебе кажется, что они не важны, значит, ты что-то не понял или понял не правильно.
— Ок. Я буду внимательно слушать.
— Начнем с метода toString();
Этот метод позволяет получить текстовое описание любого объекта. Реализация его в классе Object очень простая:
getClass() и hashCode() – это тоже методы класса Object.
Вот стандартный результат вызова такого метода
— И в чем же польза такого описания?
— Из такого описания можно узнать класс объекта, у которого вызвали данный метод. А также можно различать объекты – разным объектам соответствуют разные цифры, идущие после символа @.
Но ценность данного метода в другом. Данный метод можно переопределить в любом классе и возвращать более нужное или более детальное описание объекта.
Но и это еще не все. Благодаря тому, что для каждого объекта можно получить его текстовое представление, в Java можно было реализовать поддержку «сложения» строк с объектами.
Вот смотри:
Код | Что происходит на самом деле |
---|
— Да, я постоянно этим пользуюсь. Особенно когда пишу программу или ищу в ней ошибки. Полезный метод
Объекты в Java
1. Объекты
Все в Java является объектом.
Вернее, очень мало чего в Java объектом не является. Например, примитивные типы. Но это скорее редкое исключение, чем правило.
Что же такое объект?
Объект — это сгруппированные вместе данные и методы для того, чтобы эти данные обрабатывать. Когда мы говорим о данных, имеем в виду переменные, конечно.
Про переменные объекта говорят, что это «данные объекта» или «состояние объекта».
Про методы объекта говорят: это «поведение объекта». Состояние объекта (переменные объекта) принято менять только с помощью методов того же объекта. Менять переменные объекта напрямую (не через методы объекта) считается дурным тоном.
У каждого объекта, как и у каждой переменной, есть тип. Этот тип определяется один раз при создании объекта и поменять его в дальнейшем нельзя. Типом объекта считается его класс.
У каждого объекта есть собственная копия переменных класса (полей класса). Если в классе была объявлена нестатическая переменная int a, и ваша программа во время работы создала 10 объектов этого класса, теперь в каждом объекте есть собственная переменная int a.
Взаимодействие с объектом
Самый удобный способ работы с объектом — сохранить ссылку на объект в переменную, и потом вызывать методы у этой переменной. Выглядит это для вас знакомо:
Где переменная — это переменная, которая хранит в себе ссылку на объект, а метод — это метод класса объекта.
Если вы хотите обратиться к полю (переменной) объекта, то тоже нужно использовать оператор точка :
Где переменная — это переменная, которая хранит в себе ссылку на объект, а поле — это переменная класса (поле объекта).
2. Оператор new
Где Класс — это имя класса для объявления переменной и имя класса создаваемого объекта. Переменная — это переменная, в которую сохраняется ссылка на созданный объект. А параметры — это параметры метода создания объекта.
Какие именно могут быть параметры у объекта, решают программисты, которые пишут класс объекта.
Вы уже создавали объекты ранее, и даже пользовались именно этой конструкцией. Надеюсь, вы не забыли?
Примеры создания объектов:
Код | Описание |
---|---|
Создает объект типа String | |
Создает объект типа Scanner | |
Создает объект типа int[] : контейнер на 10 элементов типа int |
Зри в корень: java.lang.Object
В Java в вершине иерархии классов лежит класс java.lang.Object. Лежит и лежит, долгое время я им совсем не интересовался.
На собеседованиях часто спрашивают, какие в нем есть методы, поэтому они как-то сами собой выучились. Пришло время посмотреть на этот класс более внимательно. Первый вопрос, который у меня возник, есть ли вообще в исходниках Java класс java.lang.Object. Он же ведь необычный, он вполне может быть жестко зашит в реализацию, как самый верхний.
Однако, такой класс есть и я приведу тут исходники java/lang/Object.java, опустив javadoc, и попытаюсь пролить свет на некоторые моменты связанные с реализацией jvm:
Что бы я хотел отметить в этом коде?
Всего в Object 11 публичных методов, 5 обычных и 6 с нативной реализацией.
Рассмотрим обычные методы, так как их код уже доступен.
По дефолту все объекты сравниваются на равенство ссылок. Мне, кстати, в своем время понравилась шутка про то, что для того, чтобы запутать C++ программистов указатели в Java названы ссылками.
toString тоже не содержит ничего необычного, кроме разве того, что hashCode() преобразуется в шестнадцатеричную строку. И если бы apangin не написал, что нынче как только нельзя посчитать hashCode, я бы подумал, что раньше Java программисты могли найти свой объект по hashCode, т.к. он был не чем иным как ссылкой. Те 32 битные времена для многих прошли, и теперь даже не знаю, есть ли смысл в toString() выводить hashCode.
Кроме того, что wait относится к примитивам обеспечивающим многопоточность, хочется отметить бесполезность параметра nanos.
В некоторых случаях он просто добавляет одну милисекунду. Интересно, это закладка на будущее или уже есть системы в которых у wait(long timeout, int nanos) другая реализация.
Завершает парад обычных методов в java.lang.Object:
Этот метод ничего не делает, и есть куча материалов о том, что следует избегать его использования finalize и Finalizer, смысл finalize.
Теперь посмотрим на на java/lang/Object.class Например, мне интересно что в нем указано в качестве супер класса. Находим в установленном jre или jdk rt.jar, распаковываем:
И видим, что в super class у него прописаны 00 00, интересно что будет, если руками создать class файл без супер класса.
Я взял Hello.class из моей предыдущей заметки.
Открыл его в vim и заменил содержание буфера на hex дамп vim.wikia.com/wiki/Hex_dump:
Поразился мощи vim редактора. Быстренько нашел байты для super_class. Напомню, они лежат согласно спецификации через 4 байта после окончания constant_pool. Конец constant_pool ищется по тегу строки 00 01 и последовательности не нулевых байтов, когда начинаются нули идут другие разделы constant_pool. Для других class файлов это может быть не так, но в моем случае сработало.
Возвращаемся обратно к бинарному виду:
Сохраняем изменения. Запускаем наше поправленное приложение:
Ошибка, да еще не какая-нибудь, а выброшенная из нативного метода во время загрузки классов. Пойдем разбираться, за одно может поймем как выбрасывать такие ошибки.
Нам нужны исходники jdk. Я выбрал OpenJDK для исследования. Будем качать их отсюда:
И хранить в Меркурии:
Надо еще запустить:
И подождать. Отлично, исходники скачались и можно искать нашу ошибку. Я делаю это grep-ом:
Открываем classFileParser.cpp и там на 3095 строчке:
Нас интересует вот эта часть:
check_property лежит в заголовочном файле classFileParser.hpp и выглядит так:
Я стал искать где выставляется _need_verify и за что отвечает. Оказалось в classFileParser.cpp есть вот такая строчка:
verify передается при вызове:
Этот метод вызывается во многих местах, но нас интересует в hotspot/src/share/vm/classfile/classLoader.cpp:
Как же устроен should_verify_for в hotspot/src/share/vm/classfile/verifier.cpp:
Так как в should_verify_class мы передаем false, смотрим BytecodeVerificationLocal в hotspot/src/share/vm/runtime/arguments.cpp:
Зарываясь дальше можно найти черную магию с макросами в hotspot/src/share/vm/runtime/globals_extension.hpp:
Значит _need_verify тоже false и в check_property вызывается assert_property(property, msg, index, CHECK) с параметрами false, «Invalid superclass index %u in class file %s», 0, CHECK_NULL.
Собственно, здесь и выбрасывается сообщение об ошибке. Теперь посмотрим на fatal(msg), чтобы узнать как это делается.
Хотя, на часть вопроса мы уже ответили. Нельзя сделать classfile в котором для поля super_class будет значение 0 и загружать его с помощью дефолтного ClassLoader.
Класс Object в Java
На вершине иерархии классов находится класс Object, который является суперклассом для всех классов. Ссылочная переменная типа Object может указывать на объект любого другого класса, на любой массив, так как массивы реализуются как классы. В классе Object определен набор методов, который наследуется всеми классами:
protected Object clone() – создает и возвращает копию вызывающего объекта;
boolean equals(Object ob) – предназначен для переопределения
в подклассах с выполнением общих соглашений о сравнении содержимого двух объектов;
Class getClass() – возвращает объект типа Class;
protected void finalize() – вызывается перед уничтожением объекта автоматическим сборщиком мусора (garbage collection);
int hashCode() – возвращает хэш-код объекта;
String toString() – возвращает представление объекта в виде строки.
Методы notify(), notifyAll() и wait() будут рассмотрены в главе «Потоки выполнения».
Если при создании класса предполагается проверка логической эквивалентности объектов, которая не выполнена в суперклассе, следует переопределить два метода: equals(Object ob) и hashCode(). Кроме того, переопределение этих методов необходимо, если логика приложения предусматривает использование элементов в коллекциях. Метод equals() при сравнении двух объектов возвращает истину, если содержимое объектов эквивалентно, и ложь – в противном случае. При переопределении метода equals() должны выполняться соглашения, предусмотренные спецификацией языка Java, а именно:
· рефлексивность – объект равен самому себе;
· транзитивность – если метод equals() возвращает значение true при сравнении объектов x и y, а также y и z, то и при сравнении x и z будет возвращено значение true;
· непротиворечивость – при многократном вызове метода для двух не подвергшихся изменению за это время объектов возвращаемое значение всегда должно быть одинаковым;
· ненулевая ссылка при сравнении с литералом null всегда возвращает значение false.
При создании информационных классов также рекомендуется переопределять методы hashCode() и toString(), чтобы адаптировать их действия для создаваемого типа.
Метод hashCode() переопределен, как правило, в каждом классе и возвращает число, являющееся уникальным идентификатором объекта, зависящим в большинстве случаев только от значения объекта. Его следует переопределять всегда, когда переопределен метод equals(). Метод hashCode() возвращает хэш-код объекта, вычисление которого управляется следующими соглашениями:
· во время работы приложения значение хэш-кода объекта не изменяется, если объект не был изменен;
· все одинаковые по содержанию объекты одного типа должны иметь одинаковые хэш-коды;
· различные по содержанию объекты одного типа могут иметь различные хэш-коды.
Один из способов создания правильного метода hashCode(), гарантирующий выполнение соглашений, приведен ниже, в примере # 10.
Метод toString() следует переопределять таким образом, чтобы кроме стандартной информации о пакете (опционально), в котором находится класс, и самого имени класса (опционально), он возвращал значения полей объекта, вызвавшего этот метод (то есть всю полезную информацию объекта), вместо хэш-кода, как это делается в классе Object. Метод toString() класса Object возвращает строку с описанием объекта в виде:
Метод вызывается автоматически, когда объект выводится методами println(), print() и некоторыми другими.
/* пример # 10 : переопределение методов equals(), hashCode, toString():
public class Student <
private String name;
public Student(int id, String name, int age)<