Java для собеседований5 читателей тэги

Автор: Widowmaker1984

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

Вопрос 23 (многомерные массивы)

"Какие виды массивов вы знаете?"

1. В Java два вида массивов: одномерные и многомерные. Последние в Java фактически представляют из себя массивы массивов.

2. Для создания многомерных массивов используются дополнительные скобки:

int[][] a = {
{ 1, 2, 3 },
{ 4, 5, 6 }
}

2.1. Также массив может создаваться ключевым словом new:

// трехмерный массив фиксированной длины
int[][][] b = new int[2][4][4];

скрытый текст3. Размер двумерного массива измеряется интересным способом. Длина массива определяется по его первой размерности, то есть вычисляется количество рядов. Чтобы узнать количество столбцов в ряду, надо указать ряд, а затем вычислить у него количество столбцов.

// число колонок у третьего ряда
System.out.println(matrix[2].length);

4. Допустим, вы хотите представить таблицу умножения в виде многомерного массива:
int[][] multiplicationTable = new int[10][10];

В некоторых языках такой массив создается в виде единого блока из 100 значений int. Java поступает иначе. Эта строка кода выполняет три действия:

- Объявляет переменную с именем multiplicationTable, которая содержит ссылку на массив ссылок, которые в свою очередь будут указывать на массивы int.
- Создает массив из 10 элементов (первый индекс), который будет содержать ссылки на 10 одномерных массивов int. Отсюда собственно и понятие – массив массивов.
- На этой стадии создания массив ссылок заполняется значениями по умолчанию, то есть значениями null.
- Создает еще 10 массивов, каждый из которых в свою очередь является массивом из 10 элементов int.
- Присваивает ссылки на каждый из этих 10 новых массивов элементам массива, созданного на втором шаге. По умолчанию каждый элемент int каждого из этих 10 новых массивов получает значение 0.

4.1. Другими словами, представленную выше строку кода, можно записать так:

int[][] multiplicationTable = new int[10][]; // первый индекс содержит ссылки на массивы int
for (int i = 0; i < 10; i++)
multiplicationTable[i] = new int[10]; // создаем 10 массивов int

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

4.2.1. Возьмем к примеру вот такие два определения двумерных массивов:

int[][] a = new int[10][1000];
int[][] b = new int[1000][10];

В случае массива a, количество порождаемых в памяти объектов равно 11, а в случае массива b1001. Создание и обслуживание каждого объекта в памяти виртуальной машины имеет свои накладные расходы, так как виртуальная машина считает ссылки для каждого объекта, хранит его атрибуты и т.д. и т.п. Таким образом массив b может занимать в памяти в полтора, а то и в два раза больше места чем массив a.

5. При работе с многомерными массивами в Java очень важно понять, что первые индексы многомерных массивов содержат только массивы ссылок, и только последний (самый правый) индекс содержит непосредственно элемент данных типа объявленного для массива.

5.1. То есть в Java можно объявить и более чем двумерные массивы. Например:

int[][][] dim3D;

И тут важно понимать, что первый индекс данного массива, содержит массив ссылок, на второй индекс данного массива, который в свою очередь тоже содержит массив ссылок на массивы значений int. То есть если в данном случае вывести на консоль значение int[1][1], получим адрес ссылки. И только по полному индексу int[1][1][1] сможем получить значение элемента массива типа int.

5.2. Нижеприведенный код не вызовет ошибки компиляции, но вызовет ошибку во время исполнения: NullPointerException.

int[][] multiplicationTable = new int[10][];
multiplicationTable[0][0] = 10; // ошибка во время исполнения

Это происходит потому, что не был создан объект, в данном случае массив int-ов. То есть создан массив хранящий ссылки на массивы int-ов, но сами эти массивы еще не создны, поэтому обращение к несуществующему объекту вызывает ошибку. Это можно исправить следующим кодом:

int[][] multiplicationTable = new int[10][];
multiplicationTable[0] = new int [10];
multiplicationTable[0][0] = 10; // нет ошибки

5.3. Если создаете многомерные массивы, необязательно указывать все измерения массива – важно задать только крайнее слева измерение или измерения. Например, разрешены такие строки:

float[][][] globalTemperatureData = new float[360][][];
float[][][] globalTemperatureData = new float[360][180][];

Но такие варианты ошибочны:

float[][][] globalTemperatureData = new float[360][][100]; // Ошибка!
float[][][] globalTemperatureData = new float[][180][100]; // Ошибка!

Вопрос 22 (массивы)

"Что такое массив?"

1. Массив — это конечная последовательность упорядоченных элементов одного типа, доступ к каждому из которых осуществляется по его индексу. В Java массив используется для хранения коллекции данных, но бывает полезно думать о массиве как о совокупности переменных одного типа.

