Освоюємо Java/Керування порядком виконання
Java, як і інші мови програмування, підтримує умовні інструкції та цикли, що визначають порядок виконання інструкцій у програмі. В англійській мові для цього поняття застосовують термін control flow — керування течією.
Зміст |
Блоки [ред.]
Перед тим як знайомитись з керувальними структурами, спочатку необхідно ознайомитися з блоками. Блок або складена інструкція – це будь-яка кількість простих інструкцій, які оточені парою фігурних дужок. Блок визначає область видимості ваших змінних. Блоки можуть бути вкладені в середину інших блоків. Ви вже зустрічалися з блоками при створенні найпростіших програм у методі main(). Наступний приклад демонструє вкладення блоку у блок методу main:
public static void main(String[] args) { int n; . . . { int k; . . . } // змінна k визначена лише до цього місця }
Проте не можна визначати однакові змінні в двох вкладених блоках (на відміну від С++, де це можливо).
public static void main(String[] args) { int n; . . . { int k; int n; // помилка! – не можна перевизначити n у внутрішньому блоці . . . } }
Умовні інструкції [ред.]
Умовна інструкція в Java має форму:
if (умова) інструкція;
Умова повинна бути оточена дужками і, якщо, умова вірна (true) буде виконана інструкція за умовою, інакше вона не буде виконана, а буде виконана наступна інструкція після умовної інструкції.
Приклад:
int a=5;
if (a<100) System.out.println("Число менше ста");
Зазвичай, необхідно виконати не одну інструкцію, в такому разі інструкції розміщають у блоці:
if (умова){
iнструкція 1;
…..
iнструкція n;
}
В такому разі при істинності умови, виконуються усі інструкції у блоці, якщо умова невірна, то виконується наступна інструкція після закриваючої дужки блоку. Якщо ж необхідно здійснити певну дію в разі не виконання умови, то в такому разі застосовують умовну інструкцію наступного виду:
if (умова) інструкція1 else інструкція2
if (yourSales >= target) { performance = "Satisfactory"; bonus = 100 + 0.01 * (yourSales - target); } else { performance = "Unsatisfactory"; bonus = 0; }
Інструкції if можуть іти одна за одною без використання else:
if (x <= 0) if (x == 0) sign = 0; else sign = -1;
Для того, щоб програма була більш читабельна бажано застосовувати фігурні дужки:
if (x <= 0) { if (x == 0) sign = 0; else sign = -1; }
Вони нічого не змінюють, але вираз стає більш зрозумілим. Інструкція чи блок інструкцій виконується лише в разі виконання усіх умов.
Щоправда дану інструкцію можна також переписати ускладнивши умову використавши булевий оператор і (&&):
If (x <= 0&&x==0) sign = 0; else sign=-1;
Можна також використовувати повторюваність інструкцій if….else.
if (yourSales >= 2 * target) { performance = "Excellent"; bonus = 1000; } else if (yourSales >= 1.5 * target) { performance = "Fine"; bonus = 500; } else if (yourSales >= target) { performance = "Satisfactory"; bonus = 100; } else { System.out.println("You're fired"); }
Це дає можливість перевірити ряд умов, якщо попередні умови не виконуються.
Цикли [ред.]
Цикли – це послідовність інструкцій, які можуть повторно виконуватись певну кількість раз в залежності від заданої в програмі умови. Розрізняють цикли з передумовою, з післяумовою та з лічильником.
Цикл while [ред.]
Цикл while (перекладається як «доки») – це цикл з передумовою, тіло якого (тобто інструкція або блок інструкцій) виконується, якщо умова істинна. Якщо умова з самого початку хибна, то цикл не виконається жодного разу.
Загальний вигляд:
while (умова) інструкція;
Якщо немає фігурних дужок, то перша інструкція, яка йде після оголошення циклу, вважається тілом циклу. Всі інші інструкції знаходяться поза циклом. Якщо в циклі повинні виконуватись кілька інструкцій, то необхідно використати фігурні дужки. Вони також можуть використовуватись при одній інструкції заради кращого візуального розуміння коду.
while (умова){
інструкція 1;
….
інструкція N
}
В наступному прикладі демонструється мінігра із вгадуванням числа від 0 до 10, яка створена з використанням циклу while:
import java.util.*; public class Tmp { public static void main(String[] args) { Scanner in = new Scanner(System.in); // створюємо сканер вводу, // для вводу даних з консолі Random generator = new Random(); //створюємо генератор випадкових чисел System.out.println("Спробуйте відгадати число від 0 до 10"); int gn; String notEnd="Y"; while (notEnd.equals("Y")||notEnd.equals("y")){ //поки змінна notEnd рівна “Y” або “y” gn=generator.nextInt(10); //генерація випадкового числа від 0 до 10; System.out.print("Введіть число від 0 до 10: "); int number=in.nextInt(); //зчитуємо число з клавіатури if (gn==number) System.out.print("Вгадали!!! Спробуєте ще раз? (Y/N)"); else System.out.print("Не вгадали. Спробуєте ще раз? (Y/N)"); notEnd = in.next(); //отримати відповідь } } }
Результат виконання:
Спробуйте відгадати число від 0 до 10 Введіть число від 0 до 10: 6 Не вгадали. Спробуєте ще раз? (Y/N)y Введіть число від 0 до 10: 7 Вгадали!!! Спробуєте ще раз? (Y/N) n
Програма генерує випадкове число при кожному повторі циклу і пропонує вгадати його. Після вводу користувачем числа - виводить відповідне повідомлення вгадано чи ні. Після цього пропонується здійснити нову спробу. Якщо користувач вводить з клавіатури “Y" або “y”, то гра продовжується, якщо введе щось інше, то завершується.
Для генерації випадкових чисел використано клас Random, що містить методи для генерації випадкових чисел. Зокрема, у нашій програмі використано метод nextInt(), який дозволяє генерувати випадкові числа.
Для зчитування з клавіатури використано клас Scanner, який був доданий в java 5.0, для зручного вводу з клавіатури. Метод nextInt() – читає ціле число, next() – читає цілий рядок з клавіатури.
Для того, щоб переконатися, що користувач хоче продовжити гру використано метод equals() з класу String - Strint1.equals (String2), що перевіряє чи один рядок (String1) тексту рівний іншому (String2).
Цикл do/while [ред.]
Якщо необхідно, щоб умова виконувалася хоча б один раз можна скористатися циклом з післяумовою do/while:
do інструкція while (умова);
Зокрема, в програмі з вгадуванням чисел, більш логічніше було б застосувати саме даний цикл, оскільки необхідне хоча б одне виконання тіла циклу.
import java.util.*; public class Tmp { public static void main(String[] args) { Scanner in = new Scanner(System.in); // створюємо Сканер, // для вводу даних з консолі Random generator = new Random(); //створюємо генератор випадкових чисел System.out.println("Спробуйте відгадати число від 0 до 10"); int gn; String notEnd="Y"; do { gn=generator.nextInt(10); //генерація випадкового числа від 0 до 10; System.out.print("Введіть число від 0 до 10: "); int number=in.nextInt(); if (gn==number) System.out.print("Вгадали!!! Спробуєте ще раз? (Y/N)"); else System.out.print("Не вгадали. Спробуєте ще раз? (Y/N)"); notEnd = in.next(); } while(notEnd.equals("Y")||notEnd.equals("y")); } }
Цикл з лічильником for [ред.]
Цикл for – доволі часто вживаний цикл. Він застосовується при необхідності виконати інструкції певну кількість раз з одночасним збільшенням або зменшенням певної змінної. Часто використовується для здійснення перебору певних масивів даних, зокрема, також для сортування масивів. Приклад використання:
for (int i=1; i<=10; i++){ System.out.println (i); }
Наведений вище приклад виведе на консолі в стовпчик числа від 1 до 10. Як бачимо в умові циклу перший слот відводиться для ініціалізації змінної, причому оголосити змінну можна і в іншому місці. Другий слот – для умови, яка перевіряється перед виконанням ітерації, третій слот – вказує як модифікувати змінну-лічильник. Тобто в наведеному прикладі при кожному виконанні ітерації, лічильник "і" буде збільшуватися на одиницю поки не стане рівним десяти.
Найчастіше даний цикл використовується для перебору елементів масиву. Масив – це впорядкований набір даних одного типу. Найпростіший масив можна оголосити та ініціалізувати таким чином: int a[]={1,5,6,1,3};. Для того, щоб звернутися до певного елементу масиву використовуються квадратні дужки з відповідним індексом елементу. Наприклад а[3] – звернення до четвертого елементу масиву (номери елементів відраховуються з нуля). В наступному прикладі створюється масив і послідовно виводяться його елементи:
public class MyArray { public static void main(String[] args) { int a[]={1,5,6,1,3}; // створюємо масив і заповнюємо його числами // int j; int size=5; System.out.println ("Елементи масиву:"); for (int j=0;j<5;j++){ System.out.println("а["+j+"]="+a[j]); } } }
Результат виконання:
Елементи масиву: а[0]=1 а[1]=5 а[2]=6 а[3]=1 а[4]=3
Цикл «for each» [ред.]
Починаючи з java SE 5.0 в мові з’явився новий цикл, призначення якого є перебір елементів масиву або подібних до масиву типів даних (колекції).
Наприклад, вивести елементи масиву, можна таким чином:
for (int element : a) System.out.println(element);
Використання даного циклу, дозволяє уникнути проблем пов’язаних з помилками при заданні умови в класичному циклі for. В інших мовах програмування цикл такого виду так і називається foreach, проте, щоб уникнути необхідності значних змін в пакетах, в java пішли простішим шляхом і перевантажили цикл for.
Інструкції, що порушують порядок виконання [ред.]
В java відсутня інструкція goto, яка дозволяла переходити в будь-яке місце в програмі. Її використання давно вважається поганим стилем програмування, оскільки робить текст програми заплутаним. Про це, зокрема, писав ще Дональд Кнут і він же зазначав, що інколи все таки корисно її застосовувати, щоб припинити виконання певного методу, циклу чи блоку і вийти за їхні межі. Для цієї мети в java існують спеціальні інструкції. Зокрема, інструкцію break можна використати для передчасного виходу з циклу.
while (years <= 100) { balance += payment; double interest = balance * interestRate / 100; balance += interest; if (balance >= goal) break; years++; }
Таким чином додано дві умови для виходу з циклу. Хоча даний фрагмент можна також переписати наступним чином:
while (years <= 100 && balance < goal) { balance += payment; double interest = balance * interestRate / 100; balance += interest; if (balance < goal) years++; }
Якщо ми використовуємо вкладені цикли (один цикл в іншому), то інструкція break припинить цикл, в якому вона знаходиться. Якщо вона знаходиться у внутрішньому циклі, то він припинить своє виконання, а зовнішній же цикл буде виконуватись і дальше. Якщо потрібно повністю припинити виконання і внутрішнього і зовнішнього, то використовується інструкція break з міткою.
Scanner in = new Scanner(System.in); int n; read_data: while (. . .) // this loop statement is tagged with the label { . . . for (. . .) // this inner loop is not labeled { System.out.print("Enter a number >= 0: "); n = in.nextInt(); if (n < 0) // should never happen—can't go on break read_data; // break out of read_data loop . . . } } // наступна інструкція буде виконана зразу ж після інструкції break з міткою if (n < 0) // check for bad situation { // deal with bad situation } else { // carry out normal processing }
Після використання break з міткою, часто виникає необхідність перевірити чому саме цикл припинив виконання, що і робиться в кінці наведеного вище фрагменті коду програми.
Якщо ж нам потрібно виходити з циклу, а лише припинити певну його ітерацію, то для цієї використовується інструкція continue, яка переносить порядок виконання до заголовку інструкції.
Scanner in = new Scanner(System.in); while (sum < goal) { System.out.print("Enter a number: "); n = in.nextInt(); if (n < 0) continue; sum += n; // not executed if n < 0 }
Щоправда завжди можна обійтися без даних інструкцій змінивши логіку програми. Деякі з програмістів уникають використовувати інструкції break та continue.
Множинний вибір [ред.]
Інструкція if/else може бути доволі громіздкою, якщо необхідно здійснити множинний вибір з багатьох альтернатив. Тож як і в С/C++ в java існує інструкція switch, яка здійснити вибір з багатьох варіантів. Щоправда вона дещо незграбна і деякі програмісти вважають за краще уникати її використання.
Наприклад, якщо Ви організовуєте певне меню і пропонуєте користувачу вибрати, номер конкретного пункту, то можна використати наступний код:
Scanner in = new Scanner(System.in); System.out.print("Select an option (1, 2, 3, 4) "); int choice = in.nextInt(); switch (choice) { case 1: . . . break; case 2: . . . break; case 3: . . . break; case 4: . . . break; default: // bad input . . . break; }
Якщо пропустити інструкцію break, то всі інші інструкції будуть також виконані. Тобто якщо справдиться умова першого варіанту, то будуть здійснені ще й дії вказані для виконання у всіх інших варіантах.
Крім того case мітка повинна бути цілим числом або нумерованою константою. Ми не можемо перевіряти на рівність рядки таким чином. Так наступний код буде помилковим:
String input = . . .; switch (input) // ПОМИЛКА { case "A": //ПОМИЛКА . . . break; . . . }