16

Java - OOP - Exceptions Handling

  • Upload
    -

  • View
    794

  • Download
    6

Embed Size (px)

DESCRIPTION

מאמר בנושא חריגות/שגיאות בתכנות מונחה עצמים בשפת ג'אווה.

Citation preview

Page 2: Java - OOP - Exceptions Handling

Exceptions Handling – יםטיפול בחריג

תכנות בשפת ג'אווה הפך המון מטלות מכניות אשר היו בשפות אחרות בידי המתכנת למנגנונים סגורים ובמקרים מסוימים,

במטרה לאפשר לו להתעסק בתכנות אף ל"קופסאות שחורות", אשר לקחו את האחריות מהידיים המסוכנות של המתכנת,

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

פעולות המתבצעות בתכנית.

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

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

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

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

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

המשתמש בכך שמקנה לו את האפשרות להזין ערכים ולמרות שזה קיבל הוראות מפורשות בנוגע לחוקיות ההזנה, או

מחוסר הבנה או בטעות, קרה ואותו המשתמש הזין ערך קלט שגוי.

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

הפניה נעשית לאינדקס שכך שמראש אנו יודעים לפני גישה למערך ת הקלטבדיקביצוע של לדוגמה: ,שגיאהאת המראש

.תכניתו/או שהערך המוזן לא חורג מההגבלות ב חוקי שאינו חורג מגבולות המערך שהוגדר

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

כמו כן ובשכפול קוד מסיבי במקרים מסוימים , , אך מלווה בכתיבת שורות קוד רבותאמנם וטכניקה זו אפשריתהאפשריות

בעבודה מיותרת מצד המתכנת.

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

.יםחריגב

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

public class ExceptionExample1 { public static void main(String[] args) { int arrayOfInt[] = {0, 1, 2}; for(int index=1; index <= arrayOfInt.length; index++) { System.out.println(arrayOfInt[index]); } } }

:ArrayIndexOutOfBoundsExceptionמהסוג הרצה של קטע קוד זה תניב חריגת זמן ריצה

Exception in thread "main" 1 2 java.lang.ArrayIndexOutOfBoundsException: 3 at ExceptionExample1.main(ExceptionExample1.java:9)

Exceptionsנשים לב שאנו לא דאגנו בעצמנו לדווח על הודעת השגיאה זו. הודעת שגיאה זו סונתזה עבורנו ע"י אותו מנגנון

שהזכרנו שקיים "בילד אין" בשפה.

כעת משראינו את צד המערכת, נעבור לדבר על תפקידו של המתכנת בכל הסיפור.

Page 3: Java - OOP - Exceptions Handling

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

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

ם מובנים בשפה.שאינולדעת להתמודד עם מימושים שנכתבו על ידי המתכנת

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

:, כתוצאה משגיאה או תקלהThrowableמחלקה ממחלקה שיורשת באופן זה או אחר מהאובייקט

מתודה של מחלקה כתובה, או באופן ידני ע"י המתכנת במתודה שהוא אותו האובייקט יכול להיווצר בצורה אוטומטית בתוך

כתב בעצמו.

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

צב קיצון במקרה של שגיאה ניתן לנסות ולטפל בתקלה, כמו כן, ניתן לאפשר למשתמש ניסיון נוסף להשתמש בתכנית ובמ

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

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

שלא ביצענו עד כה. התכנית

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

קריסה של התכנית יש "לתפוס" את החריגה שנזרקה.

. Try-Catchבלוק המנגנון שנועד ע"מ לטפל בחריגות שנזרקות בזמן הריצה נכתב בצורת

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

.catchיותר מסוג בלוק

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

Page 4: Java - OOP - Exceptions Handling

מתאימים: catch-ו tryנראה שיפור של הדוגמה הנ"ל תוך שילוב של בלוקי