скрытый текст1.1. Размер или длина массива — это общее количество элементов в массиве. Размер массива задаётся при создании массива и не может быть изменён в дальнейшем, т. е. нельзя убрать элементы из массива или добавить их туда, но можно присвоить существующим элементам новые значения. Индекс начального элемента — 0, следующего за ним — 1 и т. д. Индекс последнего элемента в массиве — на единицу меньше, чем размер массива. Данное решение было навязано математиками, которым было удобно начинать отсчёт массивов с нуля.

1.2. В Java массивы являются объектами. Это значит, что имя, которое даётся каждому массиву, лишь указывает на адрес какого-то фрагмента данных в памяти. Кроме адреса в этой переменной ничего не хранится. Индекс массива, фактически, указывает насколько надо отступить от начального элемента массива в памяти, чтоб добраться до нужного элемента.

1.2.1. Если программа выйдет за пределы индекса массива, она остановится с ошибкой времени исполнения ArrayOutOfBoundsException.

1.3. Если нужно изменять длину, то вместо стандартного массива следует использовать списочный массив ArrayList.

1.4. Разрешается массив с интерфейсным типом таким, как тип компонент . Элементы такого массива могут иметь значением пустую ссылку (null) или экземпляры любого классового типа, который реализует интерфейс. Разрешается массив с классовым типом abstract как тип компонент. Элементы такого массива могут иметь значением - пустую ссылку (null) или экземпляры любого подкласса класса abstract, который сам не abstract.

1.5. В Java (в отличие от Cи) массив типа char — не String, и ни строка. Объект String языка Java не меняется, то есть никогда не меняется его содержание, в то время как массив типа char имеет непостоянные элементы. Метод toCharArray в классе String, возвращает массив символов, содержащий ту же последовательность символов.

2. Возможны следующие варианты объявления массива:

тип[] имя;
тип имя[];

Где тип — это тип элементов массива, а имя — уникальный идентификатор. В разных языках программирования используются разные способы, и Java позволяет использовать более привычный вариант. Но большинство предпочитает первый вариант.

2.1. Примеры:

int[] a;
double[] ar1;
double ar2[];

3. При инициализации массива можно указать его размер, либо сразу перечислить через запятую все желаемые элементы в фигурных скобках. При этом размер будет вычислен автоматически на основе той последовательности элементов, которая будет указан. В данном случае после закрывающей фигурной скобки ставится точка с запятой, чего не бывает когда это скобка закрывает какой-то блок. Если массив был создан с помощью оператора new, то каждый его элемент получает значение по умолчанию.


3.1. Примеры:
a = new int[10]; // массив из 10 элементов типа int
int n = 5;
ar1 = new double[n]; // Массив из 5 элементов double
ar2 = {3.14, 2.71, 0, -2.5, 99.123}; // Массив из 6 элементов типа double

скрытый текст3.1.2. Можно смешать два способа. Например, если требуется задать явно значения только для некоторых элементов массива, а остальные должные иметь значения по умолчанию.

int[] ar1 = new int[6]; // массив из шести элементов с начальным значением 0 для каждого элемента
ar1[3] = 5; // четвертому элементу присвоено значение 5
ar1[5] = 7; // шестому элементу присвоено значение 7


3.2. Объявить имя для массива и создать сам массив можно на одной строке по следующей схеме:

тип[] имя = new тип[размер];
тип[] имя = {эл0, эл1, …, элN};

3.2.1. Примеры:

int[] mas1 = {10,20,30};
int[] mas2 = new int[3];

скрытый текст4. Чтобы обратиться к какому-то из элементов массива для того, чтобы прочитать или изменить его значение, нужно указать имя массива и за ним индекс элемента в квадратных скобках. Например, на первый элемент массива mas1 можно ссылаться как на mas1[0], на третий элемент как mas1[2]. Элемент массива с конкретным индексом ведёт себя также, как переменная.

4.1. В качестве индекса можно использовать числа или выражения, которые возвращают положительное значение типа int. Поэтому при вычислении выражения с типом long, следует преобразовать результат в int, иначе при компиляции возникнет ошибка. С типами short и byte проблем не будет, так как они полностью укладываются в диапазон int.

4.2. Длину любого созданного массива не обязательно запоминать, потому что имеется свойство, которое его хранит. Обратиться к этому свойству можно дописав .length к имени массива. Это свойство нельзя изменять (т. е. ему нельзя ничего присваивать), можно только читать. Используя это свойство можно писать программный код для обработки массива даже не зная его конкретного размера. Например, последний элемент массива mice всегда mice[mice.length - 1].

4.3. Нужно быть осторожным с копированием массивов. Массив — это не числа, а специальный объект, который по особому хранится в памяти. Скажем, в следующем случае:

int[] luckyNumbers = anyNumbers;

Массив остается прежним, и вторая переменная обращается к нему же, а не создаёт вторую копию.

4.3.1. Если же нужна именно копия массива, следует использовать метод Arrays.copyOf().

5. Методы для массива. Класс java.util.Arrays содержит различные статические методы для поиска, сортировки, сравнения и заполнения элементов массива. Методы перегружаются для всех примитивных типов.

- copyOf() − предназначен для копирования массива
- copyOfRange() − копирует часть массива
- toString() − позволяет получить все элементы в виде одной строки
- sort() — сортирует массив методом quick sort
- binarySearch() − ищет элемент методом бинарного поиска
- fill() − заполняет массив переданным значением (удобно использовать, если нам необходимо значение по умолчанию для массива)
- equals() − проверяет на идентичность массивы
- deepEquals() − проверяет на идентичность массивы массивов
- asList() − возвращает массив как коллекцию

5.1. Класс Arrays содержит метод equals() для проверки на равенство целых массивов. Чтобы два массива считались равными, они должны содержать одинаковое количество элементов, и каждый элемент должен быть эквивалентен соответствующему элементу другого массива.

5.2. Сортировка (упорядочение по значениям) массива a производится методами Arrays.sort(a) и Arrays.sort(a, index1, index2). Первый метод упорядочивает в порядке возрастания весь массив, второй — часть элементов (от индекса index1 до индекса index2). Имеются и более сложные методы сортировки.

5.2. Метод Arrays.copyOf(оригинальный_массив, новая_длина) — возвращает массив-копию новой длины. Если новая длина меньше оригинальной, то массив усекается до этой длины, а если больше, дополняется нулями.

5.3. Если использовать вызов метода toString() непосредственно у массива, получится что-то не читаемое. Метод Arrays.toString(массив) возвращает строковое представление массива со строковым представлением элементов, заключенных в квадратные скобки. Метод deepToString() удобен для вывода многомерных массивов. Этот метод мы также уже использовали выше.

5.4. Метод Arrays.fill() позволяет быстро заполнить массив одинаковыми значениями. У метода есть восемнадцать перегруженных версий для разных типов и объектов. Метод fill() просто дублирует одно заданное значение в каждом элементе массива (в случае объектов копирует одну ссылку в каждый элемент).

Вопросы 20, 21 (операторы break и continue)

"Какой оператор используется для немедленной остановки цикла?"
"Какой оператор используется для перехода к следующей итерации цикла?"


1. Операторы цикла изменяют нормальное выполнение последовательности цикла в Java. Когда выполнение выходит из своей области, все объекты, которые были созданы автоматически в этой области будут уничтожены. Java поддерживает следующие управляющие операторы цикла:
- break
- continue

скрытый текст2. Оператор break завершает работу цикла или оператора switch, и передаёт выполнение следующему оператору, который находится сразу же после цикла или оператора switch. При этом произойдёт моментальный выход из цикла, не будет закончен даже текущий шаг (т. е. если после break присутствовали какие-то ещё операторы, они не выполнятся).

2.1. C помощью оператор break можно прервать заведомо бесконечный цикл. Оператор break обычно имеет смысл вызывать лишь при наступлении какого-то условия, иначе цикл будет завершен досрочно на первом же своём шаге. При использовании внутри набора вложенных циклов оператор break будет выходить только из самого внутреннего цикла.

2.2. Кроме выхода из цикла, он также может быть использован для завершения последовательности операторов в "ветвях" оператора switch. При этом break, который завершает оператор switch, воздействует только на данный switch-оператор (но не на включающие его циклы).

2.3. В-третьих, break может применяться как «цивилизованная» форма оператора безусловного перехода goto. Java не содержит оператора goto, потому что он выполняет переход произвольным и неструктурированным способом. Код, интенсивно использующий goto, обычно трудно понять и поддерживать. Он также отменяет некоторые оптимизации компилятора. Существует, однако, несколько мест в программе, где goto — ценная и законная конструкция управления потоком выполнения. Например, он может быть полезен, когда вы выходите из глубоко вложенного набора циклов. Чтобы обрабатывать такие ситуации, Java определяет расширенную форму оператора break. Используя ее, вы можете выйти из одного или большего количества блоков кода. Этим блокам не нужно быть частью цикла или оператора switch. Далее, вы можете определить точно, где выполнение будет продолжено, потому что данная форма break работает с меткой и обеспечивает преимущества goto, минуя его проблемы.

2.3.1. Оператор break с меткой имеет следующую общую форму:
break label;

2.3.1.1. Здесь label — имя метки, которая идентифицирует некоторый блок кода. Для именования блока поместите метку в его начале (перед открывающей блок фигурной скобкой). Метка — это любой допустимый идентификатор Java, за которым следует двоеточие. После маркировки блока, его метку можно использовать как аргумент оператора break. Это приведет к тому, что выполнение будет продолжено с конца помеченного блока.

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

