вторник, 16 июня 2020 г.

Понятие метода - конструктора в JAVA. Ключевое слово this

   Кроме обычных методов классы могут определять специальные методы, называемые конструкторами.
 
   Конструктор в Java - метод класса без типа (не static), который имеет точно такое же имя как имя класса и не возвращает никаких значений, вызываемый при инициализации (создании нового) объекта класса (каждый раз при создании нового объекта вызывается конструктор).
 
   (специальный блок кода, похожий на метод, предназначенный для инициализации объектов при их создании)

   Конструкторы выполняют инициализацию объекта (устанавливают изначальное значение инстанса сразу же при его создании).
   В конструкторе можно задать требования к входящей информации при созданиии объекта его класса.
 
 
   Конструктор (функция-конструктор) - это, по сути, шаблон для объектов класса, который предопределяет наличие и состав обязательных параметров (аргументов) у объекта (класса).

   (Java не позволит создать объект, если указанные для него аргументы отсутствуют).
 

               конструктор "медвежонок" инициализирует объект "медвежонок (побольше)"

   У каждого класса есть конструктор. Конструктор вызывается всегда, когда создаётся новый экземпляр класса.
 
   Если в классе не определено (не прописано) ни одного конструктора, то для этого класса компилятор Java автоматически (по умолчанию) создает конструктор без параметров (аргументов).
 
    Конструктор, созданный по умолчанию, срабатывает каждый раз при создании любого объекта любого класса.
      (Каждый раз, когда создается новый объект, будет вызываться как минимум один конструктор)

   Конструктор можно вызывать из другого конструктора (Java распознает какой конструктор вызывается по введённым для него аргументам).
 
   При создании для класса конструктора, конструктор по умолчанию исчезает из класса.
   При этом его аналог (конструктор без аргументов) для класса с конструктором не по умолчанию может быть создан отдельно, специально. (В этом случае станет возможным создание объектов как с предопределенным составом аргументом, так и без аргументов).


Базовые типы конструкторов в Java:
 
    1) Конструктор по умолчанию (default constructor)
    (невидимый конструктор, автоматически создаваемый компилятором)
 
    Конструктор по умолчанию создает компилятор, если программист не создал свой (Компилятор не сгенерирует конструктор по умолчанию, если программист реализовал свой конструктор).
 
    Поля экземпляра класса при таком конструкторе будут установлены значениями для полей по умолчанию:
    byte     0
    short     0
    int     0
    long     0L
    float     0.0f
    double     0.0d
    char     '\u0000'
    String (or any object)       null
    boolean     false

    2) Конструктор без аргументов (no-args constructor)
 
    Конструктор по умолчанию и Конструктор без аргументов не принимают аргументы.

    Конструктор без аргументов создает программист, при этом Конструктор по умолчанию уже не создается.

    Хотя у конструктора и не указывается возвращаемый тип, но он все же неявно возвращает тип создаваемого объекта.
    Непосредственно перед тем, как будет возвращен результат в виде ссылки на только что созданный объект, указанный конструктор будет использован для инициализации этого нового объекта.
    Таким образом, вызов Конструктора без аргументов вызывает созданный нами конструктор без аргументов и неявно возвращает результат в виде ссылки на только что созданный объект.

    Объявление переменной для хранения ссылки на создаваемый объект (чтобы можно было к нему обращаться, например, для вызова какого-то метода) выглядит следующим образом:

ТипПеременной имяПеременной = new ИмяКонструктора();

   Объект создаётся при помощи оператора new, который динамически выделяет для него память в куче (heap) и возвращает на него ссылку, которая присваивается переменной.

    Имя класса, идущее после оператора new, указывает на имя его конструктора, который определяет, что происходит при создании объекта (это может быть инициализация полей, вызов других конструкторов и т.д.).


    3) Параметризованный конструктор (parameterized constructor) 

    Параметризированный конструктор принимает аргументы (чтобы иметь возможность передавать нужные начальные значения в объект).

    Если конструкторов у класса несколько, то будет вызываться будет тот конструктор, который указывается при создании объекта набором параметров соответствующим используемому конструктору с таким же самым перечнем праметров.
 
   4) Конструктор копирования (copy constructor)
   Конструктор копирования это специальный конструктор, который принимает в качестве аргумента экземпляр того же класса для создания нового объекта на основе переданного.
   Такие конструкторы применяются тогда, когда необходимо создать копию сложного объекта, но при этом мы не хотим использовать метод clone().
   (Например, если в копии объекта какие-то поля должны отличаться).
 
   Ссылка на имя в конструкторе копирования копируется в значение нового объекта, т. е. значение нового объекта будет ссылаться на один и тот же адрес в памяти, что и оригинальный объект.
   Переменные типа String и класс LocalDateTime являются immutable и никогда не изменятся.
   Чтобы изменить их значения, им нужно будет присвоить другую ссылку. При этом эти поля других объектов будут по прежнему ссылаться на прежнее место в памяти. Заменив ссылку переменной поля порожденного объекта, значение ссылки оригинального объекта остаётся прежним.


    Поверхностное копирование (shallow copy)
    Такой механизм характеризуется копированием ссылок оригинального объекта в создаваемый объект.
    При поверхностном копировании копируются ссылки на значения из оригинального объекта. При этом, если копируемая ссылка принадлежит mutable-объекту (если переменная объекта будет ссылаться на изменяемую (mutable) переменную), то при ее изменении в любом объекте (не важно, в новом или исходном) ее значение изменится во всех других объектах. Т.е. изменения затрагивают все объекты.
 

    Глубокое копирование (deep copy)
    Этот вид копирования позволяет создавать объекты, которые будут содержать точные копии всех полей (в том числе ссылочных) оригинального объекта, но при этом будут абсолютно независимы от оригинального объекта.
    Изменение полей скопированного (склонированного) объекта не отражается на изменении полей оригинального объекта (также и в обратном случае).



    Связанные конструкторы
    Не обязательно передавать в конструктор для инициализации всех полей класса какие-то значения. Часть обязанностей можно возложить на компилятор, который присвоит полям значения по умолчанию (если это не критично). Также, в процессе работы программы, можно использовать сеттеры.



    Объект-значение (Value Objects)
    Один из вариантов использования конструкторов в Java — создание объектов-значений.
    Value Object — это объект, который не меняет своего внутреннего состояния после инициализации (становится immutable). Это значит, что если требуется изменить такой объект, то для этого придется создать его новый экземпляр, вместо того чтобы изменять существующий.



    Если мы используем ключевое слово final при определении членов класса. Это означает, что каждый из них может быть инициализирован только с помощью конструктора. Их нельзя переназначить позже в каком-либо другом методе. Мы можем считать эти значения, но не можем их изменить.
    Если мы создадим несколько конструкторов для класса, то каждый из них должен будет инициализировать каждую финальную переменную. Невыполнение этого приведет к ошибке компиляции. Например, «java: variable amount might not have been initialized».

 


   Основные требования к конструкторам:

    Класс может содержать любое количество конструкторов (NB! у них должны различаться их аргументы иначе выдаст ошибку). При большом количестве конструкторов для упрощения кода можно использовать Шаблоны проектирования из категории Creational Design Patterns. 

   Имя конструктора должно совпадать с именем класса, с учётом регистра.

   Можно считать, что имя конструктора совпадает с именем класса, а можно рассматривать конструктор как безымянный, и имя класса – как тип возвращаемого значения, так как конструктор может породить только объект класса, в котором он объявлен.
 
   Конструктор не может иметь возвращаемый тип (даже void).

   К конструктору можно применять модификаторы доступа для контроля его вызоваю

   Конструктор в классе размещается в строго определённом месте: они располагаются сразу после полей в порядке, зависящем от принимаемых ими числа аргументов - от меньшего к большему.

    Конструктор может принимать в качестве аргумента как примитивные типы данных, так и объекты

 
   Для конструктора очень важен порядок следования аргументов.
 

    Значение примитивов (int, float, double и т. д.) копируются как есть
    Ссылки на объекты типа immutable (например String), также копируются как есть. Несмотря на то, что оригинальный и порожденный объекты ссылаются на тот же самый адрес в памяти, immutable-объекты  никогда не будут изменяться
    Ссылки на mutable объекты (например Date, List и т. д.) должны копироваться при помощи глубокого копирования. Иначе оригинальный и порожденный объекты будут ссылаться на один и тот же адрес в памяти и соответственно, любые изменения объекта (оригинального или порожденного) будут отображаться на всех объектах

    Конструктор не может быть объявлен как final, static, synchronized или abstract
    Конструктор может быть перегружен (overload)

    Перегрузка конструктора означает, что какой-либо класс может иметь множество конструкторов, но при этом их списки параметров должны отличаться между собой.
    Конструкторы не наследуются подобно методам суперкласса

    Конструкторы могут иметь модификатор доступа private, что не позволит создавать его экземпляры. Например, приватный конструктор может использоваться для контроля над количеством создаваемых экземпляров: всегда можно будет создать только один объект.
        Иногда класс может быть служебным и хранить какие-то статические поля и статические методы. Необходимости в создании экземпляров таких классов нет, поэтому и в конструкторе нет смысла, но как мы уже знаем, компилятор создаст конструктор по умолчанию. Чтобы этого не произошло, мы можем сами создать пустой конструктор и сделать его закрытым, используя модификатор доступа private. Такой конструктор называется закрытый.

    Метод namesConcatinate объединяет имя и фамилию в одну строковую переменную. Закрытый конструктор BankAcoountUtils() делает невозможным создание экземпляра класса BankAccountUtils. Следующий код выведет объединенную строковую переменную без создания объекта:

