02mod, 03fin, 04full scenarios

This commit is contained in:
Ivan I. Ovchinnikov 2022-10-18 23:35:47 +03:00
parent e00ead2cbc
commit 45e15d385c
12 changed files with 656 additions and 582 deletions

Binary file not shown.

BIN
build/jc3-03.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

72
jc4-04.tex Normal file
View File

@ -0,0 +1,72 @@
% enum Color{
% RED("#FF0000"), BLUE("#0000FF"), GREEN("#00FF00");
% private String code;
% Color(String code){
% this.code = code;
% }
% public String getCode(){ return code;}
% }
%public class Main{
% public static void main(String[] args) {
% System.out.println(Color.RED.getCode());
% System.out.println(Color.GREEN.getCode());
% }
%}
% package ru.gb.jcore;
% public class Orange {
% public void squeezeJuice(){
% System.out.println("Squeeze juice ...");
% }
% class Juice{
% public void flow(){
% System.out.println("Juice dripped ...");
% }
% }
% }
% package ru.gb.jcore;
% public class Main {
% public static void main(String[] args) {
% Orange orange = new Orange();
% Orange.Juice juice = orange.new Juice();
% orange.squeezeJuice();
% juice.flow();
% }
% }
%///////////////////////////////
% public class Orange {
% private Juice juice;
% public Orange(){
% this.juice = new Juice();
% }
% public void squeezeJuice(){
% System.out.println("Squeeze juice ...");
% juice.flow();
% }
% private class Juice{
% public void flow(){
% System.out.println("Juice dripped ...");
% }
% }
% }
% public class Main {
% public static void main(String[] args) {
% Orange orange = new Orange();
% orange.squeezeJuice();
% }
% }

View File

@ -251,7 +251,7 @@
\label{pic:float-struct}
\end{figure}
Рисунок \hrf{pic:float-struct} демонстрирует, как распределяются биты в числах с плавающей запятой разных разрядностей, где S - Sign (знак), E - Exponent (8(11) разрядов поля порядка, экспонента), M - Mantissa (23(52) бита мантиссы, дробная часть числа).
Рисунок \hrf{pic:float-struct} демонстрирует, как распределяются биты в числах с плавающей запятой разных разрядностей, где S - Sign (знак), E - Exponent (8(11) разрядов поля порядка, экспонента), M - Mantissa (23(52) бита мантиссы, дробная часть числа). Также на рисунке показана так называемая, мнимая единица, она всегда есть в самом старшем разряде мантиссы, поэтому её всегда подразумевают, но в явном виде не хранят, экономя один бит информации.
Если попытаться уложить весь стандарт в два предложения, то получится примерно следующее: получить число в соответствующих разрядностях возможно по формулам:
\begin{equation*}
@ -290,12 +290,11 @@
Получится, что
\begin{equation*}
\begin{gathered}
(-1)^1 \times 2^{(124-127)} \times (1 + \frac{2097152}{2^{23}}) = 1,15652 \\
(-1)^1 \times 2^{(124-127)} \times (1 + \frac{2097152}{2^{23}}) = -0,15625 \\
\text{или, тождественно,}\\
(-1)^1 \times 1,01e-3 = \\
1\times2^{-3} + 0\times2^{-4} + 1\times2^{-5} = \\
1\times0,125 + 0\times0,0625 + 1\times0,03125 = \\
0,125 + 0,03125 = 0,15625
(-1)^1 \times 1,01e-3 = (-1)^1 \times 1 \times2^{-3} + 0\times2^{-4} + 1\times2^{-5} = \\
(-1)^1 \times 1 \times0,125 + 0\times0,0625 + 1\times0,03125 = 0,125 + 0,03125 = \\
(-1)^1 \times 0,15625 = -0,15625
\end{gathered}
\end{equation*}

View File

