вторник, 29 сентября 2020 г.

Ключевые слова Void и Static

    Методы могут возвращать или не возвращать значения.

    Тип возвращаемых данных указывают при объявлении метода - перед его именем.


Void

    Ключевое слово (модификатор) void (пустой) означает, что метод не возвращает никаких данных (значений) в переменные как результат своей работы, а просто, к примеру, выводит на экран строку.
   Если метод возвращает какие-то данные, то вместо void нужно указать возвращаемый тип (например int), а в теле метода используется хотя бы один оператор return вида:
return выражение/переменная
    где выражение/переменная - это возвращаемое значение.


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


Static

   Ключевое слово (модификатор) static в Java используется для управления памятью.
 
   Обозначенные ключевым словом static переменные принадлежат всему классу, а не конкретному объекту класса.
   Когда член класса объявлен как static (статический), он доступен до создания каких-либо объектов его класса и без ссылки на какой-либо объект.
 
   (В некоторых случаях нужно определить член класса, который будет использоваться независимо от любого объекта этого класса. Обычно обращение к члену класса должно выполняться только в сочетании с объектом его класса, однако можно создать член класса, который может использоваться самостоятельно, без ссылки на конкретный экземпляр: чтобы создать такой член, в начало его объявления нужно поместить ключевое слово static.)
 
 
   Статическими (объявленными с помощью ключевого слова (модификатора) static) могут быть: 
 
   (Ключевое слово static может использоваться при описании полей и методов класса)
 
 
   а) переменная класса  (поле класса)
 
   Статическая переменная может использоваться для обозначения общего свойства всех объектов, которое не является уникальным для каждого объекта.
    Статическое свойство Java является общим для всех объектов. Переменные экземпляров, объявленные как static, по существу являются глобальными переменными.
   При объявлении объектов их класса программа не создает никаких копий переменной static. Вместо этого все экземпляры класса совместно используют одну и ту же статическую переменную. Например название компании, колледжа и т. д. Статическая переменная задействует память только один раз во время загрузки класса: если какой-либо объект изменяет значение статической переменной, она сохраняет свое значение.
 
 
   б) метод класса
 
   Если ключевое слово static применяется в объявлении метода, то это статический метод Java.
 
• Статический метод относится к классу, а не к объекту класса
• Статический метод можно вызывать без создания экземпляра класса
   (обычный метод класса вызывается у экземпляра класса, а статический – у самого класса)
   (Если не использовать ключевое слово static и сделать метод нестатичным, необходимо создать объект класса для его вызова.)
 
• Статический метод может получать доступ к статическому члену и изменять его значение
• Методы, в объявлении которых использовано ключевое слово static, могут непосредственно работать только с локальными и статическими переменными.
• Они могут вызывать только другие статические методы.
• Они не могут ссылаться на члены типа this или super.
• Основной main метод Java является статическим, постольку, поскольку объект не является обязательным для вызова статического метода, если он был не статическим. Этот метод объявляют как static, поскольку он должен быть объявлен до создания любых объектов.
    Виртуальная машина сначала вызывает Java public static void main(), что приводит к выделению дополнительной памяти.
 
   в) блок
   Если для инициализации переменных типа static нужно выполнить вычисления, можно объявить статический блок, который будет выполняться только один раз при первой загрузке класса.
• Применяется для инициализации статического члена
• Выполняется перед методом main во время загрузки класса - внутренний класс

 
Не статические методы:

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

четверг, 24 сентября 2020 г.

Видимость переменных. Служебные слова-модификаторы доступа public, private, protected (Access modifiers)

Видимость переменных

Правила видимости переменных:

1 Переменная, объявленная в методе, существует(видна) с начала объявления метода до конца метода.

2 Переменная, объявленная в блоке кода (например if { }), существует до конца этого блока кода.

3 Переменные-аргументы метода существуют везде внутри метода.

4 Переменные класса или объекта существуют все время жизни содержащего их объекта.

