Към съдържанието
wannacode

Когато нещо се обърка

Проблем

12а вместо 12

Касата за концерта пита колко билета искаш. Билетът е 15 €.

Main.java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Колко билета? ");
int tickets = Integer.parseInt(in.nextLine());
System.out.println("Общо: " + tickets * 15 + " €");
}
}

С вход 12 програмата казва „Общо: 180 €“. Но клиентът бърза и натиска една буква повече:

Конзолата
Колко билета? 12а
Exception in thread "main" java.lang.NumberFormatException: For input string: "12а"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:662)
at java.base/java.lang.Integer.parseInt(Integer.java:778)
at Main.main(Main.java:7)

Програмата спира. Вместо сумата излиза дълъг текст, а редът „Общо“ изобщо не се стига. Какво значи този текст и как да не спира програмата заради една буква?

Идеята

Изключение

Integer.parseInt трябва да върне число. От „12а“ не може да направи число, а да върне каквото и да е число би било лъжа. Затова методът създава обект, който описва проблема, и го хвърля. Този обект се казва изключение (exception).

Ако никой не го хване, програмата спира и показва какво е станало. Този текст се казва stack trace и се чете отгоре надолу:

  • Първият ред: видът на изключението и съобщението. NumberFormatException: For input string: "12а": грешен формат на числото, ето кой текст.
  • Редовете с „at“: къде е станало. Това е стекът от извиквания от урока за рекурсията, отвътре навън. Най-горе е методът, който е хвърлил изключението, а най-долу е main.
  • Твоят ред: първият ред „at“, който сочи твоя файл. Тук: Main.java:7. Редовете над него са в кода на Java и рядко ти трябват.

Съобщенията са на английски, защото ги пише езикът, не ти.

Пробвай

Намери грешката

Програмата трябва да отпечата последния резултат от контролното. Тя спира с това:

Грешка
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at Main.lastScore(Main.java:3)
at Main.main(Main.java:8)

Прочети stack trace-а. На кой ред от програмата е грешката?

Избери реда с грешката.

Идеята

try и catch

Изключението може да се хване. Кодът, който може да хвърли, отива в try („опитай“). Какво да стане при грешка, пишеш в catch („хвани“):

Scanner in = new Scanner(System.in);
System.out.print("Колко билета? ");
try {
int tickets = Integer.parseInt(in.nextLine());
System.out.println("Общо: " + tickets * 15 + " €");
} catch (NumberFormatException e) {
System.out.println("Това не е цяло число.");
}
System.out.println("Благодарим ти!");
Вход
12а
Изход
Колко билета? Това не е цяло число.
Благодарим ти!
  • В скобите на catch пишеш кой вид изключение хващаш.
  • Ако в try всичко мине, catch се прескача. С вход 12 изходът е „Общо: 180 €“ и „Благодарим ти!“.
  • След catch програмата продължава нататък. Тя вече не спира.

Scanner in = new Scanner(System.in);
System.out.print("Колко билета? ");
try {
int tickets = Integer.parseInt(in.nextLine());
System.out.println("Общо: " + tickets * 15 + " €");
} catch (NumberFormatException e) {
System.out.println("Това не е цяло число.");
}
System.out.println("Благодарим ти!");
Стъпка по стъпка, с думи
  1. Програмата пита колко билета. Въвеждаш „12а“.
  2. В try: parseInt опитва да направи число от „12а“.
  3. Не може. Методът хвърля NumberFormatException.
  4. Изпълнението скача направо в catch. Редът с „Общо“ се прескача.
  5. catch е за точно този вид грешка: печата съобщението за клиента.
  6. След catch програмата продължава. Не спира.
По-точно, за любопитните
  • Променлива, обявена в try (тук tickets), съществува само до затварящата скоба на try. Ако ти трябва и след блока, обяви я преди try.
  • В Java името е задължително: catch (NumberFormatException e), дори да не го ползваш. По навик то е e.

Пробвай

Кое се изпълнява?

Какво ще отпечата програмата?

System.out.println("Начало");
try {
System.out.println("Чета");
int seats = Integer.parseInt("десет");
System.out.println("Прочетох " + seats);
} catch (NumberFormatException e) {
System.out.println("Грешен вход");
}
System.out.println("Край");
Отговор

Идеята

Какво пише в изключението

