חריגות (Exceptions)

מהי חריגה?

חריגה (exception) היא אירוע חריג שמתרחש בזמן הריצה וקוטע את הזרימה הרגילה של התוכנית. במקום שהתוכנית תקרוס, Java מאפשרת לתפוס את החריגה ולטפל בה.

דוגמאות קלאסיות: - חלוקה באפס - גישה לאינדקס לא קיים במערך - קובץ שלא נמצא - המרת מחרוזת לא תקינה למספר

ללא טיפול בחריגה

int[] arr = {1, 2, 3};
System.out.println(arr[5]);   // ArrayIndexOutOfBoundsException
System.out.println("שורה זו לא תרוץ");

התוכנית תקרוס עם הודעת שגיאה מכוערת.

try / catch

כך תופסים חריגות:

try {
    int[] arr = {1, 2, 3};
    System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("שגיאה: אינדקס לא קיים");
}
System.out.println("התוכנית ממשיכה");

פלט:

שגיאה: אינדקס לא קיים
התוכנית ממשיכה

מבנה try / catch / finally

try {
    // קוד שעלול לזרוק חריגה
} catch (ExceptionType e) {
    // טיפול בחריגה
} finally {
    // קוד שרץ תמיד - בין אם היתה חריגה ובין אם לא
}

finally

finally שימושי לשחרור משאבים (סגירת קבצים, חיבורים):

Scanner scanner = null;
try {
    scanner = new Scanner(new File("data.txt"));
    // ...
} catch (FileNotFoundException e) {
    System.err.println("הקובץ לא נמצא");
} finally {
    if (scanner != null) scanner.close();
}

היום עדיף להשתמש ב-try-with-resources (ראו בהמשך).

מספר catches

אפשר לתפוס חריגות שונות בצורות שונות:

try {
    // קוד
} catch (FileNotFoundException e) {
    System.err.println("הקובץ לא נמצא");
} catch (IOException e) {
    System.err.println("שגיאת קריאה");
} catch (Exception e) {
    System.err.println("שגיאה כללית: " + e.getMessage());
}

חשוב: הסדר חשוב - חריגות ספציפיות יותר קודם, הכלליות אחרי.

תפיסת כמה חריגות בחלוקה אחת

try {
    // קוד
} catch (IOException | SQLException e) {
    System.err.println("שגיאה: " + e.getMessage());
}

האובייקט של החריגה

catch (Exception e) {
    e.getMessage();        // הודעת השגיאה
    e.printStackTrace();   // הדפסת העקבה המלאה
    e.getClass().getName(); // שם המחלקה של החריגה
}

היררכיית החריגות

Throwable
  ├── Error (שגיאות חמורות, לא מטפלים בדרך כלל)
  │     ├── OutOfMemoryError
  │     └── StackOverflowError
  │
  └── Exception
        ├── RuntimeException (unchecked)
        │     ├── NullPointerException
        │     ├── ArrayIndexOutOfBoundsException
        │     ├── ArithmeticException
        │     └── NumberFormatException
        │
        └── (checked exceptions)
              ├── IOException
              ├── SQLException
              └── FileNotFoundException

Checked מול Unchecked

  • Checked - Java מחייבת לטפל (עם try/catch) או להכריז (throws). למשל IOException.
  • Unchecked - לא חייב לטפל. יורשות מ-RuntimeException. למשל NullPointerException.

throws - הכרזה על חריגה

במקום לתפוס חריגה - אפשר "להעביר אותה הלאה" דרך ההצהרה throws:

public static void readFile() throws IOException {
    String content = Files.readString(Path.of("data.txt"));
}

במקרה הזה, מי שקורא ל-readFile חייב לטפל ב-IOException בעצמו.

throw - זריקת חריגה

אפשר גם "לזרוק" חריגה בעצמנו:

public static int divide(int a, int b) {
    if (b == 0) {
        throw new ArithmeticException("אי אפשר לחלק באפס");
    }
    return a / b;
}

הבדל בין throw ל-throws

  • throw - זורק חריגה עכשיו
  • throws - מכריז שהמתודה עלולה לזרוק חריגה

חריגות מותאמות אישית

אפשר ליצור חריגות משלך:

public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

שימוש:

public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException("אין מספיק כסף בחשבון");
    }
    balance -= amount;
}

try-with-resources

הדרך המודרנית לניהול משאבים:

try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
    String line = br.readLine();
    System.out.println(line);
} catch (IOException e) {
    System.err.println("שגיאה: " + e.getMessage());
}

ה-br ייסגר אוטומטית בסוף, גם אם היתה חריגה. זה מחליף את הצורך ב-finally.

חריגות נפוצות

חריגה מתי
NullPointerException שימוש בהפנייה null
ArrayIndexOutOfBoundsException גישה לאינדקס לא קיים
ArithmeticException חלוקה באפס (לשלמים)
NumberFormatException Integer.parseInt("abc")
ClassCastException Cast לא תקין
IllegalArgumentException ארגומנט לא תקין
IOException בעיות קלט/פלט
FileNotFoundException קובץ לא נמצא
IllegalStateException מצב לא תקין של אובייקט

דוגמה מלאה

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        try {
            System.out.print("הכנס מספר: ");
            int number = Integer.parseInt(scanner.nextLine());

            System.out.print("הכנס מספר אחר: ");
            int divisor = Integer.parseInt(scanner.nextLine());

            int result = number / divisor;
            System.out.println("תוצאה: " + result);

        } catch (NumberFormatException e) {
            System.err.println("חובה להכניס מספרים");
        } catch (ArithmeticException e) {
            System.err.println("אי אפשר לחלק באפס");
        } finally {
            scanner.close();
            System.out.println("סיים");
        }
    }
}

עצות

  1. אל תתפסו כל חריגה סתם - טפלו ספציפית בחריגות שאתם יודעים מה לעשות איתן
  2. אל תתעלמו - catch (Exception e) {} ריק הוא אנטי-דפוס
  3. אל תשתמשו בחריגות לבקרת זרימה רגילה - חריגות הן למצבים חריגים
  4. הודעות ברורות - תנו הודעות שגיאה שאפשר להבין

בדקו את עצמכם

נסו לענות לבד לפני שאתם פותחים את התשובה.

  1. מתי רץ בלוק finally?

    1. תמיד, בין אם הייתה חריגה ובין אם לא
    2. רק אם לא הייתה חריגה
    3. רק אם הייתה חריגה
    4. אף פעם
    הצגת התשובה

    תשובה א. finally מיועד לניקוי שחייב לקרות בכל מקרה.

  2. מה הבעיה ב-catch (Exception e) {} ריק?

    1. אין בעיה
    2. הוא מאט את התוכנית
    3. הוא לא מתקמפל
    4. הוא בולע שגיאות בשקט, ובאגים נעלמים בלי זכר
    הצגת התשובה

    תשובה ד. זה אנטי-דפוס: השגיאה קרתה, אבל אף אחד לא יודע.

  3. מה ההבדל בין checked ל-unchecked exception?

    1. אין הבדל
    2. checked קורה רק בזמן קומפילציה
    3. unchecked לא ניתן לתפוס
    4. את checked הקומפיילר מחייב לטפל או להצהיר, unchecked לא
    הצגת התשובה

    תשובה ד. למשל IOException היא checked, ו-NullPointerException היא unchecked.