Smart-World Surf

יחידה 4: טיפול בחריגות

טיפול בשגיאות ואירועים בלתי צפויים לשיפור יציבות הקוד.
מנגנון try-catch-finallyחריגות מסומנות ובלתי מסומנותיצירת חריגות מותאמות אישיתהעברת חריגות (throws)

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

מבוא לטיפול בחריגות ב-Java

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

חריגה (Exception): אירוע בלתי צפוי המתרחש בזמן ריצת התוכנית ומפריע לזרימת הביצוע הרגילה.

מנגנון try-catch-finally: הליבה של טיפול בחריגות

הבסיס לטיפול בחריגות ב-Java הוא מנגנון ה-try-catch-finally. הוא מאפשר להגדיר בלוק קוד "מוגן" שבו עלולה להתרחש חריגה, לספק לוגיקה לטיפול בחריגות ספציפיות, ולהבטיח ביצוע של קוד מסוים ללא קשר לשאלה אם חריגה התרחשה או טופלה.

בלוק try

בלוק ה-try מכיל את הקוד שעלול לזרוק חריגה. אם חריגה מתרחשת בתוך בלוק זה, זרימת הביצוע עוברת מיד לבלוק ה-catch המתאים.

try: בלוק קוד המכיל פקודות שעלולות לזרוק חריגה.

בלוק catch

בלוק ה-catch מוגדר לאחר בלוק ה-try ומטפל בסוג ספציפי של חריגה. ניתן להגדיר מספר בלוקי catch עבור סוגי חריגות שונים. הטיפול בחריגה יכול לכלול רישום לוג, הצגת הודעה למשתמש, או ניסיון לשחזר את המצב.

catch: בלוק קוד המטפל בסוג ספציפי של חריגה שנזרקה בבלוק ה-try המקביל.

בלוק finally

בלוק ה-finally הוא אופציונלי, אך חשוב מאוד. הקוד בתוך בלוק זה מבוצע תמיד, ללא קשר לשאלה אם חריגה התרחשה, טופלה, או לא התרחשה כלל. הוא משמש בדרך כלל לשחרור משאבים (כמו סגירת קבצים, חיבורי רשת או בסיסי נתונים).

finally: בלוק קוד המבוצע תמיד, ללא קשר לשאלה אם נזרקה חריגה או טופלה.
חשיבות בלוק ה-finally: בלוק ה-finally מבטיח שמשאבים קריטיים (כמו קבצים פתוחים, חיבורי רשת או בסיסי נתונים) ייסגרו או ישוחררו באופן עקבי, ללא קשר לשאלה אם התרחשה חריגה או לא. אי-שימוש נכון ב-finally עלול להוביל לדליפות משאבים (resource leaks) ולפגוע ביציבות ובביצועי המערכת לאורך זמן. זוהי נקודה קריטית לבחינה ולתכנות נכון.

חריגות מסומנות ובלתי מסומנות (Checked vs. Unchecked Exceptions)

Java מבחינה בין שני סוגים עיקריים של חריגות, בהתבסס על האופן שבו הקומפיילר אוכף את הטיפול בהן:

חריגות מסומנות (Checked Exceptions)

חריגות שהקומפיילר מחייב לטפל בהן (באמצעות try-catch) או להצהיר עליהן (באמצעות throws) בחתימת המתודה. הן מייצגות מצבים חריגים שניתן לצפות להם ולטפל בהם בצורה אלגנטית, כמו בעיות קלט/פלט (IOException) או בעיות גישה לבסיס נתונים (SQLException). הן נועדו להבטיח שהמתכנת יתייחס למצבי כשל אפשריים.

חריגות בלתי מסומנות (Unchecked Exceptions)

חריגות שאינן מחייבות טיפול או הצהרה על ידי הקומפיילר. אלו בדרך כלל חריגות מסוג RuntimeException (או יורשיה) או Error, והן מצביעות לרוב על שגיאות תכנותיות (למשל, NullPointerException, ArrayIndexOutOfBoundsException) או בעיות חמורות במערכת שאינן ניתנות לטיפול סביר (למשל, OutOfMemoryError). הן משקפות לרוב באגים בקוד.

יצירת חריגות מותאמות אישית והעברת חריגות (throws)

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

לעיתים קרובות, החריגות המובנות ב-Java אינן מספקות מספיק מידע או אינן מתארות במדויק מצב שגיאה ספציפי ליישום שלנו. במקרים אלו, מומלץ ליצור חריגות מותאמות אישית.

  • למה? כדי לשפר את קריאות הקוד, לספק מידע ספציפי יותר על השגיאה, ולאפשר לוגיקת טיפול ממוקדת יותר.
  • איך? יוצרים מחלקה חדשה שיורשת מ-Exception (עבור חריגה מסומנת) או מ-RuntimeException (עבור חריגה בלתי מסומנת). בדרך כלל, נספק קונסטרוקטורים המקבלים הודעת שגיאה ו/או חריגה מקורית (cause).
חריגה מותאמת אישית: מחלקה חדשה המייצגת מצב שגיאה ספציפי ליישום, ויורשת מ-Exception או RuntimeException.

העברת חריגות באמצעות throws

כאשר מתודה עלולה לזרוק חריגה מסומנת, אך אינה מטפלת בה בעצמה, עליה להצהיר על כך באמצעות מילת המפתח throws בחתימת המתודה. הצהרה זו מעבירה את אחריות הטיפול בחריגה למתודה הקוראת.

throws: מילת מפתח המשמשת בהצהרת מתודה כדי לציין אילו חריגות מסומנות המתודה עלולה לזרוק, ובכך להעביר את אחריות הטיפול בהן למתודה הקוראת.
throw: מילת מפתח המשמשת בתוך בלוק קוד כדי לזרוק באופן מפורש אובייקט חריגה חדש (לדוגמה: throw new MyCustomException("שגיאה ספציפית");).

שאלות לדיון

  • מה ההבדל העיקרי בין חריגה מסומנת לבלתי מסומנת, ומתי נבחר להשתמש בכל אחת מהן?
  • תאר את סדר הביצוע של בלוקי try, catch, ו-finally במקרים שונים: ללא חריגה, עם חריגה שטופלה, ועם חריגה שלא טופלה.
  • מדוע חשוב ליצור חריגות מותאמות אישית, וכיצד עושים זאת ב-Java? תן דוגמה למקרה שימוש.
  • הסבר את ההבדל בין מילת המפתח throw לבין מילת המפתח throws, ותן דוגמה לשימוש בכל אחת.

נקודות לתשובת מודל

  • הבדל חריגות: חריגות מסומנות מחייבות טיפול או הצהרה על ידי הקומפיילר (לדוגמה: IOException), ומיועדות למצבים צפויים שניתן לשחזר מהם. חריגות בלתי מסומנות (RuntimeException ויורשיה) אינן מחייבות טיפול מפורש, ומצביעות לרוב על שגיאות לוגיות או באגים בקוד (לדוגמה: NullPointerException).
  • סדר ביצוע try-catch-finally:
    • ללא חריגה: קוד ה-try מבוצע במלואו, ואז קוד ה-finally מבוצע.
    • עם חריגה שטופלה: קוד ה-try מבוצע עד לנקודת זריקת החריגה, זרימת הביצוע עוברת לבלוק ה-catch המתאים, ואז קוד ה-finally מבוצע.
    • עם חריגה שלא טופלה: קוד ה-try מבוצע עד לנקודת זריקת החריגה, אם קיים בלוק finally הוא מבוצע, והחריגה ממשיכה להתפשט במעלה מחסנית הקריאות (stack trace) עד שהיא נתפסת או שהתוכנית קורסת.
  • חריגות מותאמות אישית: חשובות לשיפור קריאות הקוד, מתן מידע ספציפי על שגיאות יישום, והפרדת לוגיקת טיפול בשגיאות. יוצרים אותן על ידי ירושה מ-Exception (למסומנות) או RuntimeException (לבלתי מסומנות) ומתן קונסטרוקטורים מתאימים.
  • throw vs. throws:
    • throw: משמשת לזרוק *בפועל* אובייקט חריגה חדש מתוך קוד המתודה.
    • throws: משמשת ב*חתימת המתודה* כדי להצהיר על חריגות מסומנות שהמתודה עלולה לזרוק, ובכך להעביר את אחריות הטיפול בהן למתודה הקוראת.
מצאתם טעות או שחסר משהו?
→ הקודמת
פולימורפיזם וממשקים
הבאה ←
תכנות גנרי ואוספים