public class ExceptionExample2 { public static void main(String[] args) { int arrayOfInt[] = {0, 1, 2}; for(int index=1; index <= arrayOfInt.length; index++) { try { System.out.println(arrayOfInt[index]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Index not in range!"); } } } }

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

1 2 Index not in range!

:ומוגדרת כך" try-catchנקראת "בלוק התבנית הכללית

try { //Code that might throw an exception... } catch(ExceptionOfType1 eot1) { //In case of SomeException1 do... } catch(ExceptionOfType2 eot2) { //In case of SomeException2 do... }//...

מאפשר "נקודת כניסה" לחריגה מהסוג בו הוא מטפל או ממחלקה שיורשות ממחלקה מהסוג בו הוא מטפל. catch -בלוק ה

טיפוסוהבלוק השני מקבל חריגה מ eot1 בשם ExceptionOfType1ס טיפומקבל חריגה מ במקרה זה לוק הראשוןהב

ExceptionOfType2 בשם eot2.

מתאים יותר בהמשך. catchהראשון שיכול לטפל בה, מבלי תלות באם קיים בלוק catch -החריגה תיתפס ע"י בלוק ה

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

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

ועוד. הלאה החריגה

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

מחוץ לבלוק. אל הוא לא יוכר יוגדר משתנה, -try ה בלוקובתוך

משתנים שיש להם משמעות גם מחוץ לבלוקר יהגד, ל-tryלפני הכניסה לבלוק ה: פתרון לבעיה זו

.null)יקבלו ולתת להם ערך התחלתי ריק כלשהו )אובייקטים

Page 5: Java - OOP - Exceptions Handling

finally

הפעולות המותנה של בסוף ביצוע אוסףבכל מקרה פעולות שיתבצעו הוא בלוק המאפשר הרצה של finally -בלוק ה

אם תיזרק חריגה והיא לא תיתפס וגם אם לא תיזרק , כלומר, גם אם תיזרק חריגה והיא תיתפס, גם try-catch-בלוק ה

חריגה בכלל.

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

הבלוק רץ בתהליכון והופעלה באותו התהליכון מתודתstop().

הופעלה המתודהSystem.exit() לתו של האשר גורמת להפסקת פעו-JVM.

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

.catch-הבלוקי אחרי סוף רשימת הוא בלוק רשות ואין חובת שימוש בו, אך באם הוא שומש, יש להציבו finally -בלוק ה

:נראה דוגמה לשימוש בו

public class ExceptionExample3 { public static void main(String[] args) { int arrayOfInt[] = {0, 1, 2}; for(int index=1; index <= arrayOfInt.length; index++) { try { System.out.println(arrayOfInt[index]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Index not in range!"); } finally { System.out.println("This will print anyway!"); } } } }

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

!try-והתכנית תמשיך את פעולתה משורת הקוד שאחריו ולא תחזור לבלוק הfinally -הקוד שבתוך בלוק ה

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

.catch בתוך גוף של בלוק returnנקודה מעניינת לציון היא מצב בו נעשה שימוש בפקודה

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

)כאן מתבקש כמובן לחשוב על מקרים בהם ומה שמעניין הוא שפעולתו תתבצע לאחר היציאה מהמתודה!finally -ה

(סיוםהבבלוק משתנה ולאחר היציאה מהמתודה הערך הזה Booleanאו Integerמתודה שמחזירה ערך מסוים נניח מטיפוס

טרם הסיום. finally-ה, ירוץ בלוק throw או break, continue-גם במקרים בהם ייעשה שימוש ב

Page 6: Java - OOP - Exceptions Handling

:יותר לשימוש במנגנון, כך תבנית כללית כתובניתן לאם כן,

try { //Code that might throw an exception... } catch(ExceptionOfType1 eot1) { //In case of ExceptionOfType1 do... } catch(ExceptionOfType2 eot2) { //In case of ExceptionOfType2 do... }… finally { //Do this part anyway!

}

throw

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

.של השפה תבמנגנון החריגו, תוך שימוש בתוכניתבהן יעזר כאמור, יש באפשרות המתכנת ליצור חריגות משלו ולה

:הבא פןואב throw זריקה של חריגה מתבצעת בעזרת הפקודה

throw new MyException();

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

if (!myObject.exists()) { throw new SQLException("Not Exist in DB!"); }

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

NullPointerException, ArrayIndexOutOfBoundsException-, נתקלנו כבר ב'אווה מכילות סוגי חריגות רביםבג

של חריגות. ומותאמים אישית גם סוגים חדשיםר ניתן ליצוכמו שכבר צויין, ו

לשם הבהירות וקלות הזיהוי רצוי לדבוק במנהג זהך , אחובהזה לא , Exceptionבסופו את המילהה מחלקהשם לרוב, יכלול

.חריגות תארותממחלקות ששל

:throw-דוגמאות נוספות לשימוש ב

throw new Exception(); throw new RunTimeException("Illegal Input");

Page 7: Java - OOP - Exceptions Handling

?מתי

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

אמיתות קטע קוד מסוים מתבררת כשקרית.

, אינן דוגמאות טובות לשימוש במנגנון, NULLשימוש במשתנה שמכיל או ,במערך חוקי לא לאיבר שראינו: פנייהדוגמאות ה

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

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

ואלו דוגמאות שנוח להתבונן בהן מבחינה ויזואלית ורעיונית.

על מנת לעשות את האבחנה בנוגע למתי יש צורך בשימוש בחריגות ומתי לא, נשאל את עצמנו האם התנהגות התכנית

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

נניח ונתונה מתודה שמקבלת איזושהי מחלקה שרירותית ותפקידה להחזיר תשובה האם אותה המחלקה יורשת ממחלקה

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

היה רשימה וגם אם לא... המתודה תמיד תדע להחזיר ערך. זוהי דוגמה למצב בו התנהגות המתודה ברורה!היא בוחנת ת

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

דה זו אכן רשימה... ולכן כאן, אורך הרשימה שסופקה למתודה, אך שאלה זו נשענת על ההנחה שמה שסופק למתו

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

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

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

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

אינו שונה מהחיסרון שנובע מכל שימוש במנגנונים אוטומטיים מובנים אחרים. של שימוש בחריגות חיסרוןה

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

לקבל ארוחות מוכנות מבלי צורך להכין את האוכל בעצמו, המחיר כמובן היה גבוה מהרגיל, כמו כן, נדרשה התראה מראש

הד" שנוצר עקב הרצון בארוחת שף מוכנה.-שלוותה בשיחת טלפון וטיפול בהנהלת חשבונות. זהו ה"אובר

הן בעצם חריגותעובדה שעקב ה ריצהה זמןיעילות פגיעה בו זיכרוןהוא עלות בשימוש במנגנון החריגות הד" שנוצר-"אוברה

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

.א פחותוקרי יותר ארוך קוד היא והתוצאה, catch ושל try של בלוקים

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

למקרה הצורך ולא בחריגות שומישנשמור את הובאותו האופן בדיוק, עדיף יהיה לו ללכת למטבח, לשבור את הביצה ולטגן

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

Page 8: Java - OOP - Exceptions Handling

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

import java.util.Scanner; public class ExceptionExample4 { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int age = 0; boolean isInteger = false; do { System.out.println("Enter patient age (without floating point): "); String input = scanner.next(); try { age = Integer.parseInt(input); isInteger = true; } catch(NumberFormatException e) { //Exception thrown when the input isn't an int isInteger = false; } } while(age < 0 || age > 120 || !isInteger); System.out.println("Patient's age: "+age); } }

את ע"מ לדאוג לכך שהמשתמש יזין parseInt מתודהשזורקת הבחריגות נעשה שימוש ExceptionExample4בדוגמה

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

(.פורמט)

Page 9: Java - OOP - Exceptions Handling

throws

Checked exceptions

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

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

-מהשלבים, אז ההחריגה לא נתפסה באף אחד במידה ובתקווה שיש מי שמחכה לתפוס ולטפל מבחוץ. במתודה אחרת

default handler שלJava קורסת"(. –יטפל בה )אלו המקרים בהם התכנית פשוט מסתיימת עם הודעת השגיאה"

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

. throwsהמילה השמורה

:תיראה כךצהרת המתודה ה. SomeExceptionזורקת חריגה מטיפוס אשר methodName נציג לדוגמה את המתודה

private void methodName() throws SomeException { }

יותר מטיפוס אחד:, יכולה מתודה לזרוק חריגות מבאופן דומה

private void methodName() throws IOException, SQLException { }

חייב לדאוג לתפיסה ישתמש במתודה מי שמרגע שהיא הוספה, כל היא שת מתודה, כרזהצהרה זו לה המשמעות של הוספת

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

.של אותה חריגה הלאה "זריקהשהמשמעות של כך היא "

Unchecked exceptions

הכרזה יםדורש םאינ ן, אשרמה היורשים וכל RunTimeException ומהטיפוס Error מהטיפוס חריגות הן הכלל מן ותיוצא

.throwsבאמצעות המילה השמורה

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

.זה לא בידיים שלנו –וכד' ולרוב לא דברים שאנו יכולים לשלוט בהם ולכן אין צורך להצהיר על תקלות מסוג זה JVM-בעיה ב

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

לזרוקנת ידאג לתכנת כראוי. לדוגמה: מתודות שעלולות בור חריגות מסוג זה. ההנחה היא שהמתכעעל הצהרה

NullPointerException או מתודה שמקבלת מהרשת קובץ ,XML...אך הפורמט השתנה ולכן כבר לא מתאים ,

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

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

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

קריא וקל לביצוע.

javadoc

על וזורקת המתודה חריגה שהדווח על ובכך י throws@, רצוי שיכיל את התגית תשזורקת חריגושל מתודה javadoc תיעוד

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

/** * @throws IOException if the file could not be read * @throws SQLException for any database related problem */ private void methodName() throws IOException, SQLException { }

Page 10: Java - OOP - Exceptions Handling

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

המחלקהמ יורשות הרגילות החריגות כלשמתארות חריגות, המתאר את היררכיית המחלקות UML-כפי שראינו בתרשים ה

Exception המחלקה מ שיורשות מחלקותמ אוException.

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

את הצורך לממש באופן אבסולוטי את ההצהרות שלנו ובאופן קצת פחות אבסטרקטי:

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

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

מטיפוסים חריגות גם סופיכול לת ,מסוים טיפוסשמיועד לתפוס חריגות מ catch בלוק, try-catch בלוק בתוךכמו כן,

מאותו טיפוס מסוים. יםשיורש

ובמהלך, תכנית המהווה משחק מחשב שכתבנו נניח -יותר, נתבונן בדוגמה הבאה על מנת להבין את הנושא בצורה טובה

סוגי החריגות הבאים: ב השתמשנו הפרויקט

public class GameServerException extends Exception {} public class GameProtocolException extends GameServerException {} public class PlayerException extends GameServerException {}

המתודה: כמו כן, נציין שקיימת

public void doSomething() throws GameServerException { }

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

.GameProtocolException ,PlayerException מחלקה שנמצאת נמוך ממנה בסולם הירושה: מטיפוס

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

המחלקה במקרה זהומחלקה שנמצאת גבוה ממנה בסולם הירושה מטיפוס או GameServerExceptionמהטיפוס

Exception (במידת הצורך, ניתן לתפוס בתוך הבלוק גם חריגות מטיפוס ProtocolException אוClientException.)

דריסה של מתודות שמצהירות על זריקת חריגות

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

זרקה. ,חייבת לזרוק את החריגות שהמתודה המקורית, אותה היא דרסה דורסת לאהמתודה ה

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

המתודה המקורית, או מטיפוסים שיורשים מאותם טיפוסים שזרקה המתודה המקורית.

ן לגיטימי לזרוק פחות חריגות מהמתודה אותה היא דרסה, אך היא איננה יכולה לזרוק יותר מתודה דורסת יכולה באופ

ממנה.

, המתודה הדורסת תוכל לזרוק את החריגות הבאות: ()doSomethingבדוגמה שלנו, אם נדרוס את המתודה

GameServerException ,)היות וזו החריגה שהמתודה המקורית זרקה(GameProtocolException ו-

PlayerException ות מהחריגה שהמתודה המקורית זרקה(.שיורש חריגות)היות ואלו

Page 11: Java - OOP - Exceptions Handling

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

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

try { doSomething(); } catch(GameProtocolException e) { } catch(GameServerException e) { } catch(Exception e) { }

היררכי סדר הכתיבה, אם נמנע מלשמור על סדר, כלומר, על פי -Code Flowנעשית על פי ה -catchהפנייה לבלוקי ההיות ו

הנעשה בתוכם לא בא להופכים חסרי משמעות והטיפו catchם אלו, נגיע למצב בו בלוקי בלוקיהפוך מבחינת המיקום של

:למשל בצורה הבאה, משמעותלידי שימוש והופך חסר

try { doSomething(); } catch(GameServerException e) { } catch(GameProtocolException e) { }

, GameProtocolExceptionמטיפוס חריגה תיזרק אם, GameServerException-יורשת מ GameProtocolException-ו היות

יהיה לא כלל GameProtocolException של הבלוק, כך משום ,(GameServerException שלזה ) הראשון בבלוק תיתפס היא

.נגיש

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

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

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

.שמתאים לטפל בחריגה catch, אחרת, המשמעות היא שלא קיים בלוק הירושה בסולם יותר

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

הירושה בסולם יותר נמוך במקום שנמצאות המחלקותיהיו נגישים, יש לדאוג למקם ראשית בלוקים עבור catch-ה בלוקי

, שהרי אם המקרה יהיה הפוך, מה שיקרה הוא מהם גבוה שנמצאות החריגות עבור תאימיםורק לאחר מכן בלוקים מ

ממוקם החריגה תיתפס בבלוק הראשון המתאים )מבחינת הורשה(, אך לא תגיע לקבל את הטיפול הייעודי לה בבלוק הש

.כך התוכנית לא תתקמפל ובשל נמוך יותר

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

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

באופן כזה נוכל לחסוך בקוד. -הטיפול

, אשר מהווה בסיס לחריגות IOExceptionדוגמה למקרה בו טכניקה כזו תהיה שימושית הוא תפיסה של חריגה מטיפוס

רבות שיורשות ממנה ומתארות כולן תקלות קלט/פלט של נתונים באופן הבא:

catch(IOException e) {

}

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

Page 12: Java - OOP - Exceptions Handling

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

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

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

, כך:מעלה ההיררכיה )מתודה אחרת למשל(החריגה את הטיפול הראשוני ולהעביר אותה להמשך טיפול ב

catch(PrinterIOException e) {

System.out.println("Printer I/O errors gets this message!");

throw e;

}

catch(PrinterException e) {

System.out.println("Other printer errors gets this message!");

}

:(והחריגה תיזרק שוב) , יודפס למסךPrinterIOExceptionובהנחה ונזרקה חריגה מטיפוס

Printer I/O errors gets this message!

:דוגמאות להמחשהובהתאם להיררכיית המחלקות הנתונה נראה נביט בתרשים המתאר ויזואלית את הנאמר עד כה

:rrorEבהנחה ונזרקה חריגה מטיפוס

Errorשתופס חריגות מטיפוס catchבלוק חריגה זו יוכל לתפוס

Throwableשתופס חריגות מטיפוס catchחריגה זו יוכל לתפוס בלוק

:MyFirstExceptionבהנחה ונזרקה חריגה מטיפוס

MyFirstExceptionשתופס חריגות מטיפוס catchחריגה זו יוכל לתפוס בלוק

Exceptionשתופס חריגות מטיפוס catchחריגה זו יוכל לתפוס בלוק

Throwableשתופס חריגות מטיפוס catchחריגה זו יוכל לתפוס בלוק

:MySecondExceptionבהנחה ונזרקה חריגה מטיפוס

MySecondException שתופס חריגות מטיפוס catchחריגה זו יוכל לתפוס בלוק

MyFirstExceptionשתופס חריגות מטיפוס catchחריגה זו יוכל לתפוס בלוק

Exceptionשתופס חריגות מטיפוס catchחריגה זו יוכל לתפוס בלוק

Throwableשתופס חריגות מטיפוס catchחריגה זו יוכל לתפוס בלוק

Page 13: Java - OOP - Exceptions Handling

חריגות מטיפוס חדש

לא תמיד נוכל להסתפק בחריגות המובנות במערכת על מנת לתאר כל סוג של תקלה. באפשרות המתכנת לכתוב מחלקות

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

, Checked Exception. במקרה שברצוננו ליצור Unchecked Exceptionאו חריגה מסוג Checked Exceptionחריגה מסוג

, עלינו ליצור מחלקה Unchecked Exceptionובמקרה שברצוננו ליצור Exceptionעלינו ליצור מחלקה שיורשת מהמחלקה

. RuntimeException-שיורשת מ

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

.מהמקורבאופן שונה

:על מנת להבין את הנושא בצורה טובה יותר, נתבונן בדוגמה הבאה

public class MyFirstException extends Exception { /** * Serial Number */ private static final long serialVersionUID = -7000000000000000000L; /** * Constructor with no parameters */ public MyFirstException() {} /** * @param arg0 Error message */ public MyFirstException(String arg0) { super(arg0); } /** * @param arg0 Nested exception */ public MyFirstException(Throwable arg0) { super(arg0); } /** * @param arg0 Error message * @param arg1 Nested exception */ public MyFirstException(String arg0, Throwable arg1) { super(arg0, arg1); } }

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

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

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

Page 14: Java - OOP - Exceptions Handling

" :No Exception" יםהמיל אתברצוננו לתאר אי זריקה של חריגה כמקרה חריג. במקרה זה, נוכל להוסיף ש, לדוגמה, נניח

.מהמחלקה שלנו שנזרקת חריגה כל של תיאור לפני

:נוסיף את המתודה, זה במקרה

private static String printNoException(String message) {

return "No Exception: "+message; }

:תודותבמ את השינויים הבאים בצעונ

public MySecondException(String message) {

super(printNoException(message)); } public MySecondException(String message, Throwable cause) {

super(printNoException(message), cause); }

כך שתתקבל הגדרת המחלקה הבאה:

public class MySecondException extends MyFirstException { /** * Serial Number */ private static final long serialVersionUID = -8000000000000000000L; /** * @param arg0 Error message */ public MySecondException(String message) { super(printNoException(message)); } /** * @param arg0 Error message * @param arg1 Nested exception */ public MySecondException(String message, Throwable cause) { super(printNoException(message), cause); } /** * @param message */ private static String printNoException(String message) { return "No Exception: "+message; } }

כעת, נשתמש בחריגה שיצרנו:

Page 15: Java - OOP - Exceptions Handling

public class ExceptionExample5 { public static void main(String[] args) { int arrayOfInt[] = {0, 1, 2}; for(int index=1; index <= arrayOfInt.length; index++) { try { System.out.println(arrayOfInt[index]);

throw new MySecondException("No array index out of bounds exception thrown yet!");

} catch (ArrayIndexOutOfBoundsException e) { System.out.println("\nIndex not in range!"); } catch (MySecondException e) { System.out.println("Second Exception Thrown:"); System.out.println(e.getClass()); System.out.println(e.getMessage()); } finally { System.out.println("This will print anyway!"); } } } }

כך שיתקבל הפלט:

1 Second Exception Thrown: class MySecondException No Exception: No array index out of bounds exception thrown yet! This will print anyway! 2 Second Exception Thrown: class MySecondException No Exception: No array index out of bounds exception thrown yet! This will print anyway! Index not in range! This will print anyway!

Page 16: Java - OOP - Exceptions Handling

– דוגמה נוספת )הוזכרה בקצרה קודם(למקרה שהדוגמה הקודמת הייתה "יבשה מדי", ברצוני לתת

:מה כללית רעיונית, המיועדת למטרת הבנת הקונספט בלבדזוהי סכי

ממקור ברשת ושואבת ממנו נתונים. XMLשמקבלת קובץ ()retrieve נניח וכתבנו מתודה

ההתחברות, הורדת –. לכאורה, הכול עבר בסדר ע"י המפתחים שמגיע מהשרת השתנה XML-פורמט קובץ השנניח כמו כן,

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

קיים, או שקיים תחת שם משתנה אחר...שאולי כבר לא

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

.נתון

נוכל לזרוק את החריגה באופן הבא: ()retrieveבתוך המתודה

if (!myObject.match(newObject)) { throw new XMLFormatException("XML files format doesn't match!"); }

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

להציב את נוכל ממקור אחר, או ביצוע המרה של הפורמט החדש לפורמט הקיים( ולכן, XML-)למשל, ע"י בקשה לקבלת ה

הקריאה למתודה כך:

try {

retrieve(); }

ואחריו להציב בלוק שיתפוס חריגות מסוג זה, באופן הבא:

catch (XMLFormatException e) { fix(newObject); }

סיכום

.נוספים מתודות ומשתניםולהוסיף מתודות בונות עמיסניתן לה -חבה של מחלקה אחרת כמו בכל הר

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

פעולות שמתאימות לביצוע ע"י מחלקות אחרות בתכנית.

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

התקלה ותתעד אותה בקובץ לוג מסודר.שתודיע על

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

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