gb-java-devel/jtc2-02a.tex

713 lines
72 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\documentclass[j-spec.tex]{subfiles}
\begin{document}
\pagestyle{plain}
\sloppy
\tableofcontents
\section{Специализация: данные и функции}
\subsection{В предыдущем разделе}
\begin{itemize}
\item Краткая история (причины возникновения);
\item инструментарий, выбор версии;
\item CLI;
\item структура проекта;
\item документирование;
\item некоторые интересные способы сборки проектов.
\end{itemize}
\subsection{В этом разделе}
Будет рассмотрен базовый функционал языка, то есть основная встроенная функциональность, такая как математические операторы, условия, циклы, бинарные операторы. Далее способы хранения и представления данных в Java, и в конце способы манипуляции данными, то есть функции (в терминах языка называющиеся методами).
\begin{itemize}
\item \nom{Метод}{функция в языке программирования, принадлежащая классу};
\item \nom{Типизация}{классификация по типам};
\item \nom{Переполнение}{целочисленное переполнение (англ. integer overflow) — ситуация в компьютерной арифметике, при которой вычисленное в результате операции значение не может быть помещено в тип данных};
\item \nom{Инициализация}{одновременной объявление переменной и присваивание ей значения};
\item \nom{Идентификатор}{идентификатор переменной -- название переменной, по которому возможно получить доступ к области памяти, соответствующей этой переменной};
\item \nom{Typecasting}{преобразование типов переменных в типизированных языках программирования};
\item \nom{Массив}{структура данных, хранящая набор значений в непрерывной области памяти};
\end{itemize}
\subsection{Данные}
\subsubsection{Понятие типов}
Хранение данных в Java осуществляется привычным для программиста образом: в переменных и константах.
Относительно типизации языки программирования бывают типизированными и нетипизированными (бестиповыми). Нетипизированные языки не представляют большого интереса в современном программировании.
Отсутствие типизации в основном присуще чрезвычайно старым и низкоуровневым языкам программирования, например, Forth и некоторым ассемблерам. Все данные в таких языках считаются цепочками бит произвольной длины и не делятся на типы. Работа с ними часто труднее, при этом часто безтиповые языки работают быстрее типизированных, но описывать с их помощью большие проекты со сложными взаимосвязями довольно утомительно.
\begin{frm}
\info Java является языком со \textbf{строгой} (также можно встретить термин «\textbf{сильной}») \textbf{явной} \textbf{статической} типизацией.
\end{frm}
\begin{itemize}
\item Статическая -- у каждой переменной должен быть тип, и этот тип изменить нельзя. Этому свойству противопоставляется динамическая типизация.
\item Явная -- при создании переменной ей обязательно необходимо присвоить какой-то тип, явно написав это в коде. В более поздних версиях языка (с девятой) стало возможным инициализировать переменные типа \code{var}, обозначающего нужный тип тогда, когда его возможно однозначно вывести из значения справа\footnote{аналог типа \code{auto} в языке C++}. Бывают языки с неявной типизацией, например, Python.
\item Строгая(сильная) -- невозможно смешивать разнотипные данные. С другой стороны, существует, например, JavaScript, в котором запись \code{2 + true} выдаст результат \code{3}.
\end{itemize}
\subsubsection{Антипаттерн «магические числа»}
Почти во всех примерах, которые используются для обучения, можно увидеть так называемый антипаттерн -- плохой стиль для написания кода. Числа, которые находятся справа от оператора присваивания используются в коде без пояснений. Такой антипаттерн называется «магическое число». Магическое, потому что непонятно, что это за число, почему это число именно такое и что будет, если это число изменить.
В реальных проектах так лучше не делать. Заранее нужно сказать, что рекомендуется помещать все числа в коде в именованные константы, которые хранятся в начале файла. Плюсом такого подхода является возможность легко корректировать значения переменных в достаточно больших проектах.
Например, в вашем коде несколько тысяч строк, а какое-то число, скажем, возраст совершеннолетия, число 18, использовалось несколько десятков раз. При использовании приложения в стране, где совершеннолетием считается 21 год вы должны будете перечитывать весь код в поисках магических «18» и исправить их на «21». В этом вопросе будет также важно не запутаться, действительно ли это 18, которые означают совершеннолетие, а не количество карманов в жилетке Анатолия Вассермана\footnote{мы то знаем, что их 26}.
В случае с константой изменить число нужно в одном месте.
\subsection{Примитивные типы данных}
Все данные в Java делятся на две основные категории: примитивные и ссылочные. Таблица \hrf{tab:types} демонстрирует все восемь примитивных типов языка и их размерности. Чтобы отправить на хранение какие-то данные используется оператор присваивания. Присваивание в программировании -- это не тоже самое, что математическое равенство, демонстрирующее тождественность, а полноценная операция.
Все присваивания всегда происходят справа налево, то есть сначала вычисляется правая часть, а потом результат вычислений присваивается левой. Исключений нет, именно поэтому в левой части не может быть никаких вычислений.
\begin{table}[H]
\centering
\begin{tabular}{|p{17mm}|p{80mm}|p{55mm}|}
\hline
Тип & Пояснение & Диапазон \\
\hline
byte & Самый маленький из адресуемых типов, 8 бит, знаковый & [\textminus128, +127] \\
\hline
short & Тип короткого целого числа, 16 бит, знаковый & [\textminus32 768, +32 767]\\
\hline
char & Целочисленный тип для хранения символов в кодировке UTF-8, 16 бит, беззнаковый & [0, +65 535]\\
\hline
int & Основной тип целого числа, 32 бита, знаковый & [\textminus2 147 483 648, +2 147 483 647] \\
\hline
long & Тип длинного целого числа, 64 бита, знаковый & [\textminus9 223 372 036 854 775 808, +9 223 372 036 854 775 807] \\
\hline
float & Тип вещественного числа с плавающей запятой (одинарной точности, 32 бита) & \\
\hline
double & Тип вещественного числа с плавающей запятой (двойной точности, 64 бита) & \\
\hline
boolean & Логический тип данных & true, false \\
\hline
\end{tabular}
\caption{Основные типы данных в языке Java}
\label{tab:types}
\end{table}
\begin{frm}\info
По умолчанию, создавая примитивную переменную, ей из-за примитивности данных, Java присваивает начальное значение -- ноль для числовых и ложь для булева. Что ещё раз доказывает, что мы храним там просто числа в двоичной системе счисления, мы не можем туда положить пустоту, а ноль -- это тоже значение.
\end{frm}
Шесть из восьми типов имеет диапазон значений, а значит основное их отличие в объёме занимаемой памяти. У \code{double} и \code{float} тоже есть диапазоны, но они заключаются в точности представления дробной части. Диапазоны означают, что если попытаться положить в переменную меньшего типа большее значение, произойдёт «переполнение переменной».
\subsubsection{Переполнение целочисленных переменных}
Чем именно чревато переполнение переменной, легче показать на примере (по ссылке -- \href{https://habr.com/ru/company/pvs-studio/blog/306748/}{расследование} крушения ракеты из-за переполнения переменной)
\begin{frm} \excl Переполнение переменных не распознаётся компилятором. \end{frm}
Если создать переменную типа \code{byte}, диапазон которого от $[-128, +127]$, и присвоить этой переменной значение $200$ произойдёт переполнение, как если попытаться влить пакет молока в напёрсток.
\begin{frm} \info Переполнение переменной -- это ситуация, в которой происходит попытка положить большее значение в переменную меньшего типа. \end{frm}
Важным вопросом при переполнении остаётся следующий: какое в переполненной переменной останется значение? Максимальное, $127$? $200 - 127 = 73$? Какой-то мусор? Каждый язык, а зачастую и разные компиляторы одного языка ведут себя в этом вопросе по разному.
\begin{frm}
\begin{center}
\excl В современном мире гигагерцев и терабайтов почти никто не пользуется маленькими типами, но именно из-за этого ошибки переполнения переменных становятся опаснее испанской инквизиции.
\includegraphics[width=5cm]{jc-02-spanish.jpg}
\end{center}
\end{frm}
\subsubsection{Задание для самопроверки}
\begin{enumerate}
\item Возможно ли объявить в Java целочисленную переменную и присвоить ей дробное значение?
\item Магическое число -- это:
\begin{enumerate}
\item числовая константа без пояснений;
\item число, помогающее в вычислениях;
\item числовая константа, присваиваемая при объявлении переменной.
\end{enumerate}
\item Переполнение переменной -- это:
\begin{enumerate}
\item слишком длинное название переменной;
\item слишком большое значение переменной;
\item расширение переменной вследствие записи большого значения.
\end{enumerate}
\end{enumerate}
\subsubsection{Бинарное (битовое) представление данных}
После информации о переполнении, нельзя не сказать о том, что именно переполняется. Далее будут представлены сведения которые касаются не только языка Java но и любого другого языка программирования. Эти сведения помогут разобраться в деталях того как хранится значение переменной в программе и как, в целом, происходит работа компьютерной техники.
\begin{frm} \info Все современные компьютеры, так или иначе работают от электричества и являются примитивными по своей сути устройствами, которые понимают только два состояния: есть напряжение в электрической цепи или нет. Эти два состояния принято записывать в виде 1 и 0, соответственно. \end{frm}
Все данные в любой программе -- это единицы и нули. Данные в программе на Java не исключение, удобнее всего это явление рассматривать на примере примитивных данных. Поскольку в компьютере можно оперировать только двумя значениями то естественным образом используется двоичная система счисления.
\begin{table}[H]
\centering
\begin{tabular}{|r|r|r|r|}
\hline
Десятичное & Двоичное & Восьмеричное & Шестнадцатеричное\\
\hline
00 & 00000 & 00 & 0x00 \\
01 & 00001 & 01 & 0x01 \\
02 & 00010 & 02 & 0x02 \\
03 & 00011 & 03 & 0x03 \\
04 & 00100 & 04 & 0x04 \\
05 & 00101 & 05 & 0x05 \\
06 & 00110 & 06 & 0x06 \\
07 & 00111 & 07 & 0x07 \\
08 & 01000 & 10 & 0x08 \\
09 & 01001 & 11 & 0x09 \\
10 & 01010 & 12 & 0x0a \\
11 & 01011 & 13 & 0x0b \\
12 & 01100 & 14 & 0x0c \\
13 & 01101 & 15 & 0x0d \\
14 & 01110 & 16 & 0x0e \\
15 & 01111 & 17 & 0x0f \\
16 & 10000 & 20 & 0x10 \\
\hline
\end{tabular}
\caption{Представления чисел}
\label{tab:counting-systems}
\end{table}
Двоичная система счисления это система счисления с основанием два. Существуют и другие системы счисления, например, восьмеричная, но сейчас она отходит на второй план полностью уступая своё место шестнадцатеричной системе счисления. Каждая цифра в десятичной записи числа называется разрядом, аналогично в двоичной записи чисел каждая цифра тоже называется разрядом, но для компьютерной техники этот разряд называется битом.
\begin{frm}
\info Одна единица или ноль -- это один \textbf{бит} передаваемой или хранимой информации.
\end{frm}
Биты принято собирать в группы по восемь штук, по восемь разрядов, эти группы называются \textbf{байт}. В языке Java возможно оперировать минимальной единицей информации, такой как байт для этого есть соответствующий тип. Диапазон байта, согласно таблицы $[-128, +127]$, то есть байт информации может в себе содержать ровно 256 значений. Само число $127$ в двоичной записи это семиразрядное число, все разряды которого единицы (то есть байт выглядит как \code{01111111}). Последний, восьмой, самый старший бит, определяет знак числа\footnote{Для более детального понимания данной темы желательно ознакомиться с информацией о цифровой схемотехнике и хранении отрицательных чисел с применением техники дополнительного кода.}. Для нас достаточно знать формулу расчёта записи отрицательных значений:
\begin{enumerate}
\item в прямой записи поменять все нули на единицы и единицы на нули;
\item поставить старший бит в единицу.
\end{enumerate}
Так возможно получить на единицу меньшее отрицательное число, то есть преобразовав 0 получим -1, 1 будет -2, 2 станет -3 и так далее.
Числа б\'{о}льших разрядностей могут хранить б\'{о}льшие значения, таким образом, преобразование диапазонов из десятичной системы счисления в двоичную покажет что \code{byte} это один байт, \code{short} это два байта, то есть 16 бит, \code{int} это 4 байта то есть 32 бита, а \code{long} это 8 байт или 64 бита хранения информации.
\subsubsection{Задания для самопроверки}
\begin{enumerate}
\item Возможно ли число 3000000000 (три миллиарда) записать в двоичном представлении?
\item Как вы думаете, почему шестнадцатеричная система счисления вытеснила восьмеричную?
\end{enumerate}
\subsubsection{Целочисленные типы}
Целочисленных типов четыре, и они занимают 1, 2, 4 и 8 байт.
\begin{frm}
\info Технически, целочисленых типов пять, но \code{char} устроен чуть сложнее других, поэтому не рассматривается в этом разделе.
\end{frm}
Значения в целочисленных типах могут быть только целые, никак и никогда невозможно присвоить им дробных значений. Про эти типы следует помнить следующее:
\begin{itemize}
\item \code{int} -- это самый часто используемый тип. Если сомневаетесь, какой целочисленный тип использовать, используйте \code{int};
\item все целые числа, которые пишутся в коде -- это \code{int}, даже если вы пытаетесь их присвоить переменной другого типа.
\end{itemize}
Как \code{int} преобразуется в меньше типы? Если написать цифрами \textit{справа} число, которое может поместиться в переменную меньшего типа \textit{слева}, то статический анализатор кода его пропустит, а компилятор преобразует в меньший тип автоматически (строка 9 на рис. \hrf{pic:byte-overflow}).
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jc-02-byte-overflow.png}
\caption{Присваивание валидных и переполняющих значений}
\label{pic:byte-overflow}
\end{figure}
Как видно, к маленькому \code{byte} успешно присваивается \code{int}. Если же написать число которое больше типа слева и, соответственно, поместиться не может, среда разработки выдает предупреждение компилятора, что ожидался \code{byte}, а передан \code{int} (строка 10 рис. \hrf{pic:byte-overflow}).
Часто нужно записать в виде числа какое-то значение большее чем может принимать \code{int}, и явно присвоить начальное значение переменной типа \code{long}.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jc-02-int-overflow.png}
\caption{Попытка инициализации переменной типа \code{long}}
\label{pic:int-overflow}
\end{figure}
В примере на рис. \hrf{pic:int-overflow} показана попытка присвоить значение 5000000000 (пять миллиардов) переменной типа \code{long}. Из текста ошибки ясно, что невозможно положить такое большое значение в переменную типа \code{int}, а это значит, что справа \code{int}. Почему большой \code{int} без проблем присваивается к маленькому байту?
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jc-02-float-overflow.png}
\caption{Решение проблемы переполнения числовых констант}
\label{pic:overflow-solution}
\end{figure}
На рис. \hrf{pic:overflow-solution} продемонстрировано, что аналогичная ситуация возникает с типами \code{float} и \code{double}. Все дробные числа, написанные в коде -- это \code{double}, поэтому положить их во \code{float} без дополнительных усилий невозможно. В этих случаях к написанному справа числу нужно добавить явное указание на его тип.
\begin{frm}\excl
Для \code{long} пишем \code{L}, а для \code{float} -- \code{f}. Чаще всего \code{L} пишут заглавную, чтобы подчеркнуть, что тип больше, а \code{f} пишут маленькую, чтобы подчеркнуть, что мы уменьшаем тип. Но регистр в этом конкретном случае значения не имеет, можно писать и так и так.
\end{frm}
\subsubsection{Числа с плавающей запятой (точкой)}
Как видно из таблицы \hrf{tab:types}, два из восьми типов не имеют диапазонов значений. Это связано с тем, что диапазоны значений \code{float} и \code{double} заключаются не в величине возможных хранимых чисел, а в точности этих чисел после запятой.
\begin{frm} \info Числа с плавающей запятой в англоязычной литературе называются числа с плавающей точкой (от англ. floating point). Такое различие связано с тем, что в русскоязычной литературе принято отделять дробную часть числа запятой, а в европейской и американской -- точкой. \end{frm}
Хранение чисел с плавающей запятой\footnote{хорошо и подробно, но на С, в посте на \href{https://habr.com/ru/post/112953/}{Хабре}.} работает по стандарту IEEE 754 (1985 г). Для работы с числами с плавающей запятой на аппаратурном уровне к обычному процессору добавляют математический сопроцессор (FPU, floating point unit).
\begin{figure}[H]
\centering
\begin{subfigure}[b]{0.48\textwidth}
\centering
\def\svgwidth{\textwidth}
\includesvg{pics/jc-02-float-struct.svg}
\caption{double}
\label{pic:float-double}
\end{subfigure}
\hfill
\begin{subfigure}[b]{0.48\textwidth}
\centering
\def\svgwidth{\textwidth}
\includesvg{pics/jc-02-float-struct32.svg}
\caption{float}
\label{pic:float-float}
\end{subfigure}
\caption{Типы с плавающей запятой}
\label{pic:float-struct}
\end{figure}
Рисунок \hrf{pic:float-struct} демонстрирует, как распределяются биты в числах с плавающей запятой разных разрядностей, где S -- Sign (знак), E -- Exponent, 8 (или 11) разрядов поля порядка, экспонента, M -- Mantissa, 23 (или 52) бита мантиссы, дробная часть числа. Также на рисунке показана так называемая, мнимая единица, она всегда есть в самом старшем разряде мантиссы, поэтому её всегда подразумевают, но в явном виде не хранят, экономя один бит информации.
Если попытаться уложить весь стандарт в два предложения, то получится примерно следующее: получить число в соответствующих разрядностях возможно по формулам:
\begin{equation*}
\begin{gathered}
F_{32} = (-1)^S \times 2^{E-127} \times (1 + \frac{M}{2^{23}}) \\
F_{64} = (-1)^S \times 2^{E-1023} \times (1 + \frac{M}{2^{52}})
\end{gathered}
\end{equation*}
\begin{frm} \info Например:
$+0,5 = 2^{-1}$ поэтому, число будет записано как
\code{0_01111110_00000000000000000000000}, то есть знак $= 0$, мантисса $= 0$, порядок = $127 - 1 = 126$, чтобы получить следующие результаты вычислений:
$-1^0$ положительный знак, умножить на порядок
$2^{126-127 = -1} = 0,5$ и умножить на мантиссу
$1 + 0$. То есть, $-1^0 \times 2^{-1} \times (1 + 0) = 0,5.$
Отсюда становится очевидно, что чем сложнее мантисса и чем меньше порядок, тем более точные и интересные числа мы можем получить.
\end{frm}
Возьмём для примера число $-0,15625$, чтобы понять как его записывать, откинем знак, это будет единица в разряде, отвечающем за знак, и посчитаем мантиссу с порядком. Представим число как положительное и будем от него последовательно отнимать числа, являющиеся отрицательными степенями двойки, чтобы получить максимально близкое к нулю значение, но не превысить его.
\begin{equation*}
\begin{gathered}
2^{1} = 2 \\ 2^{0} = 1.0 \\ 2^{-1} = 0.5 \\ 2^{-2} = 0.25 \\ 2^{-3} = 0.125 \\ 2^{-4} = 0.0625 \\ 2^{-5} = 0.03125 \\ 2^{-6} = 0.015625 \\ 2^{-7} = 0.0078125 \\ 2^{-8} = 0.00390625
\end{gathered}
\end{equation*}
Очевидно, что $-1$ и $-2$ степени отнять не получится, поскольку мы явно уходим за границу нуля, а вот $-3$ прекрасно отнимается, значит порядок будет $127-3 = 124$, осталось понять, что получится в мантиссе.
Видим, что оставшееся после первого вычитания ($0,15625 - 0,125$) число -- это $2^{-5}$. Значит в мантиссе пишем \code{01} и остальные нули, то есть слева направо указываем, какие степени после $-3$ будут нужны. $-4$ не нужна, а $-5$ нужна.
Получится, что
\begin{equation*}
\begin{gathered}
(-1)^1 \times 2^{(124-127)} \times (1 + \frac{2097152}{2^{23}}) = -0,15625 \\
\text{или, тождественно,}\\
(-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*}
Так число с плавающей запятой возможно посчитать двумя способами: по приведённой формуле, или последовательно складывая разряды мантиссы умноженные на двойку в степени порядка, уменьшая порядок на каждом шагу.
\begin{figure}[h]
\centering
\includegraphics[width=17cm]{jc-02-float02.png}
\caption{Особенности работы с числами с плавающей запятой}
\label{pic:float-points}
\end{figure}
К особенностям работы чисел с плавающей запятой можно отнести:
\begin{itemize}
\item возможен как положительный, так и отрицательный ноль (в целых числах ноль всегда положительный);
\item есть огромная зона, отмеченная на рисунке \hrf{pic:float-points}, которая являет собой непредставимые числа, слишком большие для хранения внутри такой переменной или настолько маленькие, что мнимая единица в мантиссе отсутствует;
\item в таком числе можно хранить значения положительной и отрицательной бесконечности;
\item при работе с такими числами появляется понятие не-числа, при этом важно помнить, что \code{NaN != NaN}.
\end{itemize}
\subsubsection{Задания для самопроверки}
\begin{enumerate}
\item Сколько байт данных занимает самый большой целочисленный тип?
\item Почему нельзя напрямую сравнивать целочисленные данные и числа с плавающей запятой, даже если там точно лежит число без дробной части?
\item Внутри переполненной переменной остаётся значение:
\begin{enumerate}
\item переданное -- максимальное для типа;
\item максимальное для типа;
\item не определено.
\end{enumerate}
\end{enumerate}
\subsubsection{Символы и булевы}
Шесть из восьми примитивных типов могут иметь как положительные, так и отрицательные значения, они называются \textbf{«знаковые»} типы. В таблице есть два типа, у которых есть диапазон но нет отрицательных значений, это \code{boolean} и \code{char}
Булев тип хранит значение \code{true} или \code{false}. На собеседованиях иногда спрашивают, сколько места занимает \code{boolean}. В Java объём хранения не определён и зависит от конкретной JVM, обычно считают, что это один байт.
\begin{table}[h!]
\centering
\begin{tabular}{||c|c|c||c|c|c||c|c|c||c|c|c||}
\hline
dec & hex & val & dec & hex & val & dec & hex & val & dec & hex & val \\
\hline
000 & 0x00 & (nul) & 032 & 0x20 & \textvisiblespace & 064 & 0x40 & @ & 096 & 0x60 & \textquoteleft \\
001 & 0x01 & (soh) & 033 & 0x21 & ! & 065 & 0x41 & A & 097 & 0x61 & a \\
002 & 0x02 & (stx) & 034 & 0x22 & " & 066 & 0x42 & B & 098 & 0x62 & b \\
003 & 0x03 & (etx) & 035 & 0x23 & \# & 067 & 0x43 & C & 099 & 0x63 & c \\
004 & 0x04 & (eot) & 036 & 0x24 & \$ & 068 & 0x44 & D & 100 & 0x64 & d \\
005 & 0x05 & (enq) & 037 & 0x25 & \% & 069 & 0x45 & E & 101 & 0x65 & e \\
006 & 0x06 & (ack) & 038 & 0x26 & \& & 070 & 0x46 & F & 102 & 0x66 & f \\
007 & 0x07 & (bel) & 039 & 0x27 & \textquotesingle & 071 & 0x47 & G & 103 & 0x67 & g \\
008 & 0x08 & (bs) & 040 & 0x28 & ( & 072 & 0x48 & H & 104 & 0x68 & h \\
009 & 0x09 & (tab) & 041 & 0x29 & ) & 073 & 0x49 & I & 105 & 0x69 & i \\
010 & 0x0A & (lf) & 042 & 0x2A & * & 074 & 0x4A & J & 106 & 0x6A & j \\
011 & 0x0B & (vt) & 043 & 0x2B & + & 075 & 0x4B & K & 107 & 0x6B & k \\
012 & 0x0C & (np) & 044 & 0x2C & \textquoteright & 076 & 0x4C & L & 108 & 0x6C & l \\
013 & 0x0D & (cr) & 045 & 0x2D & - & 077 & 0x4D & M & 109 & 0x6D & m \\
014 & 0x0E & (so) & 046 & 0x2E & . & 078 & 0x4E & N & 110 & 0x6E & n \\
015 & 0x0F & (si) & 047 & 0x2F & / & 079 & 0x4F & O & 111 & 0x6F & o \\
016 & 0x10 & (dle) & 048 & 0x30 & 0 & 080 & 0x50 & P & 112 & 0x70 & p \\
017 & 0x11 & (dc1) & 049 & 0x31 & 1 & 081 & 0x51 & Q & 113 & 0x71 & q \\
018 & 0x12 & (dc2) & 050 & 0x32 & 2 & 082 & 0x52 & R & 114 & 0x72 & r \\
019 & 0x13 & (dc3) & 051 & 0x33 & 3 & 083 & 0x53 & S & 115 & 0x73 & s \\
020 & 0x14 & (dc4) & 052 & 0x34 & 4 & 084 & 0x54 & T & 116 & 0x74 & t \\
021 & 0x15 & (nak) & 053 & 0x35 & 5 & 085 & 0x55 & U & 117 & 0x75 & u \\
022 & 0x16 & (syn) & 054 & 0x36 & 6 & 086 & 0x56 & V & 118 & 0x76 & v \\
023 & 0x17 & (etb) & 055 & 0x37 & 7 & 087 & 0x57 & W & 119 & 0x77 & w \\
024 & 0x18 & (can) & 056 & 0x38 & 8 & 088 & 0x58 & X & 120 & 0x78 & x \\
025 & 0x19 & (em) & 057 & 0x39 & 9 & 089 & 0x59 & Y & 121 & 0x79 & y \\
026 & 0x1A & (eof) & 058 & 0x3A & : & 090 & 0x5A & Z & 122 & 0x7A & z \\
027 & 0x1B & (esc) & 059 & 0x3B & ; & 091 & 0x5B & [ & 123 & 0x7B & \char`\{ \\
028 & 0x1C & (fs) & 060 & 0x3C & < & 092 & 0x5C & \char`\\ & 124 & 0x7C & | \\
029 & 0x1D & (gs) & 061 & 0x3D & = & 093 & 0x5D & ] & 125 & 0x7D & \char`\} \\
030 & 0x1E & (rs) & 062 & 0x3E & > & 094 & 0x5E & \^{} & 126 & 0x7E & \~{} \\
031 & 0x1F & (us) & 063 & 0x3F & ? & 095 & 0x5F & \char`\_ & 127 & 0x7F & \code{\DEL} \\
\hline
\end{tabular}
\caption{Фрагмент UTF-8 (ASCII) таблицы}
\label{table:utf-8-ascii}
\end{table}
Тип \code{char} единственный беззнаковый целочисленный тип в языке, то есть его старший разряд хранит полезное значение, а не признак положительности. Тип целочисленный но по умолчанию среда исполнения интерпретирует его как символ по таблице utf-8 (см. фрагмент в таблице \hrf{table:utf-8-ascii}).
\begin{frm}\excl
В языке Java есть разница между одинарными и двойными кавычками.
\end{frm}
В одинарных кавычках всегда записывается символ (\code{char}), который на самом деле является целочисленным значением, а в двойных кавычках всегда записывается строка, которая фактически является экземпляром класса \code{String}. Поскольку типизация строгая, то невозможно записать в \code{char} строки, а в строки числа.
\begin{frm} \info В Java есть три основных понятия, связанных с данными переменными и использованием значений: объявление, присваивание, инициализация.
Для того чтобы \textit{объявить} переменную, нужно написать её тип и название, также часто вместо названия можно встретить термин идентификатор.
Далее в любой момент можно \textit{присвоить} этой переменной значение, то есть необходимо написать идентификатор, использовать оператор присваивания, и справа написать значение, которое вы хотите присвоить данной переменной. Поставить в конце строки точку с запятой.
Также существует понятие \textit{инициализации} -- это когда объединяются на одной строке объявление и присваивание.\end{frm}
\subsubsection{Преобразование типов}
Java - это язык со строгой статической типизацией, но преобразование типов в ней всё равно есть. Простыми словами, преобразование типов -- это когда компилятор видит, что типы переменных по разные стороны присваивания разные, начинает разрешать это противоречие, успешно или нет. Преобразование типов бывает явное и неявное. Неявное преобразование типов происходит когда компилятор в состоянии сам преобразовать типы, явное, когда ему нужна помощь.
\begin{frm}
\info В разговоре или в сообществах можно услышать или прочитать термины тайпкастинг, кастинг, каст, кастануть, и другие производные от английского typecasting.
\end{frm}
Неявное преобразование типов происходит, когда присваиваются числа переменным меньшей размерности, чем \code{int}. Число справа это \code{int}, а значит 32 разряда, а слева, например, \code{byte}, и в нём всего 8 разрядов, но ни среда ни компилятор не «поругались», потому что значение в большом \code{int} не превысило 8 разрядов маленького \code{byte}. Итак неявное преобразование типов происходит в случаях, когда, «всё и так понятно». В случае, если неявное преобразование невозможно, статический анализатор кода выдаёт ошибку, что ожидался один тип, а был дан другой.
Явное преобразование типов происходит, когда мы явно пишем в коде, что некоторое значение должно иметь определённый тип. Этот вариант приведения типов тоже был рассмотрен, когда к числам дописывались типовые квалификаторы \code{L} и \code{f}. Но чаще всего случается, что происходит присваивание переменным не тех значений, которые были написаны в тексте программы, а те, которые получились в результате каких-то вычислений.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jc-02-byte-cast-error.png}
\caption{Ошибка приведения типов}
\label{pic:byte-cast-error}
\end{figure}
На рис. \hrf{pic:byte-cast-error} приведён простейший пример, в котором очевидно, что внутри переменной \code{i0} содержится значение, не превышающее одного байта хранения, а значит возможно явно сообщить компилятору, что значение точно поместится в \code{byte}. \textit{Явно преобразовать типы}. Для этого нужно в правой части оператора присваивания перед идентификатором переменной в скобках добавить название типа, к которому необходимо преобразовать значение этой переменной.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jc-02-byte-cast-fix.png}
\caption{Верное приведение типов}
\label{pic:byte-cast-error}
\end{figure}
\subsubsection{Константность}
Constare -- (лат. стоять твёрдо). Константность это свойство неизменяемости. В Java ключевое слово \code{const} не реализовано, хоть и входит в список ключевых, зарезервированных. Константы создаются при помощи ключевого слова \code{final}. Ключевое слово \code{final} возможно применять не только с примитивами, но и со ссылочными типами, методами, классами.
\begin{frm}
\info Константа -- это переменная или идентификатор с конечным значением.
\end{frm}
\subsubsection{Задания для самопроверки}
\begin{enumerate}
\item Какая таблица перекодировки используется для представления символов?
\item Каких действий требует от программиста явное преобразование типов?
\item какое значение будет содержаться в переменной a после выполнения строки \code{int а = 10.0f/3.0f;}
\end{enumerate}
\subsection{Ссылочные типы данных, массивы}
Ссылочные типы данных -- это все типы данных, кроме восьми перечисленных примитивных. Самым простым из ссылочных типов является массив. Фактически, массив выведен на уровень языка и не имеет специального ключевого слова.
Ссылочные типы отличаются от примитивных местом хранения информации. В \textit{примитивах} данные хранятся там, где существует переменная и где написан её идентификатор, а по идентификатору \textit{ссылочного} типа хранится не значение, а ссылка. Ссылку можно представить как ярлык на рабочем столе, то есть, очевидно, что непосредственная информация хранится не там, где написан идентификатор. Такое явное разделение идентификатора переменной и данных важно помнить и понимать при работе с ООП.
\begin{frm} \info \textbf{Массив} -- это единая, сплошная область данных, в связи с чем в массивах возможно осуществление доступа по индексу \end{frm}
Самый младший индекс любого массива -- \textit{ноль}, поскольку \textbf{индекс} -- это значение смещения по элементам относительно начального адреса массива. То есть, для получения самого первого элемента нужно сместиться на ноль шагов. Очевидно, что самый последний элемент в массиве из десяти значений, будет храниться по \textit{девятому} индексу.
Массивы возможно создавать несколькими способами (листинг \hrf{lst:array-init}). В общем виде объявление -- это тип, квадратные скобки как обозначение того, что это будет массив из переменных этого типа, идентификатор (строка \hrf{codeline:arr-define}). Инициализировать массив можно либо ссылкой на другой массив (строка \hrf{codeline:arr-link}), пустым массивом (строка \hrf{codeline:arr-new}) или заранее заданными значениями, записанными через запятую в фигурных скобках (строка \hrf{codeline:arr-values}). Присвоить в процессе работы идентификатору возможно только значение ссылки из другого идентификатора или новый пустой массив.
\begin{lstlisting}[language=Java,style=JCodeStyle,caption={Объявление массива},label={lst:array-init}]
int[] array0;<@ \label{codeline:arr-define} @>
int[] array1 = array0;<@ \label{codeline:arr-link} @>
int[] array2 = new int[5];<@ \label{codeline:arr-new} @>
int[] array3 = {5, 4, 3, 2, 1};<@ \label{codeline:arr-values} @>
array2 = {1, 2, 3, 4, 5}; // <@\lh{dkgreen}{<-- здесь недопустимо присваивание}@> <@ \label{codeline:arr-invalid} @>
\end{lstlisting}
Если мы не определяем переменную, понятно, данные мы хранить не планируем. Если определяем примитивную, помним, она инициализируется нулём, а если мы определяем ссылочный идентификатор, он имеет начальное значение \code{null}, то есть явно демонстрирует, что не ссылается ни на какой объект. \textbf{Нулевой указатель} -- это гораздо более серьёзное явление, чем просто временное отсутствие объекта по идентификатору, очень часто это не инициализированные объекты и попытки вызова невозможных методов. Поэтому в работе очень часто помогает понять, что именно пошло не так \code{NullPointerException}.
\begin{frm} \excl Никак и никогда нельзя присвоить идентификатору целый готовый массив (создаваемый здесь и сейчас) в процессе работы, нельзя стандартными средствами переприсвоить ряд значений части массива (так называемые слайсы или срезы). \end{frm}
Массивы бывают как одномерные, так и многомерные. Многомерный массив -- это всегда массив из массивов меньшего размера: двумерный массив -- это массив одномерных, трёхмерный -- массив двумерных и так далее. Правила инициализации у них не отличаются. Преобразовать тип массива нельзя никогда, но можно преобразовать тип каждого отдельного элемента при чтении. Это связано с тем, что под массивы сразу выделяется непрерывная область памяти, а со сменой типа всех значений массива эту область нужно будет или значительно расширять или значительно сужать.
Ключевое слово \code{final} работает только с идентификатором массива, то есть не запрещает изменять значения его элементов.
Если алгоритм приложения предполагает создание нижних измерений массива в процессе работы программы, то при инициализации массива верхнего уровня не следует указывать размерности нижних уровней. Это связано с тем, что при инициализации, Java сразу выделяет память под все измерения, а присваивание нижним измерениям новых ссылок на создаваемые в процессе работы массивы, будет пересоздавать области памяти, получается небольшая утечка памяти.
Прочитать из массива значение возможно обратившись к ячейке массива по индексу. Записать в массив значение возможно обратившись к ячейке массива по индексу, и применив оператор присваивания.
\begin{lstlisting}[language=Java,style=JCodeStyle]
int i = array[0];
array[1] = 10;
\end{lstlisting}
В каждом объекте массива есть специальное поле (рис. \hrf{pic:array-length}), которое обозначает длину данного массива. Поле находится в классе \code{__Array__} и является публичной константой.
\begin{figure}[H]
\centering
\includegraphics[width=12cm]{jc-02-array-length.png}
\caption{Константа с длиной массива}
\label{pic:array-length}
\end{figure}
\subsubsection{Задания для самопроверки}
\begin{enumerate}
\item Почему индексация массива начинается с нуля?
\item Какой индекс будет последним в массиве из 100 элементов?
\item Сколько будет создано одномерных массивов при инициализации массива 3х3?
\end{enumerate}
\subsection{Базовый функционал языка}
\subsubsection{Математические операторы}
Математические операторы работают как и предполагается -- складывают, вычитают, делят, умножают, делают это по приоритетам, известным нам с пятого класса, а если приоритет одинаков -- слева направо. Специального оператора возведения в степень как в Python нет. Единственное, что следует помнить, что оператор присваивания продолжает быть оператором присваивания, а не является математическим тождеством, а значит сначала посчитается всё, что слева, а потом результат попробует присвоиться переменной справа. Припоминаем какие есть особенности у операции целочисленного деления, связанные с отбрасыванием дробной части.
\subsubsection{Условия}
Условия представлены в языке привычными \code{if}, \code{else if}, \code{else}, «если», «иначе если», «в противном случае», которые являются единым оператором выбора, то есть, если исполнение программы пошло по одной из веток, то в другую ветку условия программа никогда не зайдёт. Каждая ветвь условного оператора -- это отдельный кодовый блок со своим окружением и локальными переменными.
Существует альтернатива оператору \code{else if} -- использование оператора \code{switch}, который позволяет осуществлять множественный выбор между числовыми значениями. У оператора есть ряд особенностей:
\begin{itemize}
\item это оператор, состоящий из одного кодового блока, то есть сегменты кода находятся в одной области видимости. Если не использовать оператор \code{break}, есть риск «проваливаться» в следующие кейсы;
\item нельзя создать диапазон значений;
\item достаточно сложно создавать локальные переменные с одинаковым названием для каждого кейса.
\end{itemize}
\subsubsection{Циклы}
Циклы представлены основными конструкциями:
\begin{itemize}
\item \code{while () {}}
\item \code{do {} while();}
\item \code{for (;;) {}}
\end{itemize}
Цикл -- это набор повторяющихся до наступления условия действий. \code{while} -- самый простой, чаще всего используется, когда нужно описать бесконечный цикл. \code{do-while} единственный цикл с постусловием, то есть сначала выполняется тело, а затем принимается решение о необходимости зацикливания, используется для ожидания ответов на запрос и возможного повторения запроса по условию. \code{for} -- классический счётный цикл, его почему-то программисты любят больше всего.
Существует также активно пропагандируемый цикл -- \code{foreach}, работает не совсем очевидным образом, для понимания его работы необходимо ознакомиться с ООП и понятием итератора.
\subsubsection{Бинарные арифметические операторы}
В современных реалиях мегамощных компьютеров вряд ли кто-то задумывается об оптимизации скорости выполнения программы или экономии занимаемой памяти. Но всё меняется, когда программист впервые принимает сложное решение: запрограммировать микроконтроллер или другой «интернет вещей». Там в вашем распоряжении пара сотен килобайт памяти, если очень повезёт, в которые нужно не только как-то вложить текст программы и исполняемый бинарный код, но и какие-то промежуточные, пользовательские и другие данные, буферы обмена и обработки. Другая ситуация, в которой нужно начинать «думать о занимаемом пространстве» это разработка протоколов передачи данных, чтобы протокол был быстрый, не передавал по сети большие объёмы данных и быстро преобразовывался. На помощь приходит натуральная для информатики система счисления, двоичная.
Манипуляции двоичными данными представлены в Джава следующими операторами:
\begin{itemize}
\item \code{&} битовое и;
\item \code{|} битовое или;
\item \code{~} битовое не;
\item \code{^} исключающее или;
\item \code{<<} сдвиг влево;
\item \code{>>} сдвиг вправо.
\end{itemize}
Литеральные «и», «или», «не» уже знакомы по условным операторам. Литеральные операции применяются ко всему числовому литералу целиком, а не к каждому отдельному биту. Их особенность заключается в том, как язык программирования интерпретирует числа.
\begin{frm} \info В Java в литеральных операциях может участвовать только тип \code{boolean}, в то время, как, например, C++ воспринимает любой ненулевой целочисленный литерал как истину, а нулевой, соответственно, как ложь.
\end{frm}
Логика формирования значения при этом остаётся такой же, как и при битовых операциях.
\begin{table}[h!]
\centering
\begin{tabular}{||c|c|c||}
\hline
A & B & AND \\ [0.5ex]
\hline\hline
0 & 0 & 0 \\
0 & 1 & 0 \\
1 & 0 & 0 \\
1 & 1 & 1 \\
\hline
\end{tabular}
\hfill
\begin{tabular}{||c|c|c||}
\hline
A & B & OR \\ [0.5ex]
\hline\hline
0 & 0 & 0 \\
0 & 1 & 1 \\
1 & 0 & 1 \\
1 & 1 & 1 \\
\hline
\end{tabular}
\hfill
\begin{tabular}{||c|c|c||}
\hline
A & B & XOR \\ [0.5ex]
\hline\hline
0 & 0 & 0 \\
0 & 1 & 1 \\
1 & 0 & 1 \\
1 & 1 & 0 \\
\hline
\end{tabular}
\hfill
\begin{tabular}{||c|c||}
\hline
A & NOT \\ [0.5ex]
\hline\hline
0 & 1 \\
1 & 0 \\
\hline
\end{tabular}
\caption{Таблицы истинности битовых операторов}
\label{table:bin-truth-tables}
\end{table}
Когда говорят о битовых операциях, так или иначе, появляется необходимость поговорить о таблицах истинности. В таблице \hrf{table:bin-truth-tables} вы видите таблицы истинности для арифметических битовых операций. Битовые операции отличаются тем, что для неподготовленного взгляда они производят почти магические действия, потому что манипулируют двоичным представлением числа.
\begin{figure}[H]
\begin{arithmetic}
00000100&4\\
\code{&} 00000111&7\\
00000100&4\\
\end{arithmetic}
\hfill
\begin{arithmetic}
00000100&4\\
\code{|} 00000111&7\\
00000111&7\\
\end{arithmetic}
\hfill
\begin{arithmetic}
00000100&4\\
\code{^} 00000111&7\\
00000011&3\\
\end{arithmetic}
\hfill
\begin{arithmetic}
\code{~} 00000100&4\\
11111011&-5\\
\end{arithmetic}
\caption{Бинарная арифметика}
\label{fig:bit-ops}
\end{figure}
\begin{table}[h!]
\centering
\begin{tabular}{||c|c|c||}
\hline
Число & Бинарное & Сдвиг \\ [0.5ex]
\hline\hline
2 & 00000010 & \code{2 << 0} \\
4 & 00000100 & \code{2 << 1} \\
8 & 00001000 & \code{2 << 2} \\
16 & 00010000 & \code{2 << 3} \\
32 & 00100000 & \code{2 << 4} \\
64 & 01000000 & \code{2 << 5} \\
128 & 10000000 & \code{2 << 6} \\
\hline
\end{tabular}
\begin{tabular}{||c|c|c||}
\hline
Число & Бинарное & Сдвиг \\ [0.5ex]
\hline\hline
128 & 10000000 & \code{128 >> 0} \\
64 & 01000000 & \code{128 >> 1} \\
32 & 00100000 & \code{128 >> 2} \\
16 & 00010000 & \code{128 >> 3} \\
8 & 00001000 & \code{128 >> 4} \\
4 & 00000100 & \code{128 >> 5} \\
2 & 00000010 & \code{128 >> 6} \\
\hline
\end{tabular}
\caption{Битовые сдвиги}
\label{table:bit-shift}
\end{table}
С битовыми сдвигами работать гораздо интереснее и выгоднее. Они производят арифметический сдвиг значения слева на количество разрядов, указанное справа, в таблице \hrf{table:bit-shift} представлены восьмиразрядные беззнаковые числа, в битовом представлении это одна единственная единица, находящаяся в разных разрядах числа. Это демонстрация сдвига на один разряд влево, и, как следствие, умножение на два. Обратная ситуация со сдвигом вправо, он является целочисленным делением.
\begin{frm} \info
\begin{itemize}
\item \code{X && Y} -- литеральная;
\item \code{X || Y} -- литеральная;
\item \code{!X} -- литеральная;
\item \code{N << K} -- $N * 2^K$;
\item \code{N >> K} -- $N / 2^K$;
\item \code{x & y} -- битовая. 1 если оба x = 1 и y = 1;
\item \code{x | y} -- битовая. 1 если хотя бы один из x = 1 или y = 1;
\item \code{~x} -- битовая. 1 если x = 0;
\item \code{x ^ y} -- битовая. 1 если x отличается от y.
\end{itemize}
\end{frm}
\subsubsection{Задания для самопроверки}
\begin{enumerate}
\item Почему нежелательно использовать оператор \code{switch} если нужно проверить диапазон значений?
\item Возможно ли записать бесконечный цикл с помощью оператора \code{for}?
\item \code{2 + 2 * 2 == 2 << 2 >> 1}?
\end{enumerate}
\subsection{Функции}
\textbf{Функция} -- это исполняемый блок кода. Функция, принадлежащая классу называется \textbf{методом}.
\begin{lstlisting}[language=Java,style=JCodeStyle]
void method(int param1, int param2) {
//function body
}
public static void main (String[] args) {
method(arg1, arg2);
}
\end{lstlisting}
При объявлении функции в круглых скобках указываются параметры, а при вызове -- аргументы.
У функций есть правила именования: функция -- это переходный глагол совершенного вида в настоящем времени (вернуть, посчитать, установить, создать), часто снабжаемый дополнением, субъектом действия. Методы в Java пишутся lowerCamelCase. Важно, в каком порядке записаны параметры метода, от этого будет зависеть порядок передачи в неё аргументов. Методы обособлены и их параметры локальны, то есть не видны другим функциям.
\begin{frm}
\excl Нельзя писать функции внутри других функций.
\end{frm}
Все аргументы передаются копированием, не важно, копирование это числовой константы, числового значения переменной или хранимой в переменной ссылке на массив. Сам объект в метод не копируется, а копируется только его ссылка.
Возвращаемые из методов значения появляются в том месте, где метод был вызван. Если будет вызвано несколько методов, то весь контекст исполнения первого метода сохраняется, кладётся (на стек) в стопку уже вызванных методов и процессор идёт выполнять только что вызванный второй метод. По завершении вызванного второго метода, мы снимаем со стека лежащий там контекст первого метода, кладём в него вернувшееся из второго метода значение, если оно есть, и продолжаем исполнять первый метод.
Возвращаемого значения у метода может и не быть, в некоторых языках такие функции и методы называются процедурами, а в Java для них просто придумали специальное возвращаемое значение -- \code{void}. Void (от англ. пустота), интересно отметить, что это не какое-то простое emptyness, а серьёзное масштабное космическое void, чтобы подчеркнуть, что метод совсем ничего точно не возвращает.
\textbf{Вызов метода} -- это, по смыслу, тоже самое, что подставить в код сразу его возвращаемое значение.
\textbf{Сигнатура метода} -- это имя метода и его параметры. В сигнатуру метода не входит возвращаемое значение. Нельзя написать два метода с одинаковой сигнатурой.
\textbf{Перегрузка методов} -- это механизм языка, позволяющий написать методы с одинаковыми названиями и разными оставшимися частями сигнатуры, чтобы получить единообразие при вызове семантически схожих методов с разнотипными данными.
\subsection*{Практическое задание}
\begin{enumerate}
\item Написать метод «Шифр Цезаря», с булевым параметром зашифрования и расшифрования и числовым ключом;
\item Написать метод, принимающий на вход массив чисел и параметр n. Метод должен осуществить циклический (последний элемент при сдвиге становится первым) сдвиг всех элементов массива на n позиций;
\item Написать метод, которому можно передать в качестве аргумента массив, состоящий строго из единиц и нулей (целые числа типа \code{int}). Метод должен заменить единицы в массиве на нули, а нули на единицы и не содержать ветвлений. Написать как можно больше вариантов метода.
\end{enumerate}
\newpage
\printnomenclature[40mm]
\end{document}