Изключението е обект, а e в catch (NumberFormatException e) е променливата, която сочи към него. Обектът има метод за съобщението: e.getMessage().

Scanner in = new Scanner(System.in);
try {
int tickets = Integer.parseInt(in.nextLine());
System.out.println("Общо: " + tickets * 15 + " €");
} catch (NumberFormatException e) {
System.out.println("Грешка: " + e.getMessage());
}
Вход
12а
Изход
Грешка: For input string: "12а"

Съобщението е полезно за теб, когато търсиш грешката. На клиента обикновено показваш свой текст на неговия език, като „Това не е цяло число.“

Идеята

Питай, докато е вярно

Клиентът сбърка. Не е нужно да спираш. Питай отново:

Scanner in = new Scanner(System.in);
int tickets;
while (true) {
System.out.print("Колко билета? ");
try {
tickets = Integer.parseInt(in.nextLine());
break;
} catch (NumberFormatException e) {
System.out.println("Въведи цяло число, например 3.");
}
}
System.out.println("Общо: " + tickets * 15 + " €");
Вход
12а
дванайсет
12
Изход
Колко билета? Въведи цяло число, например 3.
Колко билета? Въведи цяло число, например 3.
Колко билета? Общо: 180 €

while (true) се върти, докато break не го прекъсне. А break се стига само ако parseInt е успял. При грешка изпълнението скача в catch, след него цикълът започва отначало и пита пак.

По-точно, за любопитните
  • В Java няма TryParse. Хващането на NumberFormatException е обичайният начин. Scanner има и hasNextInt(), която проверява дали следва цяло число, без да го чете.

Пробвай

Деление на нула

Не всяко изключение идва от входа. Средната стойност на поръчка е приходът, делен на броя поръчки. Но в първия ден поръчки няма. С цели числа revenue / orders спира програмата:

Грешка
Exception in thread "main" java.lang.ArithmeticException: / by zero

А с дробни числа?

Какво ще отпечата програмата?

int revenue = 90;
int orders = 0;
double average = (double) revenue / orders;
System.out.println(average);
Отговор
По-точно, за любопитните
  • Целите числа нямат стойност за безкрайност, затова там остава само изключението. При дробните числа правилата идват от стандарта за дробни числа IEEE 754, който всички езици следват.

Упражнение

Програми, които не спират

Реши задачите в редактора си. Не печатай въпроси, за да съвпадне изходът ти с примерния.

1. Текст към число. Напиши метод toNumber(String text), който връща текста като double. Прочети два реда. За всеки отпечатай числото или съобщение, че не е число. Хвани изключението в main.

Вход
123.45
123.abc
Изход
Числото е: 123.45
Грешка: 123.abc не е число.

2. Няма такъв продукт. Цените са в масив { 120, 75, 300, 45, 89 }. Прочети номер на продукт от 1 до 5 и отпечатай цената му. Ако номерът е извън масива, хвани изключението и го кажи. С вход 2 изходът е Цена: 75 €.

Вход
7
Изход
Няма продукт с номер 7.

3. Средна поръчка. Прочети прихода и броя поръчки (цели числа) и отпечатай средното с целочислено деление. Ако поръчки няма, хвани изключението. С вход 900 и 4 изходът е Средно: 225 €.

Вход
90
0
Изход
Още няма поръчки.

Още задачи

  • Възраст, докато е вярна. Питай за възраст, докато входът не е цяло число от 0 до 120. За текст като „двайсет“ кажи „Това не е число.“, а за 150 кажи „Възрастта е от 0 до 120.“ Едното е изключение, другото е if.
  • Сума на верните. Прочети 5 реда. Събери тези, които са цели числа, и преброй пропуснатите: при вход 10, abc, 5, 7.5, 3 изходът е Сума: 18, пропуснати: 2.
  • Деление по желание. Прочети две цели числа и отпечатай частното. При текст вместо число и при деление на нула програмата не спира, а пита отново за двете числа.

Тест

Провери се

1. Какво е изключение?
2. Програмата спря с дълъг текст за грешка. Откъде да започнеш да четеш?
3. В try има четири реда. Вторият хвърля изключение, а catch го хваща. Какво става с третия и четвъртия?
4. Какво става при int x = 90 / orders; при orders = 0?
5. catch (NumberFormatException e) хваща ли грешката от scores[10] в масив с 5 елемента?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·