Конструктор по умолчанию имеет тот же самый модификатор доступа, что и класс


    Конструктор класса вызывает конструктор по умолчанию его суперкласса (по цепочке вплоть до Object)
    Компилятор Java автоматически вставляет неявно вызов super () в первую строку любого конструктора. Поэтому, обращайте внимание на это правило при наследовании родительского класса.
    Первым выражением в конструкторе должен быть вызов метода this () или super()

    Конструктор и сеттеры можно (нужно) использовать совместно:

    Если используется конструктор по умолчанию, то далее предполагается, что при помощи сеттеров (setter) полям объекта присваиваются нужные нам значения, которые на момент его создания были неизвестны.
    В случае же с параметризованным конструктором, значения полей сразу инициализируются подходящими нам значениями. Один вызов такого конструктора заменяет собой вызов нескольких сеттеров. При этом создается объект с корректными значениями полей. Кроме того, параметризованный конструктор позволяет создать неизменяемый объект (immutable — это объект, состояние которого не может быть изменено после создания), что невозможно при использовании сеттеров.
    Также, возможны комбинации, когда объект создается с несколькими обязательными полями, и с полями, инициализированными значениями по умолчанию, которые в дальнейшем могут неоднократно изменяться сеттерами. 
 

 
 
О конструкторах можно посмотреть по ссылке: https://www.youtube.com/watch?v=f88zS-etDWs
 
 
 
   При инициализации аргументов используется this:

   (вообще this, как правило, применяется в следующих случаях:
      - когда у переменной экземпляра класса и переменной метода/конструктора одинаковые имена
      - для явного вызова конструктора (когда нужно вызвать конструктор одного типа (к примеру, конструктор по умолчанию или параметризированный) из другого) 
   (ключевое слово this это указатель на текущий объект класса)


   Пример применения метода-конструктора:


public class Tea {

    String name;

     int age;

 //конструктор для класса Tea

     public void setTea(String name, String sort) {

    this.name = name;

    this.sort = sort;

 }

    public static void main(String[] args) {

        Tea cup = new Tea("Green", '"Japanese");

    }

 }

 

   Также завершает заголовок конструктора throws-выражение, поскольку создать ошибку – это единственный способ для конструктора не создавать объект: если конструктор выполнился без ошибок, то объект гарантированно создается.
 
 
   Тело конструктора пустым быть не может и поэтому всегда описывается в фигурных скобках (скобки при необходимости могут быть пустыми).

   Также тело конструктора может содержать любое количество return-выражений без аргументов.
   Если процесс исполнения дойдет до такого выражения, то на этом месте выполнение конструктора будет завершено.
 
    Так как при вызове конструктора осуществляется создание и инициализация объекта, такой процесс не может происходить без обращения к конструкторам всех родительских классов.
   Поэтому вводится обязательное правило - первой строкой в конструкторе должно быть обращение к родительскому классу, которое записывается с помощью ключевого слова super, за которым идет перечисление аргументов.
 
   В теле конструктора используется ключевое слово this с круглыми скобками, которое позволяет вызывать другой конструктор, передавая в него аргументы (не любой, а только тот, который подходит по количеству и типам передаваемых аргументов). Это нормальная практика вызывать конструкторы по цепочке (в рамках текущего класса), когда часть значений используется по умолчанию, а какие-то являются новыми.
   Но бывает и другая ситуация, когда мы хотим перед отработкой конструктора потомка вызвать конструктор суперкласса. В таком случае нужно использовать ключевое слово super().
 
 
   Создание объекта

   Создание объекта начинается при исполнении выражения с ключевым словом new, за которым следует имя класса, от которого будет порождаться объект, и набор аргументов для его конструктора.
   По этому набору определяется, какой именно конструктор будет использован, и происходит его вызов.
   Первая строка его тела содержит вызов родительского конструктора. В свою очередь, первая строка тела конструктора родителя будет содержать вызов далее к его родителю, и так далее. Восхождение по дереву наследования заканчивается, очевидно, на классе Object, у которого есть единственный конструктор без параметров.
   Именно в этот момент JVM порождает объект, и далее начинается процесс его инициализации.
   Выполнение начинает обратный путь вниз по дереву наследования.
 
   У самого верхнего родителя, прямого наследника от Object, происходит продолжение исполнения конструктора со второй строки. Когда он будет полностью выполнен, необходимо перейти к следующему родителю на один уровень наследования вниз и завершить выполнение его конструктора, и так далее.
   Наконец, можно будет вернуться к конструктору исходного класса, который был вызван с помощью new, и также продолжить его выполнение со второй строки.

   По его завершению объект считается полностью созданным, исполнение выражения new будет закончено, а в качестве результата будет возвращена ссылка на порожденный объект.

Комментариев нет:

Отправить комментарий

Наследование в Java

   «родительское/дочернее»     Наследование (inheritance) - свойство системы, позволяющее описать (создать) новый класс на основе уже су...