חריגות (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("סיים");
}
}
}
עצות
- אל תתפסו כל חריגה סתם - טפלו ספציפית בחריגות שאתם יודעים מה לעשות איתן
- אל תתעלמו -
catch (Exception e) {}ריק הוא אנטי-דפוס - אל תשתמשו בחריגות לבקרת זרימה רגילה - חריגות הן למצבים חריגים
- הודעות ברורות - תנו הודעות שגיאה שאפשר להבין
בדקו את עצמכם
נסו לענות לבד לפני שאתם פותחים את התשובה.
-
מתי רץ בלוק
finally?הצגת התשובה
תשובה א. finally מיועד לניקוי שחייב לקרות בכל מקרה.
-
מה הבעיה ב-
catch (Exception e) {}ריק?הצגת התשובה
תשובה ד. זה אנטי-דפוס: השגיאה קרתה, אבל אף אחד לא יודע.
-
מה ההבדל בין checked ל-unchecked exception?
הצגת התשובה
תשובה ד. למשל IOException היא checked, ו-NullPointerException היא unchecked.