(Их видимость дополнительно регулируется специальными модификаторами доступа: public, private).

5 Статические переменные классов существуют все время работы программы.

(Их видимость также определяется модификаторами доступа).


Служебные слова-модификаторы доступа

 

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

   (Эти служебные слова определяют места в коде, где к этим данным можно обратиться)

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

   Модификаторы private и protected нужны, чтобы защитить поля от нежелательного изменения или доступа из других программ, которые используют класс.

 

default (package-private) (без модификатора, модификатор по-умолчанию)

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

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

   Этот модификатор еще иногда называют «package», так как доступ к переменным и методам открыт для всего пакета, в котором находится их класс.
 

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

  private 

Область доступа - только из своего класса (в котором объявлен): помеченные им методы и поля (переменные) доступны (видны и могут быть использованы) только в этом классе.

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

   Это самая высокая степень закрытости – только свой класс. 


  protected 

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


   Последовательность слов-модификаторов по убыванию уровня закрытости: private, default, protected, public.
   *Во время наследования возможно изменения модификаторов доступа в сторону большей видимости - это сделано для того, чтобы не нарушался принцип LSP (Liskov substitution principle) для наследуемого класса. 


МодификаторДоступ из своего класса (в котором объявлен)
Доступ для классов своего пакетаДоступ из любого класса
public+++
без модификатора (package)++-
private+--

понедельник, 14 сентября 2020 г.

Полезные книги и ссылки

Ссылки:

 Официальная документация по Java: https://docs.oracle.com/javase/8/docs/index.html


 Список книг:

   Кэти Сиерра, Берт Бейтс - Изучаем Java (Head-First Java)

   Герберт Шилдт - Java 8. Полное руководство

   Брюс Эккель - Философия Java

   Чарльз Петцольд - Код

   Адитья Бхаргава - Грокаем алгоритмы



Какое программное обеспечение необходимо для программирования на Java

   Для того, чтобы начать программировать на Java необходимо:

 

   1 Установить и настроить программное обеспечение для разработки и запуска программ - JDK (Java Development Kit):

   - Перейти на сайт Oracle https://www.java.com/ru/download/help/windows_manual_download.xml

   - Выбрать и скачать установочный файл в зависимости от вашей операционной системы

   - Выполнить установку, следуя рекомендациям программы-установщика

   - Настроить переменную окружения, если установка производится на Windows

      Инсталлятор Java не выполняет настройку переменных окружения, поэтому необходимо сделать это вручную после установки.

   Во-первых, необходимо установить переменную JAVA_HOME, которая должна указывать на директорию, в которую установлена Java:

   Для Windows:

   Для установки переменных окружения открыть свойства компьютера, например, через меню “Пуск”.

   Затем нужно выбрать “Дополнительные параметры системы”, в открывшемся диалоге перейти на вкладку “Дополнительно” и нажать кнопку “Переменные среды”, после чего появится диалог настройки переменных окружения.

   Если переменная окружения JAVA_HOME уже есть - необходимо её отредактировать, если нет - создать новую.

   В качестве значения указывается путь к директории, куда установлена Java. К примеру:

   c:\Program Files\Java\jdk1.8.0_25\, если устанавливался JDK

   либо c:\Program Files\Java\jre1.8.0_25\, если устанавливался только JRE.

   Во-вторых, чтобы можно было запускать Java из консоли, переменная PATH должна быть правильно настроена (Эта переменная указывает операционной системе список директорий, в которых нужно искать исполняемые файлы):

  После установки значения переменной JAVA_HOME требуется отредактировать значение переменной PATH, добавив туда путь к директории, где системе нужно искать исполняемые файлы Java, то есть %JAVA_HOME%\bin

   NB! Если устанавливается JDK, то в названии директории указывается номер версии, поэтому впоследствии, при установке более новых версий, необходимо менять значение переменной окружения JAVA_HOME.

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

 

   Удаление лишних файлов:

   Запускается консоль (cmd) и выполняется команда where java.

   Результат - путь к исполняемому файлу java.exe, который операционная система должна успешно обнаружить в том месте, куда установлен Java.

   Если файл не нашёлся - значит неправильно настроена переменная PATH.

   Иногда бывает, что находятся лишние исполняемые файлы

   (Происходит это из-за того, что инсталлятор Java вместо того, чтобы правильно настроить переменные окружения, пытается положить исполняемые файлы в директорию C:\Windows\system32 )

   Это может привести к рассогласованию версий Java (в разных директориях разные версии).

   Поэтому надо удалить из каталога C:\Windows\system32 исполняемые файлы java.exe, javaw.exe и javaws.exe, если они там обнаружатся.

      

   2 Скачать и установить одну из сред разработки программного обеспечения (приложение для написания программ на Java) - IDE (Integrated Development Environment) 

   Среда разработки  может включать в себя такие инструменты, как:

   - текстовый редактор с подсветкой кода

   - компилятор или интерпретатор

   - браузер классов, инспектор объектов и диаграмму иерархии классов

   - средства автоматизации сборки

   - отладчик

   - средства для интеграции с системами управления версиями (Git)

   - инструменты для упрощения конструирования графического интерфейса пользователя

 

   Примеры IDE:

      - IntelliJ IDEA

      - Eclipse

      - NetBeans

      - JCreator

      - Notepad (Блокнот)

