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

Объектно ориентированное программирование и принципы ООП


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

    То есть все программы состоят из объектов, связанных между собой, вид и функционал объектов определяется классами.

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

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

   1) абстракция данных
   «важное/неважное»

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

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

    В зависимости от целей и задач, можно рассуждать об одном и том же объекте на разных уровнях абстракции.

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

 



   2) инкапсуляция
   «ключевое/подробности»

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

   Используется для быстрой и безопасной организации иерархической управляемости.
   Доступ к данным (полям) предоставляется посредством публичных методов (геттеров/сеттеров). Это защитный барьер позволяет хранить информацию в безопасности внутри объекта.
   Одни языки (например, С++, Java или Ruby) отождествляют инкапсуляцию с сокрытием, но другие (Smalltalk, Eiffel, OCaml) различают эти понятия.


   Прочитать больше об инкапсуляции, геттерах и сеттерах можно здесь: https://javaika.blogspot.com/2019/02/encapsulation-getters-and-setters.html


   3) наследование
   «родительское/дочернее»

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

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

    Подробнее о наследовании здесь: https://javaika.blogspot.com/2022/10/nasledovanie.html


   4) полиморфизм подтипов
   «единое/множественное»

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

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

Ограничения

  • Статические методы не поддерживают полиморфного поведения, т.к. они существуют на уровне класса, а не на уровне отдельных объектов

Достоинства

  • универсальность кода;

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

  • снижение сложности программ, т.к. разрешая использование одного интерфейса для единого класса действий. При этом выбор конкретного действия, в зависимости от типа, возлагается на компилятор.

 

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

   Другой формой полиморфизма является перегрузка метода, когда его поведение определяется набором передаваемых в метод аргументов.
 
   Перегрузка метода (method overloading) - механизм, позволяющий использовать методы с одним и тем же именем, но с разными типами и/или количеством параметров (несколько версий одного метода с разным набором входных параметров): при вызове метода, в зависимости от типа и количества передаваемых параметров, система выберет именно ту версию, которая наиболее подходит.
 
(Механизм перегрузки метода позволяет методу проявлять различное поведение в зависимости от того, какие аргументы он принимает.)

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

    Связывание (binding) — присоединение вызова метода к телу метода.

    Раннее связывание (early binding) — это связывание, происходящее во время компиляции. Также его называют статическим.

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

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

     Позднее связывание (late binding) — это связывание, которое выполняется во время выполнения программы. Его также называют динамическим (dynamic) или связыванием на стадии выполнения (runtime binding). При позднем связывании определяется фактический тип объекта для вызова именно его метода.


    Связывание всех методов в Java осуществляется полиморфно, через позднее связывание — это значит, что мы можем писать код для базового класса, который будет работать во всех производных классах от него.

 

    Восходящее преобразование (upcasting) — это когда, например метод в качестве параметра типа базового класса принимает тип класса наследника. Но это разумно, т.к. класс наследник является разновидностью класса предка. Данный факт позволяет преобразовывать ссылку на объект наследника в ссылку на объект предка.

Восходящим оно называется потому, что стрелки наследования идут снизу вверх, более того корень иерархии всегда вверху. Данное преобразование безопасно, т.к. это по сути переход от конкретного типа к более общему, что часто ведет к потере методов, но не приобретению.

Преобразования можно делать и в обратном порядке — это называется нисходящее преобразование (downcasting).

Типы отношений между классами

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

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

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

Если еще проще, то вся разница между агрегацией и композицией состоит в том, каким образом инициализируются поля в классе-контейнере и как эта инициализация влияет на время их жизни после смерти основного класса.

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

А, если создать объект, а потом передал его в качестве аргумента в сеттер или в конструктор, а потом присвоить его полю, то со смертью класса — все созданные таким образом объекты будут жить и дальше — это агрегация.

А теперь приведу примеры того, что пишут в интернете, возможно, кому-то они покажутся проще.

 
 

    Агрегация (aggregation; «has-a» — есть, имеет, содержит) применяется когда один класс должен быть контейнером для других классов. Причем время существования содержащихся в нем классов (полей) никак не зависит от времени существования класса контейнера.

    Агрегация — отношение «часть-целое» между двумя равноправными объектами, когда один объект (контейнер) имеет ссылку на другой объект. Оба объекта могут существовать независимо: если контейнер будет уничтожен, то его содержимое — нет.