@ -1,4 +1,4 @@
\immediate\write18{texcount -sum -1 prog-draft.tex > /tmp/wordcount.tex}
\immediate\write18{texcount -sum -1 program-draft.tex > /tmp/wordcount.tex}
\documentclass[12pt, a4paper]{article}
\usepackage[english,russian]{babel} % локализация и переносы
\usepackage{fancyhdr}
@ -18,8 +18,7 @@
\usetikzlibrary{mindmap,trees}
\titlelabel{\thetitle. \quad}
\babelfont{rm}{PT Astra Serif}
\babelfont{sf}{PT Astra Serif}
\babelfont{rm}{IBM Plex Sans}
\babelfont{tt}{PT Mono}
\graphicspath{{./pics/}}
@ -95,7 +94,7 @@
child[concept color=orange!20!] { node[concept] {Много\-поточ\-ность} }
child[concept color=orange!20!] { node[concept] {Generics} }
}}
child[concept color=orange!20!] { node[concept] {Development Kit}
child[concept color=orange!20!] { node[concept] {Develop\-ment Kit}
[clockwise from=300]
child[concept color=orange!20!] { node[concept] {}
[clockwise from=45]
@ -159,21 +158,16 @@
child[concept color=green!40!] { node[concept] {JVM} }
}
child[concept color=green!20!] { node[concept] {Структура проекта} }
child[concept color=green!20!] { node[concept] {Управ\-ление проектом}
[clockwise from=90]
child[concept color=green!40!] { node[concept] {Jar файлы} }
child[concept color=orange!20!] { node[concept] {Gradle/ Maven} }
child[concept color=red!20!] { node[concept] {Bazel} }
}
}
child[concept color=green!20!] { node[concept] {Core}
[clockwise from=90]
[clockwise from=135]
child[concept color=blue!20!] { node[concept] {Базовые функции языка} }
child[concept color=green!40!] { node[concept] {Данные} }
child[concept color=green!40!] { node[concept] {Функции} }
child[concept color=blue!20!] { node[concept] {ООП} }
child[concept color=orange!20!] { node[concept] {Исключе\-ния} }
}
child[concept color=orange!20!] { node[concept] {Development Kit}
child[concept color=orange!20!] { node[concept] {Develop\-ment Kit}
[clockwise from=300]
child[concept color=gray!20!] { node[concept] {Потоки ввода-вывода} }
};
@ -188,14 +182,13 @@
\endhead
Л & Платформа: история и окружение & Краткая история (причины возникновения); JDK, JRE, JVM, выбор версии; CLI: сборка, множественные источники, пакеты, запуск; структура проекта: пакеты, классы, метод main, комментарии, документирование;\\
С & Компиляция и интерпретация кода & Ручная компиляция проекта в нескольких классах и нескольких пакетах; настройка Jupyter Notebook with IJava Kernel; Формирование веб-страницы из стандартного javadoc к своим хеловорлдам\\
Л & Управление проектом: сборщики проектов & Управление проектом: Jar-файлы; Gradle/Maven: зависимости, выгрузка артефакта, публичные репозитории, свойства проекта, приватные репозитории (хостинг);
Bazel\\
С & Сборка проекта & Maven сборка, Gradle сборка, компиляция jar-файла. Настройка хранения кэша сборщиков, настройка дополнительных репозиториев и аплоуда артефактов.\\
Л & Специализация: данные и функции & Базовые функции языка: математические операторы, условия, циклы, бинарные операторы; Данные: типы, преобразование типов, константы и переменные (примитивные, ссылочные), бинарное представление, массивы (ссылочная природа массивов, индексация, манипуляция данными); Функции: параметры, возвращаемые значения, перегрузка функций;\\
С & Функции, манипулирующие данными & Задания, использующие ссылочные свойства массивов, перегрузку функций. Добавить бинарные арифметики, двоичные представления чисел\\
Л & Специализация: ООП & Инкапсуляция: Классы и объекты (внутренние классы, вложенные классы, static, private/public, final, интерфейс взаимодействия с объектом), перечисления (создание, конструкторы перечислений, объекты перечислений, дополнительные свойства); Наследование: extends, Object (глобальное наследование), protected, преобразование типов, final; Полиморфизм: override, abstract, final;\\
Л & Специализация: ООП & Инкапсуляция: Классы и объекты (static, private/public, final, интерфейс взаимодействия с объектом); Наследование: extends, Object (глобальное наследование), protected, преобразование типов, final; Полиморфизм: override, abstract, final;\\
С & Классы и объекты & Использование свойств статичности сущностей. Сложные случаи несоблюдения инкапсуляции, Демонстрация порядка работы конструкторов при наследовании, сложные случаи полиморфизма.\\
Л & Специализация: Тонкости работы & Файловая система и представление данных; Пакеты \code{java.io}, \code{java.nio}, \code{String}, \code{StringBuilder}, string pool, ?JSON/XML?\\
Л & ООП и исключения & внутренние классы, вложенные классы; перечисления (создание, конструкторы перечислений, объекты перечислений, дополнительные свойства); Механизм и понятие, введение в \textbf{многопоточность}, throw; Наследование и полиморфизм в исключениях; Способы обработки исключений (try/catch, throws, finally); try-with-resources, штатные и нештатные ситуации \\
С & Обработка исключений & Создание, выбрасывание и обработка исключений, обработка исключений в стиле «до захвата ресурса», проброс исключений\\
Л & Специализация: Тонкости работы & Файловая система и представление данных; Пакеты \code{java.io}, \code{java.nio}, \code{String}, \code{StringBuilder}, string pool, ?JSON/XML?\\
С & Внешний мир & Создание, отправка и принятие данных в общепринятых форматах. Сохранение и загрузка состояния программы между запусками, работа с большими текстами (поиск, замена, генерация)\\
\hline
\end{longtable}
@ -259,19 +252,25 @@ Bazel\\
\begin{tikzpicture}
\path[mindmap,concept color=magenta!70!,text=black]
node[concept] {Java}
[clockwise from=30]
[clockwise from=0]
child[concept color=green!20!] { node[concept] {Core}
[clockwise from=120]
[clockwise from=0]
child[concept color=orange!20!] { node[concept] {Интер\-фейсы} }
child[concept color=orange!20!] { node[concept] {Исключе\-ния} }
child[concept color=orange!20!] { node[concept] {Много\-поточ\-ность} }
child[concept color=orange!20!] { node[concept] {Generics} }
}
child[concept color=orange!20!] { node[concept] {Development Kit}
[clockwise from=0]
child[concept color=orange!20!] { node[concept] {Develop\-ment Kit}
[clockwise from=-30]
child[concept color=gray!20!] { node[concept] {GUI (Swing/ JavaFX)} }
child[concept color=green!40!] { node[concept] {Коллекции} }
};
}
child[concept color=green!20!] { node[concept] {Управ\-ление проектом}
[clockwise from=-30]
child[concept color=green!40!] { node[concept] {Jar файлы} }
child[concept color=orange!20!] { node[concept] {Gradle/ Maven} }
child[concept color=red!20!] { node[concept] {Bazel} }
}
;
\end{tikzpicture}
\subsection{План по лекциям и семинарам}
@ -280,8 +279,6 @@ Bazel\\
Блок курса & Название & Содержание
\\ \hline
\endhead
Л & Исключения & Механизм и понятие, введение в \textbf{многопоточность}, throw; Наследование и полиморфизм в исключениях; Способы обработки исключений (try/catch, throws, finally); try-with-resources, штатные и нештатные ситуации \\
С & Обработка исключений & Создание, выбрасывание и обработка исключений, обработка исключений в стиле «до захвата ресурса», проброс исключений\\
Л & Интерфейсы & Понятие и принцип работы, implements; Наследование и множественное наследование интерфейсов; Значения по-умолчанию\\
С & & \\
Л & Обобщённое программирование & \\
@ -292,6 +289,9 @@ Bazel\\
С & Паралеллизм и асинхронность & \\
Л & Графический интерфейс пользователя & GUI (Swing): Окна и свойства окон, \textbf{Многопоточность} и абстргирование асинхронных вызовов; менеджеры размещений и проведение параллелей с веб-фреймворками, разделение на фронт-энд и бэк-энд; JPanel и рисование, Обработка действий пользователя \\
С & Простейшие интерфейсы пользователя & Кнопки, канва для рисования, смена кадров, движение, работа с параллельностью (закрепление материала по многопоточности) \\
Л & Управление проектом: сборщики проектов & Управление проектом: Jar-файлы; Gradle/Maven: зависимости, выгрузка артефакта, публичные репозитории, свойства проекта, приватные репозитории (хостинг);
Bazel\\
С & Сборка проекта & Maven сборка, Gradle сборка, компиляция jar-файла. Настройка хранения кэша сборщиков, настройка дополнительных репозиториев и аплоуда артефактов.\\
\hline
\end{longtable}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,7 +7,9 @@
Экран & Слова \\ \hline
\endhead
Титул & Перейдём к интересному: что можно хранить в джаве, как оно там хранится, и как этим манипулировать \\ \hline
Титул & Продолжим наше погружение в джаву, рассмотрим \\ \hline
Титул2 & более детально хранение и поговорим об объектной природе языка \\ \hline
На прошлом уроке & На прошлом уроке мы рассмотрели базовый функционал языка, то есть основную встроенную функциональность, такую как математические операторы, условия, циклы, бинарные операторы. Также разобрали способы хранения и представления данных в Java, и в конце поговорили о способах манипуляции данными, то есть о функциях (в терминах языка называющиеся методами) \\ \hline
@ -17,19 +19,29 @@
Чертёж (набросок рисунка) кота & Что такое класс? С точки зрения ООП, класс определяет форму и сущность объекта и является логической конструкцией, на основе которой построен весь язык Java. Наиболее важная особенность класса состоит в том, что он определяет новый тип данных, которым можно воспользоваться для создания объектов этого типа, т.е. класс — это шаблон (чертёж), по которому создаются объекты (экземпляры класса). Для определения формы и сущности класса указываются данные, которые он должен содержать, а также код, воздействующий на эти данные.\\ \hline
% --- Комментатор_Ильнар В курсах специализации ребятам сначала показывается API существующих библиотек, а потом говорится что если вам нужен класс (тип данных) которого еще нет, то мы можем создать свое и вот тут возникает создание своих классов. Как вариант эту идею тут можно повторить. Сказать что создаем мы свои классы, когда у нас не хватает уже созданных. Математикам в универе я обычно говорю про тип данных для комплексных чисел, тут это не прокатит конечно, но можно саму идею озвучить.
Котик и стопки документов & Если мы хотим работать в нашем приложении с документами, то необходимо для начала объяснить приложению, что такое документ, описать его в виде класса (чертежа) Document. Рассказать какие у него должны быть свойства: название, содержание, количество страниц, информация о том, кем он подписан и т.д. В этом же классе мы описываем что можно делать с документами: печатать в консоль, подписывать, изменять содержание, название и т.д. Результатом такого описания и будет класс Document. Однако это по-прежнему всего лишь чертеж.\\ \hline
% --- Комментатор_Ильнар Возможно тут надо вспомнить про api. У нас получается есть хранимые данные (состояния) и способы взаимодействия с ними, которые программистами называются API. И показать в твоих примерах что есть что.
Буквы, написанные по трафарету чтобы было видно сам трафарет. документы, одинаковые по структуре и разные по содержанию. & Если нам нужны конкретные документы, а нам они обязательно нужны, то необходимо создавать объекты: документ №1, документ №2, документ №3. Все эти документы будут иметь одну и ту же структуру (название, содержание, ...), с ними можно выполнять одни и те же действия (печатать, подписать, ...) НО наполнение будет разным (например, в первом документе содержится приказ о назначении работника на должность, во втором, о выдаче премии отделу разработки и т.д.). \\ \hline
03-Структура & Начнём с малого, напишем свой первый класс. Представим, что нам необходимо работать в нашем приложении с котами. Java ничего не знает о том, что такое коты, поэтому нам необходимо создать новый класс (тип данных), и объяснить что же такое кот. Создадим проект, его структура нам не в новизну, и будет иметь вид как на слайде, в мейне пока что простой хелловорлд, а вот с новым файлом кота пойдём разбираться, Создадим его для простоты в том же пакете, что и мейн \\ \hline
03-Структура & Начнём с малого, напишем свой первый класс. Представим, что нам необходимо работать в нашем приложении с котами. Java ничего не знает о том, что такое коты, поэтому нам необходимо создать новый класс (тип данных), и объяснить что такое кот. Создадим проект, его структура нам не в новизну, и будет иметь вид как на слайде, в мейне пока что простой хелловорлд, а вот с новым файлом кота пойдём разбираться, Создадим его для простоты в том же пакете, что и мейн \\ \hline
лайвкод 03-кот & Начнем описывать класс Cat. как мы все прекрасно помним, имя класса должно совпадать с именем файла, в котором он объявлен, т.е. класс Cat должен находиться в файле Cat.java. Пусть у котов есть три свойства: name (кличка), color (цвет) и age (возраст); и они пока ничего не умеют делать. Класс Cat будет иметь следующий вид. (String name; String color; int age;)\\ \hline
% +++ Комментатор_Ильнар Тут с котом тоже давай выделим состояние и api. Состояние кличка, вес и т.п., api - покормить, погладить и т.п. Начнем с параметров кота (т.е. с хранения состояния)
лайвкод 03-кот & Начнем описывать в классе Cat так называемый API кота. как мы все прекрасно помним, имя класса должно совпадать с именем файла, в котором он объявлен, т.е. класс Cat должен находиться в файле Cat.java. Пусть у котов есть три свойства: name (кличка), color (цвет) и age (возраст); совокупность этих свойств называется состоянием, и коты пока ничего не умеют делать. Класс Cat будет иметь следующий вид. (String name; String color; int age;) свойства класса, записанные таким образом в виде переменных называются полями\\ \hline
03-экземпляр-кота & Мы рассказали Java что такое коты, теперь если мы хотим создать в нашем приложении конкретного кота, а я напомню, что в 90\% случаев мы сильно хотим создавать те или иные экземпляры, следует воспользоваться оператором new Cat(); в основном классе программы. Более подробно разберём, что происходит в этой строке, чуть позже. Пока же нам достаточно знать, что мы создали объект типа Cat (экземпляр класса Cat), и запомнить эту конструкцию. Для того чтобы с ним (экземпляром) работать, можем положить его в переменную, которой дать идентификатор cat1. \\ \hline
лайвкод 03-разные-коты & Припоминаем разницу между объявлением, присваиванием и инициализацией, становится понятно, что тут произошло и как ещё можно создавать котов. Припоминаем также, что в переменной не лежит сам объект, а только ссылка на него, подробнее, как и обещал, позже. Объект cat1 создан по чертежу Cat, и значит у него есть поля name, color, age, с которыми можно работать (получать или изменять их значения). Для доступа к полям объекта служит оператор точка, которая связывает имя объекта с именем поля. Например, чтобы присвоить полю color объекта cat1 значение "Белый", нужно выполнить следующий код: cat1.color = "Белый"; Прошу, не запутайтесь, класс кота мы описали в отдельном файле, а создавать объекты и совершать манипуляции следует в мейне, не может же кот сам себе имя назначить \\ \hline
лайвкод 03-два-кота & Операция-точка служит для доступа к полям и методам объекта по его имени. Рассмотрим пример консольного приложения, работающего с объектами класса Cat. создадим двух котов, один будет белым барсиком 4х лет, второй чёрным мурзиком шести лет, и просто выведем информацию о них в терминал \\ \hline
% +++ Комментатор_Ильнар Выше используется термин поле класса, надо его пояснить, хотя бы простыми словами, чтобы народ не путался.
лайвкод 03-два-кота & Операция-точка служит для доступа к полям и методам объекта по его имени. Мы уже пользовались оператором точка для доступа к полю с длиной массива, например. Рассмотрим пример консольного приложения, работающего с объектами класса Cat. создадим двух котов, один будет белым барсиком 4х лет, второй чёрным мурзиком шести лет, и просто выведем информацию о них в терминал \\ \hline
% +++ Комментатор_Ильнар Тут надо напомнить что оператором точка точно уже все пользовались, когда у массива длину находили или взаимодействовали с любыми другими классами, например с ArrayList.
03-класс-объекты1 & Вначале мы создали два объекта типа Cat: cat1 и cat2, соответственно они имеют одинаковый набор полей name, color, age, почему? потому что они принадлежат одному классу, созданы по одному шаблону. Однако каждому из них мы в эти поля записали разные значения. Как видно из результата печати в консоли, изменение значения полей одного объекта, никак не влияет на значения полей другого объекта. Данные объектов cat1 и cat2 изолированы друг от друга. А значит мы делаем вывод о том, поля хранятся в классе, а значения полей хранятся в объектах. \\ \hline
@ -47,17 +59,27 @@
лайвкод 03-один-кот & Именно от оператора нью будет зависеть, сколько именно объектов будет создано в программе. На первый взгляд может показаться, что переменной cat2 присваивается ссылка на копию объекта cat1, т.е. переменные cat1 и cat2 будут ссылаться на разные объекты в памяти. Но это не так. На самом деле cat1 и cat2 будут ссылаться на один и тот же объект. Присваивание переменной cat1 значения переменной cat2 не привело к выделению области памяти или копированию объекта, лишь к тому, что переменная cat2 содержит ссылку на тот же объект, что и переменная cat1.\\ \hline
03-один-кот-2 & Таким образом, любые изменения, внесённые в объекте по ссылке cat2, окажут влияние на объект, на который ссылается переменная cat1, поскольку это один и тот же объект в памяти, как и в примере на слайде, где мы как будто бы указали возраст второго кота 6 лет, а при выводе, возраст 6 лет оказался и у первого кота.
\\ \hline
03-один-кот-2 & Таким образом, любые изменения, внесённые в объекте по ссылке cat2, окажут влияние на объект, на который ссылается переменная cat1, поскольку это один и тот же объект в памяти, как и в примере на слайде, где мы как будто бы указали возраст второго кота 6 лет, а при выводе, возраст 6 лет оказался и у первого кота. Это довольно легко себе представить как ярлыки для запуска одной и той же программы на рабочем столе и в меню Пуск. Или если мы на один и тот же шкафчик в раздевалке наклеим два номера - сам шкафчик можно будет найти по двум ссылкам \\ \hline
% +++ Комментатор_Ильнар Я бы предложил привести какой-то пример типа шкафчика в раздевалке, где на один шкафчик наклеено 2 номерка. в итоге шкафчик то один и тот же, хотя найти его можно по двум номеркам. Но внутри то это не 2 ящика, что положишь в один сразу будет "доступно" в другом. Или номерки еще на что-то заменить, чтобы не было путаницы с массивом. Но это так в виде мыслей.
Метод - это функция, принадлежащая классу & Ранее мы уже говорили о том, что в языке Java любая программа состоит из классов и функций, которые могут описываться только внутри них. Именно поэтому все функции в языке Java являются методами. А метод, как мы помним, это - функция, являющаяся частью некоторого класса, которая может выполнять операции над данными этого класса. \\ \hline
03-нестатик & Метод для своей работы может использовать поля объекта и/или класса, в котором определен, напрямую, без необходимости передавать их во входных параметрах. Это похоже на использование глобальных переменных в функциях, но в отличие от глобальных переменных, метод может получать прямой доступ только к членам класса. Самые простые методы работают с данными объектов.\\ \hline
% +++ Комментатор_Ильнар Вот тут я бы сказал с состояниями разобрались, теперь го к действиям, т.е. к api нашего класса. Комментарий к слайду про метод.
03-нестатик & Метод для своей работы может использовать поля объекта и/или класса, в котором определен, напрямую, без необходимости передавать их во входных параметрах. Это похоже на использование глобальных переменных в функциях, но в отличие от глобальных переменных, метод может получать прямой доступ только к членам класса. Самые простые методы работают с данными объектов. Методы формируют АПИ классов, то есть способ взаимодействия с классами, интерфейс \\ \hline
Лайвкод 03-метод & Вернёмся к примеру с котиками. Все мы знаем, что котики умеют урчать, мяукать и смешно прыгать. В целях демонстрации мы в описании этих действий просто будем делать разные выводы в консоль, хотя мы и можем научить нашего котика выбирать минимальное значение из массива, но это было бы неожиданно. Итак опишем метод например подать голос и прыгать. \\ \hline
Лайвкод 03-метод-вызов & Обращение к методам выглядит очень похожим на стандартный способом, через точку, как к полям. Теперь когда мы хотим позвать нашего котика, он нам скажет, мяу, я имя котика, а если мы решили что котику надо прыгать, он решит, прилично-ли это - прыгать в его возрасте. Как видно, барсик замечательно прыгает, а мурзик от прыжков воздержался, хотя попрыгать мы попросили их обоих \\ \hline
Вопросы для проверки
\begin{enumerate}
\item Что такое класс?
\item Что такое поле класса?
\end{enumerate} & Что такое класс? Класс это шаблон группы объектов. Класс - это новый тип данных в программе
Что такое поле класса? Поле (атрибут) класса — это характеристика объекта, также можно сказать, что это часть его состояния.
Вопрос: На какие три этапа делится создание объекта? Создание идентификатора, Выделение памяти под наш объект, сохранение ссылки на объект в переменную. \\ \hline
шрифтом курьер слово static
викисловарь - статика этимология & Теперь, когда мы более-менее хорошо знаем, что такое класс и объект, хотелось бы остановиться на специальном модификаторе - static, делающем переменную или метод "независимыми" от объекта. Если формально, то: Static — модификатор, применяемый к полю, блоку, методу или внутреннему классу, он указывает на привязку субъекта к текущему классу. Для использования таких полей и методов, соответственно, объект создавать не нужно. В Java большинство членов служебных классов являются статическими.\\ \hline
@ -88,8 +110,27 @@
03-статическое-поле-признак & Создав тех же самых котов, которых мы создавали весь урок, мы получим двух мурзиков и ни одного барсика. Почему это произошло? По факту переменная у нас одна на всех, и значение тоже одно, а значит каждый раз мы меняем именно его, а все остальные коты ничего не подозревая смотрят на значение общей переменной и бодро его возвращают. Поэтому, чтобы не запутаться, к статическим переменным, как правило, обращаются не по ссылке на объект — cat1.name, а по имени класса — Cat.name. \\ \hline
03-статические-поля & К слову, статические переменные — редкость в Java. Вместо них применяют статические константы. Они определяются ключевыми словами static final и по соглашению о внешнем виде кода пишутся в верхнем регистре. \\ \hline
% +++будет в дз+++ Комментатор_Ильнар Как один из примеров использоваиня статических полей я обычно даю подчет количества созданных котов, это можно дать когда с конструкторами разберетесь. То есть в конструктор засовываем инкремент на статическое поле count и при каждом вызове конструктора будем его увеличивать)
Введение в управление памятью & Понимая поверхностно, как организовано создание и хранение объектов, можем углубиться в эту тему. Это факультативная часть, выходящая достаточно далеко за рамки джуниор позиции. \\ \hline
Вопросы для проверки
\begin{enumerate}
\item Какое свойство добавляет ключевое слово static полю или методу?
\begin{enumerate}
\item неизменяемость
\item принадлежность классу
\item принадлежность приложению
\end{enumerate}
\item Может ли статический метод получить доступ к полям объекта?
\begin{enumerate}
\item не может
\item может только к константным
\item может только к неинициализированным
\end{enumerate}
\end{enumerate} & Какое свойство добавляет ключевое слово static полю или методу? неизменяемость принадлежность классу принадлежность приложению. Конечно, принадлежность классу, как следствие отсутствие необходимости создавать объект для таких полей и методов.
Может ли статический метод получить доступ к полям объекта? не может - может только к константным - может только к неинициализированным. не может, потому что объект всегда знает какого он класса, а классы никогда не знают, какие именно объекты были от него созданы. \\ \hline
отбивка Введение в управление памятью & Понимая поверхностно, как организовано создание и хранение объектов, можем углубиться в эту тему. Это факультативная часть, выходящая достаточно далеко за рамки джуниор позиции. \\ \hline
03-память & Это глубокое погружение в управление памятью Java позволит расширить ваши знания о том, как работает куча, ссылочные типы и сборка мусора. Поможет лучше понять глубинные процессы и, как следствие, писать более хорошие программы. Для оптимальной работы приложения JVM делит память на область стека (stack) и область кучи (heap). Всякий раз, когда мы объявляем новые переменные, создаем объекты или вызываем новый метод, JVM выделяет память для этих операций в стеке или в куче. Далее будет представлена модель организации памяти в Java. Чуть позже мы её рассмотрим подробнее, а начнем со стека. \\ \hline
@ -129,8 +170,9 @@ Permanent Generation — эта область содержит метаинфо
\item Когда эта область памяти полностью заполняется, Java бросает java.lang.OutOfMemoryError;
\item Доступ к ней медленнее, чем к стеку;
\item Эта память, в отличие от стека, автоматически не освобождается. Для сбора неиспользуемых объектов используется сборщик мусора;
\item В отличие от стека, куча не является потокобезопасной и ее необходимо контролировать, правильно синхронизируя код.
\item В отличие от стека, который создаётся для каждого потока свой, куча не является потокобезопасной, поскольку для всех одна, и ее необходимо контролировать, правильно синхронизируя код.
\end{itemize} \\ \hline
% +++ Комментатор_Ильнар К последнему пункту напомнить еще раз о чем ты говорил, что стеки для каждого потока свои, а куча для всех одна.
Сборщик мусора & Вроде разобрались, разделили память так, как её разделяет ЖВМ. Предлагаю взглянуть на то, как ЖВМ осуществляет управление неиспользуемыми объектами \\ \hline
@ -199,6 +241,12 @@ Permanent Generation — эта область содержит метаинфо
- Стековая память существует лишь какое-то время работы программы, а память в куче живет с самого начала до конца работы программы;
- Если память стека полностью занята, то Java Runtime бросает java.lang.StackOverflowError, а если память кучи заполнена, то бросается исключение java.lang.OutOfMemoryError: Java Heap Space;
- Размер памяти стека намного меньше памяти в куче. Из-за простоты распределения памяти (LIFO), стековая память работает намного быстрее кучи. \\ \hline
Вопросы для проверки
По какому принципу работает стек?
Что быстрее, стек или куча?
Что больше, стек или куча? & По какому принципу работает стек? лифо. Что быстрее, стек или куча? стек Что больше, стек или куча? куча \\ \hline
КОНСТРУКТОР & После такого, копания в шестерёнках, время расслабиться и перейти к разбору конструкторов Java. Вернёмся к нашим барсикам. \\ \hline
@ -207,6 +255,7 @@ Permanent Generation — эта область содержит метаинфо
03-плохой-конструктор лайвкод & Для инициализации объектов при создании в Java предназначены конструкторы. Конструктор - это частный случай метода в том смысле, что он тоже выполняет какие-то действия. Имя конструктора обязательно должно совпадать с именем класса, возвращаемое значение не пишется. Если создать конструктор класса Cat как показано на слайде, он автоматически будет вызываться при создании объекта. Теперь, при создании объектов класса Cat, все коты будут иметь одинаковые имена, цвет и возраст (это будут белые двухлетние Барсики). \\ \hline
03-параметризированный-конструктор лайвкод & При использовании конструктора из предыдущего примера, все созданные коты будут одинаковыми, пока мы вручную не поменяем значения их полей. Чтобы можно было указывать начальные значения полей наших объектов необходимо создать параметризированный конструктор. В приведенном на слайде примере, в параметрах конструктора используется первая буква от названия поля, это сделано для упрощения понимания логики заполнения полей объекта. И очень скоро будет заменено на более корректное использование ключевого слова this. \\ \hline
% +++ Комментатор_Ильнар Где-то тут я обычно рассказываю что "java" создает за нас пустой конструктор по умолчанию, если мы никакой конструктор не создали (обычно лезу в папку out и показываю что в скомпилированных классах такой конструктор есть, а в исходниках пусто). Потом говорю заклинание "такой конструктор создается только в том случае, если мы сами вообще никакой конструктор не создали. Стоит нам создать хотя бы 1 конструктор, как java перестает создавать за нас свои конструкторы, считая что мы сами умные". Вижу что дальше это есть)
03-вызов-параметризированного лайвкод & При такой форме конструктора, когда мы будем создавать в программе кота, необходимо будет обязательно указать его имя, цвет и возраст. Набор полей, которые будут заполнены через конструктор, вы определяете сами, то есть вы не обязаны заполнять все поля, которые есть в классе записывать в параметры конструктора, но при вызове обязаны заполнить аргументами все, что есть в параметрах, как при вызове метода.
@ -249,6 +298,18 @@ public Cat (Cat cat) {
То, что мы имеем здесь, это неглубокая копия, что удобно и довольно просто, поскольку все наши поля int и два String в данном случае являются либо примитивными, либо неизменяемыми типами. Если класс Java имеет изменяемые поля, например, массивы, то мы можем вместо простой сделать глубокую копию внутри его конструктора копирования. При глубокой копии вновь созданный объект не должен зависеть от исходного, потому что мы создаем отдельную копию каждого изменяемого объекта, а значит, например, просто скопировать ссылку на массив будет недостаточно. Такая вложенность может быть любая и определяется поставленной задачей. \\ \hline
Вопросы для проверки
Для инициализации нового объекта абсолютно идентичными значениями свойств переданного объекта используется
- пустой конструктор
- конструктор по-умолчанию
- конструктор копирования
Что означает ключевое слово this?
& Для инициализации нового объекта абсолютно идентичными значениями свойств переданного объекта используется - пустой конструктор - конструктор по-умолчанию - конструктор копирования. Конечно же копирования
Что означает ключевое слово this? это ссылка на текущий экземпляр класса, на используемый в данный момент объект\\ \hline
Инкапсуляция & Получше узнав о классах и объектах в джава, добавив для себя понимания организации этого добра в памяти, можно приступить к полному погружению в ООП. Начнём с Инкапсуляции.\\ \hline
Инкапсуляция - определение (от лат...) & Инкапсуляция связывает данные с манипулирующим ими кодом и позволяет управлять доступом к членам класса из отдельных частей программы, предоставляя доступ только с помощью определенного ряда методов, что позволяет предотвратить злоупотребление этими данными. То есть класс должен представлять собой "черный ящик", которым возможно пользоваться, но его внутренний механизм защищен от повреждений. \\ \hline
@ -261,623 +322,226 @@ public Cat (Cat cat) {
03-МД-без-протекта & модификатор private определяет доступность только внутри класса, и предпочтительнее всех. Вообще, хорошая практика предоставлять минимальный доступ к переменным в своём классе, чтобы те, кто им пользуются не внесли какие-то неожиданные изменения в его работу. Возникает законный вопрос, как так получается, что мы пользуемся всякими сложными механизмами без особенно долгого осмысливания, как они устроены и на чем основана их работа? Как мы можем дёргать за все эти ниточки ЖРЕ, если максимальная приватность - это очень хорошо? Все просто: создатели всех библиотек предоставляют простой и удобный интерфейс к своим разработкам. \\ \hline
продублировать 3-26 (поля класса кот) лайвкод & Внимательно осмотрев класс кота мы приходим к выводу, что хранить возраст котов очень неудобно, потому что каждый год нужно будет обновлять это значение для каждого объекта кота в программе, а это может оказаться утомительно. Выходом может оказаться хранение не возраста, а неизменяемого параметра даты рождения и подсчёт возрастка каждый раз, когда его запрашивают. Ведь человеку, который запрашивает возраст котика, не интересно, каким образом получено значение, прочитано из поля или вычислено, ему важен конечный результат. Вот это и есть сокрытие реализации. \\ \hline
cat.name = " ";
cat.color = "GREEEEEN";
cat.age = -12345;
лайвкод & Внимательно рассмотрим класс котика, хотя, конечно, мы его писали, что может пойти не так? А не так здесь может быть очень многое. Например, кто-то может создать хорошего кота, а потом его переименовать, перекрасить в зелёный цвет или сделать ему отрицательный возраст. В результате в программе находятся объекты с некорректным состоянием. Коту такое явно не понравится. \\ \hline
cat.name = "";
cat.color = "GREEEEEN";
cat.age = -12345;
лайвкод & Внимательно рассмотрим класс котика, хотя, конечно, мы его писали, что может пойти не так? А не так здесь может быть очень многое. Например, кто-то может создать хорошего кота, а потом его переименовать, перекрасить в зелёный цвет или сделать ему отрицательный возраст. Коту такое явно не понравится. \\ \hline
03-кот-с-дефолтными-полями лайвкод & Какая допущена ошибка? Все поля находятся в пакетном доступе. К ним можно обратиться в любом месте пакета: достаточно просто создать объект Cat — и всё, у любого программиста есть доступ к его данным напрямую через оператор точки. Нам, создателям класса Кот, нужно как-то защитить наши данные от некорректного вмешательства извне. Во-первых, все поля необходимо помечать модификатором private. \\ \hline
03-кот-с-приватными-полями & Private — самый строгий модификатор доступа в Java. Если его использовать, поля класса Cat не будут доступны за его пределами. Теперь поля вроде защищены от посягательства других программистов, но есть проблема, доступ к ним закрыт “намертво” - в программе нельзя даже получить вес существующей кошки, если это понадобится. Это тоже довольно неудобно, в таком виде наш класс практически нельзя использовать. \\ \hline
03-публичные-геттеры лайвкод & Нам нужно решить вопросы с получением и изменением значений полей. На помощь к нам приходят геттеры и сеттеры. Название происходит от английского “get” — “получать” (т.е. “метод для получения значения поля”) и set — “устанавливать”.
& \\ \hline
& \\ \hline
(написать по одному а далее сгенерировать геттеры и сеттеры идеей)
Теперь у нас есть возможность получать данные и устанавливать их. Например, с помощью getColor мы можем получить значение окраса котика.\\ \hline
Какой ужас, взгляните на эти значения. Данный класс котика позволяет присваивать полям безумные значение. В результате в программе находятся объекты с некорректным состоянием, типа вот этого котика с зелёным цветом и с возрастом -12345 лет.
Какая же тут была допущена ошибка? Все поля находятся в публичном доступе. К ним можно обратиться в любом месте программы: достаточно просто создать объект Cat — и все, у любого программиста есть доступ к его данным напрямую через оператор “.”
Нам нужно как-то защитить наши данные от некорректного вмешательства извне.
Во-первых, все переменные экземпляра (поля) необходимо помечать модификатором private. Private — самый строгий модификатор доступа в Java. Если ты его используешь, поля класса Cat не будут доступны за его пределами.
Теперь поля вроде как защищены. Но получается, что доступ к ним закрыт “намертво”: в программе нельзя даже получить вес существующей кошки, если это понадобится. Это тоже не вариант: в таком виде наш класс практически нельзя использовать.
Что же, нам нужно решить вопросы с получением и изменением значений полей. На помощь к нам приходят геттеры и сеттеры.
Название происходит от английского “get” — “получать” (т.е. “метод для получения значения поля”) и set — “устанавливать”
Предлагаю посмотреть на класс со стороны применения этих методов:
03-вызов-через-геттеры лайвкод & Теперь из другого класса (Main) есть доступ к полям Cat, но только через геттеры. Обратите внимание — у геттеров стоит модификатор доступа public, то есть они доступны из любой точки программы. А что, если нам хочется обновить возраст котика? Не проблема, тут можно использовать сеттер. У многих мог возникнуть вопрос, а в чём разница? Разница в первую очередь в том, что сеттер — это полноценный метод. А в метод, в отличие от поля, можно заложить необходимую логику проверки, чтобы не допустить неприемлемых значений. Например, можно не позволить назначение отрицательного числа в качестве возраста \\ \hline
public class Cat {
03-убираем-лишние-сеттеры лайвкод & Важно, что создавая для класса геттеры и сеттеры мы не только получаем возможность накручивать на поля дополнительную логику, но и получаем возможность регулировать доступ к полям. Например, если в программе нужно запретить менять котикам окрас, то мы просто удаляем из класса соответствующий сеттер. \\ \hline
private String name;
private String color;
private int age;
продублировать 3-26 (поля класса кот) лайвкод & Внимательно осмотрев класс кота мы приходим к выводу, что хранить возраст котов очень неудобно, потому что каждый год нужно будет обновлять это значение для каждого объекта кота в программе, а это может оказаться утомительно. Выходом может оказаться хранение не возраста, а неизменяемого параметра даты рождения и подсчёт возрастка каждый раз, когда его запрашивают. Ведь человеку, который запрашивает возраст котика, не интересно, каким образом получено значение, прочитано из поля или вычислено, ему важен конечный результат. Это сокрытие реализации. \\ \hline
public Cat(String name, String color, int age) {
this.name = name;
this.color = color;
this.weight = age;
}
03-хранение-дат-вместо-возраста лайвкод & Допишем метод получения возраста и поле года рождения, чтобы сделать инкапсуляцию более явной. Теперь видно сокрытие реализации - у кота нет поля возраст, но есть метод получения возраста, и тому, кто быудет вызывать этот метод совершенно нет дела как устроен котик внутри. \\ \hline
public Cat() {
}
Вопросы для проверки
void voice() {
System.out.println(name + " meows");
}
перечислите модификаторы доступа
void jump() {
if (this.age < 5) System.out.println(name + " jumps");
}
инкапсуляция - это
- архивирование проекта
- сокрытие информации о классе
- создание микросервисной архитектуры
& перечислите модификаторы доступа. приват, дефолт (пэкприв), протект, паблик. инкапсуляция - это - архивирование проекта - сокрытие информации о классе - создание микросервисной архитектуры. Конечно же сокрытие сведений об устройстве класса \\ \hline
public String getName() {
return name;
}
отбивка Наследование & Второй кит ООП после инкапсуляции - наследование \\ \hline
public void setName(String name) {
this.name = name;
}
03-класс-пса лайвкод & Представим, что мы хотим создать помимо класса котиков, класс собачек. Данный класс будет выглядеть очень похожим образом, только он будет не мяукать, а гавкать, и заменим обоим животным прыжок на простое перемещение на лапках
public int getAge() {
return age;
}
(буквально скопипастить кота и заменить название на пса, заменить прыжок на движение на будущее)
public void setAge(int age) {
this.age = age;
}
Вроде выглядит неплохо, пока мы не сталкиваемся с необходимостью описать классы для целого зоопарка. Мы видим, что в наших классах есть очень много одинаковых полей и методов. А как мы помним из занятия по циклам - если что-то повторяется в коде несколько раз - это очень плохо и надо срочно от этого избавиться, чтобы не плодить сотни строк кода, в которых даже разбираться никто не будет. \\ \hline
public String getColor() {
return color;
}
Наследование в программировании - это
public void setColor(String color) {
this.color = color;
}
}
Наследование в Java реализуется ключевым словом extends (англ. - расширять) & Тут нам на помощь приходит наследование. И кот и пёс являются животными, и у всех описываемых нами животных есть имя, возраст, окрас. И все описываемые нами животные могут бегать, прыгать, и откликаться на имя. Создадим так называемый родительский, он же базовый, класс, животное. И поместим в него общие поля. \\ \hline
03-класс-животное-поля лайвкод & Создав так называемый родительский класс, или суперкласс, и поместив в него поля, мы можем убрать поля из кота и пса. Если полей много, лаконичность описания родственных классов может быть весьма ощутимой. Так что же нужно, чтобы унаследовать какой-то класс? Чтобы унаследовать один класс от другого, нужно после объявления нашего класса указать ключевое слово extends и написать имя родительского класса. Реализуем котика и собачку\\ \hline
Теперь у нас есть возможность получать данные и устанавливать их. Например, с помощью getColor ьы можем получить значение окраса котика.
Давайте посмотрим пример создания такого котика:
03-класс-животное-методы лайвкод & В целом, достаточно безболезненно можно перенести и одинаковые методы, ведь из информации о хранении всего в памяти мы помним, что методы хранятся в классах, а значит хранить их лучше в одном экземпляре, а не тиражировать одинаковые методы в разных классах. Перенос кода в родительский класс показал наличие проблемы. Что же с этими классами не так? Вспоминаем информацию о модификаторах доступа и находим неприятность. Модификатор private определяет область видимости только внутри класса, а если нам нужно чтобы переменную было видно ещё и в классах-наследниках, нужен хотя бы модификатор доступа по умолчанию. А если мы создаём класс наследник в каком-то другом пакете, то и он нам не подойдёт. \\ \hline
public class Main {
03-МД-полный & Пришло время резюмировать наши знания о модификаторах доступа и поговорить о последнем. К членам данных и методам класса можно применять следующие модификаторы доступа: private - содержимое класса доступно только из методов данного класса; public - есть доступ фактически отовсюду. default - содержимое класса доступно из любого места пакета, в котором этот класс находится. protected (защищенный доступ) содержимое доступно также как с дефолтным модификатором, но ещё и для классов-наследников; \\ \hline
public static void main(String[] args) {
03-класс-животное-перенос-конструктора лайвкод & Про поля и методы проговорили, теперь умеем наследоваться и весьма сокращать количество написанного кода за счёт ООП, пришло время вернуться к конструкторам. Надо же разобраться какой и когда вызывается и после кого. Рассмотрим интересный момент - если мы перенесём одинаковые конструкторы кота и пса в общий класс животного - наша программа снова перестанет работать, давайте разбираться, почему. \\ \hline
Cat murka = new Cat("Мурка", "Чёрный", 4);
String murkaName = murka.getName();
String murkaColor = murka.getColor();
int murkaAge = murka.getAge();
03-конструкторы-вызов лайвкод & Начнём мы с того, что при создании объекта в первую очередь вызывается конструктор его базового класса, а только потом — конструктор самого класса, объект которого мы создаем. То есть при создании объекта Cat сначала отработает конструктор класса Animal, а только потом конструктор Cat. Чтобы убедиться в этом — упростим конструкторы и добавим в конструкторы Cat и Animal вывод в консоль. Как мы помним, если в классе был написан код хотя бы одного конструктора, то конструктор по-умолчанию не создаётся. А без конструктора по-умолчанию, классы-наследники не понимают, что им вызывать. \\ \hline
03-конструкторы-коррекция лайвкод & Мы можем явно вызвать конструктор базового класса в конструкторе класса-потомка. Базовый класс еще называют “суперклассом”, поэтому в Java для его обозначения используется ключевое слово super. Здесь такое же ограничение, как и при вызове конструкторов данного класса - вызов такого конструктора может быть только один и быть только первой строкой. \\ \hline
System.out.println("Имя кота: " + murkaName);
System.out.println("Окрас кота: " + murkaColor);
System.out.println("Возраст кота: " + murkaAge);
}
}
03-класс-птица лайвкод & Создадим ещё один класс, наследника животного, чтобы использовать наследование как положено, по назначению. Внимательный зритель мог заметить, что наследование реализуется через ключевое слово extends, это значит расширять. Для нас важно, что класс-родитель расширяется функциональностью или свойствами класса-наследника. Это позволяет нам, например. добавить в птичку такое свойство как высота полёта и такой метод как летать, в дополнение к тому, что умеет животное. \\ \hline
03-дополнительное-поле лайвкод & В конструкторе птицы мы вызвали конструктор Animal и передали в него три поля. Нам осталось явно проинициализировать только одно поле — высоту полёта, которого в Animal нет. Ранее, мы говорили о том, что при создании объекта в первую очередь вызывается конструктор класса-родителя. Именно поэтому слово super() всегда должно стоять в конструкторе первым, иначе логика работы конструкторов будет нарушена и программа выдаст ошибку. \\ \hline
Вывод в консоле:
this vs. super & this и super - это два специальных ключевых слова в Java, которые представляют соответственно текущий экземпляр класса и суперкласс. Начинающие Java-программисты часто путают эти слова и обнаруживают слабую осведомленность об их специальных свойствах, о которых нередко спрашивают на интервью по Java Сore. Вот, например, пара вопросов, из того, что сразу приходит на ум, Можно ли присвоить другое значение ключевому слову this в Java? и какая разница между ключевыми словами this и super в Java. \\ \hline
Имя кота: Мурка
Окрас кота: Чёрный
Возраст кота: 5
this() super() 03-родитель-в-наследнике & Как уже было сказано, главное отличие между this и super в том, что this представляет текущий экземпляр класса, в то время как super - текущий экземпляр родительского класса. Один из примеров использования переменных this и super — мы уже разбирали примеры вызовов конструкторов одного из другого, так называемые вызовы конструкторов по цепочке, это возможно благодаря использованию ключевых слов this и super. Внутри класса для вызова своего конструктора без аргументов используется this(), тогда как super() используется для вызова конструктора без аргументов, или как его ещё называют, конструктора по умолчанию родительского класса. Ну или как мы сделали ранее, вообще любой другой конструктор, передав ему соответствующие параметры. \\ \hline
this.field this.method() super.field super.method() & Ещё this и super в Java используются для обращения к переменным текущего экземпляра класса и его родителя. Вообще-то, к ним можно обращаться и без префиксов super и this, но только если в текущем блоке нет локальных переменных с такими же именами, в противном же случае использовать имена с префиксами придется обязательно, но это не страшно, поскольку в таком виде они даже более читабельны. Классическим примером такого подхода является использование this внутри конструктора, который принимает параметр с таким же именем, как и у переменной экземпляра. \\ \hline
Теперь из другого класса (Main) есть доступ к полям Cat, но только через геттеры. Обрати внимание — у геттеров стоит модификатор доступа public, то есть они доступны из любой точки программы.
А что, если нам хочется обновить возраст котика? Не проблема, тут спокойно можно использовать геттер:
большими буквами: множественное наследование запрещено & Пока разговариваем о текущих и родительских классах, нужно явно сказать, что у всей этой прекрасной объектной ориентированности есть одно важное ограничение: для каждого создаваемого подкласса можно указать только один суперкласс. В Java не поддерживается множественное наследование, то есть наследование одного класса от нескольких суперклассов. Зато возможно каскадное наследование, то есть класс-наследник вполне может быть чьим-то родителем. Пример такого поведения рассмотрим немного позже. \\ \hline
public class Main {
Главный класс Object 03-каскадное-наследование & Если класс-родитель не указан, таковым считается класс Object. Таким образом можно сделать вывод о том, что любой класс в джава так или иначе - наследник обжекта и соответственно всех его свойств и методов, мы ещё не раз будем возвращаться к этому примечательному факту. Вернёмся к нашим котикам, собачкам и птичкам. В этой иерархии классов можно проследить следующую цепь наследования: Object все классы неявно наследуются от него -> Animal -> Cat,Dog,Bird. Рассмотрим слайд и попробуем вспомнить о преобразовании примитивных типов. \\ \hline
public static void main(String[] args) {
03-через-общие-ссылки лайвкод & Суперклассы обычно размещаются выше подклассов, поэтому на вершине наследования находится класс Object, а в самом низу Cat Dog Bird. Объект подкласса представляет объект суперкласса, выражаясь проще мы можем ко всем котикам обращаться через общее название Животное и вообще ко всем объектам в программе мы можем обратиться через класс обжект. Поэтому в программе мы можем написать следующим образом:
Object animal = new Animal("Cat", "Black", 3);
Object cat = new Cat("Murka", "Black", 4);
Object dog = new Dog("Bobik", "White", 2);
Animal dogAnimal = new Bird("Chijik", "Grey", 3, 10);
Animal catAnimal = new Cat("Marusya", "Orange", 1); \\ \hline
Cat murka = new Cat("Мурка", "Чёрный", 4);
03-апкаст-даункаст лайвкод & Это так называемое восходящее преобразование (от подкласса внизу к суперклассу вверху иерархии) или upcasting. Такое преобразование осуществляется автоматически. Обратное не всегда верно. Например, объект Animal не всегда является объектом Cat или Dog. Поэтому нисходящее преобразование или downcasting от суперкласса к подклассу автоматически не выполняется. В этом случае нам надо использовать операцию преобразования типов. Object animal = new Cat("Murka", "Black", 4); Cat cat = (Cat)animal; cat.move(); Обратите внимание, что в данном случае переменная animal приводится к типу Cat. И затем через объект cat мы можем обратиться к функционалу кота. Важно при этом, что изначально оператором new был создан объект кота, а не обжект или энимал \\ \hline
отбивка instanceof & Ещё один момент из темы преобразования типов - это оператор instanceof. \\ \hline
System.out.println("Изначальный возраст кота — " + murka.getAge());
barsik.setName(5);
System.out.println("Новый возраст кота — " + murka.getAge());
}
}
оператор instanceof возвращает истину, если объект принадлежит классу или его суперклассам и ложь в противном случае & Нередко данные приходят извне, и мы можем точно не знать, какой именно объект эти данные представляют. Соответственно возникает большая вероятность столкнуться с ошибкой преобразования типов. И перед тем, как провести преобразование типов, мы можем проверить, а можем ли мы выполнить приведение с помощью оператора instanceof\\ \hline
03-проверка-принадлежности лайвкод & Object cat = new Cat("Murka", "Black", 4); if (cat instanceof Dog) { Dog dogIsCat = (Dog) cat; dogIsCat.jump(); } else { System.out.println("Conversion is invalid"); } Выражение cat instanceof Dog проверяет, является ли переменная cat объектом типа Dog. Но так как в данном случае явно кот не является собакой, то такая проверка вернет значение false, и преобразование не сработает. А вот выражение cat instanceof Animal выдало бы true и вывело бы Murka jumps (написать сравнение с животным) \\ \hline
Вывод в консоль:
отбивка ключевое слово final & помните, какую формулировку я просил запомнить? переменная с конечным значением \\ \hline
Изначальный возраст кота — 4
Новый возраст кота — Василий - 5
слайд из 2 про final добавить «класс с финальной реализацией, никаких других не будет» & Как хорошо, когда у нас есть возможность наследоваться от класса, забирать его свойства и поведение, но что, если нам стало нужно, чтобы от нашего класса никто больше не смог унаследоваться? На помощь приходит ключевое слово final. Оно может применяться к классам, методам, переменным (в том числе аргументам методов). Нам на данный момент интересен момент с классом, как можно запретить наследование. Фактически мы говорим, что это класс с финальной реализацией, никаких других не будет \\ \hline
03-файнал-птица лайвкод & То есть, если мы логически понимаем, что у нас от птичек уже нельзя наследоваться, то мы их можем пометить этим ключевым словом и тогда, если мы в коде начнём писать класс например, попугайчика, и укажем, что наследуемся от птицы, то у нас выведется Cannot inherit from final. \\ \hline
Сейчас у многих мог возникнуть вопрос, а в чём разница? Что сеттер — это полноценный метод. А в метод, в отличие от поля, ты можешь заложить необходимую тебе логику проверки, чтобы не допустить неприемлемых значений. Например, можно легко не позволить назначение отрицательного числа в качестве возраста:
отбивка Абстракция & иногда её даже выделяют как четвёртый принцип ООП, с чем я в корне не согласен \\ \hline
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
System.out.println("Ошибка! Возраст не может быть отрицательным числом!");
}
}
начальный эскиз животного желательно найти такую картинку, чтобы из одинакового кружка получались разные животные & Мы разобрались с наследованием в целом, с поведением конструкторов, с преобразованием типов и т.д. До беседы о полиморфизме осталось разобрать момент, который называется абстракцией. Есть мнение, что в ООП это означает, что при проектировании классов и создании объектов необходимо выделять только главные свойства сущности, и отбрасывать второстепенные, лично я с этим не согласен только лишь потому, что конкретно к ООП это отношения не имеет, а касается вообще всего программирования в целом.
Так вот, абстрактный класс — это написанная максимально широкими мазками, о-о-о-чень приблизительная «заготовка» для группы будущих классов. Эту заготовку нельзя использовать в чистом виде — слишком «сырая». Но она описывает некое общее состояние и поведение, которым будут обладать будущие классы — наследники абстрактного класса. \\ \hline
Так вот это является одним из трёх китов ООП - Инкапсуляцией.
Еще один момент хотелось бы немного затронуть - это Enum. Кроме отдельных примитивных типов данных и классов в Java есть такой тип как enum или перечисление. Перечисления представляют набор логически связанных констант. Объявление перечисления происходит с помощью оператора enum, после которого идет название перечисления. Затем идет список элементов перечисления через запятую:
03-абстрактный-метод лайвкод & Но начнём мы не совсем с этого. Абстрактными могут быть не только классы, но и методы, это, кстати, порождает интересное ограничение, но об этом буквально через минуту. Абстрактный метод - это метод без реализации. Если внимательно посмотреть на наших животных, становится очевидно, что все они умеют издавать свой звук. Именно это поведение у них как бы общее, но для всех немного разное, нас это напрямую приводит к полиморфизму, но остановимся в шаге от него. Мы точно знаем, при проектировании животного, что все животные должны издавать звук, но не можем сказать, какой именно. Поэтому, мы говорим, что у животного есть метод издать звук, но реализацию этого метода в животном мы написать не можем, слишком мало сведений. Поэтому помечаем метод как абстрактный. Кому-то это может показаться, что это очень похоже на объявление функции в С++, да, вам не показалось. \\ \hline
enum Season { WINTER, SPRING, SUMMER, AUTUMN }
03-абстрактное-животное лайвкод & Рассмотрим пример класса энимэл, что в нём изменилось, когда мы добавили в него абстрактные методы? верно, сам класс перестал быть верно описанным, компилятор утверждает, что что-то не так. А и правда, что будет, если программа попытается вызвать метод войс у животного, реализации-то нет? Прежде всего, класс энимал максимально абстрактно описывает нужную нам сущность — животное. Слово abstract на это недвусмысленно намекает. В мире не существует «просто животных». Есть губки, иглокожие, хордовые и т.д.
Данный класс теперь слишком абстрактный, чтобы программа могла с ним нормально взаимодействовать, а значит просто является чертежом по которому будут создаваться дальнейшие классы животных. Отметим этот факт явно, написав ключевое слово abstract у класса, теперь компилятору всё нравится \\ \hline
Перечисление фактически представляет новый тип, поэтому мы можем определить переменную данного типа и использовать ее:
абстрактный метод - это метод не содержащий реализации (объявление метода)
public class Main{
public static void main(String[] args) {
Season current = Season.SPRING;
System.out.println(current);
}
}
абстрактный класс - класс содержащий хотя бы один абстрактный метод
абстрактный класс нельзя инстанциировать & Что произошло? а произошло буквально следующее - мы сказали, что животное теперь слишком абстрактно, недостаточно детализирует объекты в нашей программе, Какого оно вида, к какому семейству относится, какие у него характеристики — непонятно. Было бы странно увидеть его в программе. Никаких «просто животных» в природе не существует. Только собаки, кошки, лисы и кто там ещё... В общем, мы взяли и просто-напросто запретили создавать экземпляры таких животных (можно ещё встретить термин инстанциировать). Очевидно, что абстрагирование метода вынуждает нас абстрагировать класс, но не наоборот, абстрактный класс необязательно должен содержать абстрактные методы, фактически, это просто запрещение создания экземпляров \\ \hline
Выведет SPRING.
Каждое перечисление имеет статический метод values(). Он возвращает массив всех констант перечисления:
Вопросы для проверки
\begin{enumerate}
\item Какое ключевое слово используется при наследовании?
\begin{enumerate}
\item parent
\item extends
\item как в С++, используется двоеточие
\end{enumerate}
\item super - это
\begin{enumerate}
\item ссылка на улучшенный класс
\item ссылка на расширенный класс
\item ссылка на родительский класс
\end{enumerate}
\item Не наследуются от Object
\begin{enumerate}
\item строки
\item потоки ввода-вывода
\item ни то ни другое
\end{enumerate}
\end{enumerate}
& Какое ключевое слово используется при наследовании? parent - extends - как в С++, используется двоеточие. экстендс
public class Main{
public static void main(String[] args) {
Season[] seasons = Season.values();
for (Season s : seasons) { System.out.println(s); }
}
}
super - это - ссылка на улучшенный класс - ссылка на расширенный класс - ссылка на родительский класс. конечно родительский
Не наследуются от Object - строки - потоки ввода-вывода - ни то ни другое. верный ответ ни то ни другое, потому что всё, кроме примитивов наследуется от обжекта
\\ \hline
А метод ordinal() возвращает порядковый номер определенной константы (нумерация начинается с 0).
Перечисления, как и обычные классы, могут определять конструкторы, поля и методы. Например:
отбивка Полиморфизм & Наконец, к теме, которая вызывает у новичков отторжение, но является основой для самых мощных инструментов программирования в ООП стиле. \\ \hline
\begin{verbatim}
public class Main{
public static void main(String[] args) {
System.out.println(Color.RED.getCode());
System.out.println(Color.GREEN.getCode());
}
}
полиморфизм это возможность объектов с одинаковой спецификацией иметь различную реализацию (Overriding) & полиморфизм выражается возможностью переопределения поведения суперкласса (часто можно встретить утверждение, что при помощи перегрузки, но это мы обсудим позже). Основная суть в том, что в классе-родителе имеется некоторый метод, но реализация этого метода разная у каждого класса-наследника, фактически это и есть полиморфизм, нам осталось его только правильно оформить и рассмотреть в деталях, как мы любим. \\ \hline
enum Color{
RED("#FF0000"), BLUE("#0000FF"), GREEN("#00FF00");
private String code;
Color(String code){
this.code = code;
}
public String getCode(){ return code;}
}
03-полиморфизм-переопределение лайвкод & Что мы сделали? В наших классах-потомках мы определили такие же методы, как и объявленный метод класса родителя, который хотим изменить. Пишем в нем новый код. И все как будто старого метода в классе-родителе и не было. У нас есть класс Animal, у которого есть метод voice(), мы наследуемся от него, создав класс котика и хотим назначить ему мяуканье. \\ \hline
\end{verbatim}
Аннотации реализуют вспомогательные интерфейсы.
Перечисление Color определяет приватное поле code для хранения кода цвета, а с помощью метода getCode оно возвращается. Через конструктор передается для него значение. Следует отметить, что конструктор по умолчанию приватный, то есть имеет модификатор private. Любой другой модификатор будет считаться ошибкой. Поэтому создать константы перечисления с помощью конструктора мы можем только внутри перечисления.
Это было введение в первое часть ООП, дальше переходим ко второму - Наследование.
Наследование:
Аннотация @Override проверяет, действительно ли метод переопределяется, а не перегружается & Важный момент, вы могли заметить в примерах наследования было уже переопределение из абстрактных классов, и там следует добавить так называемую аннотацию @Override. Она помогает компилятору понять, что в этом месте мы собираемся переопределить, а не перегрузить поведение предка. Если мы ошиблись в сигнатуре метода, то компилятор нам сразу об этом скажет. Настоятельно рекомендую всегда использовать данную аннотацию. \\ \hline
extends;
Object (глобальное наследование);
protected;
конструкторы в наследовании. Кто кого вызывает - авокадо
конструкторы по умолчанию при наследовании и что делать при ситуации, когда отсутствует у родителя - эксепшн
super, опираясь на this;
преобразование типов (скрины вспомнить. С ссылочными тыры-пыры);
final (переопределение - не надо, а предотвращение наследования);
абстракт
03-змейка лайвкод & То есть видим, что полиморфизм используется когда нам нужно описать поведение абстрактного класса или в целом назначить разным наследникам разное поведение, одинаково названное в родителе. Но есть и ситуации, когда все классы делают что-то одинаково, а один делает это как-то иначе. Создадим класс змейка.
Давайте представим, что мы хотим создать помимо класса котиков, но и класс для собачек. Данный класса будет выглядеть так:
По очевидным причинам змейка не может ходить на лапках. Поэтому это поведение у змейки будет переопределено.
public class Dog {
Видим, что перемещение всех животных нас устраивает, а змейка делает это по-своему. Также, например, можно было создать черепаху, которая не умеет бегать, рыбу, которая не издаёт звуков, слона, который не умеет прыгать, в отличие от остальных, практикующих «среднее» поведение \\ \hline
public String name;
public String color;
public int age;
03-хайдинг лайвкод & Стоить помнить, что переопределять можно только нестатические методы. Статические методы не наследуются в привычном смысле и, следовательно, не переопределяются. Можем припомнить, что создавать объекты котиков и птичек мы можем под любыми идентификаторами как самих соответствующих классов, так и их родителей. Создав в животном и коте статический метод с одинаковой сигнатурой мы сможем наблюдать то, что называется хайдингом, иначе сокрытием или перекрытием.
public Dog(String name, String color, int age) {
this.name = name;
this.color = color;
this.weight = age;
}
Энимал статик войд идентифай()(энимал)
Кэт статик войд идентифай()(кэт)
public Dog() {
}
видим, что класс унаследовался и метод должен переопределиться, внешне если создать энимал а и кэт ц, будет похоже, что так и произошло, но если сделать энимал а и энимал ц то видим что поведение изменилось. Это довольно очевидная вещь, если подумать. Статические члены класса относятся к классу, т.е. к типу переменной. Поэтому, логично, что если Cat имеет тип Animal, то и метод будет вызван у Animal, а не у Cat. \\ \hline
void voice() {
System.out.println(name + " grrrr");
}
Полиморфизм в языках программирования и теории типов — способность функции обрабатывать данные разных типов
void jump() {
if (this.age < 5) System.out.println(name + " jumps");
}
параметрический полиморфизм и ad-hoc-полиморфизм
public String getName() {
return name;
}
Широко распространено определение полиморфизма, приписываемое Бьёрну Страуструпу: «один интерфейс — много реализаций» & Полиморфизм - это гораздо более широкое понятие, чем просто переопределение методов, в эту тему завязаны разные интересные теории типов и информации, множество парадигм программирования и другое. Мы же подойдём к этому вопросу с утилитарной точки зрения, один интерфейс - множество реализаций, как утверждает автор языка С++ мы рассмотрели, остался ещё один вариант, который тем не менее не дотягивает до истинного полиморфизма, но и до него мы доберёмся, когда будем говорить об обобщённом программировании. \\ \hline
public void setName(String name) {
this.name = name;
}
к полиморфизму также относится перегрузка методов (Overloading) & Использование более одного метода с одним и тем же именем, но с разными параметрами в одном и том же классе или между суперклассом и подклассами в Java называется перегрузкой (Overloading). То есть, внешне выглядит, будто используется один метод вместо множества, выполняющих аналогичные действия.
public int getAge() {
return age;
}
(в среде программирования) помним, что можно выводить в терминал как строки, так и цифры, также и символы, неужели строгая типизация сломалась и можно в один метод записать разные типы данных? нет на самом деле в классе System.out, который фактически PrintStream, есть довольно много перегрузок функции печати.\\ \hline
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
03-перегрузка-перемещения лайвкод & Перегрузка работает также, как работала без всех этих сложностей с ООП, ничего нового, но для порядка стоит создать возможность коту перемещаться не только абстрактно, но и на какое-то конкретное место или на какое-то конкретное количество шагов. void jump() void jump(String place) void jump(int count). Как видно, методы имеют одинаковые названия, но отличаются по количеству параметров и их типу. Их как и раньше можно использовать и вызывать. Просто в зависимости от введённого количества и типа параметров, будет выполняться соответствующее тело метода \\ \hline
public void setColor(String color) {
this.color = color;
}
}
Что же, вот и описание класса для собачек. Вроде выглядит неплохо, пока мы не сталкиваемся с необходимостью описать классы для целого зоопарка. Мы видим, что в наших классах есть очень много одинаковых полей и методов.
А как мы помним из занятия по циклам - если что-то повторяется в коде несколько раз - это очень плохо и надо срочно от этого избавиться, чтобы не плодить сотни строк кода, в которых даже разбираться никто не будет. Тут нам на помощь приходит наследование. И кот и пёс являются животными, и у всех описываемых нами животных есть имя, возраст, окрас. И все описываемые нами животные могут бегать, прыгать, и откликаться на имя. Создадим так называемый родительский, он же базовый, класс, животное. И поместим в него общие поля.
public class Animal {
private String name;
private String color;
private int age;
абстрагируйте
классифицируйте
снова абстрагируйте
инкапсулируйте & В итоге, чтобы стиль вашей программы соответствовал концепции ООП и принципам ООП java следуйте следующим советам:
public Animal(String name, String color, int age) {
this.name = name;
this.color = color;
this.age = age;
}
public Animal() {
}
выделяйте главные характеристики объекта;
выделяйте общие свойства и поведение и используйте наследование при создании объектов;
используйте абстрактные типы для описания объектов;
старайтесь всегда скрывать методы и поля, относящиеся к внутренней реализации класса. \\ \hline
void jump() {
if (this.age < 5) System.out.println(name + " jumps");
}
Вопросы для проверки
\begin{enumerate}
\item Является ли перегрузка полиморфизмом
\begin{enumerate}
\item да, это истинный полиморфизм
\item да, это часть истинного полиморфизма
\item нет, это не полиморфизм
\end{enumerate}
\item Что обязательно для переопределения?
\begin{enumerate}
\item полное повторение сигнатуры метода
\item полное повторение тела метода
\item аннотация Override
\end{enumerate}
\end{enumerate}
& Является ли перегрузка полиморфизмом да, это истинный полиморфизм - да, это часть истинного полиморфизма - нет, это не полиморфизм. правильный ответ - это часть истинного полиморфизма, хотя если смотреть на этот вопрос узко, это совершенно точно не полиморфизм в ad-hoc понимании
public String getName() {
return name;
}
Что обязательно для переопределения? полное повторение сигнатуры метода - полное повторение тела метода - аннотация Override. Правильный ответ - это полное повторение сигнатуры, потому что смысл как раз в изменении тела, а аннотация оверрайд - это вспомогательный инструмент, который точно не является обязательным \\ \hline
public void setName(String name) {
this.name = name;
}
на этом уроке мы & на этой лекции мы поговорили о достаточно большой теме - реализации ООП в джава. рассмотрели классы и объекты, а также наследование, полиморфизм и инкапсуляцию. Дополнительно немного поговорили об устройстве памяти. На следующей лекции рассмотрим внутренние и вложенные классы, перечисления и исключения, нас ждут очень интересные темы, не переключайтесь \\ \hline
public int getAge() {
return age;
}
ДЗ перечисление справа &
\begin{enumerate}
\item написать класс кота так, чтобы каждому объекту кота присваивался личный порядковый целочисленный номер
\item написать классы кота, собаки, птицы, наследники животного. У всех есть три действия: бежать, плыть, прыгать. Действия принимают размер препятствия и возвращают булев результат. Три ограничения: высота прыжка, расстояние, которое животное может пробежать, расстояние, которое животное может проплыть. Следует учесть, что коты не любят воду.
\item * добавить механизм, создающий 25\% разброс значений каждого ограничения для каждого объекта
\end{enumerate}
public void setAge(int age) {
this.age = age;
}
В качестве домашнего задания попробуйте описать класс кота таким образом, чтобы каждому объекту кота присваивался личный порядковый целочисленный номер.
public String getColor() {
return color;
}
написать классы кота, собаки, птицы, наследующиеся от животного. У всех животных должено быть три действия: бежать, плыть, прыгать. Действия должны принимать целочисленный параметр размера препятствия и возвращать булев результат - успешно или неуспешно оно было преодолено. Соответственно, три ограничения: высота прыжка, расстояние, которое животное может пробежать, расстояние, которое животное может проплыть. Следует учесть, что коты не любят воду.
public void setColor(String color) {
this.color = color;
}
}
в качестве задания со звёздочкой, добавить механизм, создающий 25\% разброс значений каждого ограничения для каждого объекта, то есть если у кота максимальное расстояние бега 100 единиц, то каждый барсик и мурзик должен создаваться с ограничениями от 75 до 125 единиц.\\ \hline
Надо много учиться, чтобы знать хоть немного.
Пока не очень ясно зачем мы так делаем, ну есть у нас класс животное и хорошо, всё отлично работает, можно больше ничего не писать. Но, не тут то было, несмотря на все свои сходства, наши животные, например, издают разные звуки, поэтому, унаследовав от родительского класса основные свойства и поведение, мы в классах наследниках описываем разные методы например, реакции на оклик.
Так что же нужно, чтобы унаследовать какой-то класс? Чтобы унаследовать один класс от другого, нужно после объявления нашего класса указать ключевое слово extends и написать имя родительского класса. Реализуем котика и собачку:
class Cat extends Аnimal {
void voice() {
System.out.println(getname() + " meows");
}
}
class Dog extends Аnimal {
void voice() {
System.out.println(name + " grrrr"); //name fail
}
}
У всей этой прекрасной объектной ориентированности есть одно ограничение: для каждого создаваемого подкласса можно указать только один суперкласс. В Java не поддерживается множественное наследование от нескольких суперклассов в одном подклассе. Если класс-родитель не указан, таковым считается класс Object. Также, мы не смогли бы определить поле имя, как показано в примере с собачкой, хотя могла получить доступ к методу геттеру получения значения имени.
Что же с ним не так? Вспоминаем информацию о модификаторах доступа и находим несостыковку. Модификатор private определяет область видимости только внутри класса, а если нам нужно чтобы переменную было видно ещё и в классах-наследниках, нужен модификатор доступа по умолчанию. А если мы создаём класс наследник в каком-то другом пакете, то и вовсе protected. Это просто нужно запомнить, что если мы хотим создавать наследников от нашего класса, то те поля, которые будут наследоваться, должны быть не приватными, а защищёнными.
Всё же, давайте чуток подробнее рассмотрим модификаторы доступа в наследовании.
К членам данных и методам класса можно применять следующие модификаторы доступа:
private. В этом случае член данных класса или метод класса доступен только из методов данного класса. Из методов подклассов и объектов (экземпляров) данного класса доступа нет;
protected (защищенный доступ). В этом случае есть доступ из данного класса, объекта этого класса а также подкласса. Тем не менее, нет доступа из объекта подкласса;
public. В этом случае есть доступ из данного класса, подкласса, объекта данного класса а также объекта подкласса.
Также, стоит упомянуть об особенностях каждого из модификаторов.
Если члены данных или методы класса объявлены с модификатором доступа private, тогда они считаются:
доступными из методов класса, в котором они объявлены;
недоступными для всех других методов любых классов (подклассов), экземпляров (объектов) любых классов.
Если члены данных или методы класса объявлены с модификатором доступа protected, тогда они считаются:
доступными из методов класса, в котором они объявлены;
доступными из методов подкласса. Это касается и случая, когда унаследованный класс объявляется в другом пакете;
доступными из экземпляров (объектов) данного класса;
доступными из экземпляров (объектов) подкласса.
Модификатор доступа public применяется если нужно получить доступ к члену данных или методу класса из любой точки программы. Доступ к public членам данных и методам класса имеют:
методы данного класса;
методы подкласса;
объекты данного класса;
объекты подкласса;
объекты, которые объявлены в методах классов, которые находятся в других пакетах.
И последний модификатор доступа - это default. Он не обозначается ключевым словом, поскольку установлен в Java по умолчанию для всех полей и методов.
Случаи его применения ограничены, как и у модификатора protected. Чаще всего default-доступ используется в пакете, где есть какие-то классы-утилиты, не реализующие функциональность всех остальных классов в этом пакете.
А теперь пришло время вернуться к конструкторам. Надо же разобраться какой и когда вызывается и после кого.
Будем снова рассматривать пример с животными.
Начнём мы с того, что при создании объекта в первую очередь вызывается конструктор его базового класса, а только потом — конструктор самого класса, объект которого мы создаем.
То есть при создании объекта Cat сначала отработает конструктор класса Animal, а только потом конструктор Cat.
Чтобы убедиться в этом — упростим классы и добавим в конструкторы Cat и Animal вывод в консоль.
public class Animal {
public Animal() {
System.out.println("Ctor Animal");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("Ctor Cat");
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
Вывод в консоль:
Ctor Animal
Ctor Cat
Мы можем явно вызвать конструктор базового класса в конструкторе класса-потомка. Базовый класс еще называют “суперклассом”, поэтому в Java для его обозначения используется ключевое слово super.
Давайте сделаем это с классами энимэл и котика. Возьмём старый класс энимал и модифицируем класс котика.
Класс энимэл с геттерами и сеттерами:
public class Animal {
private String name;
private String color;
private int age;
public Animal(String name, String color, int age) {
this.name = name;
this.color = color;
this.age = age;
}
public Animal() {
}
// getters and setters
// ...
}
И модифицированный класс котика:
public class Cat extends Animal {
private String voice;
public Cat(String name, String color, int age, String voice){
super(name, color, age);
this.voice = voice;
}
void voiceCat() {
System.out.println(getName() + " " + voice);
}
}
После, вызвать:
public class Main {
public static void main(String[] args) {
Cat cat = new Cat("Murka", "Black", 4, "muuuur");
cat.voiceCat();
}
}
Вывод в консоль:
Murka muuuur
В конструкторе Cat мы вызвали конструктор Animal и передали в него три поля. Нам осталось явно проинициализировать только одно поле — voice, которого в Animal нет.
Ранее, мы говорили о том, что при создании объекта в первую очередь вызывается конструктор класса-родителя. Так вот, именно поэтому слово super() всегда должно стоять в конструкторе первым!
Иначе логика работы конструкторов будет нарушена и программа выдаст ошибку.
Компилятор знает, что при создании объекта класса-потомка сначала вызывается конструктор базового класса. И если будет выполнена попытка вручную изменить это поведение - он не позволит этого сделать.
Перед продолжением, хотелось бы сделать небольшое отступление, раз уж мы заговорили о super(), нужно бы и пояснить разницу между им и this.
this и super - это два специальных ключевых слова в Java, которые представляют соответственно текущий экземпляр класса и его суперкласса. Java-программисты часто путают эти слова и обнаруживают слабую осведомленность об их специальных свойствах, о которых нередко спрашивают на интервью по Java Сore. Вот, например, пара вопросов, из того, что сразу приходит на ум, о this и super, Можно ли присвоить другое значение ключевому слову this в Java? и какая разница между ключевыми словами this и super в Java.
Так вот, как я уже сказал, главное отличие между this и super в Java в том, что this представляет текущий экземпляр класса, в то время как super - текущий экземпляр родительского класса. Вот один из примеров использования переменных this и super — мы уже разбирали примеры вызовов конструкторов одного из другого, т.н. вызовы конструкторов по цепочке, это возможно благодаря использованию ключевых слов this и super. Внутри класса для вызова своего конструктора без аргументов используется this(), тогда как super() используется для вызова конструктора без аргументов, или как его ещё называют, конструктора по умолчанию родительского класса. Ну или как мы сделали ранее, вообще любой другой конструктор, передав ему соответствующие параметры.
Ещё this и super в Java используются для обращения к переменным экземпляра класса и его родителя. Вообще-то, к ним можно обращаться и без префиксов super и this, но только если в текущем блоке такие переменные не перекрываются другими переменными, т.е. если в нем нет локальных переменных с такими же именами, в противном же случае использовать имена с префиксами придется обязательно, но это не беда, т.к. в таком виде они даже более читабельны. Классическим примером такого подхода является использование this внутри конструктора, который принимает параметр с таким же именем, как и у переменной экземпляра.
Теперь вернёмся к нашим котикам и собачкам с наследованием.
В этой иерархии классов можно проследить следующую цепь наследования: Object (все классы неявно наследуются от типа Object) -> Animal -> Cat|Dog.
Давайте рассмотрим картинку ниже и попробуем вспомнить о преобразовании типов.
Суперклассы обычно размещаются выше подклассов, поэтому на вершине наследования находится класс Object, а в самом низу Cat и Dog.
Объект подкласса также представляет объект суперкласса. Поэтому в программе мы можем написать следующим образом:
public class Main {
public static void main(String[] args) {
Object animal = new Animal("Cat", "Black", 3);
Object cat = new Cat("Murka", "Black", 4, "muuuur");
Object dog = new Dog("Bobik", "White", 2, "woof");
Animal dogAnimal = new Dog("Manya", "Grey", 3, "woof");
Animal catAnimal = new Cat("Marusya", "Orange", 1, "myau");
}
}
Это так называемое восходящее преобразование (от подкласса внизу к суперклассу вверху иерархии) или upcasting. Такое преобразование осуществляется автоматически.
Обратное не всегда верно. Например, объект Animal не всегда является объектом Cat или Dog. Поэтому нисходящее преобразование или downcasting от суперкласса к подклассу автоматически не выполняется. В этом случае нам надо использовать операцию преобразования типов.
Object animal = new Cat("Murka", "Black", 4, "muuuur");
Cat cat = (Cat)animal;
cat.voiceCat();
В данном случае переменная animal приводится к типу Cat. И затем через объект cat мы можем обратиться к функционалу объекта Cat.
Еще один момент из этой темы - это оператор instanceof.
Нередко данные приходят извне, и мы можем точно не знать, какой именно объект эти данные представляют. Соответственно возникает большая вероятность столкнуться с ошибкой. И перед тем, как провести преобразование типов, мы можем проверить, а можем ли мы выполнить приведение с помощью оператора instanceof:
Object cat = new Cat("Murka", "Black", 4, "muuuur");
if(cat instanceof Dog){
Dog dogIsCat = (Dog) cat;
dogIsCat.jump();
}
else{
System.out.println("Conversion is invalid");
}
Выражение cat instanceof Dog проверяет, является ли переменная cat объектом типа Dog. Но так как в данном случае явно не является, то такая проверка вернет значение false, и преобразование не сработает.
Вывод консоли:
Conversion is invalid
А вот выражение cat instanceof Animal выдало бы true и вывело бы Murka jumps:
Object cat = new Cat("Murka", "Black", 4, "muuuur");
if(cat instanceof Animal){
Animal catAnimal = (Animal) cat;
catAnimal.jump();
}
else{
System.out.println("Conversion is invalid");
}
Как хорошо, когда у нас есть возможность наследоваться от класса, но что, если нам стало нужно, чтобы от нашего класса никто больше не смог унаследоваться?
В java есть ключевое слово final. Оно может применяться к классам, методам, переменным (в том числе аргументам методов).
Нам на данный момент интересен момент с классом, как можно запретить наследование.
Если мы взглянем на класс String, то увидим, что он объявлен как final. И унаследоваться от него нельзя:
public final class String{
//...
}
class SubString extends String{ //Ошибка компиляции
}
То есть, если мы логически понимаем, что у нас от котиков уже нельзя наследоваться, то мы их можем пометить этим ключевым словом:
public final class Cat extends Animal {
private String voice;
public Cat(String name, String color, int age, String voice){
super(name, color, age);
this.voice = voice;
}
void voiceCat() {
System.out.println(getName() + " " + voice);
}
}
И тогда, если мы в коде начнём писать класс и укажем, что наследуемся от кота, то у нас выведется Cannot inherit from final 'org.example.Cat.
Хорошо, мы разобрались с наследованием в целом, с поведением конструкторов, с преобразованием типов и т.д. Осталось разобрать момент, который называется абстракцией.
В ООП это означает, что при проектировании классов и создании объектов необходимо выделять только главные свойства сущности, и отбрасывать второстепенные.
Так вот, абстрактный класс — это максимально абстрактная, о-о-о-чень приблизительная «заготовка» для группы будущих классов. Эту заготовку нельзя использовать в готовом виде — слишком «сырая». Но она описывает некое общее состояние и поведение, которым будут обладать будущие классы — наследники абстрактного класса.
Рассмотрим пример абстрактного класса энимэл:
public abstract class Animal {
private String name;
private String color;
private int age;
public Animal(String name, String color, int age) {
this.name = name;
this.color = color;
this.age = age;
}
public Animal() {
}
public abstract void jump() ;
public abstract void voice();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
Таким образом выглядит абстрактный класс энимал. Что в нём такого особенного? Прежде всего, он максимально абстрактно описывает нужную нам сущность — животное. Слово abstract находится здесь недаром. В мире не существует «просто животных». Есть губки, иглокожие, хордовые и т.д.
Данный абстрактный класс просто является чертежом по которому будут создаваться дальнейшие классы животных. Допишем для пример класс Dog:
public class Dog extends Animal{
private String voice;
public Dog(String name, String color, int age, String voice){
super(name, color, age);
this.voice = voice;
}
@Override
public void jump() {
if (getAge() < 5) System.out.println(getName() + " jumps");
}
@Override
public void voice() {
System.out.println(getName() + " " + voice);
}
}
Использование:
Animal dogAnimal = new Dog("Manya", "Grey", 3, "woof");
dogAnimal.jump();
dogAnimal.voice();
Вывод:
Manya jumps
Manya woof
Это во многом похоже на то, о чем мы говорили в наследовании. Только там у нас класс Animal и его методы не были абстрактными. Но у такого решения есть целый ряд минусов, которые в абстрактных классах исправлены.
Первое и главное — экземпляр абстрактного класса создать нельзя.
Тот же пример можно привести с животными. Представьте, если бы в программе появились объекты Animal — «просто животное». Какого оно вида, к какому семейству относится, какие у него характеристики — непонятно. Было бы странно увидеть его в программе. Никаких «просто животных» в природе не существует. Только собаки, кошки, лисы, кроты и другие.
Шарль Луи Монтескье & на этой ноте мы с вами возьмём паузу до следующей лекции, не забывайте быть умничками и беречь себя \\ \hline
\end{longtable}

339
scenarios/jtc4-04b.tex Normal file
View File

@ -0,0 +1,339 @@
\documentclass[../j-spec.tex]{subfiles}
\begin{document}
\section{Специализация: ООП и исключения}
\begin{longtable}{|p{35mm}|p{135mm}|}
\hline
Экран & Слова \\ \hline
\endhead
Титул & Здравствуйте, продолжим беседу об ООП \\ \hline
Отбивка & и дополнительно затронем вопрос исключений. \\ \hline
На прошлом уроке & мы поговорили о достаточно большой теме - реализации ООП в джава. рассмотрели классы и объекты, а также наследование, полиморфизм и инкапсуляцию. Дополнительно немного поговорили об устройстве памяти. На следующей лекции рассмотрим внутренние и вложенные классы, перечисления и исключения, нас ждут очень интересные темы, не переключайтесь \\ \hline
На этом уроке & На этой лекции в дополнение к предыдущей, разберём такие понятия как внутренние и вложенные классы; процессы создания, использования и расширения перечислений. Детально разберём уже знакомое вам понятие исключений и их тесную связь с многопоточностью в джава. Посмотрим на исключения с точки зрения ООП, обработаем немного исключений, а также раз и навсегда разделим понятия штатных и нештатных ситуаций.\\ \hline
отбивка Перечисления & Начнём с небольшой темы, с перечислений \\ \hline
Перечисление - это упоминание объектов, объединённых по какому-либо признаку & Кроме восьми примитивных типов данных и классов в Java есть специальный тип, выведенный на уровень синтаксиса языка - enum или перечисление. Перечисления представляют набор логически связанных констант. Объявление перечисления происходит с помощью оператора enum, после которого идет название перечисления. Затем идет список элементов перечисления через запятую. \\ \hline
лайвкод 04-сезоны & Если копнуть немного глубже, перечисления - это такие специальные классы, содержащие внутри себя собственные статические экземпляры. Сложноватая мысль, если нужно, повторите её про себя несколько раз. а я пока напишу перечисление времён года
enum Season { WINTER, SPRING, SUMMER, AUTUMN }.
Когда мы доберёмся до рассмотрения внутренних и вложенных классов, в том числе статических, дополнительно это проговорим. \\ \hline
лайвкод 04-один-сезон & Перечисление фактически представляет новый тип данных, поэтому мы можем определить переменную данного типа и использовать её. Переменная типа перечисления может хранить любой объект этого исключения.
Season current = Season.SPRING; System.out.println(current);
Интересно также то, что вывод в терминал и запись в коде у исключений полностью совпадают, поэтому, в терминале мы видим .... \\ \hline
лайвкод 04-перечислить & Каждое перечисление имеет статический метод values(). Он возвращает массив всех констант перечисления, далее мы можем этим массивом манипулировать как нам нужно, например, вывести на экран все его элементы.
Season[] seasons = Season.values(); for (Season s : seasons) { System.out.printf("s ", s); }
Именно в этом примере, я использую цикл foreach для прохода по массиву, потому что у перечислений нет индексов, а показать их нужно. Если коротко, данный цикл возьмёт последовательно каждый элемент перечисления, присвоит ему имя s точно также, как мы это делали в примере на две строки выше, и сделает эту переменную С доступной в теле цикла в рамках одной итерации, на следующей итерации будет взят следующий элемент, и так далее \\ \hline
лайвкод 04-порядковый номер & Также в перечисления встроен метод ordinal() возвращающий порядковый номер определенной константы (нумерация начинается с 0).
System.out.println(current.ordinal())
Обратите внимание на синтаксис, метод можно вызвать только у конкретного экземпляра перечисления, а при попытке вызова у самого класса перечисления
System.out.println(Seasons.ordinal())
мы ожидаемо получаем ошибку невозможности вызова нестатического метода из статического контекста.\\ \hline
03-статические-поля & как мы с вами помним из пояснения связи классов и объектов, такое поведение возможно только если номер элемента как-то хранится в самом объекте. Мы видим в перечислениях очень примечательный пример инкапсуляции - мы не знаем, хранятся ли на самом деле объекты перечисления в виде массива, но можем вызвать метод вельюс. Мы не знаем, хранится ли в каждом объекте перечисления его номер, но можем вызвать его метод ординал. А раз перечисление - это класс, мы можем определять в нём поля, методы, конструкторы и прочее. \\ \hline
лайвкод 04-перечисление-цвет & Перечисление Color определяет приватное поле code для хранения кода цвета, а с помощью метода getCode оно возвращается.
enum Color {
RED("\#FF0000"), GREEN("\#00FF00"), BLUE("\#0000FF");
String code;
Color (String code) { this.code = code; }
String getCode() { return code; }
}
Через конструктор передается для него значение. Следует отметить, что конструктор по умолчанию приватный, то есть имеет модификатор private. Любой другой модификатор будет считаться ошибкой. Поэтому создать константы перечисления с помощью конструктора мы можем только внутри перечисления. И что косвенно намекает нам на то что объекты перечисления это статические объекты внутри самого класса перечисления. Также важно, что механизм описания конструкторов класса работает по той же логике, что и обычные конструкторы, то есть создав собственный конструктор мы уничтожили конструктор по-умолчанию, впрочем, мы его можем создать, если это будет иметь смысл для решаемой задачи. \\ \hline
лайвкод 04-перечисление-с-полем & Исходя из сказанного ранее можно сделать вывод, что с объектами перечисления можно работать точно также, как с обычными объектами, что мы и сделаем, например, выведя информацию о них в консоль
for (Color c : Color.values()) { System.out.printf("s(s) ", c, c.getCode()); } \\ \hline
отбивка Вложенные и внутренние (Nested) классы & Взглянем на чуть более, скажем так, комплексный момент - вложенные классы. На самом деле мы очень хорошо подготовились к этой теме и сейчас должно быть не так уж и сложно. \\ \hline
03-вложенные-классы & В Java есть возможность создавать классы внутри других классов и их разделяют на два вида: Non-static nested classes — нестатические вложенные классы. По-другому их еще называют inner classes — внутренние классы; Static nested classes — статические вложенные классы. Непосредственно внутренние классы подразделяются ещё на два подвида. Помимо того, что внутренний класс может быть просто внутренним классом, он еще бывает: локальным классом (local class); анонимным классом (anonymous class). Анонимные классы мы пока что не будем рассматривать, отложим на несколько лекций. \\ \hline
лайвкод 04-класс-апельсин & Разберём всё по порядку и начнём мы с внутренних классов. Почему их так называют?
public class Orange
public void squeezeJuice() System.out.println("Squeeze juice ...");
class Juice public void flow() System.out.println("Juice dripped ...");
Всё просто, их создают внутри другого класса. Рассмотрим на примере апельсина с реализацией, как это предлагает официальная документация оракла.\\ \hline
04-использование-апельсина & В основной программе мы должны будем создать отдельно апельсин, отдельно его сок через вот такую интересную форму вызова конструктора и можем отдельно работать как с апельсином, так и его соком.
Orange orange = new Orange(); Orange.Juice juice = orange.new Juice(); orange.squeezeJuice(); juice.flow();
Здесь всё прозрачно и последовательно, но не очень хорошо соответствует жизни. \\ \hline
лайвкод 04-технологичный-апельсин & Важно, что мы же программисты, разработчики. Ещё немного и инженеры, уж мы то понимаем, что когда мы сдавливаем апельсин из него сам по себе течёт сок, а когда апельсин попадает к нам в программу он сразу снабжается соком. Поэтому мы можем слегка модифицировать наш код
public class Orange
private Juice juice; public Orange() this.juice = new Juice();
public void squeezeJuice()
System.out.println("Squeeze juice ..."); juice.flow();
private class Juice public void flow()
System.out.println("Juice dripped ...");
Что мы сделали? Мы создали объект апельсина. Создали один его, если можно так выразиться, «подобъект» — сок. Далее, мы описали потенциальное наличие у апельсина сока, как его части, поэтому создали внутри класса апельсин класс сока. При создании апельсина создали сок, то есть можно сказать что описали некоторое созревание, Решив выдавить сок у апельсина - объект сока сообщил о том, что начал течь \\ \hline
лайвкод 04-апельсиновый сок & И в основной программе осталось выполнить довольно привычное нам создание апельсина
Orange orange = new Orange(); orange.squeezeJuice();
После чего произойдёт достаточно логичное для сдавливания апельсина действие - вытекание сока \\ \hline
04-схема-работы-внутреннего-класса & Таким образом очевидно что мы создаем апельсин и внутри него создается сок при создании каждого объекта апельсина то есть у каждого апельсина будет свой собственный сок который мы можем выжать сдавив апельсин. в этом смысл внутренних классов не статического типа. Нужные нам методы вызываются у нужных объектов. Все просто и удобно.
И кстати вполне возможно что в будущем нам это пригодится такая связь объектов и классов называется композиции есть ещё ассоциация и агрегация а именно эта композиция. \\ \hline
На одном слайде 04-апельсин и 04-технологичный-апельсин & Если класс полезен только для одного другого класса, то вполне логично встроить его в этот класс и хранить их вместе. Использование внутренних классов увеличивает инкапсуляцию. Оба примера достаточно отличаются реализацией. Мой пример подразумевает "более сильную" инкапсуляцию, так как извне ко внутренниму классу доступ получить нельзя, поэтому создание объекта внутреннего класса происходит в конструкторе основного класса - в апельсине. Вы можете создавать объект сока где вам это нужно, не обязательно в конструкторе. С другой стороны, у примера из документации есть доступ извне ко внутреннему классу сок но всё равно только через основной класс апельсина. Как и собственно создать объект сока можно только через объект апельсина. \\ \hline
Особенности внутренних классов (последовательное появление элементов перечисления)
\begin{itemize}
\item внутренний объект не существует без внешнего;
\item внутренний имеет доступ ко всему внешнему;
\item внешний не имеет доступа ко внутреннему без создания объекта;
\end{itemize}
& Давайте познакомимся с важными особенностями внутренних классов: объект внутреннего класса не может существовать без объекта внешнего класса. Это логично: для того мы и сделали Juice внутренним классом, чтобы в нашей программе не появлялись то тут, то там апельсиновые соки из воздуха.
код внутреннего класса имеет доступ ко всем полям и методам экземпляра (так же как и к статическим членам) окружающего класса, включая все члены, даже объявленные как private, на самом деле, от нас это скрыто, но объект внутреннего класса получает неявную ссылку на внешний объект, который его создал, и поэтому может обращаться к членам внешнего объекта без дополнительных уточнений;
экземпляр внешнего класса не имеет доступа ни к каким членам экземпляра внутреннего класса на прямую, то есть без создания экземпляра внутреннего класса внутри своих методов (И это логично, так как экземпляров внутреннего класса может быть создано сколько угодно много, и к какому же из них тогда обращаться?);\\ \hline
\begin{itemize}
\item у внутренних классов есть модификаторы доступа;
\item внутренний класс не может называться как внешний;
\item во внутреннем классе нельзя иметь не-final статические поля;
\item Объект внутреннего класса нельзя создать в статическом методе «внешнего» класса
\item Со внутренними классами работает наследование и полиморфизм.
\end{itemize} & у внутреннего класса, как и у любого члена класса, может быть установлен один из трех уровней видимости: public, protected или private. Если ни один из этих модификаторов не указан, то по умолчанию применяется пакетная видимость. Это влияет на то, где в нашей программе мы сможем создавать экземпляры внутреннего класса. Единственное сохраняющееся требование — объект внешнего класса тоже обязательно должен существовать и быть видимым;
внутренний класс не может иметь имя, совпадающее с именем окружающего класса или пакета. Это важно помнить. Правило не распространяется ни на поля, ни на методы;
внутренний класс не может иметь полей, методов или классов, объявленных как static (за исключением полей констант, объявленных как static и final). Статические поля, методы и классы являются конструкциями верхнего уровня, которые не связаны с конкретными объектами, в то время как каждый внутренний класс связан с экземпляром окружающего класса;
Объект внутреннего класса нельзя создать в статическом методе «внешнего» класса. Это объясняется особенностями устройства внутренних классов. У внутреннего класса могут быть конструкторы с параметрами или только конструктор по умолчанию. Но независимо от этого, когда мы создаем объект внутреннего класса, в него незаметно передается ссылка на объект внешнего класса. Ведь наличие такого объекта — обязательное условие. Иначе мы не сможем создавать объекты внутреннего класса. Но если метод внешнего класса статический, значит, объект внешнего класса может вообще не существовать. А значит, логика работы внутреннего класса будет нарушена. В такой ситуации компилятор выбросит ошибку;
Также, внутренние классы имеют право наследовать другие классы, реализовывать интерфейсы и выступать в роли объектов наследования. Это уже более сложная тема, которую вы можете рассмотреть самостоятельно при желании. \\ \hline
03-вложенные-классы & Как мы помним классы это новый тип данных для нашей программы поэтому стоит ли упоминать что мы можем создавать классы а также их описывать например внутри методов это довольно редко используется но синтаксически язык позволяет это сделать. Первое, что нужно вспомнить перед изучением — их место в структуре вложенных классов. Исходя из схемы мы можем понять, что локальные классы — это подвид внутренних классов. Однако, у локальных классов есть ряд важных особенностей и отличий от внутренних классов. Главное заключается в их объявлении. \\ \hline
лайвкод 04-локальный-внутренний-класс & Локальный класс объявляется только в блоке кода. Чаще всего — внутри какого-то метода внешнего класса. Например, это может выглядеть так:
public class Animal
void performBehavior(boolean state)
class Brain void sleep()
if(state) System.out.println("Sleeping");
else System.out.println("Not sleeping");
Brain brain = new Brain();
brain.sleep();
некоторое животное, у которого утанавливается состояние спит оно или нет. метод performBehavior() принимает на вход булевое значение и определяет, спит ли животное. И внутри этого метода мы объявили наш локальный класс Brain \\ \hline
лайвкод 04-вызов-с-локальным-классом & Соответственно, снаружи это просто вызов метода
Animal animal = new Animal();
animal.performBehavior(true);
мог возникнуть логичный вопрос: зачем? Зачем объявлять класс именно внутри метода? Почему не использовать обычный внутренний класс? Действительно, можно было бы просто сделать класс Brain внутренним. Другое дело, что итоговое решение зависит от структуры, сложности и предназначения программы. \\ \hline
Особенности локальных классов (последовательное появление элементов перечисления)
\begin{itemize}
\item сохраняет доступ ко всем полям и методам внешнего класса;
\item должен иметь свои внутренние копии всех локальных переменных;
\item имеют ссылку на окружающий экземпляр.
\end{itemize} & Соответственно, теперь рассмотрим особенности:
локальный класс сохраняет доступ ко всем полям и методам внешнего класса, а также ко всем константам, объявленным в текущем блоке кода, то есть полям и аргументам метода объявленным как final. Но начиная с JDK 8 локальный класс может обращаться к любым полям и аргументам метода объявленным в текущем блоке кода, даже если они не объявлены как final, но только в том случае если их значение не изменяется после инициализации;
локальный класс должен иметь свои внутренние копии всех локальных переменных, которые он использует (эти копии автоматически создаются компилятором). Единственный способ обеспечить идентичность значений локальной переменной и ее копии объявить локальную переменную как final. Опять же напомню, что это все было справедливо до JDK 7 включительно. В JDK 8 ситуация поменялась и можно обойтись без объявления переменной как final, но не менять ее значение в коде после инициализации. Хотя по большому счету лучше, для самоконтроля, все таки объявлять переменные как final;
экземпляры локальных классов, как и экземпляры внутренних классов, имеют окружающий экземпляр, ссылка на который неявно передается всем конструкторам локальных классов. В результате определение внутренних классов можно описать так: подобно полям и методам экземпляра, каждый экземпляр внутреннего класса связан с экземпляром класса, внутри которого он определен (то есть каждый экземпляр внутреннего класса связан с экземпляром его окружающего класса). Вы не можете создать экземпляр внутреннего класса без привязки к экземпляру внешнего класса. То есть сперва должен быть создан экземпляр внешнего класса, а только затем уже вы можете создать экземпляр внутреннего класса. \\ \hline
отбивка Статические вложенные классы & Мы поговорили о нестатических внутренних классах (non-static nested classes) или, проще, внутренних классах. Рассмотрим статические вложенные классы (static nested classes). Чем они отличаются от остальных? \\ \hline
лайвкод 04-статический-класс & При объявлении такого класса мы используем уже знакомое нам ключевое слово static. Возьмём класс нашего котика и заменим метод voice() на статический класс. Объясняется это, как вы уже слышали на прошлом уроке, что допустим, мы находимся дома и у нас отрыто окно, мы слышим разные звуки, которые доносятся из окна. Из этих звуков мы можем разобрать звук мурчания котика. И тут мы понимаем, что котика мы не видим, а при этом слышим.
public class Cat private String name, color;
private int age; public Cat()
public Cat(String name, String color, int age)
this.name = name; this.color = color;
this.age = age;
static class Voice
private final int volume;
public Voice(int volume) this.volume = volume;
public void sayMur() System.out.printf(
"A cat purrs with volume dn", volume);
То есть, такое мурчание котика может присутствовать без видимости и понимания, что это такой за котик. Также, добавим возможность установить уровень громкости его мурчанья \\ \hline
04-отличия-статик-и-не & В чем отличие между статическим и нестатическим вложенными классами? Объект статического класса не хранит ссылку на конкретный экземпляр внешнего класса. Если помните, только что мы говорили о том, что в каждый экземпляр внутреннего класса незаметно для нас передается ссылка на объект внешнего класса. Без объекта внешнего класса объект внутреннего просто не мог существовать. Для статических вложенных классов это не так. Объект статического вложенного класса вполне может существовать сам по себе. В этом плане статические классы более независимы, чем нестатические. \\ \hline
лайвкод 04-использование-статического & Довольно важный и вместе с тем довольно очевидный момент заключается в том, что при создании такого объекта нужно указывать название внешнего класса,
Cat.Voice voice = new Cat.Voice(100);
voice.sayMur();
примерно так. \\ \hline
лайвкод 04-последнее-о-статике & И ещё одна особенность - разный доступ к переменным и методам внешнего класса. Статический вложенный класс может обращаться только к статическим полям внешнего класса. При этом неважно, какой модификатор доступа имеет статическая переменная во внешнем классе. Даже если это private, доступ из статического вложенного класса все равно будет. Все вышесказанное касается не только доступа к статическим переменным, но и к статическим методам. Слово static в объявлении внутреннего класса не означает, что можно создать всего один объект.
for (int i = 0; i < 4; i++)
Cat.Voice voice = new Cat.Voice(100 + i); voice.sayMur();
Не следует путать объекты с переменными. Если мы говорим о статических переменных — да, статическая переменная класса существует в единственном экземпляре. Но применительно ко вложенному классу static означает лишь то, что его объекты не содержат ссылок на объекты внешнего класса. В случае примера с котиком - мы слышим мурчание с разной громкостью и непонятно одного и того же котика или это другой. А самих объектов мы можем создать сколько угодно \\ \hline
отбивка Механизм исключительных ситуаций & Наконец-то, тема, которая почему-то вызывает у новичков отторжение недоумение и полное непонимание. \\ \hline
Исключение - это отступление от общего правила, несоответствие обычному порядку вещей & Думаю, тут надо начать с такой, немного философской части. Посмотрите пока что на этот вступительный слайд, он хорошая и в нём нет ничего сложного. Мы изучаем программирование, а что такое язык программирования? Это в первую очередь набор инструментов. Смотрите, например, есть строитель или вот лучше - художник. У художника есть набор всевозможных красок, кистей, холстов, карандашей, мольберт, ластик и куча-много-чего-ещё. Это всё его инструменты, с их помощью он делает свои важные художественные штуки. Тоже самое для программиста, у программиста есть язык программирования, который предоставляет ему инструменты: циклы, условия, классы, функции, методы, ООП, фрейморки, библиотеки... Исключения - это один из инструментов. Смотрите на исключения как на ещё один инструмент для работы программиста. Работает он достаточно специфично, и является достаточно высокоуровневым, исключения представляют из себя некую подсистему языка, которая является неотъемлемой частью любого мало-мальски серьёзного проекта.\\ \hline
В общем случае, возникновение исключительной ситуации, это ошибка в программе, но основным вопросом является следующий:
\begin{itemize}
\item ошибка в коде программы,
\item ошибка в действиях пользователя
\item ошибка в аппаратной части компьютера
\end{itemize} & Итак бывают такие ситуации, когда в процессе выполнения программы возникают ошибки. При возникновении ошибок создаётся объект класса Исключение, и в этот объект записывается какое-то максимальное количество информации о том, какая ошибка произошла, чтобы потом прочитать и понять, где же проблема. Соответственно эти объекты можно ловить, связывать и бросать в подвал для дальнейших выяснений... Но в программировании это называется гуманным термином “обрабатывать”. То есть вы можете как-то повлиять на ход программы, когда она уже запущена, и сделать так, чтобы она не прекратила работу, увидев деление на ноль, например, а выдала пользователю сообщение, и отменила только одну последнюю операцию. Сегодня поговорим о том, как отличить штатную ситуацию от нештатной. \\ \hline
04-иерархия-исключений & Исключения все наследуются от класса Throwable и могут быть как обязательные к обработке, так и необязательные. Есть ещё подкласс Error, но он больше относится к аппаратным сбоям или серьёзным алгоритмическим или архитектурным ошибкам, и нас не интересует, потому что поймав что-то вроде OutOfMemory мы средствами Java прямо в программе ничего с ним сделать не сможем, такие ошибки надо обрабатывать и исключать в процессе разработки ПО. Или, возможно, в системном программировании, но не в прикладном. Нас интересует подкласс Throwable-Exception<RuntimeException и например какой-нибудь IOException. Вообще их куча много-много, на досуге можете полистать список на сайте оракл. Но можете считать, что все исключения, с которыми вы можете работать наследуются от Throwable-Exception. Важная информация. Все эксепшены, кроме наследников рантайма надо обрабатывать. \\ \hline
лайвкод 04-мартёшка-методов & Давайте рассмотрим примерчики, напишем пару-тройку методов, и сделаем матрёшку,
из мэйна вызываем метод2, оттуда метод1, оттуда метод0, а в методе0 всё как всегда портим, пишем
private static int div0() return 1 / 0;
ArithmeticException является наследником класса RuntimeEcxeption поэтому статический анализатор его не подчеркнул, и ловить его вроде как не обязательно, спасибо большое разработчикам джава, надёжность кода не повысилась, единообразность работы всех исключений нарушилась, всё хорошо.\\ \hline
лайвкод 04-метод-деления & Выходит, на примере деления на ноль можно всё хорошо сразу и объяснить. допустим у нас есть какой-то метод который возможно мы даже сами написали, который, скажем, целочисленно делит два целых числа. немного его абстрагируем
private static int div0(int a, int b) return a / b;
Если посмотреть на этот метод с точки зрения программирования, он написан очень хорошо - алгоритм понятен, метод с единственной ответственностью, всё супер. Однако, из поставленной перед методом задачи очевидно, что он не может работать при всех возможных входных значениях. То есть если у нас вторая переменная равна нулю, то это неправильно. И что же с этим делать? \\ \hline
лайвкод 04-исключение-1 & Нам нужно как-то запретить пользователю передавать в качестве делителя ноль. Самое простое - ничего не делать, но мы так не можем.
private static int div0(int a, int b)
if (b != 0) return a / b; return ???;
Потому что метод должен что-то вернуть, а что вернуть, неизвестно, ведь от нас ожидают результат деления. Поэтому, допустим можем руками сделать проверку (б == 0ф) и выкинуть пользователю так называемый объект исключения throw new RuntimeException("деление на ноль") а иначе вернём а / б.
private static int div0(int a, int b)
if (b == 0) throw new RuntimeException("parameter error");
return a / b; \\ \hline
лайвкод 04-исключение-2 & Вызываем метод и пробуем делить 1 на 2. А вот если мы второй параметр передадим 0 то у нас выкинется исключение,
System.out.println(div0(1,2));
System.out.println(div0(1,0));
то есть по сути new... это конструктор, нового объекта какого-то класса, в который мы передаём какой то параметр, в данном конкретном случае это строка с сообщением. Зафиксируем пока что эту мысль. \\ \hline
отбивка введение в многопоточность & Кажется многовато отступлений, но без этого точно никак нельзя продолжать. \\ \hline
04-метод-броска & Итак, что происходит? Ключевое слово throw заставляет созданный объект исключения начать свой путь по родительским методам, пока этот объект не встретится с каким-то обработчиком. в нашем текущем случае - это дефолтный обработчик виртуальной машины, который в специальный поток err выводит так называемый стектрейс, и завершает дальнейшее выполнение метода.\\ \hline
04-поток-ерр & Далее по порядку: поток ерр. Все программы в джава всегда многопоточны. Понимаете вы многопоточность или нет, знаете ли вы о её существовании или нет, не важно, многопоточность есть всегда. не будем вдаваться в сложности прикладной многопоточности, у нас ещё будет на это довольно много времени, пока что поговорим в общем. в чём смысл? смысл в том, что на старте программы запускаются так называемые потоки, которые работают псевдопараллельно и предназначены каждый для решения своих собственных задач, например, это основной поток, поток сборки мусора, поток обработчика ошибок, потоки графического интерфейса. Основная задача этих потоков - делать своё дело и иногда обмениваться информацией.\\ \hline
04-стектрейс & В упомянутый же парой минут ранее стектрейс кладётся максимальная информация о типе исключения, его сообщении, иерархии методов, вызовы которых привели к исключительной ситуации. Если не научиться читать стектрейс, если честно, можно расходиться по домам и не думать о серьёзном большом программировании. Итак стектрейс. Когда у нас случается исключение - мы видим, что случилось оно в потоке мэйн, и является объектом класса RuntimeException сообщение мы тоже предусмотрительно приложили. Первое что важно понять, что исключение - это объект класса. Далее читаем матрёшку - в каком методе создался этот объект, на какой строке, в каком классе. Далее смотрим кто вызвал этот метод, на какой строке, в каком классе. Это вообще самый простой стектрейс, который может быть. Бывают полотна по несколько десятков строк, клянусь, сам видел. Особенно важно научиться читать стектрейс разработчикам андроид, потому что именно такие стектрейсы будут вам прилетать в отчёт. У пользователя что то упало, он нажал на кнопку отправить отчёт, и вам в консоль разработчика прилетел стектрейс, который будет являться единственной доступной информацией о том, где вы или пользователь накосячили.\\ \hline
лайвкод 04-простой-пример-исключения & Если мы не напишем никакого исключения, кстати, оно всё равно произойдёт. Это общее поведение исключения. Оно где-то случается, прекращает выполнение текущего метода, и начинает лететь по стеку вызовов вверх. Возможно даже долетит до дефолтного обработчика, как в этом примере.
int[] arr = {1};
System.out.println(arr[2])
Некоторые исключения генерятся нами, некоторые самой джавой, они вполне стандартные, например выход за пределы массива, деление на ноль, и классический нуль-поинтер. \\ \hline
лайвкод 04-объект-исключения & Посмотрим на исключения под немного другим углом. Создадим какой-нибудь учебный класс, psvm и создадим экземпляр класса исключения
RuntimeException e = new RuntimeException();
Если просто сейчас запустить программу, то ничего не произойдёт, нам нужно наше исключение, как бы это сказать, активировать, выкинуть, возбудить сгенерировать. Для этого есть ключевое слово
throw e;
Запускаем, видим. Компилятор ошибок не обнаружил и всё пропустил, а интерпретатор наткнулся на класс исключения, и написал нам в консоль следующее, в основном потоке программы возникло вот такое исключение в таком пакете в таком классе на такой строке.
\\ \hline
лайвкод 04-рантайм-выводы & Усложним создадим паблик стэтик воид методА, выкинем исключение в нём, и вызовем его из мэйна.
теперь по вот этому стэктрейсу можем проследить что откуда вызвалось и как мы дошли до исключительной ситуации. Можем в нашем исключении даже написать наше кастомное сообщение и использовать все эти штуки в разработке. Можем унаследоваться от какого-то исключения и создать свой класс исключений, об этом чуть позже, в общем, всё зависит от поставленной задачи. Достаточно гибкая штука эти исключения. Ну и поскольку это рантаймэксепшн то обрабатывать его на этапе написания кода не обязательно, компилятор на него не ругается, всё круто. \\ \hline
04-иерархия-исключений & На самом деле на этом интересные особенности обработки исключений наследников Runtime, также называемых анчекд заканчивается далее будет гораздо интереснее рассматривать исключения обязательные для обработки, потому что статический анализатор кода не просто их выделяет, а обязывает их обрабатывать на этапе написания кода. И просто не скомпилирует проект если в коде есть необработанные так или иначе исключения также известные как чекд. Заменим Runtime исключение на обычное обязательное к обработке. ((удалить слово Runtime))\\ \hline
04-пробуй-лови & Давайте наше исключение ловить. Первое, и самое важное, что надо понять - это почему что-то упало, поэтому не пытайтесь что то ловить, пока не поймёте что именно произошло, от этого понимания будет зависеть способ ловли. Исключение ловится двухсекционным оператором try-catch, а именно, его первой секцией try. Это секция, в которой предполагается возникновение исключения, и предполагается, что мы можем его поймать. А в секции catch пишем имя класса исключения, которое мы ловим, и имя объекта, в который мы положим экземпляр нашего исключения. Секция catch ловит указанное исключение и всех его наследников. Это важно. Рекомендуется писать максимально узко направленные секции catch, потому что надо стараться досконально знать как работает ваша программа, и какие исключения она может выбрасывать. Ну и ещё потому что разные исключения могут по-разному обрабатываться, конечно-же. Секций catch может быть сколько угодно много. Как только мы обработали объект исключения, он уничтожается, дальше он не поднимается, и в следующие catch не попадает. Мы, конечно, можем его же насильно пульнуть выше, ключевым словом throw. \\ \hline
? & Так вот, когда какой-то наш метод выбрасывает исключение вы обязаны либо вынести объявление этого исключения в сигнатуру метода, что будет говорить тем, кто его вызывает о том, что в методе может возникнуть исключение, либо мы это исключение должны непосредственно в методе обработать, иначе у вас ничего не скомпилируется. Примером одного из таких исключений служит ИО это сокращение от инпут-аутпут и генерируется, когда, вы не поверите, возникает ошибка ввода-вывода. То есть при выполнении программы что-то пошло не так и она, программа не может произвести ввод-вывод в штатном режиме. На деле много чего может пойти не так, операционка заглючила, флешку выдернули, устройство телепортировалось в микроволновку, и всё, случился ИОЭксепшн, не смогла программа прочитать или написать что то в потоке ввода-вывода. Соответственно, уже от этого ИОЭ возникают какие-то другие, вроде FileNotFoundException, которое мы тоже обязаны обработать. Например, мы хотим чтобы наша программа что то там прочитала из файла, а файла на нужном месте не оказалось, и метод чтения генерирует исключение. \\ \hline
? & В случае, если мы выносим объявление исключения в сигнатуру, вызывающий метод должен обработать это исключение точно таким-же образом - либо в вызове, либо вынести в сигнатуру. Исключением из этого правила является класс RuntimeException. Все наследники от него, включая его самого, обрабатывать не обязательно. Туда входит деление на ноль, индексаутофбаунд экзепшн например. то есть те ошибки, которые компилятор пропускает, а возникают они уже в среде исполнения. Обычно уже по названию понятно что случилось, ну и помимо говорящих названий, там ещё содержится много инфы, хотя-бы даже номер строки, вызвавшей исключительную ситуацию. Общее правило работы с исключениями одно - если исключение штатное - его надо сразу обработать, если нет - надо дождаться, пока программа упадёт. Повторю, все исключения надо обрабатывать, примите это как расплату за использование языка Java.\\ \hline
лайвкод ? & Вернёся к нашему учебному классу и Усложним ещё. в методеА вызовем методБ. а в методеБ выкинем то что нам обязательно надо обработать, например IOE. Вот тут то и начинается веселье. потому что IOE это не наследник рантайм эксепшена, и мы обязаны обрабатывать на этапе компиляции. Тут у нас есть две опции. многим уже известный try-catch, синтаксис его не совсем очевидный, так что тут надо просто сделать усилие и запомнить. пишем
try { methodB() } catch (имя класса исключения которое хотим поймать и идентификатор экземпляра) { }.
Если возникло исключение, попадём в кэтч, и тут можем делать что хотим, чтобы программа не упала. Конечно можно написать в кэтч общий класс вроде Throwable или Exception, вспомним про родительские классы, и тогда он поймает вообще все исключения которые могут быть, даже те, которые мы, возможно не ожидаем. Очень часто в процессе разработки нужно сделать, чтобы нам в процессе выполнения что-то конкретное об исключении выводилось на экран, для этого у экземпляра есть метод гетМессадж. Пишем
sout(e.getMessage()).
Ещё чаще бывает, что выполнение программы после выбрасывания исключения не имеет смысла и мы хотим чтобы программа упала. Вот тут очень интересный финт придумали. Мы выкидываем новое рантаймэкзепшн, передав в него экземпляр отловленного исключения используя довольно хитрый конструктор копирования. Во как
catch throw new runtimeexception(e); \\ \hline
лайвкод ? & Второй вариант обработки исключений - мы в сигнатуре метода пишем
throws IOE,
и через запятую все остальные возможные исключения этого метода. Всё, у нас с ним проблем нет, но у метода который его вызовет - появились. И так далее наверх. Тут всё достаточно просто. Далее пробуем описать какое-то исключение, которое мы обязаны будем ловить. Предлагаю переименовать наш учебный класс и его методы в некоторую имитацию потока ввода-вывода.
Класс ТестСтрим
методА = конструктор
методБ = инт читать
Внутри методаБ давайте создадим какой-нибудь FileInputStream который может генерить FileNotFoundException который на самом деле является наследником IOE, который наследуется от Exception. Никаких рантайм, значит обработать мы его обязаны. Два варианта у нас есть, либо мы его укутаем в try-catch, либо вот не можете вы написать обработчик, потому что не знаете как должна обрабатываться данная исключительная ситуация, и обработать её должна сторона, которая вызывает метод чтения, в таком случае пишем, что метод может генерить исключения. Всё, вы теперь свободны от обработки этого исключения в методе чтения. Но теперь подчёркивается метод мейн... и здесь мы встаём перед той-же дилеммой. и так далее по стэку вызовов любой глубины. \\ \hline
вовочка перед печкой «и так сойдёт» & Важный момент. Задачи бывают разные. Исключения - это инструмент, который нетривиально работает. Важно при написании кода понять, возникающая исключительная ситуация - штатная, или нештатная. В большинстве случаев - ситуации нештатные, поэтому надо уронить приложение и разбираться с тем, что произошло. Допустим для вашего приложения вот стопроцентно какой-то файл должен быть, без него дальше нет смысла продолжать. Что делать, если его нет? Явно не пытаться в него что то писать, правда? Самое плохое, что можно сделать - ничего не делать. Это самое страшное, когда программа повела себя как-то не так, а мы об этом даже не узнали. Допустим мы хотим прочитать файл, вывести в консоль, но мы глотаем исключение, выведя стектрейс куда-то-там какому-то разработчику и наши супер-важные действия не выполнились. Надо ронять. Как ронять? да throw new RuntimeException(e). Крайне редко случаются ситуации, когда исключение надо проглотить. \\ \hline
лайвкод ? & Давайте обрабатывать дальше. Все помнят, что потоки надо закрывать? Даже если не знали, теперь знаете. Вот, допустим, у нас в нашем модном потоке открылся файл, что то из него прочиталось, потом метод упал с исключением, а файл остался незакрытым, ресурсы заняты. Давайте даже допишем свой класс TestStream пусть его конструктор выбрасывает IOE, метод read выбрасывает IOE и будет метод close, везде будем логировать успешность в read пока оставляем исключение throw new IOE("reading error") в close "closed". Штатно возвращаем из read единичку и логируем, что всё прочитали в мейне. Как всё будет происходить штатно?
TestStream stream = new TestStream();
int a = stream.read()
stream.close()
Далее представим что в методе read что то пошло не так, выбрасываем исключение, и что видим в консоли? создали поток, исключение, конец программы. Что же делать? а делать секцию finally. Секция finally будет выполнена в любом случае, будет исключение, не будет исключения, не важно. Но тут тоже большое спасибо разработчикам джавы, мы не видим наш поток, то есть его надо вынести наружу, а ещё он оказывается не инициализирован, значит надо написать что то типа TestStream stream = null. Теперь всё должно отработать. \\ \hline
лайвкод ? & Теперь немного неприятностей. Написали мы блок finally, вроде даже избавились от проблемы с закрытием потока. А как быть, если исключение возникло при создании этого потока? Тогда получается, у нас метод закрытия будет пытаться выполниться от ссылки на null. Нехорошо, знаете-ли, получается. Давайте всё сломаем, прям в конструкторе нашего потока выкинем IOE. Получили NPE в блоке finally. Очевидное решение - ставим в секции finally условие, и если поток не равен нулю, закрываем. Вроде клёво. Меняем тактику. Конструктор отрабатывает нормально. Метод чтения всё ещё генерирует исключение, но бац и в методе закрытия что-то пошло не так, и вылетело исключение. Ну вот так не повезло в жизни. Что делаем? Оборачиваем в try-catch. Вроде снова всё классно. Но и тут мы можем наткнуться на неприятность. Допустим, что нам надо в любом случае ронять приложение (в кэтч допишем throw new Runtime...). Тогда если у нас try поймал исключение, и выкинул его, потом finally всё равно выполнится, и второе исключение затрёт первое, мы его не увидим. Ну а поскольку первое для нас важнее, то второе - максимум что мы можем сделать - это залогировать исключение в консольку. Так дела обстояли в седьмой джаве. \\ \hline
лайвкод ? & Что предлагает нам восьмая джава? try-с-ресурсами. Поток - это ресурс, абстрактное понятие. Как с этим работать? Сейчас будет небольшое забегание вперёд, пока что предлагаю просто запомнить магию. Выражаясь строго формально, мы должны реализовать интерфейс Closeable. А что это за интерфейс? (Ctrl+click) там содержится всего один метод close(), который умеет бросать IOE. Напишем везде пока что штатное поведение в нашем тестовом потоке. Залогируем. Далее синтаксис try-с-ресурсами. Все потоки начиная с восьмой джавы реализуют интерфейс Closeable. Стираем все ужасы, которые мы написали, пишем
try(TestStream stream = new TestStream())
int a = stream.read();
catch (IOException e)
new RuntimeException(e)
И всё, мы поток не закрываем. За это у нас ответит сама джава. Запустим и проверим. Никаких close() и finally, всё хорошо. Самое классное - если мы ломаем метод read() то трай с ресурсами всё равно наш поток корректно закроет. Иногда вы можете видеть вывод в консоль и стектрейс вразнобой, ничего страшного, просто исключения вылетают в поток error а вывод в консоль в стандартный вывод. Ну и у них иногда случаются асинхронности. А теперь вообще самый смак - ломаем метод закрытия, и джава очень правильно поступит, она выкатит наверх основное исключение, но и выведет "подавленное" исключение, вторичное в стектрейс. По-человечески, красиво, информативно, глаз радуется. Рекомендуется по возможности использовать вот такую конструкцию. Но научиться пользоваться надо и тем и тем, естественно. \\ \hline
& Последнее на сегодня про исключения это наследования и Полиморфизм исключения тема не очень большая и в целом не сложная потому что вы уже знаете что такое класса объекты как классы могут наследоваться и что такое Полиморфизм. Особенно застрять внимание на объектно-ориентированном программирования исключениях скорее всего не нужно потому что было неоднократно сказано что исключение это тоже классы и есть какие-то наследники исключений генерируются и выбрасываю объекты исключений единственное что важно упомянуть это то что под система исключений работает немного не тривиально впрочем это вы могли заметить и сами но мы можем создавать собственные исключения с собственными смыслами и сообщениями и точно также их выбрасывать вместо стандартных наследоваться мы можем от любых исключений единственное что важно это то что не рекомендуется наследоваться от классов throwable и error когда описываете исключение механика checked и unchecked исключений сохраняется при наследование поэтому создав наследник RuntimeException вы получаете не проверяемые на этапе написания кода исключение \\ \hline
Не стыдись учиться в зрелом возрасте: лучше научиться поздно, чем никогда. Эзоп & На этом вроде бы мы закончили рассмотрение основных аспектов ООП и исключений, дальше нас ждёт выход за пределы одной программы и ещё больше механизмов языка. Не теряйте внимания, будьте умничками. \\ \hline
\end{longtable}
\end{document}