JavaRush /Java блог /Random /Отличия конструкторов от обычных методов.
fog
18 уровень

Отличия конструкторов от обычных методов.

Статья из группы Random
Конструктор - это специальный метод, который предназначается для первичной установки значений полей объекта. На первый взгляд конструкторы объектов не сильно отличаются от обычных методов объекта. И действительно внутри конструктора мы можем делать всё то же, что и в обычных методах объекта: выводить текст в консоль, обращаться ко всем полям и методам нового объекта, выбрасывать исключения и так далее. Так же как и обычные методы, конструкторы могут иметь аргументы. Так же как и перегруженных методов, конструкторов может быть несколько с различными сигнатурами. Так же как и дженерик-методы конструкторы могут быть параметризованы переменными типов. Даже если мы заглянем в байт-код генерируемый компилятором, в месте где должен быть вызов конструктора, мы обнаружим обращение к некоторому методу с именем <init> вызов которого не отличается от вызова других методов объекта. А найдя байт-код этого метода мы обнаружим, что он и содержит результат компиляции нашего конструктора. Кажется что отличий от обычных методов не много, но они есть, и довольно существенные. Для начала давайте разберёмся, а для чего нам собственно нужны конструкторы? Для хранения и обработки каких либо данных, будь то примитивные типы, массивы, или объекты нам необходим некоторый объём памяти. Это могут быть регистры процессора, место на стеке, либо кусочек пространства, выделенный в секции данных процесса, либо в динамически размещаемой части памяти (куче). Во многих языках программирования, в целях ускорения, при запросе программой нового кусочка памяти, память отдавалась программе не отчищенной, и могла содержать произвольные данные, которые были сохранены в этой ячейки памяти ранее. Подготовка и запись в такой кусок памяти необходимых значений, чтобы в итоге там оказался какая-либо осмысленная структура данных, ложилась целиком на плечи программиста. Вполне естественно программисты хотели облегчить себе жизнь и писали подпрограммы для инициализации (то есть установки начальных значений) для часто используемых структур данных. Такие подпрограммы применялись практически постоянно, так что создатели языка Java, решили сделать подобные подпрограммы инициализации обязательными для вызова при создании объектов, и назвали их конструкторами. Когда в Java создаётся новый объект происходит следующее: Сначала менеджер памяти Java выделяет объём памяти необходимый для размещения объекта. При этом учитываются не только поля объявленные непосредственно в классе создаваемого объекта, но так же поля объявленные во всех предках этого класса. Дополнительно в этот объем включается пространство для размещения структур которые используются Java-машиной для внутренних нужд. Все поля такой "заготовки" автоматически устанавливаются в дефолтные значения - null для ссылочных типов, 0 для чисел и false для boolean. После этого, автоматически вызывается конструктор класса, задача которого установить начальные значения полей объекта. Если в обычном методе первый оператор может быть любым, то у конструктора гораздо меньше свободы. Первым оператором конструктора должен быть либо явный вызов другого конструктора того же класса, либо явный или неявный вызов конструктора родительского класса. Явный вызов конструкторов того же класса осуществляется с помощью ключевого слова this за которым следует набор аргументов заключённый в скобки. Явный вызов конструктора родительского класса производится точно так же, но при этом используется ключевое слово super. В аргументах явного вызова конструктора того же, либо родительского класса нельзя обращаться к полям и методам объекта, равно как и использовать ключевые слова this и super, так как явный вызов конструктора вводит статический контекст. Для неявного вызова конструктора родительского класса писать ничего не надо, но при этом неявно вызывается конструктор по-умолчанию, который должен существовать и быть видимым для текущего класса. При этом, следует иметь ввиду, что если цепочка вызова родительских конструкторов прервётся до того как конструктор класса Object, находящийся на вершине цепочки, успешно завершит свою работу, то объект не будет финализируемым, то есть метод finalize() такого объекта никогда вызван не будет. После завершения работы конструктора родительского класса, управление неявно передаётся на блоки инициализаторов экземпляра и инициализаторы полей экземпляра текущего класса. Инициализаторы исполняются в том порядке, в каком они встречаются в тексте программы. Лишь после завершения работы инициализаторов управление передаётся оставшейся части конструктора. Остальные особенности конструкторов касаются модели памяти Java. Если класс, либо один из его предков, переопределяет метод finalize(), то завершение работы конструктора случится до (happens-before) запуска метода finalize(). Если какой-либо поток увидел ссылку на объект после завершения работы конструктора, то гарантируется что этот поток увидит корректно инициализированные final-поля объекта, инициализация которых произошла до завершения работы конструктора.
Комментарии (4)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ant Уровень 29
16 ноября 2022
Читаю "Философия Java" главу по загрузчику классов. Брюс пишет, что конструктор это статический член класса. Пытался найти что либо по этому поводу, но ничего не нашел. Можешь что-нибудь сказать, на этот счёт?)
Павел Уровень 11
7 июля 2022
Интересная тема, даже не думал про конструкторы с дженериками) Я бы примеры кода для наглядности добавил