Данный вид связи на UML-диаграмме обозначается в виде линии с незакрашенным ромбиком (ромбик всегда находится со стороны целого, а простая линия со стороны составной части).

 

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

     Данный вид связи на UML-диаграмме обозначается в виде линии с закрашенным ромбиком (ромбик всегда находится со стороны целого, а простая линия со стороны составной части).

    Разница между агрегацией и композицией

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

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

    Композицию часто предпочитают наследованию

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

Достоинства

  • контроль видимости;
  • реализация может быть заменена во время выполнения (run-time);
  • слабая связанность, так как класс-интерфейс не зависит от реализации.
 
 
 
 
   Объект в программировании - сущность в цифровом пространстве, обладающая состоянием и поведением, имеющая поля и методы.
   Объекты (экземпляры класса) принадлежат одному или нескольким классам, которые определяют поведение (являются моделью) объекта.
 

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

   Инстанцирование (англ. instantiation) — создание экземпляра класса. Создать экземпляр класса = инстанцировать класс. Порождающие шаблоны используют полиморфное инстанцирование.

   Анонимный объект (англ. anonymous object) — это объект, который принадлежит некоторому классу, но не имеет имени.

   Инициализация (англ. initialization) — присвоение начальных значений полям объекта.

   Время жизни объекта - время с момента создания объекта (конструкция) до его уничтожения (деструкция).
 
   Подробнее о классах здесь: https://javaika.blogspot.com/2020/11/java-class.html
 
 
   Объекты обладают свойствами наследования, инкапсуляции и полиморфизма.
   Объекты обладают такими характеристиками как состояние и поведение.
 
   Состояние объекта (state) — это значения, которые принимают его поля (fields). Со сменой значения полей — меняется состояние объекта.
   Поведение объекта (behavior) — это набор его методов (methods), которые могут изменять состояние объекта либо выполнять иные функции.
 






Больше об этом можно прочитать здесь: https://topjava.ru/blog/oops-concepts-in-java

Понятие метода - конструктора в 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 будет закончено, а в качестве результата будет возвращена ссылка на порожденный объект.

воскресенье, 14 июня 2020 г.

3.1 Переменные в JAVA

   Переменные – это контейнеры для хранения данных.


   Все данные в Java хранятся с помощью переменных.

   Переменные в Java имеют три свойства: тип, имя и значение.

   Имя необходимо для того, чтобы отличать одну переменную от другой.


   Тип переменной определяет тип данных, которые в ней можно хранить. Переменная может хранить значения только того же типа, что и она сама.

   Переменные делятся на два типа: примитивы (простые типы) и ссылки.
 
   Примитивы хранят основные значения (простые битовые структуры), включая целые числа, булевы значенияи числа с плавающей точкой.
   Ссылки хранят ссылки на объекты.

   Подробнее о типах данных здесь: https://javaika.blogspot.com/2018/12/tipy-dannih-v-java.html

 
 
    Значение – это объект, данные или информация, которая хранится в переменной.
   В переменной не примитивного типа хранится ссылка на объект.

   Чтобы создать переменную используется команда вида: «тип имя».

   Пример создания переменной:
int aj;
   Данная запись создает переменную типа int c именем aj.

   Чтобы занести значения в переменную используется операция присваивания (=), которая копирует значение из одной переменной в другую.

   Пример занесения значения в переменную:

  i = 3;
  (В переменную i заносится значение 3)

   k = k + 1;
   (В переменную k заносится значение переменной k +1)

   Можно создавать переменные и присваивать им значения в одной строке:

   String per = "Text";

   Для сравнения в языке Java используется двойное равно «= =».


   Правила:

   1 Нельзя создать две переменных с одинаковыми именами в одном методе, но можно - в разных методах?
   2 Имя переменной может содержать латинские буквы и цифры, не может содержать пробелов, символов +,- и д.р.
   3 В языке Java имеет значение регистр букв: peR и per - это имена разных переменных.
 
 
   Переменные экземпляра - переменные, описывающие состояние объекта.

   Локальные переменные - переменные, объявленные внутри метода. Локальная переменная существует только в том блоке кода (методе), в котором она объявлена (существует с момента объявления и до конца блока кода, в котором объявлена).

   Аргументы - значения, передающиеся методу из возвращаемого кода.

   Переменные возвращаемых значений - значения, возвращаемые методов в вызывающий код.

суббота, 16 марта 2019 г.

