Smart-World Surf

יחידה 9: טיפול בשגיאות וחריגות

אסטרטגיות לניהול מצבי שגיאה בתוכניות.
חריגות (Exceptions)טיפול בשגיאות בזמן ריצהמנגנוני try-catch

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

הבנת שגיאות וחריגות בתוכנה

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

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

שגיאות מול חריגות: הבדלים ודוגמאות

שגיאה (Error)

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

חריגה (Exception)

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

טיפול בשגיאות בזמן ריצה (Runtime Error Handling): התהליך של זיהוי, יירוט וניהול אירועים בלתי צפויים (חריגות) המתרחשים בזמן שהתוכנית פועלת, במטרה למנוע קריסה לא מבוקרת ולשמור על יציבות ואמינות המערכת.

מנגנון ה-try-catch: עקרונות ויישום

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

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

זרימת העבודה של try-catch-finally

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

התפשטות חריגות (Exception Propagation)

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

שיטות עבודה מומלצות ואתגרים בטיפול בחריגות

לא לבלוע חריגות (Don't Swallow Exceptions): זוהי טעות נפוצה וחמורה. "בליעת" חריגה מתרחשת כאשר בלוק catch תופס חריגה אך אינו מבצע שום פעולה משמעותית (למשל, בלוק catch ריק או כזה שרק מדפיס הודעה כללית ולא מטפל בשגיאה). זה מונע מהתוכנית לדעת ששגיאה התרחשה, מקשה על איתור באגים ועלול להוביל למצבים בלתי צפויים וקריסות מאוחרות יותר. תמיד יש לטפל בחריגה באופן משמעותי: לתעד אותה, להציג הודעה למשתמש, לנסות להתאושש, או לזרוק חריגה חדשה (או את אותה חריגה) ברמה גבוהה יותר.

סוגי חריגות וטיפול ממוקד

  • חריגות מסומנות (Checked Exceptions): (נפוץ בג'אווה) חריגות שהמהדר (compiler) דורש מהמתכנת לטפל בהן במפורש (באמצעות try-catch או הצהרה על throws). הן מייצגות מצבים שניתן לצפות מראש ולטפל בהם.
  • חריגות בלתי מסומנות (Unchecked Exceptions): (נפוץ בג'אווה, C#) חריגות שהמהדר אינו דורש לטפל בהן במפורש. הן לרוב מצביעות על שגיאות תכנותיות (כמו NullPointerException) או כשלים בלתי הפיכים.
  • טיפול ספציפי מול כללי: עדיף לתפוס חריגות ספציפיות ככל האפשר (למשל, FileNotFoundException לפני IOException) כדי לאפשר טיפול מדויק יותר. בלוק catch כללי (למשל, Exception) צריך לשמש כמוצא אחרון, ורק לאחר בלוקי catch ספציפיים.

חריגות מותאמות אישית (Custom Exceptions)

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

שאלות לדיון

  • הסבר את ההבדל העיקרי בין "שגיאה" ל"חריגה" והדגם מתי נכון להשתמש במנגנון try-catch עבור כל אחד מהם.
  • תאר את תהליך התפשטות חריגה (Exception Propagation) במחסנית הקריאות. מדוע הבנה זו חשובה למתכנת?
  • מתי ולשם מה נשתמש בבלוק finally במנגנון טיפול בחריגות? תן דוגמה קונקרטית.
  • מהי הסכנה ב"בליעת חריגות" (Swallowing Exceptions)? כיצד ניתן למנוע זאת?
  • כיצד טיפול נכון בחריגות תורם לאמינות וליציבות של תוכנה?

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

  • שגיאה מול חריגה: שגיאה היא כשל קריטי (למשל, OutOfMemoryError) שאינו ניתן לטיפול בדרך כלל, בעוד שחריגה היא אירוע בלתי צפוי (למשל, FileNotFoundException) שניתן לטפל בו ולהתאושש ממנו. try-catch מיועד לטיפול בחריגות.
  • התפשטות חריגה: כאשר חריגה נזרקת בפונקציה ואינה נתפסת, היא עוברת לפונקציה הקוראת, וכן הלאה במעלה מחסנית הקריאות. זה חשוב כדי לדעת היכן למקם את בלוק ה-catch המתאים וכיצד לנהל את זרימת התוכנית במקרה של כשל.
  • שימוש ב-finally: בלוק finally מיועד לקוד שיש לבצע בכל מקרה, ללא קשר אם חריגה התרחשה או טופלה. שימושי במיוחד לשחרור משאבים (סגירת קבצים, חיבורי DB) כדי למנוע דליפות משאבים.
  • סכנת בליעת חריגות: בליעת חריגות (בלוק catch ריק או לא מטפל) מסתירה שגיאות, מקשה על איתור באגים, ועלולה להוביל למצבים בלתי צפויים, נתונים שגויים או קריסות מאוחרות יותר. יש לתעד, לדווח, לנסות התאוששות או לזרוק מחדש את החריגה.
  • תרומה לאמינות ויציבות: טיפול נכון בחריגות מונע קריסות לא מבוקרות, מאפשר לתוכנה להתאושש ממצבי כשל, מספק משוב למשתמש ולמפתחים, ושומר על שלמות הנתונים והמשאבים.
מצאתם טעות או שחסר משהו?
→ הקודמת
פרדיגמת התכנות מונחה העצמים
הבאה ←
מקביליות וקונקרנטיות