Что такое JVM JRE JDK IDE

   IDE (Integrated Development Environment) (Интегрированная среда разработки) - среда разработки программного обеспечения (приложение для написания программ).

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

   Примеры IDE: - IntelliJ IDEA - Eclipse - NetBeans - JCreator - Notepad (Блокнот)


    JDK (Java Development Kit) (Набор для разработки Java) - набор инструментов для разработчика, необходимых для написания программ на Java.

   В состав JDK входит сам язык Java (его синтаксис), Java-машина, компилятор Java-классов и другое, что может понадобиться Java-разработчику: стандартные библиотеки классов Java (стандартный набор классов), различные вспомогательные утилиты, документацию, примеры программного кода, JRE и JVM.

 

   Скачать JDK для подходящей платформы можно по ссылке



   JRE (Java Runtime Environment) - среда выполнения (исполнения) Java.

      (Минимальная реализация виртуальной машины, необходимая для исполнения готовых Java-приложений)

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

      В отличие от JDK, не содержит компилятора и других средств разработки.

      Подробнее о работе JRE можно прочитать по ссылке.


   JVM (Java Virtual Machine) - виртуальная (то есть реализованная программно) машина Java.

      Виртуальная машина является прослойкой между процессором и программным кодом.

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

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

   Под платформой обычно понимают сочетание аппаратной (процессор) и программной (операционная система, ОС) части.

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

   Также JVM осуществляет управление и оптимизацию памяти, используемой приложением.


   Виртуальная машина (JVM) для выполнения программы запускается с помощью утилиты java.


   Принцип использовани JVM:

      1) Программист создаёт файлы программы с расширением .java

      2) Файлы программы с расширением .java компилируются и получаются файлы с расширением .class, в которых уже хранится байт-код

      3) JVM считывает байт-код, интерпретирует его в машинный код, передавая его для исполнения платформе

      4) Команды программы выполняются платформой


   Подробнее о работе JVM можно прочитать здесь: https://javaika.blogspot.com/2018/11/2-java.html

   А также здесь: https://topjava.ru/blog/what-is-the-jvm


   Ниже приведена коцептуальная диаграмма продуктов Oracle, составляющих Java SE:

         Здесь:

            Java language - язык Java (его синтаксис).

            Java - Java runtime launcher - консольная утилита,позволяющая запустить программу. Она нужна для запуска JVM с полученными class-файлами.

            Javac - Java compiler (компилятор) - консольная утилита для компиляции кода. Она компилирует java-файлы в class-файлы, содержащие байт-код.

            Javadoc - Java documentation generator - утилита для генерации документации на основе комментариев в коде

            Jar - Java archive tool - утилита для работы с Jar.

            Javap - Java class file disassembler

            ...

            Java Web Start - Java Web Services Tools


   О том, как происходит напиcание и исполнение программ с их помощью можно прочитать здесь: https://javaika.blogspot.com/2018/11/2-java.html

