\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}