9 Создание GUI. Пакет Javax.Swing и краткое описание его классов

   GUI (graphical user interface) - это графический пользовательский интерфейс.

   Задача создания GUI - создание гибкого и дружелюбного интерфейса для использования приложения пользователями.


   Для того, чтобы разобраться, как создавать GUI на Java, познакомимся с пакетом javax.swing библиотеки Java API.


   Для подключения библиотеки Swing в приложении необходимо импортировать библиотеку javax.swing.

   Для создания графического пользовательского интерфейса приложения используют различные классы пакета javax.swing.

Классы, предназначенные для создания окон (JWindow, JFrame, JDialog)

   о классе Javax.Swing.JWindow:

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

   о классе Javax.Swing.JFrame:

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

   Класс JFrame(), если не задать параметры создаст пустое окно.
   Класс JFrame(String title) создаст пустое окно с заголовком title. Для определения заголовка окна можно также использовать метод setTitle() класса JFrame.

   Используемые методы для определения параметров окна:
      1) setSize(int width, int height) - для определения размеров окна
   Если не определить размеры окна, то оно будет иметь нулевую высоту независимо от того, что в нем находится. Здесь в размеры окна включены границы и строка заголовка.

      2) setLocation() - для задания положения окна

      3) setBounds(int x, int y, int width, int height) - для задания размеров окна и его положения (путём указания расположения его левого верхнего угла)

      4) setDefaultCloseOperation(int operation) - для определение действия при завершении программы.
   Данный метод позволяет указать, какое действие будет произведено при предварительной обработке события processWindowEvent() перед закрытием окна.
   По умолчанию в качестве параметра operation используется константа HIDE_ON_CLOSE, убирающая окно с экрана при его закрытии.
   При использовании константы EXIT_ON_CLOSE создаётся указание, что при закрытии окна необходимо закончить работу приложения.
   При использовании константы DO_NOTHING_ON_CLOSE - ничего не делать при закрытии окна. К окну JFrame подключается слушатель и обработка события закрытия окна выполняется в методе windowClosing. Алгоритм обработки построен таким образом, что окно закрывается при третьей попытке. Номер попытки отображается в метке интерфейса.

      5) setVisible(boolean visible) и show() - для того, чтобы сделать окно видимым.
   По умолчанию окно создается невидимым.
   Чтобы отобразить окно на экране вызывается метод setVisible с параметром true. Если вызвать его с параметром false, окно станет невидимым.
   Метод show(), вдобавок, проводит валидацию содержащихся в окне компонентов.

      6) pack() - для того, чтобы "упаковать" имеющиеся в окне компоненты так, чтобы они занимали столько места, сколько им необходимо. Компоненты при вызове этого метода переходят в «видимое» состояние, хотя и не появляются на экране до вызова одного из следующих методов

      7) setTitle() - для определения заголовка окна.

      8) setIconImage() - для определения иконки окна, которая располагается на кнопке свернутого окна или в заголовке окна в нормальном состоянии.

