Как задать несколько условий в if java
Одним из фундаментальных элементов многих языков программирования являются условные конструкции . Данные конструкции позволяют направить работу программы по одному из путей в зависимости от определенных условий.
В языке Java используются следующие условные конструкции: if..else и switch..case
Конструкция if/else
Выражение if/else проверяет истинность некоторого условия и в зависимости от результатов проверки выполняет определенный код:
После ключевого слова if ставится условие. И если это условие выполняется, то срабатывает код, который помещен в далее в блоке if после фигурных скобок. В качестве условий выступает операция сравнения двух чисел.
Так как, в данном случае первое число больше второго, то выражение num1 > num2 истинно и возвращает значение true . Следовательно, управление переходит в блок кода после фигурных скобок и начинает выполнять содержащиеся там инструкции, а конкретно метод System.out.println(«Первое число больше второго»); . Если бы первое число оказалось бы меньше второго или равно ему, то инструкции в блоке if не выполнялись бы.
Но что, если мы захотим, чтобы при несоблюдении условия также выполнялись какие-либо действия? В этом случае мы можем добавить блок else :
Но при сравнении чисел мы можем насчитать три состояния: первое число больше второго, первое число меньше второго и числа равны. С помощью выражения else if , мы можем обрабатывать дополнительные условия:
Также мы можем соединить сразу несколько условий, используя логические операторы:
Здесь блок if будет выполняться, если num1 > num2 равно true и одновременно num1>7 равно true .
Конструкция switch
Конструкция switch/case аналогична конструкции if/else , так как позволяет обработать сразу несколько условий:
После ключевого слова switch в скобках идет сравниваемое выражение. Значение этого выражения последовательно сравнивается со значениями, помещенными после операторов сase . И если совпадение найдено, то будет выполняет соответствующий блок сase .
В конце блока сase ставится оператор break , чтобы избежать выполнения других блоков. Например, если бы убрали оператор break в следующем случае:
то выполнился бы блок case 8 , (поскольку переменная num равна 8). Но так как в этом блоке оператор break отсутствует, то начал бы выполняться блок case 9 .
Если мы хотим также обработать ситуацию, когда совпадения не будет найдено, то можно добавить блок default , как в примере выше. Хотя блок default необязателен.
Также мы можем определить одно действие сразу для нескольких блоков case подряд:
Тернарная операция
Тернарную операция имеет следующий синтаксис: [первый операнд — условие] ? [второй операнд] : [третий операнд] . Таким образом, в этой операции участвуют сразу три операнда. В зависимости от условия тернарная операция возвращает второй или третий операнд: если условие равно true , то возвращается второй операнд; если условие равно false , то третий. Например:
Здесь результатом тернарной операции является переменная z. Сначала проверяется условие x<y . И если оно соблюдается, то z будет равно второму операнду — (x+y), иначе z будет равно третьему операнду.
Условные операторы в Java: if-else, switch и «Элвис»
Чтобы эффективно работать с условными операторами на языке Java, необходимо знать, какими они бывают и для каких сценариев подходят. Обсудим это и некоторые нововведения из Java 13.
Условный оператор if
Оператор if позволяет задать условие, в соответствии с которым дальнейшая часть программы может быть выполнена. Это основной оператор выбора, который используется в языке Java. Он начинается с ключевого слова if и продолжается булевым выражением — условием, заключённым в круглые скобки.
В качестве примера рассмотрим простое равенство, при истинности которого программа выведет результат:
Поскольку условие истинно, в выводе программы мы увидим:
Условный оператор if-else в Java
else в языке Java означает «в ином случае». То есть если условие if не является истинным, выводится то, что в блоке else :
Это же сработает и без ключевого слова else , но чтобы код был читабельным и логичным, не следует пренебрегать else , как это сделано в следующем примере:
А теперь давайте создадим несколько условий с использованием конструкции if-else . Выглядит это таким образом:
Как видим, только третье условие истинно, поэтому выводится именно a = 20 , а все остальные блоки игнорируются.
Вложенный if
Кроме того, может производиться проверка сразу на несколько условий, в соответствии с которыми выполняются разные действия. Представим, что у нас есть две переменные, на основе которых можно создать два условия:
В результате программа заходит в оба блока и делает два вывода, потому как оба условия истинны.
«Элвис»
По сути, это сокращенный вариант if-else . Элвисом его прозвали за конструкцию, которая напоминает причёску короля рок-н-ролла — ?: . Данный оператор также принято называть тернарным. Он требует три операнда и позволяет писать меньше кода для простых условий.
Само выражение будет выглядеть следующим образом:
Как видите, с помощью тернарного оператора можно существенно сократить код. Но не стоит им злоупотреблять: для сложных условий используйте другие операторы выбора Java, дабы не ухудшать читаемость кода.
Условный оператор switch в Java
Оператор выбора switch позволяет сравнивать переменную как с одним, так и с несколькими значениями. Общая форма написания выглядит следующим образом:
Рассмотрим распространённый пример с днями недели:
break при этом прерывает процесс проверки, поскольку соответствие условия уже найдено. Но начиная с Java 13, вместо break в условном операторе switch правильнее использовать yield — ключевое слово, которое не только завершает проверку, но и возвращает значение блока.
Кроме того, с 12 версии Java конструкция switch-case также претерпела некоторые изменения. Если вы используете в работе IntelliJ IDEA, данная среда разработки сама подскажет, как оптимизировать switch-case под новые версии.
Вот преобразованный код из нашего примера с некоторыми изменениями:
Вывод:
Задачи на условные операторы Java
Определите, что выведут в консоль следующие примеры, без проверки в компиляторе.
1. В переменной min лежит число от 0 до 59. Определите в какую четверть часа попадает это число (в первую, вторую, третью или четвертую):
2. Условные операторы. Операторы циклов. Функции и рекурсия. Массивы.
Условные операторы позволяют направить работу программы по одному из путей в зависимости от определенных условий.
В языке Java используются следующие условные операторы:
- if/else
- switch/case
Оператор if
Оператор if проверяет истинность некоторого условия и в зависимости от результатов проверки выполняет определенный код. После ключевого слова if ставится условие. И если это условие выполняется, то срабатывает код, который помещен в далее в блоке if после фигурных скобок:
Например, в данном случае первое число больше второго, то выражение num1 > num2 истинно и возвращает значение true . Следовательно, управление переходит в блок кода после фигурных скобок и начинает выполнять содержащиеся там инструкции, а конкретно метод System.out.println(«Первое число больше второго») ;. Если бы первое число оказалось бы меньше второго или равно ему, то инструкции в блоке if не выполнялись бы вообще.
Оператор if/else
Оператор if/else проверяет истинность некоторого условия и в зависимости от результатов проверки выполняет либо один блок кода, либо другой блок кода. В этом случае можно добавить блок else :
При сравнении чисел мы можем насчитать три состояния: первое число больше второго, первое число меньше второго и числа равны. Дополнительные условия можно обрабатывать при помощи вложенных операторов if/else :
Данный код эквивалентен следующему коду:
Используя логические операторы, можно соединить несколько условий:
Здесь блок if будет выполняться, если num1 > num2 равно true и одновременно num1 > 7 равно true .
Оператор switch/case
Оператор switch/case аналогичен оператору if/else , так как позволяет обработать сразу несколько условий. После ключевого слова switch в скобках идет сравниваемое выражение. Значение этого выражения последовательно сравнивается со значениями, помещенными после операторов сase . И если совпадение найдено, то будет выполняет соответствующий блок сase . В конце блока сase ставится оператор break, чтобы избежать выполнения других блоков.
Если убрать оператор break в следующем случае, то выполнится блок case 8 , (поскольку переменная num равна 8 ). Но так как в этом блоке оператор break отсутствует, то также начнёт выполняться блок case 9 .
Если мы хотим также обработать ситуацию, когда совпадения не будет найдено, то можно добавить блок default . Блок default необязателен.
Можно определить одно действие сразу для нескольких блоков case подряд:
Циклы
Еще одним видом управляющих конструкций являются циклы. Циклы позволяют в зависимости от определенных условий выполнять определенное действие множество раз. В языке Java есть следующие виды циклов:
- for
- while
- do. while
Цикл for
Цикл for имеет следующее формальное определение:
Рассмотрим стандартный цикл for:
Первая часть объявления цикла — int i = 1 создает и инициализирует счетчик i . Счетчик необязательно должен представлять тип int . Это может быть и любой другой числовой тип, например, float . Перед выполнением цикла значение счетчика будет равно 1. В данном случае это то же самое, что и объявление переменной.
Вторая часть — условие, при котором будет выполняться цикл. В данном случае цикл будет выполняться, пока i не достигнет 9.
И третья часть — изменение переменной-счётчика. Например, приращение счетчика на единицу. Опять же нам необязательно увеличивать на единицу. Можно уменьшать: i— .
В итоге блок цикла сработает 8 раз, пока значение i не станет равным 9. И каждый раз это значение будет увеличиваться на 1.
Все 3 части при объявлении цикла for являются необязательными. Например, можно написать так:
Либо можно опустить ряд блоков:
Цикл for может определять сразу несколько переменных и управлять ими:
Цикл do
Цикл do сначала выполняет код цикла, а потом проверяет условие в инструкции while . И пока это условие истинно, цикл повторяется.
В данном случае код цикла сработает 7 раз, пока j не окажется равным нулю. Важно отметить, что цикл do гарантирует хотя бы однократное выполнение действий, даже если условие в инструкции while не будет истинно. Так, мы можем написать:
Хотя переменная j изначально меньше 0, цикл все равно один раз выполнится.
Цикл while
Цикл while сразу проверяет истинность некоторого условия, и если условие истинно, то код цикла выполняется:
Операторы continue и break
Оператор break позволяет выйти из цикла в любой его момент, даже если цикл не закончил свою работу:
Когда счетчик станет равным 5, сработает оператор break , и цикл завершится.
Оператор continue используется, если надо не завершать цикл, а просто перейти к следующей итерации:
В этом случае, когда выполнение цикла дойдет до числа 5, программа просто пропустит это число и перейдет к следующему.
Массивы
Массив представляет набор однотипных значений. Объявление массива похоже на объявление обычной переменной, которая хранит одиночное значение, причем есть два способа объявления массива:
Например, определим массив чисел:
Java поддерживает оба варианта синтаксиса, но рекомендуемый способ написания — type[] name . Так как скобки написаны рядом с типом, гораздо быстрее происходит понимание, что тип — массив, в противном случае приходится искать их уже у названия переменной.
После объявления массива мы можем инициализовать его:
Создание массива производится с помощью следующей конструкции: new тип_данных[количество_элементов] , где new — ключевое слово, выделяющее память для указанного в скобках количества элементов. Например, nums = new int[4]; — в этом выражении создается массив из четырех элементов int , и каждый элемент будет иметь значение по умолчанию — число 0.
Массивы являются ссылочными типами, поэтому требуют выделения памяти в куче при помощи оператора new . Массивы являются объектами (пусть и специального рода).
Также можно сразу при объявлении массива инициализировать его:
При подобной инициализации все элементы массива имеют значение по умолчанию. Для числовых типов (в том числе для типа char ) это число 0, для типа boolean это значение false , а для остальных объектов это значение null . Например, для типа int значением по умолчанию является число 0, поэтому выше определенный массив nums будет состоять из четырех нулей.
Однако также можно задать конкретные значения для элементов массива при его создании:
Стоит отметить, что в этом случае в квадратных скобках не указывается размер массива, так как он вычисляется по количеству элементов в фигурных скобках.
После создания массива мы можем обратиться к любому его элементу по индексу, который передается в квадратных скобках после названия переменной массива:
Индексация элементов массива начинается с 0, поэтому в данном случае, чтобы обратиться к четвертому элементу в массиве, нам надо использовать выражение nums[3] .
И так как у нас массив определен только для 4 элементов, то мы не можем обратиться, например, к шестому элементу: nums[5] = 5; . Если мы так попытаемся сделать, то мы получим ошибку.
Рисунок 1. Устройство массивов в памяти
Обратите внимание, что элементы расположены в памяти последовательно, индексы начинаются с нуля. В памяти ссылка на массив всегда указывает на первый элемент массива, а индекс n означает смещение на n размеров элементов массива.
Например, если задан массив типа int[] a и индекс n , то a[0] будет указывать на адрес начала массива a , так как происходит 0 смещений. Выражение a[3] помогает получить значение ячейки памяти, смещенной на \(3 * size_
Собственно, так как индекс — это смещение, массивы и считаются с нуля, а обращение по тому индексу, которого нет в массиве, равносильно попытке обратиться к ячейке памяти, недоступной массиву(и не являющейся его частью), что приводит к ошибке.
Длина массива
Важнейшее свойство, которым обладают массивы, является свойство length , возвращающее длину массива, то есть количество его элементов:
Нередко бывает неизвестным последний индекс, и чтобы получить последний элемент массива, мы можем использовать это свойство:
Многомерные массивы
Ранее мы рассматривали одномерные массивы, которые можно представить как цепочку или строку однотипных значений. Но кроме одномерных массивов также бывают и многомерными. Наиболее известный многомерный массив — таблица, представляющая двухмерный массив:
Визуально оба массива можно представить следующим образом:
Рисунок 2. Одномерные и многомерные массивы
Проще говоря, многомерный массив — это массив, состоящий из массивов. Каждый элемент массива (хранящийся в куче) хранит ссылку на свой внутренний массив (также хранящийся в куче).
Поскольку массив nums2 двухмерный, он представляет собой простую таблицу. Его также можно было создать следующим образом: int[][] nums2 = new int[2][3]; . Количество квадратных скобок указывает на размерность массива. А числа в скобках — на количество строк и столбцов. И также, используя индексы, мы можем использовать элементы массива в программе:
Объявление трехмерного массива могло бы выглядеть так:
Массив «лесенкой»
Многомерные массивы могут быть также представлены как «зубчатые массивы» или «массивы лесенкой». В вышеприведенном примере двухмерный массив имел 3 строчки и три столбца, поэтому у нас получалась ровная таблица. Но мы можем каждому элементу в двухмерном массиве присвоить отдельный массив с различным количеством элементов:
Методы(функции)
Если переменные и константы хранят некоторые значения, то методы содержат собой набор операторов, которые выполняют определенные действия.
В других языках методы могут также называться функциями и процедурами, но общепринятое название в Java — методы.
Общее определение методов выглядит следующим образом:
По умолчанию главный класс любой программы на Java содержит метод main, который служит точкой входа в программу:
Ключевые слова public и static являются модификаторами. Далее идет тип возвращаемого значения. Ключевое слово void указывает на то, что метод ничего не возвращает.
Затем идут название метода — main и в скобках параметры метода — String[] args . И в фигурные скобки заключено тело метода — все действия, которые он выполняет.
Создадим еще несколько методов:
Здесь определены два дополнительных метода: hello и welcome , каждый из которых выводит некоторую строку на консоль. Методы определяются внутри класса — в данном случае внутри класса Program , в котором определен метод main .
Но если мы скомпилируем и запустим данную программу, то мы ничего не увидим на консоли. В примере выше мы определили два метода, но мы их нигде не вызываем. По умолчанию в программе Java выполняется только метод main и все его содержимое. Поэтому, если мы хотим, чтобы другие методы тоже выполнялись, их надо вызвать в методе main .
Вызов метода осуществляется в форме:
После имени метода указываются скобки, в которых перечисляются аргументы — значения для параметров метода.
Например, определим и выполним несколько методов:
Также следует отметить, что чтобы вызвать в методе main другие методы, которые определены в одном классе с методом main , они должны иметь модификатор static .
Модификатор static относит методы к контексту класса и не требует создания объекта для вызова метода. Методы, которые не помечены модификатором static , требует создания объекта для их вызова.
В итоге после компиляции и выполнения программы мы увидим на консоли:
Параметры методов
С помощью параметров мы можем передать в методы различные данные, которые будут использоваться для вычислений. Например:
А при вызове этого метода в программе нам необходимо передать на место параметров значения, которые соответствуют типу параметра:
Рассмотрим другой пример:
Метод может принимать параметры переменной длины одного типа. Например, нам надо передать в метод набор числел и вычислить их сумму, но мы точно не знаем, сколько именно чисел будет передано — 3, 4, 5 или больше. Параметры переменной длины позволяют решить эту задачу:
Возврат значения из метода
Методы могут возвращать некоторое значение. Для этого применяется оператор return :
После оператора return указывается возвращаемое значение, которое является результатом метода. Это может быть литеральное значение, значение переменной или какого-то сложного выражения.
При этом возвращаемое значение всегда должно иметь тот же тип, что значится в определении функции. И если функция возвращает значение типа int , то после оператора return стоит целочисленное значение, которое является объектом типа int . Как в данном случае это сумма значений параметров метода.
Метод может использовать несколько вызовов оператора return для возваращения разных значений в зависимости от некоторых условий:
Выход из метода
Оператор return применяется не только для возвращаения значения из метода, но и для выхода из метода. В подобном качестве оператор return применяется в методах, которые ничего не возвращают, то есть имеют тип void :
Перегрузка методов
В программе мы можем использовать методы с одним и тем же именем, но с разными типами и/или количеством параметров. Такой механизм называется перегрузкой методов (method overloading).
Здесь определено три варианта или три перегрузки метода sum() , но при его вызове в зависимости от типа и количества передаваемых параметров система выберет именно ту версию, которая наиболее подходит.
Стоит отметить, что на перегрузку методов влияют количество и типы параметров. Однако различие в типе возвращаемого значения для перегрузки не имеют никакого значения. Например, в следующем случае методы различаются по типу возвращаемого значения:
Однако перегрузкой это не будет считаться. Более того такая программа некорректна и попросту не скомилируется, так как метод с одним и тем же количеством и типом параметров определен несколько раз.
Рекурсия
Отдельно рассмотрим рекурсивные функции. Главное отличие рекурсивных функций от обычных методов состоит в том, что они рекурсивная функция может вызывать саму себя — напрямую или косвенно.
Например, рассмотрим функцию, которая вычисляет факториал числа:
Вначале проверяется условие: если вводимое число не равно 1, то мы умножаем данное число на результат этой же функции, в которую в качестве параметра передается число x-1 . То есть происходит рекурсивный спуск. И так дальше, пока не дойдем того момента, когда значение параметра не будет равно единице.
Рекурсивная функция обязательно должна иметь некоторый базовый вариант, который использует оператор return и который помещается в начале функции. В случае с факториалом это if (x == 1) return 1; . И все рекурсивные вызовы должны обращаться к подфункциям, которые в конечном счете сходятся к базовому варианту. Так, при передаче в функцию положительного числа при дальнейших рекурсивных вызовах подфункций в них будет передаваться каждый раз число, меньшее на единицу. И в конце концов мы дойдем до ситуации, когда число будет равно 1, и будет использован базовый вариант.
Хотя в данном случае нужно отметить, что для определения факториала есть более оптимальные решения на основе циклов:
При использовании рекурсии важно также понимать механизм её работы. При вызове функции в стеке вызовов (Call stack) создается новый StackFrame, который содержит в себе информацию о вызванной функции, её аргументы, содержит локальные переменные и так далее. Созданный StackFrame добавляется в стек вызовов. При выходе из функции он удаляется из стека вызовов. Этот механизм работает по принципу LIFO. Пример стека вызовов показан на рисунке ниже.
Рисунок 2. Стек вызовов
Так как рекурсия это вызов метода — при каждом новом рекурсивном вызове информация о методе, переданных аргументах и так далее — также помещается в стек вызовов. Базовый случай позволяет остановить процесс добавления новых стек-фреймов в стек вызовов и вернуться обратно.
К сожалению, оперативная память не бесконечная, а стек вызовов и того меньше, поэтому при наличии слишком большого числа рекурсивных вызовов может произойти переполнение стека (Ошибка StackOverflowError ). При этом программа более не способна будет работать и завершится аварийно.
Некоторые алгоритмы гораздо проще записать с помощью рекурсии, а некоторые — гораздо лучше решить с помощью цикла. Любой алгоритм, который реализован с помощью цикла, можно реализовать с помощью рекурсии, и наоборот. В качестве техники, которая часто применяется для упрощения и повышения эффективности работы рекурсии, используется мемоизация значений.