Винятки - це спеціальний механізм для контролю над помилками в програмі. Ось приклади помилок, які можуть виникнути в програмі:
- Програма намагається записати файл на заповнений диск.
- Програма намагається викликати метод у змінної, яка зберігає посилання - null.
- Програма намагається розділити число на 0.
Всі ці дії призводять до виникнення помилки. Зазвичай це призводить до закриття програми - продовжувати виконувати далі код не має сенсу.
Механізм обробки помилок:
- Коли виникає помилка, Java-машина створює спеціальний об'єкт - exception - виняток, в який записується вся інформація про помилку. Для різних помилок є різні винятку.
- Потім це «виключення» призводить до того, що програма тут же виходить з поточної функції, потім виходить з наступної функції, і так поки не вийде з методу main. Потім програма завершується. Ще кажуть, що Java-машина «розкручує тому стек викликів».
Для обробки винятків є спеціальна конструкція try-catch. Ось як це працює:
Ось приклад програми, яка перехоплює виняток - ділення на 0. І продовжує працювати.
public
class ExceptionExample2
{
public static void main(String[]
args)
{
System.out.println("Program
starts");
try
{
System.out.println("Before
method1 calling");
method1();
System.out.println("After
method1 calling. Never will be shown");
}
catch (Exception e)
{
System.out.println("Exception
has been caught");
}
System.out.println("Program
is still running");
}
public static
void method1()
{
int a = 100;
int b = 0;
System.out.println(a / b);
}
}
Ось що буде виведено на екран:
"Program starts"
"Before
method1 calling"
"Exception
has been caught"
"Program
is still running"
Не буде виведено «After method calling. Never will be shown »?! - У рядку 25 було ділення на нуль. Це призвело до виникнення помилки - виключення. Java-машина створила об'єкт ArithmeticException з інформацією про помилку. Цей об'єкт є винятком.
Усередині методу method1 виникло виключення. І це призвело до негайного завершення цього методу. Воно привело б і до завершення методу main, якби не було блоку try-catch.
Якщо всередині блоку try виникає виняток то, він захоплюється в блоці catch. Залишок коду в блоці try не буде виконаний, а відразу почнеться виконання блоку catch. Іншими словами цей код працює так:
- Якщо всередині блоку try виникло виключення, то код перестає виконуватися, і починає виконуватися блок catch.
- Якщо виняток не виник, то блок try виконується до кінця, а catch ніколи так і не почне виконуватися.
А що означає Exception всередині catch? - Всі виключення - це класи, успадковані від класу Exception. Ми можемо перехопити будь-яке з них, вказавши в блоці catch його клас, або все відразу, вказавши загальний батьківський клас - Exception. Потім з змінної e (ця змінна зберігає посилання на об'єкт виключення), можна отримати всю необхідну інформацію про виниклу помилку.
У Java всі винятки діляться на два типи - контрольовані (checked) і неконтрольовані (unchecked): ті, які перехоплювати обов'язково, і ті, які перехоплювати не обов'язково. За замовчуванням - всі виключення обов'язково потрібно перехоплювати.
У своєму коді ти сам можеш викидати виключення, навіть можеш написати свої власні виключення.
Якщо в методі виникають виключення ClassNotFoundException і FileNotFoundException, програміст зобов'язаний вказати їх в сигнатурі методу (в заголовку методу). Це checked виключення.
Якщо в методі main хочеш викликати метод якогось об'єкта, в заголовку якого прописано throws FileNotFoundException, ... то треба зробити одне з двох:
- Перехоплювати виключення FileNotFoundException, ...Тоді доведеться вернути код виклику небезпечного методу в блок try-catch
- Чи не перехоплювати виключення FileNotFoundException, ...Тобі доведеться додати ці винятки в список throws свого методу main.
public static void main(String[] args) throws FileNotFoundException, ClassNotFoundException{
method1();
}
public static void method1() throws FileNotFoundException, ClassNotFoundException{
//тут виникне виключення FileNotFoundException, такого файлу немає
FileInputStream fis = new FileInputStream("C2:\\badFileName.txt");
}
method1();
}
public static void method1() throws FileNotFoundException, ClassNotFoundException{
//тут виникне виключення FileNotFoundException, такого файлу немає
FileInputStream fis = new FileInputStream("C2:\\badFileName.txt");
}
А тут перехоплюємо його за допомогою try-catch:
Спосіб 2: перехоплення винятку:
public static void main(String[] args){
try{
method1();
}catch(Exception e){
}
}
public static void method1() throws FileNotFoundException, ClassNotFoundException{
//тут виникне виключення FileNotFoundException, такого файлу немає
FileInputStream fis = new FileInputStream("C2:\\badFileName.txt");
}
Не обробляємо виключення - потрібно прокинути їх далі, тому, хто знає як
public static void method2() throws FileNotFoundException, ClassNotFoundException{
method1();
}
Обробляємо один виняток, інший - передаємо:
public static void method3() throws ClassNotFoundException{
try{
method1();
}catch (FileNotFoundException e)
{
System.out.println("FileNotFoundException has been caught.");
}
}
Спосіб 2: перехоплення винятку:
public static void main(String[] args){
try{
method1();
}catch(Exception e){
}
}
public static void method1() throws FileNotFoundException, ClassNotFoundException{
//тут виникне виключення FileNotFoundException, такого файлу немає
FileInputStream fis = new FileInputStream("C2:\\badFileName.txt");
}
Не обробляємо виключення - потрібно прокинути їх далі, тому, хто знає як
public static void method2() throws FileNotFoundException, ClassNotFoundException{
method1();
}
Обробляємо один виняток, інший - передаємо:
public static void method3() throws ClassNotFoundException{
try{
method1();
}catch (FileNotFoundException e)
{
System.out.println("FileNotFoundException has been caught.");
}
}
Перехоплюємо обидва - нічого не передаємо:
public static void method4(){
try{
method1();
}catch (FileNotFoundException e)
{
System.out.println("FileNotFoundException has been caught.");
}catch (ClassNotFoundException e)
{
System.out.println("ClassNotFoundException has been caught.");
}
}
Але є вид винятків - це RuntimeException і класи, успадковані від нього. Їх перехоплювати не обов'язково. Це unchecked виключення. Вважається, що це важко прогнозовані виключення і передбачити їх появу практично неможливо. З ними можна робити все те ж саме, але вказувати в throws їх не потрібно.
Завдання 1.
Є метод, який викидає два винятки, успадковані від Exception, і два успадкованих від RuntimeException: NullPointerException, ArithmeticException, FileNotFoundException, URISyntaxException. Потрібно перехопити NullPointerException і FileNotFoundException, але не перехоплювати ArithmeticException і URISyntaxException.
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import java.util.HashMap;
public class Solution
{
public static void main(String[] args)
{
//напишіть тут код
method1();
//напишіть тут код
}
public static void method1() //напишіть тут код
{
int i = (int) (Math.random() * 4);
if (i == 0)
throw new //напишіть тут код ;
if (i == 1)
throw new //напишіть тут код ;
if (i == 2)
throw new //напишіть тут код ;
if (i == 3)
throw new //напишіть тут код ;
}
}
public class Solution
{
public static void main(String[] args) {
processExceptions(new Solution());
}
public static void processExceptions(Solution obj) {
//напишіть тут код
obj.method1();
obj.method2();
obj.method3();
//напишіть тут код
}
{
public static void main(String[] args)
{
//напишіть тут код
method1();
//напишіть тут код
}
public static void method1() //напишіть тут код
{
int i = (int) (Math.random() * 4);
if (i == 0)
throw new //напишіть тут код ;
if (i == 1)
throw new //напишіть тут код ;
if (i == 2)
throw new //напишіть тут код ;
if (i == 3)
throw new //напишіть тут код ;
}
}
Завдання 2. Перехоплення checked винятків
У методі processExceptions обробіть всі checked виключення. Потрібно вивести на екран кожен checked виняток. Можна використовувати тільки один блок try.
import java.io.IOException;
import java.rmi.RemoteException;
public class Solution {
public static void main(String[] args) {
processExceptions(new Solution());
}
public static void main(String[] args) {
processExceptions(new Solution());
}
public static void processExceptions(Solution obj) {
//напишіть тут код
obj.method1();
obj.method2();
obj.method3();
}
public void method1() //напишіть тут код{
//напишіть тут код
}
public void method2() //напишіть тут код{
//напишіть тут код
}
public void method3() //напишіть тут код{
//напишіть тут код
}
}
//напишіть тут код
obj.method1();
obj.method2();
obj.method3();
}
public void method1() //напишіть тут код{
//напишіть тут код
}
public void method2() //напишіть тут код{
//напишіть тут код
}
public void method3() //напишіть тут код{
//напишіть тут код
}
}
Завдання 3. Перехоплення unchecked винятків
У методі processExceptions обробіть всі unchecked виключення. Потрібно вивести стек-трейс кожного виключення, використовуючи метод printStack. Можна використовувати тільки один блок try.public class Solution
{
public static void main(String[] args) {
processExceptions(new Solution());
}
public static void processExceptions(Solution obj) {
//напишіть тут код
obj.method1();
obj.method2();
obj.method3();
//напишіть тут код
}
public static void printStack(Throwable throwable) {
System.out.println(throwable);
//напишіть тут код
}
public void method1(){
//напишіть тут код
}
public void method2() {
//напишіть тут код
}
public void method3() {
//напишіть тут код
}
}