Окна Swing (JWindow, JFrame, JDialog) поддерживают два типа событий:

   WindowListener - позволяет узнать об изменениях в состоянии окна
   WindowFocusListener - сообщает о получении или потере компонентами окна фокуса ввода

   о классе Javax.Swing.JDialog:

   Диалоговые окна JDialog чаще всего используются в приложениях для получения дополнительной информации с целью установки параметров приложения, вывода важной вспомогательной или отладочной информации.
   Диалоговые окна при помощи этого класса можно создавать обычными или модальными (modal) - блокирующими доступ к остальным окнам приложения, пока пользователь не закончит работу с модальным диалоговым окном.
   Модальные диалоговые окна располагаются поверх основного окна приложения.
   Внешний вид диалоговых окон мало отличается от окон с рамкой JFrame, но обычно у них меньше элементов управления окна (чаще всего, имеется только кнопка закрытия окна) и отсутствует системное меню.
   Как и JFrame поддерживает закрытие окна, а в остальном сходен с другими окнами Swing.
   При создании диалоговых окон Swing необходимо указать «родительское окно», которым может быть окно с рамкой JFrame или другое диалоговое окно JDialog. Имеется также конструктор, не требующий «родительского» окна, но использующий вспомогательное прозрачное окно.


   Элементы управления - кнопки, флажки, переключатели, выключатели, меню и его элементы, и д.р. (JButton, JToggleButton, ButtonGroup, JRadioButton, JCheckBox)


   Выключатель JToggleButton
      Чаще всего используется в панелях инструментов, где он играет роль флажков.
      По внешнему виду это та же самая кнопка, только ее можно нажать, и она остается в этом состоянии.

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

   Переключатели JRadioButton
      Переключатель JRadioButton унаследован от выключателя JToggleButton и отличается от него только внешним видом.
      JRadioButton используют, как правило, при объединении нескольких переключателей в группу и реализации выбора «один из многих». По одиночке в интерфейсе их практически не используют; обычно эту роль исполняют флажки.

   Флажки JCheckBox
      Также как и переключатели, отличаются от выключателей только внешним видом.
      Флажки используются там, где нужно предоставить пользователю возможность что-то включить или выключить. Они также могут группироваться, но, в отличие от JRadioButton, никогда не реализуют выбор «один из нескольких», а всегда позволяют выбрать несколько равноценных вариантов.


   Текстовые компоненты библиотеки Swing (JTextField, JTextArea, JTextPane)

   Пакет javax.swing.text библиотеки Swing содержит компоненты для работы с текстом.
   Основные возможности всех текстовых компонентов Swing и их базовая архитектура описаны в абстрактном классе JTextComponent. Именно от этого класса унаследованы все текстовые компоненты Swing, будь то простое текстовое поле или многофункциональный редактор.
   Помимо того что в данном базовом классе задаются общие для всех текстовых компонентов свойства и действия (такие как цвет выделенного текста, цвет самого выделения, курсор, сам текст, механизмы работы с буфером обмена), в нем описывается взаимодействие практически всех составных частей пакета javax.swing.text.
   Текстовые компоненты имеют архитектуру MVC. Модель текстовых компонентов представлена довольно простым интерфейсом Document, который позволяет получать информацию об изменениях в документе и хранящийся в нем текст, а также при необходимости изменять полученный текст.

   JLabel - текст с иконкой

   JLabel используют, если нужен какой-то компонент, чтобы отобразить какое-то сообщение пользователю или сделать для поля ввода текстовую метку, или показать иконку.
   Текст, который показывает JLabel выделять нельзя, только смотреть.

   Списки (JList, JCombobox)

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

   В отличие от обычного списка JList, раскрывающийся список JComboBox используется для выбора одной строки из нескольких вариантов и показывает только выбранный в данный момент элемент.

   JComboBox позволяет также вводить собственные значения.


   Панели JTabbedPane, JSplitPane, JScrollPane, JViewPort

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

   Разделяемая панель JSplitPane используется для гибкого распределения пространства между двумя компонентами. Она позволяет произвольным образом изменить соотношение размеров связанных компонентов.

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

   Компонента JViewPort является основой панели прокрутки, и который можно использовать в приложениях как «видоискатель».

   Таблицы JTable

   Таблица JTable позволяет отображать двухмерную информацию в виде строк и столбцов, настраивать и сортировать данные, выводить их в любом подходящем виде, управлять заголовками таблицы и ее выделенными элементами.
   JTable использует три модели, поставляющие ей данные и сохраняющие изменения при изменении этих данных.
   Первая и самая важная модель реализует интерфейс TableModel и хранит данные ячеек таблицы и дополнительную служебную информацию об этих ячейках.
   Вторая модель реализует интерфейс TableColumnModel и управляет столбцами таблицы. TableColumnModel позволяет добавлять, перемещать и находить столбцы, узнавать об изменениях в их расположении, с ее помощью можно изменить расстояние между столбцами и находящимися в них ячейками.
   Модель TableColumnModel имеет собственную модель выделения списка ListSelectionModel. Так что можно формально говорить о четырех моделях таблицы JTable. Просто одна из этих моделей вызывается неявно, через другую модель.
   Третья модель таблицы SelectionModel используется для выделения списка. Она управляет выделением строк таблицы и не затрагивает столбцы.


   Инструмент для отображения иерархической информации JTree

   Отображение иерархической информации в библиотеке Swing реализуется с помощью дерева JTree, основой которого является корневой узел root.
   Потомки корневого узла, могут иметь своих потомков, и т.д.
   Информация о ветвях дерева хранится в узлах nodes.
   Узел без потомков является листом leaf дерева.
   Потомки одного узла являются близнецами siblings.
   Путь path в дереве определяет последовательность узлов, начиная от корня, по которым следует пройти, чтобы найти нужный нам узел.
   Пакет javax.swing.tree хранит основные «строительные» классы деревьев Swing : интерфейс TreeNode, описывающий узел дерева, модель дерева с его данными, реализующую интерфейс TreeModel, модель выделения узлов дерева TreeSelectionModel, позволяющую настраивать различные режимы выделения, класс TreePath, описывающий путь в дереве.


   Визуальные компоненты, позволяющие устанавливать данные в определенном диапазоне и отображать информацию, представляющую часть некоторого диапазона (JSlider, JProgressbar, JSpinner)

   Ползунки JSlider предоставляют пользователю наглядную возможность плавного изменения значения от минимального до максимального.
   Индикатор процесса JProgressBar используется для динамического отображения выполнения какого-либо процесса в приложении и позволяет оценить, на каком этапе выполнения находится интересующий нас процесс и сколько ориентировочно времени он может еще занять.
   Счетчик JSpinner позволяет выбрать значение из некоторого набора, который можно «прокручивать» в обе стороны. В отличие от списков (раскрывающийся список JComboBox, простой список JList) на экран не вызывается всплывающее окно и количество элементов в счетчике JSpinner может быть неограниченным.

   Диалоговые окна JOptionPane

   Класс JOptionPane предназначен для вывода в графический интерфейс приложения разнообразной информации и выбора простых данных, работа с которым связана с вызовом одного из многочисленных статических методов, создающих и выводящих на экран модальное диалоговое окно стандартного вида.
   В диалоговых окнах JOptionPane можно выводить самую разнообразную информацию и, при необходимости, размещать в них дополнительные компоненты.
   JOptionPane унаследован от базового класса JComponent библиотеки Swing, так что можно работать с ним напрямую, т.е. создавать экземпляры класса JOptionPane и настраивать их свойств
   Использование стандартных диалоговых окон существенно упрощает разработку приложения и позволяет ускорить процесс освоения пользователем интерфейса.

   Диалоговое окно JFileChooser

   Компонент JFileChooser представляет собой контейнер, в котором расположены несколько компонентов, списков и кнопок, «управляющих» выбором файлов.
   JFileChooser - это компонент, унаследованный от класса JComponent, так что можно включить его в любое место интерфейса.
   С этим компонентом просто настроить и вывести на экран несложное диалоговое окно для открытия файла или сохранения в нем данных.

