מה זו 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 הראשונה היא כמעט תמיד המקום הראשון לבדוק.