вторник, 8 сентября 2020 г.

Управление исключениями (возникающими ошибками) в Java

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

  Исключения (ошибки при выполнении программы) могут возникать в результате:

   - неправильных действий пользователя

   - отсутствии необходимого ресурса на диске

   - потери соединения с сервером по сети

   - ошибки программирования

   - неправильное использование API.

 

   Настраивают управление исключениями для того, чтобы программа чётко знала, как поступать в ситуации появления ошибок при выполнении программы.

   (чтобы предупредить и решить исключительные ситуации в программе, для того чтобы её выполнение могло быть продолжено)

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

  Использование исключений в Java позволяет повысить отказоустойчивость программы за счет использования «запасных» путей, отделить логику основного кода от кода обработки исключительных ситуаций за счет использования блоков catch, а также дает возможность переложить обработку исключений на пользователя нашего кода с помощью throws.

 

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

 

try, catch, finally, throws

 

   1) try – определяет (заключает под собой) блок кода, в котором может произойти исключение;

   2) catch – определяет (заключает под собой) блок кода, в котором происходит обработка исключения;

   (Действия при сбое, который может случиться внутри оператора try)

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

(Блок finally часто используется для того, чтобы закрыть открытые в блоке try потоки или освободить ресурсы. Однако при написании программы не всегда возможно уследить за закрытием всех ресурсов. Для облегчения этой задачи существует конструкция try-with-resources, которая автоматически закрывает ресурсы, открытые в блоке try).

   Эти ключевые слова используются для создания в программном коде специальных обрабатывающих конструкций: try{}catch, try{}catch{}finally, try{}finally{}.

   На стадии разработки программы мы «ограждаем» опасные участки кода в отношении исключений с помощью блока try{}, предусматриваем «запасные» пути с помощью блока catch{}, в блоке finally{} мы пишем код, который выполняется в программе при любом исходе.

 

   При появлении исключения в блоке try обработчик исключения ищется в следующем за ним блоке catch. Если в catch есть обработчик данного типа исключения – управление переходит к нему. Если нет, то JVM ищет обработчик этого типа исключения в цепочке вызовов методов до тех пор, пока не будет найден подходящий catch. После выполнения блока catch управление передается в необязательный блок finally. В случае, если подходящий блок catch не найден, JVM останавливает выполнение программы, и выводит стек вызовов методов – stack trace, выполнив перед этим код блока finally при его наличии.

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


  4) throw – используется для возбуждения исключения

  5) throws – Это ключевое слово в сигнатуре метода означает, что при определенных условиях метод, может выбросить исключение

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

   Такое предупреждение является частью интерфейса метода и предоставляет право пользователю на собственный вариант реализации обработчика исключения (предоставляя написание кода по обработке исключения в Java пользователю метода.).

   После throws указывается тип выбрасываемого исключения.

 

   Типы выбрасываемых исключений:

   Обычно это наследники класса Exception Java.

   Поскольку Java является объектно-ориентированным языком, все исключения в Java представляют собой объекты.

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

   Иерархия исключений java:




   Throwable - название класса "предка", от которого унаследовано множество возможных исключительных ситуаций.

   Далее всё это множество исключительных ситуаций можно разделить на две группы:

   1) Непроверяемые на стадии компиляции (неконтролируемые) (unchecked)

   Это Error (исключения, унаследованные из класса Error) + RuntimeException (часть исключений - наследников класса Exception)

  RuntimeException – исключения, генерируемые JVM во время выполнения программы. Часто причиной возникновения их являются ошибки программирования.

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

   Это ошибки, возникающие при выполнении программы в результате сбоя работы JVM, переполнения памяти или сбоя системы. Обычно они свидетельствуют о серьезных проблемах, устранить которые программными средствами невозможно, такими как деление на 0, нулевой указатель и т.п.  

 

