Menu

Java Exceptions: שגיאות, stack trace וזריקת חריגות

מה זו exception ב-Java, איך קוראים stack trace, ההבחנה בין checked ל-unchecked, היררכיית החריגות ואיך לזרוק חריגות משלכם.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

מה זו exception

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

אם אף אחד לא מטפל בה, החריגה מגיעה לראש התוכנית, ה-JVM מדפיס stack trace, והתהליך יוצא עם סטטוס שונה מאפס.

שימו לב ש-"after" אף פעם לא מודפס. ברגע ש-numbers[5] מחושב, נזרקת ArrayIndexOutOfBoundsException ושאר main ננטש: גישה מעבר לאינדקס החוקי האחרון של מערך היא אחת הדרכים הנפוצות ביותר לגרום לחריגה.

קריאת stack trace

כשחריגה לא מטופלת, מקבלים פלט כזה. הוא נראה מאיים, אבל הוא בסך הכול רשימה:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Main.divide(Main.java:8)
	at Main.main(Main.java:4)

קראו אותו מלמעלה למטה:

  • השורה הראשונה היא הסוג (ArithmeticException) וההודעה (/ by zero).
  • כל שורת at ... היא stack frame. העליונה היא המקום שבו החריגה נזרקה בפועל: divide בשורה 8. מתחתיה נמצא הקורא, main בשורה 4.

שורת ה-at העליונה היא המקום הראשון לבדוק. ה-frames שמתחתיה עונים על השאלה "איך הגענו לכאן?"

הריצו את זה, וה-trace יצביע ישר על השורה a / b, ואז יראה את main כקורא. שרשרת הקריאות הזו היא כלי הדיבוג השימושי ביותר ש-Java נותנת לכם בחינם.

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

כל חריגה היא אובייקט, וכולן יורשות מ-Throwable. שני הענפים החשובים:

  • Error: בעיות חמורות שה-JVM מעלה, כמו OutOfMemoryError או StackOverflowError. בדרך כלל לא תופסים אותן.
  • Exception: בעיות שהתוכנית יכולה לצפות ולטפל בהן באופן סביר. בתוכה נמצאת RuntimeException, מחלקת האב של הבאגים היומיומיים כמו NullPointerException.
Throwable
├── Error                 (don't catch: OutOfMemoryError, StackOverflowError)
└── Exception
    ├── IOException        (checked)
    ├── SQLException       (checked)
    └── RuntimeException   (unchecked)
        ├── NullPointerException
        ├── ArithmeticException
        └── ArrayIndexOutOfBoundsException

מכיוון שאלה מחלקות אמיתיות, חריגה יכולה לשאת הודעה ואפשר לשאול אותה על עצמה:

getMessage() מחזירה את הטקסט שאחרי הנקודתיים ב-stack trace; getClass().getSimpleName() נותנת את סוג החריגה לפי שם.

Checked מול Unchecked

זו ההבחנה שמכשילה חדשים יותר מכל.

  • חריגות unchecked מרחיבות את RuntimeException. הן מסמנות בדרך כלל באג בקוד שלכם: null שלא ציפיתם לו, אינדקס שגוי, חלוקה באפס. הקומפיילר לא מחייב אתכם לטפל בהן.
  • חריגות checked מרחיבות את Exception אבל לא את RuntimeException (למשל IOException). הן מייצגות מצבים שאינם בשליטתכם: קובץ חסר, חיבור רשת שנפל. הקומפיילר מחייב אתכם לתפוס אותן או להצהיר עליהן.

אם מתודה יכולה לזרוק checked exception, היא חייבת לומר זאת עם throws, וכל קורא חייב להתמודד עם זה:

הסירו את throws Exception מ-main והקוד אפילו לא יתקמפל: זה הקומפיילר שאוכף את החוזה של checked. חריגות unchecked אף פעם לא דורשות את זה.

לזרוק חריגות משלכם

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

תמיד כללו הודעה שמסבירה מה היה שגוי, ורצוי גם את הערך הבעייתי: אתם בעתיד, כשתקראו את ה-stack trace, תודו לעצמכם. IllegalArgumentException ו-IllegalStateException הן השתיים שתשתמשו בהן הכי הרבה כשמאמתים קלט.

מלכודות נפוצות

  • בליעת חריגות. תפיסת חריגה בלי לעשות כלום (בלוק ריק) מסתירה באגים. לכל הפחות תעדו אותה ביומן; בדרך כלל צריך לטפל בה או לזרוק אותה שוב.
  • תפיסה רחבה מדי של Exception. תפיסת Exception הבסיסית (או גרוע מזה, Throwable) יכולה להסתיר בעיות שלא התכוונתם לטפל בהן. תפסו את הסוג הספציפי שאתם מצפים לו.
  • קריאת ה-trace מלמטה למעלה. ה-frame הרלוונטי ביותר הוא שורת ה-at העליונה, לא התחתונה. התחילו שם.
  • בלבול בין Error ל-Exception. אל תנסו להתאושש מ-OutOfMemoryError או מ-StackOverflowError; תקנו את הגורם הבסיסי במקום.

הבא בתור: try-catch

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

שאלות נפוצות

מה זו exception ב-Java?

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

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

חריגות checked (תתי-מחלקות של Exception אבל לא של RuntimeException, למשל IOException) חייבות להיתפס או להיות מוצהרות עם throws, והקומפיילר אוכף את זה. חריגות unchecked (תתי-מחלקות של RuntimeException, למשל NullPointerException ו-ArrayIndexOutOfBoundsException) מסמנות בדרך כלל באגים בקוד ולא צריכות הצהרה. גם Error (כמו OutOfMemoryError) היא unchecked, ובדרך כלל לא אמורים לתפוס אותה.

איך קוראים stack trace ב-Java?

קוראים מלמעלה למטה. השורה הראשונה מציינת את סוג החריגה ואת ההודעה (למשל java.lang.ArithmeticException: / by zero). כל שורת at ... אחריה היא stack frame שמראה את המתודה, הקובץ ומספר השורה, החל מהמקום שבו נזרקה החריגה וחזרה דרך הקוראים. שורת ה-at הראשונה היא כמעט תמיד המקום הראשון לבדוק.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל