713 lines
72 KiB
TeX
713 lines
72 KiB
TeX
\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}
|