Список непроверяемых исключений:

   ArithmeticException - арифметическая ошибка, например, деление на ноль

   ArrayIndexOutOfBoundsException - выход индекса за границу массива

   ArrayStoreException - присваивание элементу массива объекта несовместимого типа

   ClassCastException - неверное приведение

   EnumConstantNotPresentException - попытка использования неопределённого значения перечисления

   IllegalArgumentException - неверный аргумент при вызове метода

   IllegalMonitorStateException - неверная операция мониторинга

   IllegalStateException - некорректное состояние приложения

   IllegalThreadStateException - запрашиваемая операция несовместима с текущим потоком

   IndexOutofBoundsException - тип индекса вышел за допустимые пределы

   NegativeArraySizeException - создан массив отрицательного размера

   NullPointerException - неверное использование пустой ссылки

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

   NumberFormatException - неверное преобразование строки в числовой формат SecurityException - попытка нарушения безопасности

   StringIndexOutOfBounds - попытка использования индекса за пределами строки

   TypeNotPresentException - тип не найден

   UnsupportedOperationException - обнаружена неподдерживаемая операция

 

 

   2) Проверяемые на стадии компиляции (предвидимые еще на стадии написания программы) (контролируемые) (checked)

   Это Exception (кроме RuntimeException)

   Основная часть работы разработчика на Java при работе с исключениями – обработка таких ситуаций: проверяемые исключения должны быть явно пойманы в теле метода или объявлены в секции throws метода.

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


   Список проверяемых системных исключений, которые можно включать в список throws:

   ClassNotFoundException - класс не найден

   CloneNotSupportedException - попытка клонировать объект, который не реализует интерфейс Cloneable

   IllegalAccessException - запрещен доступ к классу

   InstantiationException - попытка создать объект абстрактного класса или интерфейса

   InterruptedException - поток прерван другим потоком

   NoSuchFieldException - запрашиваемое поле не существует

   NoSuchMethodException - запрашиваемый метод не существует

   ReflectiveOperationException - исключение, связанное с рефлексией

 

   Пример использования ключевых слов в java-программе:

//метод считывает строку с клавиатуры

public String input() throws MyException {//предупреждаем с помощью throws,
// что метод может выбросить исключение MyException
      BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String s = null;
//в блок try заключаем код, в котором может произойти исключение, в данном
// случае компилятор нам подсказывает, что метод readLine() класса
// BufferedReader может выбросить исключение ввода/вывода
    try {
        s = reader.readLine();
// в блок  catch заключаем код по обработке исключения IOException
    } catch (IOException e) {
        System.out.println(e.getMessage());
// в блоке finally закрываем поток чтения
    } finally {
// при закрытии потока тоже возможно исключение, например, если он не был открыт, поэтому “оборачиваем” код в блок try
        try {
            reader.close();
// пишем обработку исключения при закрытии потока чтения
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }

    if (s.equals("")) {
// мы решили, что пустая строка может нарушить в дальнейшем работу нашей программы, например, на результате этого метода нам надо вызывать метод substring(1,2), поэтому мы вынуждены прервать выполнение программы с генерацией своего типа исключения MyException с помощью throw
        throw new MyException("String can not be empty!");
    }
    return s;
}
 

 Этот же пример с помощью try-with-resources:

public String input() throws MyException {
    String s = null;
    try(BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))){
        s = reader.readLine();
   } catch (IOException e) {
       System.out.println(e.getMessage());
   }
    if (s.equals("")){
        throw new MyException ("String can not be empty!");
    }
    return s;
}

вторник, 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

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