3. Оператор continue заставляет цикл пропустить оставшуюся часть его тела, сразу перейти к его следующей итерации. В циклах while или do...while, контроль сразу же переходит в логическое выражение. В цикле for ключевое слово continue распоряжается процессом так, чтобы сразу же перейти к оператору обновления, а затем к проверке условия. По сути это goto-переход мимо следующих операций тела в конец блока цикла.

3.1. В использовании continue полезен крайне редко. Одна из причин этого заключается в том, что Java обеспечивает богатый набор операторов цикла, которые устраивают большинство приложений. Однако, для тех специальных ситуаций, в которых необходимо досрочное прекращение итерации, оператор continue обеспечивает структурный способ выполнения этой задачи.

3.2. Как и в операторе break, в continue можно определить метку, указывающую, какой включающий цикл следует продолжить.

4. Кроме break и continue в Java имеется третий оператор управления: return. Он используется для явного возврата из метода, т. е. передает программное управление обратно в вызывающую программу. Оператор return относят к категории операторов перехода. Его можно использовать в любом месте метода для выполнения перехода обратно в вызывающую метод программу. При этом он немедленно заканчивает выполнение метода, в котором он находится.

Вопросы 17, 18, 19 (циклы)

"Какие циклы вы знаете, в чем их отличия?"
"Что такое «итерация цикла»?"
"Какие параметры имеет цикл for, можно ли их не задать?"


1. Цикл — это многократно повторяющийся фрагмент программы. В java существует два типа циклов: типа «пока» и типа «n-раз». Первый тип «пока» предназначен для повторения какого-то действия до тех пор, пока выполняется некоторое условие. Пример: увеличивать число на 5 до тех пор, пока оно не станет трёхзначным. Второй тип «n-раз» предназначен для повторения каких-то действий заранее известное количество раз. Пример: умножить число само на себя 4 раза.

скрытый текст1.1. Однократное выполнение кода, размещенного в повторяющемся фрагменте ("теле цикла"), называется его итерацией.

2. Цикл типа «пока» (операторы while и do…while). Оператор while повторяет указанные действия до тех пор, пока его параметр имеет истинное значение. Общая форма оператора:


while (УсловиеВыполнения) {
ТелоЦикла;
}

скрытый текст2.1. Пока УсловиеВыполнения выполняется (результат его вычисления равен true), будет выполняться и ТелоЦикла, где может быть один оператор или их группа. Когда условие становится ложным, программа передаёт управление на строчку сразу после цикла. Ключевым моментом цикла while является то, что его тело может ни разу не выполниться.

2.2. Бывает цикл типа «пока» с постпроверкой условия. Для его записи используется конструкция из операторов do…while. Общая форма оператора:


do {
ТелоЦикла;
} while (УсловиеВыполнения);

скрытый текст2.2.1. Отличие данного оператора от while только в том, что он является оператором постусловия (сначала выполнит, потом проверит). То есть, даже если условие не выполняется никогда, всё равно действие будет выполнено один раз. Если логическое выражение истинно, контроль переходит обратно, чтобы выполнить операторы, и они в цикле выполняются снова. Этот процесс повторяется до тех пор, пока логическое выражение не станет ложным.

3. Цикл типа «n-раз» (оператор for). Он позволяет эффективно написать цикл, который должен выполниться определенное количество раз. Цикл for полезен, когда известно, сколько раз должна быть повторена задача. Общая форма оператора:


for (Инициализация; УсловиеВыполнения; Обновление) {
ТелоЦикла;
}

скрытый текст3.1. Процесс управления в цикле:
- Стадия инициализации выполняется первой, и только один раз. Этот шаг позволяет объявлять и инициализировать любые переменные для управления циклом, и он заканчивается точкой с запятой(;).
- Далее логическое выражение. Если оно истинно, тело цикла выполняется, если оно ложно, тело цикла не будет выполнено и контроль переходит к следующему оператору.
- После того как тело цикла for выполняется, контроль переходит обратно к оператору обновления. Он позволяет обновлять какие-либо переменные для управления циклом, и записывается без точки с запятой в конце.
- Логическое выражение затем оценивается снова. Если истинно, цикл выполняется и процесс повторяется. Если ложно, цикл for завершается.

3.1.1 Цикл можно записать следующим образом: for(;;); будет аналогично while(true).

3.2. Улучшенный цикл for — в основном используется для обхода коллекцией элементов, включая массивы. Был введен начиная с Java 5, позволяет достигнуть большей краткости кода. Наиболее фундаментальным отличием использования по сравнению со "старым for" является отсутствие необходимости использовать счетчик. Общая форма оператора:


for (Объявление: Выражение)
{
ТелоЦикла
}

скрытый текст3.2.1. Объявление — это переменная, например Object listElement. Эта переменная должна иметь тип, совместимый с каждым элементом списка, массива или коллекции, по которым производится итерация. Выражение — выражение, вычисляющее что-то, по чему можно делать итерацию (возвращает коллекцию или массив). Может быть переменной, вызовом функции или комплексным выражением. Тело Цикла — один или несколько операторов, применяемых ко всем элементам коллекции или массива.

Вопрос 16 (операторы if и switch)

"Какова роль и правила написания оператора выбора?"

1. В Java есть так называемые конструкции ветвления:
- условный оператор if;
- оператор switch;

2. Общая форма условного оператора if в Java такая:

if (Условие1) {
//действие(-я), которые выполняются, если Условие1 истинно;
}
else if (Условие2) {
//действие(-я), которые выполняются, если Условие2 истинно;
}
(...)
else if (УсловиеN) {
//действие(-я), которые выполняются, если УсловиеNn истинно;
}
else {
//действие(-я), которые выполняются, если все условия ложные;
}

скрытый текст2.1. Оператор всегда начинается со слова if, за которым всегда идут круглые скобки с условием. После круглых скобок никогда не ставится точка с запятой. Для того, чтобы указать альтернативный вариант ("если не выполняется, тогда...") используется слово else. Если условий несколько, каждое из них будет записываться через комбинацию else if, после которых в круглых скобках записывается альтернативное условие. Последний вариант ("если не то, не то и не то, тогда...") записывается через else без условия.

3. Конструкции с операторами if else, предлагающими большое количество условных ветвлений, могут выглядеть очень громоздкими. Поэтому в тех случаях, когда необходимо повторять проверку значения одной и той же переменной, есть более элегантное решение с помощью оператора switch. Его часто называют оператором выбора. Оператор switch эффективнее набора вложенных операторов if. Выбор осуществляется в зависимости от целочисленного выражения. Общая форма оператора выглядит так:

switch (ВыражениеДляСравнения {
case Совпадение1:
команда;
break;
case Совпадение2:
команда;
break;
case Совпадение3:
команда;
break;
default:
оператор;
break;
}

скрытый текст3.1. Параметр ВыражениеДляСравнения - выражение, в результате вычисления которого получается как правило целое число. Команда switch сравнивает результат ВыражениеДляСравнения с каждым последующим Совпадением. Если обнаруживается совпадение, то исполняется команда или набор команд, которые прописаны за данным оператором. Если совпадений не будет, то исполняется команда после ключевого слова default. Однако оператор default не является обязательным. В случае без него при отсутствии совпадений программа не выполняет никаких действий.

3.2. Каждая секция case обычно заканчивается командой break, которая передаёт управление к концу команды switch. Если ее не использовать, выполнение кода продолжится. Хотя иногда это и используется. Если код в блоках case совпадает, их можно объединить.

case 4:
case 6:
case 9:
case 11:
numDays = 30;
break;

3.3. Оператор switch отличается от оператора if тем, что он может выполнять проверку только равенства, а оператор if может вычислять результат булева выражения любого типа. Две константы case в одном и том же операторе switch не могут иметь одинаковые значения. Тип каждого значения должен быть совместим с типом выражения. Можно использовать простые типы byte, short, char, int. Также можно использовать Enum и String (начиная с JDK7), и специальные классы, которые являются обёрткой для примитивных типов: Character, Byte, Short, Integer.

Вопрос 15 (побитовые операции)

"Какая арифметическая операция выполняется при побитовом сдвиге вправо/влево?"

1. Для целых числовых типов данных — long, int, short, char и byte, определен дополнительный набор операторов, с помощью которых можно проверять и модифицировать состояние отдельных битов соответствую­щих значений. В таблице приведена сводка таких операторов. Операторы битовой арифметики работают с каждым битом как с самостоятельной величиной. Предположим, если a = 60; и b = 13; то в двоичном формате они будут следующие:
a = 0011 1100
b = 0000 1101

2. Список побитовых операций (без вариаций с присвоением):
~ (побитовое унарное отрицание (NOT))
&(побитовое И (AND))
| (побитовое ИЛИ (OR))
^ (побитовое исключающее ИЛИ (XOR))
>> (сдвиг вправо)
<< (сдвиг влево)
>>> (сдвиг вправо с заполнением нулями)

скрытый текст2.1. Примеры выполнения побитовых операций:
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011

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

3.1.При применении оператора сдвига влево к операнду типа int биты теряются, как только они сдвигаются за пределы 31 позиции. Если операнд имеет тип long, биты теряются после сдвига за пределы 63 позиции.

3.2. Автоматическое повышение типа, используемое в Java, может привести к странным результатам при выполнении сдвига в значениях типа byte, short. При вычислении выражений тип значений byte и short повышается до типа int. Это означает, что результатом выполнения сдвига влево значения типа byte или short будет значение int, и сдвинутые влево позиции не будут отброшены до тех пор, пока они не будут сдвинуты за пределы 31 позиции. Более того, при повышении до типа int отрицательное значение типа byte или short получит дополнительный знаковый разряд. Следовательно, старшие биты будут заполнены единицами. Поэтому при выполнении сдвига влево в значении типа byte сначала будет повышение до типа int и лишь затем сдвиг. Поэтому для получения требуемого сдвинутого значения типа byte необходимо отбросить три старших байта результата. Простейший способ достижения этого - обратное приведение результата к типу byte.

byte x = 64;
byte y;
int i;
i = x << 2; // сдвиг влево
y = (byte) (x << 2); // сдвиг влево с приведением

Результат:
i равно: 256
y равно: 0

Поскольку для выполнения вычислений тип переменной повышается до int, сдвиг влево на две позиции значение 64 (0100 0000) приводит к значению 256 (1 0000 0000). Однако, переменная y содержит значение не 256, а 0, поскольку после сдвига крайний единичный бит оказывается сдвинутым за пределы допустимого диапазона.

3.3. Приёмом сдвига влево часто пользуются в программах, где происходит много сложных вычислений. По сути, это замена операции умножения на 2, которая в силу особенностей процессора гораздо эффективнее. Но при этом следует соблюдать осторожность, так как при сдвиге единичного бита в старшую позицию (бит 31 или 63) значение становится отрицательным.

4. Оператор >> означает сдвиг вправо. Он перемещает все биты своего левого операнда вправо на число позиций, заданное правым операндом. Когда биты левого операнда выдвигаются за самую правую позицию слова, они теряются. При сдвиге вправо освобождающиеся старшие (левые) разряды сдвигаемого числа заполняются предыдущим содержимым знакового разряда. Такое поведение называют расширением знакового разряда, из-за него знаки отрицательных чисел сохраняются при сдвиге.

4.1. Примеры выполнения операций сдвига:
A << 2 равно 0011 1100 << 2 равно 1111 0000 равно 240
A >> 2 равно 0011 1100 >> 2 равно 0000 1111 равно 15

4.2. Иногда требуется, чтобы при сдвиге вправо расширение знакового разряда не происходило, а освобождающиеся левые разряды просто заполнялись бы нулями. Для этого используется оператор >>>:

int x = -17, z1, z2;
// x: 11111111 11111111 11111111 11101111
z1 = x >> 2;
// z1 = -5: 11111111 11111111 11111111 11111011
z2 = x >>> 2;
// z2 = 1073741819 : 00111111 11111111 11111111 11111011

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

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

5. Как и бинарные арифметические операторы побитовые операции имеют составные формы, которые объединяет побитовый оператор с оператором присваивания.

&=(побитовое И (AND) с присваиванием)
|= (побитовое ИЛИ (OR) с присваиванием)
^= (побитовое исключающее ИЛИ (XOR) с присваиванием)
>>= (сдвиг вправо с присваиванием)
<<= (сдвиг влево с присваиванием)
>>>= (сдвиг вправо с заполнением нулями с присваиванием)

Вопрос 14 (оператор присвоения, отношения, арифметические бинарные операции)

1. Операция присваивания. Присвоение переменной значения константы, другой переменной или выражения (переменных и/или констант, разделенных знаками операций), называется операцией присваивания и обозначается знаком "=".

1.1 В Java допустимо многократное использование операции присваивания в одном выражении. Например, x1 = x2 = x3 = 0;
Эта операция выполняется справа налево, т.е. сначала переменной x3 присваивается значение 0, затем переменной x2 присваивается значение переменной x3 (0), и, наконец, переменной x1 присваивается значение переменной x2 (0).

скрытый текст2. Арифметические бинарные операции. Используются для вычислений так же как в алгебре. Допустимые операнды должны иметь числовые типы. Например, исполь­зовать эти операторы для работы с логическими типами нельзя, но можно для работы с типом char.

В Java определены следующие арифметические бинарные операции:
- сложение "+";
- вычитание "-";
- умножение "*";
- деление "/";
- вычисление остатка от деления целых чисел "%".

2.1. Вычисление остатка возвращает остаток от деления первого числа на второе, причем результат будет иметь тот же знак, что и делимое), например, 5%3 будет равно 2, а (-7)%(-4) будет равен -3. Может использоваться и для вещественных переменных (типа float или double.

2.2. Для каждого из арифметических операторов есть форма, в которой одновременно с заданной операцией выполняется присваивание. Это +=; -= , *= ; += ; %=.

3. Для того, чтобы можно было сравнивать два значения, в Java имеется набор операторов, описывающих отношение и равенство. Значения любых типов, включая целые и вещественные числа, символы, логические значения и ссылки, можно сравнивать, используя оператор проверки на равенство == и неравенство !=. Один знак (=) — это оператор присваи­вания.

3.1. Список операторов отношения:
- равно (==)
- не равно (!=)
- больше (>)
- меньше (<)
- больше или равно (>=)
- меньше или равно (<=)

Вопрос 14 (унарные операции)

"Какие унарные операции вы знаете?"

1. Унарные операции — это операции, которые выполняются над одним операндом (бинарные над двумя, тринарные — над тримя).

2. В Java имеются следующие семь унарных операций:
+ (унарный плюс)
- (унарный минус)
++ (префиксный либо постфиксный инкремент; только для целых)
-- (префиксный либо постфиксный декремент; только для целых)
~ (побитовое инвертирование; только для целых)
! (логическое отрицание; только для boolean)
( ) (приведение типа)

2.1. Унарные операторы + и - отличаются от бинарных операторов + и -, которые трактуются, как сложение и вычитание. Унарный + не имеет никакого эффекта, кроме подчёркивания положительной природы численного литерала. Унарный - меняет знак выражения с плюса на минус и наоборот.

скрытый текст2.2. Инкремент увеличивает значение переменной на 1; декремент уменьшает. По сути, являются сокращенным вариантом записи для сложения или вычитания из операнда единицы. В зависимости от того, используется операция в префиксной или постфиксной форме (стоит до или после переменной) меняется порядок выполнения операций: инкремент/декремент выполняются до или после прочих.

z=++y (вначале значение переменной y увеличивается на 1, а затем ее значение присваивается переменной z)
z=y++ (вначале значение переменной y присваивается переменной z, а потом значение переменной y увеличивается на 1)

2.3. Побитовое инвертирование. Каждый примитивный тип Java представляется в виртуальной машине так, что представление не зависит от платформы. Это означает, что битовый шаблон, используемый для представления отдельного числа, будет всегда тем же самым. Таким образом и манипулирование битами — процесс более эффективный, в силу независимости от платформы. Побитовое инвертирование означает, что в двоичном представлении числа 0 заменяется на 1, а 1 — на 0. Например, применение этого оператора к байту с содержимым 00001111 даст 11110000.

2.4. Оператор ! инвертирует логическое значение выражения. Например, !true = false. Этот оператор часто используется в тестовой части if () конструкции.

2.5. Приведение типа используется для явной конвертации выражения в заданный тип. Операция возможна только для допустимых типов. И во время компиляции, и во время выполнения программы, приведение типа проверяется на корректность.

2.5.1.Приведение типа применяется для изменения типа значений примитивного типа. Например, можем форсировать конвертацию double к int как, например, в следующем фрагменте:

int circum = (int)(Math.PI * diameter);

Здесь приведение типа выражается фрагментом (int). Если бы этот фрагмент отсутствовал, компилятор бы выдал ошибку, поскольку значение типа double, возвращаемое арифметическим выражением не может быть точно представлено значением типа int, к которому присваивается. Присвоение типа — это способ, которым программист говорит компилятору: "Я знаю, что такое присвоение может быть рискованным, но верь мне, ведь я — специалист". Конечно, если результат при этом теряет точность так, что программа не функционирует должным образом, - это ответственность программиста.

2.5.2. Присвоение типа может быть применено и к ссылочным типам. Это типично, например, для случаев, когда используются контейнеры типа Vector. Если вы помещаете объект типа String в Vector, то когда вы его извлекаете, тип, возвращаемый методом elementAt(), будет Object. Для использования извлечённого объекта как String нужно применить приведение типа, как, например, в следующем фрагменте кода:

Vector v = new Vector();
v.add ("Hello");
String s = (String)v.get(0);

Вопросы 10, 11, 12, 13 (логические операции)

"Какие логические операции и операторы вы знаете?"
"В чем разница краткой и полной схемы записи логических операторов?"
"Что такое таблица истинности?"
"Что такое тернарный оператор выбора?"

1. Логические операторы работают только с операндами типа boolean. Все логические операторы с двумя операндами объединяют два логических значения, образуя результирующее логическое значения. Не следует путать их с побитовыми логическими операторами.

1.1. Список логических операторов Java:
& Логическое AND (И)
&& Сокращённое AND
| Логическое OR (ИЛИ)
|| Сокращённое OR
^ Логическое XOR (исключающее OR (ИЛИ))
! Логическое унарное NOT (НЕ)
&= AND с присваиванием
|= OR с присваиванием
^= XOR с присваиванием
== Равно
!= Не равно
?: Тернарный (троичный) условный оператор

скрытый текст2. Результаты выполнения логических операторов (AND, OR, XOR, NOT).
AND возвращает true, если оба операнда равны true. OR, если хотя бы один. XOR если только один. NOT возвращает true, если операнд false; и наоборот.

2.1. Таблица истинности — это таблица, описывающая логическую функцию (значения операндов и возвращаемые значения).
http://developer.alexanderklimov.ru/android/java/logic_operators.php

3. Кроме стандартных операторов AND (&) и OR (|) существуют сокращённые операторы && и ||. Если взглянуть на таблицу истинности, видно, что результат выполнения оператора OR всегда равен true, когда значение левого операнда равно true. Аналогично, результат выполнения оператора AND всегда равен false, когда значение первого операнда равно false. Поэтому при использовании сокращенных операторов (в отличие от "стандартных") значение второго операнда не вычисляется, если результат можно определить уже по первому.

3.1. Это становится удобным в случаях, когда значение правого операнда зависит от значения левого. Например, if (mouse != 0 && weight / mouse < 1000) в отличие от версии со стандартным AND не приведет к ошибке, если значение mouse равняется 0.

3.2. Сокращённые варианты операторов AND и OR принято использовать в ситуациях, когда требуются именно операторы булевой логики. А их односимвольные родственники обычно используются для побитовых операций.

4. В языке Java есть также специальный тернарный условный оператор, которым можно заменить определённые типы операторов if-then-else - это оператор ?:

4.1. Тернарный оператор использует три операнда. Выражение записывается в следующей форме:
[логическоеУсловие] ? [выражение1] : [ выражение2]

Если логическоеУсловие равно true, то вычисляется выражение1, и его результат становится результатом выполнения всего оператора. Если же логическоеУсловие равно false, вычисляется выражение2, и его значение становится результатом работы оператора. Оба операнда выражение1 и выражение2 должны возвращать значение одинакового (или совместимого) типа.

4.2. Пример использования оператора выбора для вычисления значения по модулю:
absval = val < 0 ? -val : val;
Аналогичный код с использованием if:
if(val < 0) absval = -val;
else absval = val;

Вопрос 9 (функция main)

"Что вы знаете о функции main, какие обязательные условия ее определения?"

1. Всякое приложение должно содержать метод с именем main . Он может быть один на все приложение или содержаться в нескольких его классах. Метод main() записывается как обычный метод, может содержать любые описания и действия, но он обязательно должен быть открытым (public), статическим (static), не иметь возвращаемого значения (void). Его аргументом обязательно должен быть массив строк (String[]). По традиции этот массив называют args, хотя имя может быть любым. Туда передаются параметры из командной строки, с помощью которых можно предоставить приложению нужную информацию или настроить его для запуска нужным образом.

public static void main (String args []) {}

скрытый текст1.1 Эти особенности возникают из-за того, что метод main() вызывается автоматически исполняющей системой Java в самом начале выполнения приложения. При вызове интерпретатора java указывается класс, где записан метод main(), с которого надо начать выполнение. Поскольку классов с методом main() может быть несколько, можно построить приложение с дополнительными точками входа, начиная выполнение приложения в разных ситуациях из различных классов.

1.1.1 Когда вы запускается java.exe (или javaw.exe в Windows), на самом деле происходит несколько вызовов Java Native Interface (JNI). JNI - это инструмент, который мы используем, когда нам приходится соединяться между миром виртуальной машины (JVM) и миром C, С++ и т.д. java.exe - это простое приложение C, которое анализирует командную строку, создает новый массив String в JVM для хранения этих аргументов, анализирует имя класса, указанное при вызове как содержащее main(), использует вызовы JNI для поиска самого метода main(), затем вызывает метод main(), передавая вновь созданный строковый массив в качестве параметра.

1.2. Иногда метод main() заносят в каждый класс с целью отладки. В этом случае в метод main() включают тесты для проверки работы всех методов класса.

2. При вызове интерпретатора java можно передать в метод main() несколько параметров, которые интерпретатор заносит в массив строк. Эти параметры перечисляются в строке вызова java через пробел сразу после имени класса. Если параметр содержит пробелы, надо заключить его в кавычки. Кавычки не будут включены в параметр, это только ограничители.

2.1. Примеры вызова main из класса Echo и соответствующих им значений параметра args:

java Echo Hello XXI century: массив из трех элементов Hello, XXI, century
java Echo "Hello XXI century": массив из одного элемента Hello XXI century
java Echo Hello "XXI century": массив из двух элементов Hello, XXI century

Лучшее   Правила сайта   Вход   Регистрация   Восстановление пароля

Материалы сайта предназначены для лиц старше 16 лет (16+)