Не без использования java-online.ru (хороший ресурс)

суббота, 2 марта 2019 г.

8 Что такое библиотека JAVA API и как её использовать

   Вместе с Java поставляются сотни готовых классов, которые содержатся в библиотеке Java API.



   API (application programming interface) - программный интерфейс приложения, интерфейс прикладного программирования - описание способов (набор классов, процедур, функций, структур или констант), которыми одна компьютерная программа может взаимодействовать с другой программой.
   Обычно API входит в описание какого-либо интернет-протокола, программного каркаса (фреймворка) или стандарта вызовов функций операционной системы.
   Часто реализуется отдельной программной библиотекой или сервисом операционной системы.
   Используется программистами при написании приложений.
 
   Число API для платформы Java огромно.
   Есть три типа Java API:
1) официальный основной Java API, содержащийся в JDK или JRE, в одном из вариантов Java. Три варианта платформы Java: Java ME (Micro edition), Java SE (Standard edition) и Java EE (Enterprise edition).
2) дополнительные официальные API, которые можно загрузить отдельно. Спецификации этих API определяются в соответствии с Java Specification Request (JSR), а иногда и некоторые из этих API позднее включаются в основные API платформы (наиболее ярким примером такого рода является Swing).
3) неофициальный API, разработанный третьими сторонами, но не связанный с JSR.
   Третьи стороны могут свободно реализовать любые спецификации JSR для официального API (даже для основного API языка), при условии, что они соответствуют Technology Compatibility Kit (TCK) для данного JSR (TCK представляет собой набор тестов для проверки соответствия реализаций для JSR). Результатом этой свободы является то, что у многих официальных API больше реализаций, чем эталонных реализаций от Sun

   Документацию по Java, в т.ч. API можно посмотреть здесь:
https://docs.oracle.com/en/java/index.html
https://www.oracle.com/technetwork/java/api-141528.html


   Пример использования API:

   Для того, чтобы увидеть в браузере строчку «Hello, world!», достаточно создать HTML-документ с минимальным заголовком и простейшим телом, содержащим данную строку.
   Когда браузер откроет этот документ, программа-браузер передаст имя файла (или уже открытый дескриптор файла) библиотеке, обрабатывающей HTML-документы.
   Та, в свою очередь, при помощи API операционной системы прочитает этот файл и разберётся в его устройстве, затем последовательно вызовет через API библиотеки стандартных графических примитивов операции типа «очистить окошко», «написать „Hello, world!“ выбранным шрифтом».
   Во время выполнения этих операций библиотека графических примитивов обратится к библиотеке оконного интерфейса с соответствующими запросами, и уже эта библиотека обратится к API операционной системы, чтобы записать данные в буфер видеокарты.


   Стандартная библиотека Java представляет собой большой набор классов, которые можно использовать при создании приложений.
   В библиотеке Java API классы сгруппированы в пакеты.

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

   *Пакеты, названия которых начинается с javax изначально появились в Java как расширения, а потом повысили свой статус. 

   Чтобы использовать класс из библиотеки API нужно знать, в каком пакете он находится.
   Примеры пакетов из библиотеки Java API:

   а) Пакет  java.lang
      Этот пакет библиотеки всегда импортируется в любое Java-приложение, поскольку содержит базу, без которой в принципе не возможно программировать
      Содержит классы System (System.out.println), String, Double, Enum, Math (Math.random()) и т. д.

   б) Пакет java.util
      Этот пакет библиотеки содержит набор классов-утилит и интерфейсов, реализующих широко используемые структуры данных - коллекции.
      Название этого пакета библиотеки - Java collections framework.
      Это коллекция стандартных структур данных, таких как списки, карты, очереди, стеки.
      Несмотря на то, что это framework, он не имеет явных критических участков кода и работает как библиотека.
      Содержит, к примеру, класс ArrayList.

   в) Пакеты для создания графического интерфейса
      - java.awt (от Abstract Window Toolkit)
      - javax.swing
      Служит для работы с GUI , содержит классы пользовательского графического интерфейса (GUI)
      Пакет javax.swing, в прошлом являлся  расширением пакета java.awt.
      ПО сравнению с более ранним пакетом библиотеки AWT, Swing предоставляет более гибкие интерфейсные компоненты. В отличие от AWT, компоненты Swing разработаны для кросс-платформенной работы, в то время как компоненты AWT повторяют интерфейс исполняемой платформы без изменений.
      AWT использует только стандартные элементы операционной системы для отображения, т.е. для каждого элемента создается отдельный объект ОС (окно), в связи с чем, AWT не позволяет создавать элементы произвольной формы (возможно использовать только прямоугольные компоненты).
      Элементы управления на основе AWT всегда отображаются поверх Swing-элементов, т.к. все Swing компоненты отображаются на поверхности контейнера.

г) Пакеты для работы с аудиофайлами
   - javax.sound.midi
   - javax.sound.midi.spi
   - javax.sound.sampled
   - javax.sound.sampled.spi

д) Набор пакетов для обеспечения безопасных Интернет-коммуникаций
    Этот набор называется Java Secure Socket Extension, JSSE  - Расширение Java для безопасных сокетов.
   Он предназначен для реализации безопасных сетевых соединений. В JSSE реализованы протоколы SSL и TLS. Библиотека включает функциональность по шифрованию данных, аутентификации сервера и клиента, проверки целостности сообщений.
   - javax.net
   - javax.net.ssl
   - java.security.cert

е) Набор пакетов для контроля (мониторинга) и управления приложениями, системными объектами, устройствами (например, принтерами) и сервис-ориентированными (компьютерными) сетями.
   Данные ресурсы представляются MBean-объектами (от англ. Managed Bean - управляемый Java Bean).
   Этот набор пакетов называется Java Management Extensions, JMX - Управленческие расширения Java.
   Для разработки приложений, использующих данную технологию, может применяться Java Dynamic Management Kit. Предоставляемый интерфейс позволяет динамически управлять классами.

   - javax.management
   - javax.management.loading
   - javax.management.modelmbean
   - javax.management.monitor
   - javax.management.openmbean
   - javax.management.relation
   - javax.management.remote
   - javax.management.remote.rmi
   - javax.management.timer

ж) Пакет java.io
   Применяется для чтения файлов, работающих потоками, объектами класса Pipe и т.д.

з) Пакет java.nio
   Является альтернативой java.io. Эта библиотека отвечает за неблокирующий ввод-вывод, позволяя интенсивно использовать операции ввода-вывода.


   Использовать библиотечные классы в своей программе можно двумя способами:
      1) Набирать полное имя класса из библиотеки Java каждый раз при его использовании.
          К примеру, для класса ArrayList из пакета java.util полное имя выглядит так:
java.util.ArrayList

          Чтобы упростить себе жизнь, используют второй способ:

      2) Предварительно импортировать этот класс, поместив оператор import в начале своего исходного файла:

import java.util.ArrayList;
public class MyClass {...}

   *Исключение – пакет java.lang, который как будто импортируется по умолчанию


   Основными проблемами существующих многоуровневых систем API являются:

      - Сложность портирования программного кода с одной системы API на другую (например, при смене ОС)
      - Потеря функциональности при переходе с более низкого уровня на более высокий: каждое новое усовершенствование и дополнение API создаётся для облегчения выполнения некоторого стандартного набора операций, но при этом может затрудняться, либо становиться принципиально невозможным выполнение некоторых других операций, которые предоставляет более низкий уровень API

суббота, 16 февраля 2019 г.

7 Что такое инкапсуляция. Геттеры и сеттеры (Getters and setters)

   Здесь вы узнаете об инкапсуляции в Java.


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

   В общем случае в языках программирования термин «инкапсуляция» относится к одному или двум утверждениям сразу:
   - это механизм языка, позволяющий ограничить доступ одних компонентов программы или пользователей к другим;
   - это языковая конструкция, позволяющая связать данные с методами, предназначенными для обработки этих данных.

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

   //не делайте так :)

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

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


   Инкапсуляцию в Java реализуют (обеспечивают) при помощи служебных слов-модификаторов доступа (private, protected, package default, public), явно задающие область видимости каждого поля и метода класса (которые позволяют ограничивать доступ к элементам данных ("прятать" данные)).

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

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

   Механизм действия инкапсуляции таков: для того, чтобы защитить объекты от нежелательных и недопустимых изменений она вынуждает остальной код обращаться к этим объектам не напрямую, а опосредованно, с помощью методов-сеттеров, в которые добавляют условия, гарантирующие допустимые значения полей.
 
   Переменная, скрытая модификатором с ограниченным доступом (например, private) и доступна только через геттер и сеттер, инкапсулирована.
 
   Некоторые фреймворки такие, как Hibernate, Spring, Struts и т. д. могут проверять информацию или внедрять свой служебный код через геттер и сеттер, поэтому при интеграции кода с ними необходимо иметь эти методы.

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

    Это сделано для того чтобы малейшее изменение в классе (например изменение реализации способа хранения данных или их обработки и тд) не влекло за собой изменения в работе класса-клиента. Т.е. пользователи класса не должны страдать от его внутренних изменений.

 

Достоинства:

  • сокрытие реализации позволяет сократить временные затраты на поиск ошибок: если мы контролируем доступ к атрибуту, то при возникновении проблемы нам не придется беспокоиться об отслеживании каждого фрагмента кода, который мог бы изменить значение соответствующего атрибута — оно может быть изменено только в одном месте (с помощью сеттера)

  • также инкапсуляция заставляет пользователя играть по правилам, показывая ему только необходимые методы и скрывая не нужные

  • инкапсуляция отделяет изменяемую часть класса от постоянной. Данное свойство полезно, т.к. позволяет безболезненно для пользователя менять код скрытых методов без каких-либо для него проблем, т.к. сигнатуры публичных методов, которые ему доступны, не меняются, а изменяются только скрытые (приватные) методы

Недостатки:

  • необходимо писать дополнительный код в виде геттеров и сеттеров, которые зачастую захламляют код

 


   О служебных словах-модификаторах доступа я написал здесь: https://javaika.blogspot.com/2018/12/3-java.html

   Метод-геттер (Getter - с англ. получатель) - это метод, с помощью которого получают (считывают) значение переменной, доступ к которой напрямую ограничен.
    Геттер иногда называют accessor.
   
  
   Метод-сеттер (Setter, от англ.set - устанавливать) - это метод, с помощью которого задают или изменяют значение переменной (присваивают какое-либо значение инкапсулированному полю), например, обработав при этом недопустимые присваивания.
   Сеттер иногда называют mutator.

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

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

 
   Пример метода-геттера и метода-сеттера:

сlass Telefon {
    private int telefon_nomer;
    private int telefon_balance;
    // getBalance () – это метод getter, который считывает значение переменной account_balance:
    public int getBalance () {
       return this.telefon_balance;
    }
    // setNomer () – это метод-сеттер, который устанавливает или обновляет значение переменной telefon_nomer:
    public void setNomer (int num) {
       this.telefon_nomer = num;
    }
}
 



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


   Принцип именования геттеров и сеттеров должен соответствовать конвенции Java об именовании:
   Геттеры именуются так:
      "get" + имя переменной, для которой реализуется этот метод с большой буквы (например, getMoney, getCake).
   Если переменная имеет тип boolean, то геттер будет содержать префикс is:
      "is" + имя переменной, для которой реализуется этот метод с большой буквы (например, isMoney, isCake)
 
   Сеттеры именуются так:
      "set" + имя переменной, для которой реализуется этот метод с большой буквы (например, setMoney, setCake).

   Метод геттер не имеет параметров (т.е. в скобках ничего не пишется) и возвращает значение одной переменной (одного поля).
   Метод сеттер всегда имеет модификатор void и только один параметр, для изменения значения одного поля.

   *Вместо прописывания геттеров и сеттеров вручную можно генерировать их с помощью IDE и различных пакетов.
 
 
 
   Использование геттеров и сеттеров для public-полей делает их бесполезными.
   Если в сеттер передаётся ссылка на объект, то нужно не копировать её во внутреннюю переменную напрямую, а, вместо этого, делать её копию (например, используя метод copyOf или copyOfRange) и только тогда присваивать её полю.
   Возврат геттером ссылки на объект: не возвращайте ссылку на исходный объект в геттере, а возвращайте копию.
 
 
 
   Реализация геттеров и сеттеров для:
 
    - переменных примитивных типов

    Переменные примитивных типов можно свободно передавать/возвращать прямо в сеттере/геттере, потому что Java автоматически копирует их значения.
 
    - объектов системных классов 

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

    Для объектов типа Date внешние классы не должны иметь доступ к их оригиналам, т.к. объекты класса java.util.Date являются изменяемыми.
   Данный класс реализует метод clone() из класса Object, который возвращает копию объекта, но использовать его для этих целей не стоит.
    Поскольку Date не является окончательным классом, нет га­рантии, что метод clone() возвратит объект, класс которого именно java.util.Date: он может вернуть экземпляр ненадежного подкласса, созданного специально для нанесения ущерба. Такой подкласс может, например, записы­вать ссылку на каждый экземпляр в момент создания последнего в закрытый статический список, а затем предоставить злоумышленнику доступ к этому списку. В результате злоумышленник получит полный контроль над всеми эк­земплярами копий. Чтобы предотвратить атаки такого рода, не используйте метод clone() для создания копии параметра, тип которого позволяет нена­дежным сторонам создавать подклассы.
    Таким образом для объектов типа Date нужно создавать каждый раз новый экземпляр и работать с ним.
 
    - коллекций
 
     Для коллекций из элементов типа String не требуется специальной реализации, так как объекты этого типа неизменяемы (immutable).
     Для коллекции из String'ов одним из решений проблемы, при которой коллекция может быть изменена из кода, находящегося за пределами геттера и сеттера, является использование конструктора, который принимает другую коллекцию в качестве аргумента.
   При создании новой коллекции на основе имеющейся, объекты, размещенные в оригинальной коллекции, не будут скопированы. Вместо этого в новую коллекцию будут скопированы только ссылки на эти объекты. И хоть в итоге две коллекции и различны, но содержат одни и те же объекты.
 
    Для коллекций пользовательских типов данных необходимо учитывать следующие ключевые моменты:
        -  самостоятельно реализовать метод clone()
        - в сеттере добавить клонирование элементов из исходной коллекции в конечную
        - в геттере создать новую возвращаемую коллекцию, используя клонирование элементов из исходной коллекции в новую
        
   Так, например, ArrayList, HashMap, HashSet и т. д. реализуют свои собственные методы clone(). Эти методы якобы возвращают копии, но на самом деле копируют лишь ссылки (происходит неглубокое копирование). Об этом прямо написано в Javadoc метода clone() класса ArrayList:
   "возвращает копию этого экземпляра ArrayList (сами элементы не копируются)".
    Таким образом, нельзя использовать метод clone() этих классов коллекций и необходимо самостоятельно реализовать метод clone().
 

    -  собственных классов

    Если вы создаете объект своего пользовательского класса, вам следует для него реализовать метод clone().
    Правила реализации геттера и сеттера для собственного класса:
        -  Реализуйте метод clone() самостоятельно
        - Возвращайте клонированный объект из геттера
        - Используйте клонированный объект в сеттере
 
 
   Подробнее с данной темой можно ознакомиться здесь: https://topjava.ru/blog/gettery-i-settery-v-java

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

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