הקדמה
חריגה היא אירוע שגיאה שיכול לקרות במהלך ביצוע תכנית ומפריע לזרימתה הרגילה. ב-Java קיימת דרך יציבה ומבוססת עצם לטיפול במצבי חריגה שנקראת טיפול בחריגות ב-Java.
חריגות ב-Java יכולות להתרחש מסוגים שונים של מצבים כמו קלט שגוי של המשתמש, כשיבוש בחומת החומה, כשל בחיבור הרשת או שרת מסד נתונים שאינו פעיל. הקוד שמגדיר מה לעשות במצבי חריגה מסוימים נקרא טיפול בחריגות.
השלכת ותפיסת חריגות
ב-Java, נוצרת אובייקט חריגה כאשר אירעה טעות בביצוע הפקודה. אובייקט החריגה מכיל המון מידע ניפוי כמו היררכיה של המתודות, מספר השורה בה אירעה החריגה וסוג החריגה.
אם יש חריגה בתוך שיטה, התהליך של יצירת אובייקט החריגה והעברתו לסביבת ההרצה נקרא "השלכת החריגה". זרימת התוכנית הרגילה נעצרת וה-Java Runtime Environment (JRE) מנסה למצוא את טפס החריגה. מטפס החריגה הוא קטע קוד שיכול לעבד את אובייקט החריגה.
- הלוגיקה למציאת טפס החריגה מתחילה עם חיפוש בשיטה בה התרחשה השגיאה.
- אם לא נמצא טפס מתאים, החיפוש ימשיך לשיטת הקורא.
- וכך הלאה.
לכן, אם קונקס השיחה של השיטה הוא A->B->C
ויש חריגה בשיטה C
, אז החיפוש אחר טפס החריגה המתאים יתקדם מ-C->B->A
.
אם טפס החריגה המתאים נמצא, אז אובייקט החריגה יועבר לטפס כדי לעבד אותו. הטפס נחשב ל-"תופס את החריגה". אם לא נמצא טפס החריגה המתאים, אז התוכנית תסתיים ותדפיס מידע על החריגה לקונסול.
גרם הטיפול בחריגות ב-Java משמש לטיפול בשגיאות זמן ריצה בלבד. שגיאות זמן קומפילציה חייבות להיתקן על ידי מפתח התוכנה שכותב את הקוד, אחרת התוכנית לא תופעל.
מילות המפתח לטיפול בחריגות ב-Java
Java מספקת מילות מפתח ספציפיות למטרות טיפול בשגיאות.
-
השלכת – אנו יודעים כי אם חולה טעות, אובייקט חריגה נוצר ואז זירות הריצה של Java מתחילות לעבד אותן. לפעמים ייתכן שנרצה לייצר חריגות מפורשות בקוד שלנו. לדוגמה, בתוכנית אימות משתמש, עלינו לזרוק חריגות ללקוחות אם הסיסמה היא
null
. מילת המפתחthrow
משמשת לזריקת חריגות לזירות הריצה כדי לטפל בהן. -
הזריקה – כאשר אנו זורקים חריגה בשיטה ולא מטפלים בה, אז עלינו להשתמש במילת המפתח
throws
בחתימת השיטה כדי להודיע לתכנית הקוראת על החריגות שעשויות להיזרק על ידי השיטה. השיטה הקוראת עשויה לטפל בחריגות אלה או להעביר אותן לשיטת הקורא השלה באמצעות מילת המפתחthrows
. אנו יכולים לספק מספר חריגות במקטע ה-throws
, וניתן להשתמש בו עם השיטהmain()
גם. -
נסה-תפוס – אנו משתמשים בבלוק
try-catch
לטיפול בחריגות בקוד שלנו.try
הוא תחילת הבלוק ו-catch
נמצא בסופו של הבלוקtry
כדי לטפל בחריגות. אנו יכולים לקבוע מספר של בלוקיcatch
עם בלוקtry
. בלוק ה-try-catch
יכול להיות מקונן גם. בלוק ה-catch
מחייב פרמטר שצריך להיות מסוגException
. - לבסוף – הבלוק
finally
הוא אופציונלי וניתן להשתמש בו רק עם בלוקtry-catch
. מאחר וחריגה משהה את תהליך הביצוע, יתכן שיהיו לנו משאבים פתוחים שלא ייסגרו, לכן נוכל להשתמש בבלוקfinally
. הבלוקfinally
מתבצע תמיד, בין אם קרתה חריגה או לא.
דוגמת טיפול בחריגות
- השיטה
testException()
מזריקה חריגות באמצעות המילה השמורהthrow
. החתימה של השיטה משתמשת במילת המפתחthrows
כדי להודיע לקורא את סוגי החריגות שהיא עשויה לזרוק. - בשיטת ה-
main()
, אני מטפל בחריגות באמצעות בלוקtry-catch
בשיטת ה-main()
. כאשר אני לא מטפל בהן, אני מעביר אותן לזמן ריצה בעזרת פסקה המשמשת לכך בשיטת ה-main()
. - השיטה
testException(-10)
אף פעם לא תתבצע עקב החריגה ואז נתבצע הבלוקfinally
.
השיטה printStackTrace()
היא אחת השיטות המועילות במחלקת Exception
למטרות ניפוי שגיאות.
הקוד הזה יפליט את התוצאה הבאה:
Outputjava.io.FileNotFoundException: Negative Integer -5
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:24)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:10)
Releasing resources
Exception in thread "main" java.io.IOException: Only supported for index 0 to 10
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:27)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:19)
נקודות חשובות לשים לב אליהן:
- אין לנו אפשרות להשתמש בבלוק
catch
אוfinally
ללא הצהרתtry
. - A
try
statement should have eithercatch
block orfinally
block, it can have both blocks. - אין אפשרות לכתוב קוד בין בלוקי
try-catch-finally
. - ניתן להכיל מספר רב של בלוקי
catch
עם פקודתtry
אחת. - בלוקי
try-catch
יכולים להיות מקוננים בדומה להצהרותif-else
. - ניתן להכיל רק בלוק
finally
אחד עם הצהרתtry-catch
.
היררכיה של יוצאי חריגות ב-Java
כפי שנאמר מראש, כאשר חריגה נגרמת, נוצרת עצם חריגה. יוצאי החריגות ב-Java הם היררכיים ונעשה שימוש בהורשה כדי לקטגורז את סוגי החריגות השונים. Throwable
הוא המחלקה האב של היררכיית יוצאי החריגות ב-Java ויש לה שני אובייקטי ילד – Error
ו־Exception
. Exception
מחולקות נוספת ל-Checked Exception
s ול-Runtime Exception
s.
- שגיאות:
Error
s הן תרחישים חריגים שאינם בתחום היישום, ואי אפשר לצפות ולהתאושש מהם. לדוגמה, כשיש תקלה בחומרה, התמוטטות של מכונת ה-Java (JVM), או שגיאת אי-זיכרון. זה הסיבה שיש לנו היררכיה נפרדת שלError
s ואנו לא צריכים לנסות להתמודד עם מצבים אלה. כמה מה-Error
s הנפוצים הםOutOfMemoryError
ו־StackOverflowError
. - שגיאות מאומתות: שגיאות מאומתות הן תרחישים חריגים שאנו יכולים להניח בתוכנית ולנסות להתאושש מהם. לדוגמה,
FileNotFoundException
. עלינו לתפוס את החריג הזה ולספק הודעה שימושית למשתמש ולרשום אותה בצורה תקינה לצורך ניפוי שגיאות. ה-Exception
הוא המחלקה האב של כל ה-Exception
מאומתות. אם אנו מזיניםException
מאומתת, עלינו לתפוס אותה באותו השיטה, או שעלינו להעביר אותה לקורא באמצעות המילת מפתחthrows
. - שגיאות זמן ריצה: שגיאות זמן ריצה נגרמות על ידי תכנות רע. לדוגמה, ניסיון לאחזר איבר ממערך. עלינו לבדוק תחילה את אורך המערך לפני שננסה לאחזר את האיבר, אחרת זה עשוי לגרום ל-
ArrayIndexOutOfBoundException
בזמן ריצה.RuntimeException
הוא המחלקה האב של כל שגיאות הזמן הריצה. אם אנו מזינים שגיאת זמן ריצה בשיטה, אין צורך לציין אותן בעצם החתימה של השיטה באמצעות הביטויthrows
. ניתן למנוע שגיאות זמן ריצה עם תכנות יותר טוב.
כמה שיטות שימושיות של מחלקות החריגות
Java Exception
וכל תתי-מחלקותיה אינן מספקות שום שיטות ספציפיות, וכל השיטות מוגדרות במחלקת הבסיס – Throwable
. מחלקות ה-Exception
נוצרו כדי לציין סוגים שונים של תרחות Exception
כך שנוכל לזהות בקלות את הסיבה המרכזית ולטפל ב-Exception
לפי סוגו. מחלקת ה-Throwable
מיישמת את הממשק Serializable
לאינטרואופרביליות.
כמה מהשיטות השימושיות של מחלקת ה-Throwable
הן:
- public String getMessage() – שיטה זו מחזירה את ההודעה מסוג
String
שלThrowable
וההודעה יכולה להיספק בעת יצירת החריגה דרך בנאי החריגה שלה. - public String getLocalizedMessage() – שיטה זו ניתנת כך שתתת-מחלקות יכולות לדרוס אותה כדי לספק הודעה התואמת למקום נקרא. יישום המחלקה
Throwable
משתמש בשיטתgetMessage()
כדי להחזיר את ההודעה של החריגה. - public synchronized Throwable getCause() – שיטה זו מחזירה את הסיבה לחריגה או
null
אם הסיבה אינה ידועה. - public String toString() – שיטה זו מחזירה את המידע על
Throwable
בפורמט שלString
, ה-String
שמוחזר מכיל את שם מחלקת ה-Throwable
וההודעה המקומית. - public void printStackTrace()– מתודה זו מדפיסה את מידע מעקב המחסנית לזרם השגיאה התקני, מתודה זו מוטעית, וניתן להעביר
PrintStream
אוPrintWriter
כארגומנט כדי לכתוב את מידע מעקב המחסנית לקובץ או לזרם.
שיפורים בניהול משאבים אוטומטי ב-Java 7 ובלוק catch
אם אתה מתפס הרבה חריגות בבלוק try
אחד, תשים לב שקוד הבלוק catch
בדרך כלל מורכב מקוד חוזר לרישום השגיאה. ב-Java 7, אחת התכונות הייתה שיפור בבלוק catch
שבו אפשר לתפוס מספר חריגות בבלוק catch
אחד. הנה דוגמה לבלוק catch
עם תכונה זו:
קיימות מספר מגבלות כגון זה שאובייקט החריגה הוא סופי ואין אפשרות לשנות אותו בתוך בלוק catch
, קרא את הניתוח המלא ב שיפורים בבלוק ה-Catch של Java 7.
רוב הזמן, אנו משתמשים בבלוק finally
רק כדי לסגור את המשאבים. לפעמים אנו שוכחים לסגור אותם ומקבלים חריגות זמן ריצה כאשר המשאבים מוגמרים. חריגות אלו קשות לאיתור בדיקה, וייתכן כי נצטרך לחפש בכל מקום בו אנו משתמשים במשאב הזה כדי לוודא שסגרנו אותו. ב-Java 7, אחת השיפורים היה try-with-resources
בו יכולים ליצור משאב באותו ביטוי try
ולהשתמש בו בתוך בלוק try-catch
. כאשר הביצוע יוצא מהבלוק try-catch
, הסביבה הריצה מסגרת באופן אוטומטי את המשאבים אלו. הנה דוגמה לבלוק try-catch
עם השיפור הזה:
A Custom Exception Class Example
Java מספקת הרבה מחלקות חריגות שניתן להשתמש בהן, אך לפעמים עשוי להיות עלינו ליצור את המחלקות המותאמות שלנו. לדוגמה, כדי להודיע לקורא על סוג ספציפי של חריגה עם הודעה מתאימה. אנו יכולים לקבוע שדות מותאמים אישית למעקב, כגון קודי שגיאה. לדוגמה, נניח שאנו כותבים שיטה לעיבוד קבצי טקסט בלבד, כך שנוכל לספק לקורא קוד שגיאה מתאים כאשר סוג אחר של קובץ נשלח כקלט.
ראשית, יש ליצור MyException
:
לאחר מכן, יש ליצור CustomExceptionExample
:
ניתן להגדיר שיטה נפרדת לעיבוד סוגים שונים של קודי שגיאה שאנו מקבלים משיטות שונות. חלקם ייצרמו משום שאנו עשויים לא לרצות להודיע למשתמש על כך, או חלקם נזרוק חזרה כדי להודיע למשתמש על הבעיה.
הנה אני מרחיב על Exception
כך שכל פעם שהחרגה הזו נפלאת, יש לטפל בה בתוך השיטה או להחזירה לתכנית הקוראת. אם אנו מרחיבים את RuntimeException
, אין צורך לציין זאת ב-Clause throws
.
זו הייתה החלטת עיצוב. השימוש ב-Checked Exception
s מספק יתרון בסיוע למפתחים להבין אילו חרגים יש לצפות ולקבוע פעולות מתאימות לטיפול בהם.
Best Practices for Exception Handling in Java
- Use Specific Exceptions – במחלקות בסיס של ההיררכיה של Exception איןן ספק מידע שימושי, זו הסיבה שב-Java יש כל כך הרבה מחלקות חריגה, כמו למשל
IOException
עם מחלקות משנה כמוFileNotFoundException
,EOFException
, וכו'. תמיד עלינו להשתמש ב-throw
וב-catch
במחלקות חריגה ספציפיות כך שהקורא ידע בקלות את הסיבה הבסיסית של החרגה ויכול לעבד אותן. זה יעשה את תהליך התיקונים קל יותר ויסייע ליישמר את החרגות במקום. - Throw Early or Fail-Fast – עלינו לנסות להשליך חרגות בכמה שיותר מוקדם. נשמע, לדוגמה, בשיטת
processFile()
, אם נעביר את הפרמטרnull
לשיטה זו, נקבל את החרג הבא:
OutputException in thread "main" java.lang.NullPointerException
at java.io.FileInputStream.<init>(FileInputStream.java:134)
at java.io.FileInputStream.<init>(FileInputStream.java:97)
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:42)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
בזמן האיתור ותיקון שגיאות, יש להסתכל בקריאה לעקוב אחרי עקבות ה-Stack בקפידה כדי לזהות את המיקום המדויק של החריגה. אם נשנה את הלוגיקה של המימוש שלנו כך שנבדוק מוקדם על ידי הוספת קטעי קוד כמו שלמטה:
, אז יתכן שסטאק החריגה יצביע על המקום המדויק של החריגה עם הודעה ברורה:
Outputcom.journaldev.exceptions.MyException: File name can't be null
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:37)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
- Catch Late – מאחר ששפת Java דורשת מאיתנו לטפל בחריגות בדרך שהיא או להכריז עליהן בחתימת המתודה, לפעמים מפתחים יוכלו להשתמש במימוש כמו שמתואר כאן, בו נעטיף ב-
catch
חריגה ונרשום את השגיאה ללוג. אך הפרקטיקה הזו יכולה לגרום לנזק משום שהתכנית הקוראת אינה מקבלת הודעה אודות החריגה. עלינו להשתמש ב-catch
רק כאשר יש לנו את היכולת לטפל בחריגה באופן המתאים. לדוגמה, במתודה למעלה, אני משתמש ב-throw
כדי להעביר את החריגות חזרה למתודת הקורא על מנת שתטפל בהן. אפשר גם שתוכל להשתמש במתודה זו באפליקציות אחרות שיכולות לרצות לעבד את החריגה בדרך שונה. - Closing Resources – מאחר שחריגות משהות את העיבוד של התוכנית, עלינו לסגור את כל המשאבים בבלוק ה-
finally
או להשתמש בשדרוג Java 7 שלtry-with-resources
כדי שהריצה של Java תסגור אותם עבורנו. - רישום חריגות – תמיד כדאי לרשום את הודעות החריגה ובעת הזרקת חריגות לספק הודעה ברורה כך שהקורא יוכל לדעת בקלות למה התרחשה החריגה. כדאי גם להימנע מבלוק
catch
ריק שמטפל רק בחריגה ואינו מספק פרטים משמעותיים על החריגה למטרות דיבוג. - בלוק תפיסת חריגה אחד למספר חריגות – רוב הזמן אנו רושמים את פרטי החריגה ומספקים הודעה למשתמש, במקרה זה כדאי להשתמש בתכונת Java 7 לטיפול במספר חריגות בבלוק
catch
יחיד. גישה זו תפחית את גודל הקוד שלנו, וגם ייראה נקי יותר. - שימוש בחריגות מותאמות אישית – תמיד עדיף להגדיר אסטרטגיית טיפול בחריגות בזמן העיצוב ולאחר מכן, במקום לזרוק ולתפוס מספר חריגות, נוכל ליצור חריגה מותאמת אישית עם קוד שגיאה, והתוכנית הקוראת תוכל לטפל בקודי שגיאה אלה. גם רעיון טוב ליצור שיטת יוליטי לעיבוד קודי שגיאה שונים ולהשתמש בהם.
- תקני שמות ואריזה – כאשר אתה יוצר חריגה מותאמת אישית שלך, וודא שהיא מסתיימת ב-
Exception
כך שיהיה ברור מהשם עצמו שזו מחלקת חריגה. וגם, וודא שהם מארוזים כמו שנעשה בכיתת פיתוח Java (JDK). לדוגמה,IOException
היא החריגה הבסיסית לכל פעולות הקובץ. - השתמש בחריטות בזהירות – חריטות הן יקרות, ולפעמים אין צורך לזרוק חריטות כלל, וניתן להחזיר משתנה בוליאני לתכנית הקוראת כדי לציין האם הפעולה הייתה מוצלחת או לא. זה מועיל במקרים בהם הפעולה אופציונלית, ואין רצונך שהתוכנית שלך תתעקש כי היא נכשלה. לדוגמה, בעת עדכון מחירי המניות במסד הנתונים משירות אינטרנט של צד שלישי, ייתכן שתרצה להימנע מזריקת חריטות אם החיבור נכשל.
- תיעוד החריטות שנזרקות – השתמש ב-Javadoc
@throws
כדי לציין בבהירות את החריטות שנזרקות על ידי השיטה. זה מאוד מועיל כאשר אתה מספק ממשק ליישומים אחרים לשימוש.
מסקנה
במאמר זה, למדת על טיפול בחריטות ב-Java. למדת על throw
ו-throws
. גם למדת על בלוקי try
(ובלוקי try-with-resources
), catch
, ו־finally
.
Source:
https://www.digitalocean.com/community/tutorials/exception-handling-in-java