Java מספק גישה חזקה ואובייקטית לטפל במצבי יוצאים מן הכלל הידועים כמו שמירת יוצאים מן הכלל ב-Java. לפני זמן קצר כתבתי פוסט ארוך על שמירת יוצאים מן הכלל ב-Java והיום אני מפרט כמה שאלות חשובות שאלות יוצאים מן הכלל ב-Java עם תשובות כדי לעזור לך ברישיונות.
- מהו יוצא מן הכלל ב-Java?
- מהן מילות המפתח של שמירת יוצאים מן הכלל ב-Java?
- הסבר על היררכיה של יוצאים מן הכלל ב-Java?
- מהן השיטות החשובות של מחלקת היוצאים מן הכלל ב-Java?
- הסבר על תכונת ARM ב-Java 7 ובלוק ריבוי תפיסה?
- מה ההבדל בין יוצאים מן הכלל בודקים ובין יוצאים מן הכלל לא בודקים ב-Java?
- מה ההבדל בין המילה והזכות ב-Java?
- איך לכתוב יוצאים מן הכלל מותאמים אישית ב-Java?
- מהו OutOfMemoryError ב-Java?
- מהם תרחישים שונים הגורמים ל"יוצא מן הכלל בסבב הראשי"?
- מה ההבדל בין סופי, לבסוף ולסיים ב-Java?
- מה קורה כשיוצאת שגיאה מהשיטה הראשית?
- האם נוכל להשיג קובץ לוקח ריק?
- ספק כמה טיפים לעיסוק בשגיאות ב-Java?
- מה הבעיה עם התוכניות הבאות ואיך אנו מתקנים אותן?
1. מהי שגיאה ב-Java?
שגיאה היא אירוע שגיאה שיכול לקרות במהלך הביצוע של תוכנית ומשבש את הזרם הרגיל שלה. השגיאה יכולה להיווצר ממצבים שונים כמו נתונים שגויים שנכנסו על ידי המשתמש, כשל בחומרה, כשל בחיבור רשת, וכו'. בכל פעם שכל סוג של שגיאה מתרחשת בעת ביצוע משפט ב-Java, נוצרת אובייקט שגיאה, ואז JRE מנסה למצוא מטפל שגיאה כדי לטפל בשגיאה. אם מצא מטפל שגיאה מתאים אז האובייקט שגיאה מועבר לקוד המטפל כדי לעבד את השגיאה, הידוע כ-לתפוס את השגיאה. אם לא נמצא מטפל שגיאה אז היישום זורק את השגיאה לסביבת הריצה ו-JRE מסיים את התוכנית. ממדיניות עיסוק בשגיאות ב-Java משמשת לטפל בשגיאות ביצוע בלבד, שגיאות קומפילציה אינן נטועות על ידי ממדיניות עיסוק בשגיאות.
2. מהם מילות המפתח לטיפול בחריגות ב-Java?
קיימות ארבע מילות מפתח המשמשות בטיפול בחריגות ב-Java.
- throw: לעיתים קרובות אנו רוצים ליצור מפעיל חריגה באופן מפורש ולאחר מכן לזרוק אותו כדי לעצור את עיבוד התוכנית הרגיל. מילת המפתח throw משמשת לזריקת חריגות לריצה כדי לטפל בהן.
- throws: כאשר אנו מזרים חריגה מאומתת בשיטה ולא מטפלים בה, אז עלינו להשתמש במילת המפתח throws בחתימת השיטה כדי להודיע לתוכנית הקוראת את החריגות שעשויות להיות מזורקות על ידי השיטה. השיטה הקוראת עשויה לטפל בחריגות אלה או להעביר אותן לשיטת הקוראת שלה באמצעות מילת המפתח
throws
. ניתן לספק מספר חריגות במשפט throws וניתן להשתמש בו עם שיטת main(). - try-catch: אנו משתמשים בבלוק try-catch לטיפול בחריגות בקוד שלנו. try הוא ההתחלה של הבלוק ו-catch הוא בסופו של בלוק ה-try כדי לטפל בחריגות. ניתן להכיל מספר בלוקי catch עם try ובלוקי try-catch יכולים להיות מקוננים גם כן. בלוק catch מחייב פרמטר הצריך להיות מסוג Exception.
- סוף סוף: בלוק ה finally הוא אופציונלי וניתן להשתמש בו רק עם בלוק try-catch. מאחר שחריגה מפסיקה את תהליך הביצוע, ייתכן ויהיו לנו משאבים פתוחים שלא ייסגרו, לכן אנו יכולים להשתמש בבלוק finally. בלוק finally מופעל תמיד, בין אם חריגה מתרחשת או לא.
3. הסבר את היררכיית החריגות ב-Java?
היוצא מן הכללים של Java הם היררכיים ומורשים משמשים לסווג סוגים שונים של יוצאי חריגה. Throwable
הוא המחלקה האב של ההיררכיה של יוצאי החריגה של Java ויש לו שני אובייקטים ילד – Error
ו־Exception
. יוצאי החריגה מחולקים גם ליציאות שנבדקות וליציאות של זמן ריצה. שגיאות הם תרחישים יוצאי דופן שאינם בטווח של היישום ואי אפשר לצפות ולהתאושש מהם, לדוגמה, כשל בחומרה, קריסת JVM או שגיאת אי זכרון. יציאות שנבדקות הן תרחישים יוצאי דופן שאנו יכולים לצפות בהם בתוך התוכנית ולנסות להתאושש מהם, לדוגמה, FileNotFoundException. עלינו לתפוס את היצוא הזה ולספק הודעה שימושית למשתמש ולשמור על זה כהלכה לצורך ניפוי שגיאות. Exception
הוא המחלקה האב של כל היציאות הנבדקות. יציאות של זמן ריצה נגרמות על ידי תכנות רע, לדוגמה, ניסיון לאחזר איבר מהמערך. עלינו לבדוק את אורך המערך תחילה לפני שננסה לאחזר את האיבר אחרת זה עשוי לזרוק ArrayIndexOutOfBoundException
בזמן ריצה. RuntimeException
הוא המחלקה האב של כל יציאות הזמן הריצה.
4. מהם שיטות החשיבות של מחלקת יוצאות חריגה של Java?
יוצאת חריגה וכל המחלקות המשניות שלה אינן מספקות שום שיטה מסוימת וכל השיטות מוגדרות במחלקת הבסיס Throwable.
- String getMessage() – שיטה זו מחזירה את מחרוזת ההודעה של Throwable וההודעה יכולה להיות מסופקת במהלך יצירת החריגה דרך בנאי החריגה שלה.
- String getLocalizedMessage() – שיטה זו מסופקת כך שמחלקות משנה יכולות לדרוס אותה כדי לספק הודעות ספציפיות למיקום לקוחי לתוכנית הקוראת. מימוש המחלקה Throwable של שיטה זו פשוט משתמש בשיטת
getMessage()
כדי להחזיר את ההודעה של החריגה. - synchronized Throwable getCause() – שיטה זו מחזירה את הסיבה לחריגה או null אם הסיבה לא ידועה.
- String toString() – שיטה זו מחזירה את המידע אודות Throwable בתבנית מחרוזת, המחרוזת המוחזרת מכילה את שם מחלקת התקלה והודעת האזור.
- void printStackTrace() – שיטה זו מדפיסה מידע אודות מעקב המחסנית לנתוני הזרימה התקניים של השגיאה, שיטה זו מוטענת וניתן להעביר PrintStream או PrintWriter כארגומנט כדי לכתוב את מידע מעקב המחסנית לקובץ או לנתוני הזרימה.
הסבר את יכולת ה-ARM ב-Java 7 ובלוק התפיסה הרב-תפקידי?
אם אתה תופס מספר רב של חריגות בבלוק try יחיד, תגלה שקוד בלוק התפיסה נראה כברה ובעיקר מורכב מקוד מיותר לייעוץ על השגיאה. לתת דגש על כך, יתרון אחד של Java 7 היה בלוק התפיסה הרב-תפקידי שבו ניתן לתפוס מספר חריגות בבלוק תפיסה יחיד. בלוק התפיסה עם תכונה זו נראה כמו בהמשך:
catch(IOException | SQLException | Exception ex){
logger.error(ex);
throw new MyException(ex.getMessage());
}
לרוב, אנו משתמשים בבלוק finally כדי לסגור את המשאבים ולפעמים שוכחים לסגור אותם ומקבלים חריגות בזמן ריצה כאשר המשאבים נגמרים. חריגות אלה קשות לניפוק וייתכן שנצטרך לחפש בכל מקום שאנו משתמשים בסוג המשאב הזה כדי לוודא שאנו סוגרים אותו. לכן, אחת משדרוגי Java 7 הייתה יכולת נסיון עם משאבים שבו ניתן ליצור משאב בהצהרת הנסיון עצמה ולהשתמש בו בתוך בלוק התפיסה try-catch. כאשר הביצוע יוצא מבלוק התפיסה try-catch, הסביבה בריצה יסגור אוטומטית את המשאבים האלה. דוגמה לבלוק try-catch עם שדרוג זה היא:
try (MyResource mr = new MyResource()) {
System.out.println("MyResource created in try-with-resources");
} catch (Exception e) {
e.printStackTrace();
}
למידע נוסף על כך, ראה Java 7 ARM.
6. מה ההבדל בין חריגות שנבדקות לבין חריגות שאינן מנובדות ב-Java?
- חריגות שנבדקות צריכות להיטפל בהן בקוד באמצעות בלוק try-catch או שהשיטה צריכה להשתמש במילת המפתח throws כדי להודיע לקורא על החריגות שנבדקות שעשויות להתקיים מהשיטה. חריגות שאינן מנובדות אינן דרושות להיטפל בהן בתוכנית או להזכירן במקטע ה-throws של השיטה.
Exception
היא מערכת האב של כל החריגות שנבדקות בעוד ש־RuntimeException
היא מערכת האב של כל החריגות שאינן מנובדות. חשוב לציין ש-RuntimeException היא מחלקה משנה של Exception.- חריגות שנבדקות הן מצבי שגיאה שדורשים להתמודד עם הם בקוד, אחרת יתקבל שגיאת זמן הקומפילציה. לדוגמה, אם משתמשים ב־FileReader כדי לקרוא קובץ, הוא זורק
FileNotFoundException
ואנו חייבים לתפוס אותו בבלוק try-catch או לזרוק אותו שוב לשיטת הקורא. חריגות שאינן מנובדות נגרמות בעיקר על ידי תכנות רע, לדוגמה, NullPointerException כאשר מפעילים שיטה על ידי הפנייה לאובייקט בקישור שאינו מבודד כי הוא אינו מבודד כשאנו נרגשים על ידי הפסקת המחרוזת. לדוגמה, אני יכול לכתוב שיטה שמסירה את כל התווים הקולניים מהמחרוזת. זהו אחריות הקורא לוודא שאינו מעביר מחרוזת ריקה. אוכל לשנות את השיטה כדי להתמודד עם סטים אלה אבל זהירות, הקורא צריך לטפל בכך.
7. מה ההבדל בין מילת המפתח throw לבין המילת המפתח throws ב-Java?
מילת המפתח throws משמשת עם החתימה של השיטה כדי להכריז על החריגויות שהשיטה עשויה לזרוק בעוד שמילת המפתח throw משמשת כדי להפריע לזרימת התוכנית ולהעביר את אובייקט החריגה לטפסים בזמן ריצה כדי לטפל בו.
8. כיצד לכתוב חריגות מותאמות אישית ב-Java?
ניתן להרחיב את מחלקת Exception או כל אחת ממחלקותיה כדי ליצור מחלקת חריגה מותאמת אישית. מחלקת החריגה המותאמת אישית יכולה להכיל משתנים ושיטות משלה כדי להעביר קודי שגיאה או מידע קשור לחריגה אחרת לטפסים המטפלים בחריגה. דוגמה פשוטה לחריגה מותאמת אישית מוצגת למטה.
package com.journaldev.exceptions;
import java.io.IOException;
public class MyException extends IOException {
private static final long serialVersionUID = 4664456874499611218L;
private String errorCode="Unknown_Exception";
public MyException(String message, String errorCode){
super(message);
this.errorCode=errorCode;
}
public String getErrorCode(){
return this.errorCode;
}
}
9. מהו OutOfMemoryError ב-Java?
OutOfMemoryError ב-Java הוא מחלקה משנה של java.lang.VirtualMachineError והוא מושלך על ידי JVM כאשר נגמרה לו זיכרון ערימה. ניתן לתקן שגיאה זו על ידי מספקים יותר זיכרון כדי להפעיל את היישום ה-Java באמצעות אפשרויות ה-Java. $>java MyProgram -Xms1024m -Xmx1024m -XX:PermSize=64M -XX:MaxPermSize=256m
10. מהם התרחשויות שונות שגורמות "Exception in thread main"?
חלק מהתרחשויות הנפוצות של חריגת תהליך הראשי הן:
- חריגה בתהליך הראשי java.lang.UnsupportedClassVersionError: חריגה זו מתרחשת כאשר הקובץ ה-Java שלך מאוביין מגרסת JDK אחרת ואתה מנסה להריץ אותו מגרסת Java אחרת.
- חריגה בתהליך הראשי java.lang.NoClassDefFoundError: ישנם שני וריאנטים של חריגה זו. הראשון הוא כאשר אתה מספק את שם המחלקה המלא עם סיומת .class. הסצנריו השני הוא כאשר לא נמצאה את המחלקה.
- חריגה בתהליך הראשי java.lang.NoSuchMethodError: main: חריגה זו מתרחשת כאשר אתה מנסה להריץ מחלקה שאין לה את השיטה הראשית.
- חריגה בתהליך "ראשי" java.lang.ArithmeticException: כל פעם שיש חריגה מסוג זה מתבצעת מהשיטה הראשית, היא מדפיסה את החריגה בקונסול. החלק הראשון מסביר שיש חריגה מתוך השיטה הראשית, החלק השני מדפיס את שם מחלקת החריגה ואז אחרי נקודותיה, הוא מדפיס את הודעת החריגה.
קרא עוד על זה ב-Java Exception in thread main.
11. מה ההבדל בין final, finally, ו- finalize ב-Java?
final ו-finally הם מילות מפתח ב-Java בעוד finalize הוא שיטה. מילת המפתח final יכולה להיות בשימוש עם משתני מחלקה כך שלא יתאפשר שינוי בהם מאוחר יותר, עם המחלקה כדי למנוע ממחלקות אחרות להרחיב אותה, ועם שיטות כדי למנוע דריסה על ידי מחלקות מורשות, finally משמשת כמילת מפתח עם בלוק try-catch כדי לספק פקודות שתמיד יופעלו גם אם יש חריגה, נהוג לסגור משאבים באמצעות finally. שיטת finalize() מופעלת על ידי אוסף הזבל לפני שהאובייקט מושמד, זו דרך נהדרת לוודא שכל המשאבים הגלובליים נסגרים. מתוך השלוש, רק finally קשורה לטיפול בחריגות ב-Java.
12. מה קורה כאשר יוצאת חריגה על ידי השיטה הראשית?
כאשר חריגה נזרקת על ידי שיטת main(), הרצת Java מסיימת את התוכנית ומדפיסה את ההודעה על החריגה ואת מעקב הערימה בקונסולת המערכת.
13. האם יכולים להיות לנו בלוק catch ריק?
יכולים להיות לנו בלוק catch ריק אך זהו דוגמה לתכנות רע. לעולם לא נעשה בלוק catch ריק מכיוון שאם החריגה נתפסת על ידי הבלוק הזה, לא תהיה לנו מידע אודות החריגה וזה יהיה ניסיון מערכתי לאיתור הבאג. עלינו לכתוב לפחות הצהרת לוג להזנת פרטי החריגה בקונסולת או בקבצי לוג.
14. ספק כמה פרקטיות טיפול בחריגות ב-Java?
כמה מהפרקטיות הטובות הקשורות לטיפול בחריגות ב-Java הם:
- השתמש בחריגות ספציפיות לקלות באיתור הבאגים.
- זרוק יוצאות בשלב מוקדם (Fail-Fast) בתוכנית.
- תפוס יוצאות בשלב מאוחר בתוכנית, תן לקורא לטפל ביוצאת.
- השתמש בתכונת ARM של Java 7 כדי לוודא שמשאבים נסגרים או השתמש בבלוק finally כדי לסגור אותם באופן תקין.
- רשום תמיד הודעות יוצאת לצורך ניפוי שגיאות.
- השתמש בבלוק multi-catch כדי לקבל קוד סגור יותר.
- השתמש ביוצאות מותאמות אישית כדי לזרוק סוג יחיד של יוצאת מ-API של היישום שלך.
- עקוב אחרי כללי הקריאה, תמיד הסתיים עם Exception.
- תיעוד את היוצאות שמוזרקות על ידי שיטת השימוש ב-@throws ב-Javadoc.
- יוצאות הן יקרות, לכן זרוק אותן רק כאשר יש משמעות. אחרת תוכל לתפוס אותם ולספק תגובה ריקה או ריקה.
קרא עוד עליהם בפרטים ב-שיטות המיטוב של טיפול ביציאות של Java.
15. מה הבעיה עם התוכניות למטה וכיצד נתקן אותן?
במקטע זה, נבחן שאלות תכנות הקשורות ליישוב יציאות של Java.
-
מה הבעיה עם התוכנית למטה?
package com.journaldev.exceptions; import java.io.FileNotFoundException; import java.io.IOException; public class TestException { public static void main(String[] args) { try { testExceptions(); } catch (FileNotFoundException | IOException e) { e.printStackTrace(); } } public static void testExceptions() throws IOException, FileNotFoundException { } }
התוכנית למעלה לא תעזוב להידרוס ותקבל הודעת שגיאה "החריג FileNotFoundException כבר נלכד על ידי IOException האלטרנטיבי". זה בגלל ש FileNotFoundException הוא מחלקה משנה של IOException, ישנם שני דרכים לפתור את הבעיה הזו. הדרך הראשונה היא להשתמש בבלוק תפיסה יחיד עבור שני החריגים.
try { testExceptions(); } catch(FileNotFoundException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
דרך אחרת היא להסיר את FileNotFoundException מבלוק התפיסה הרב-תפקודי.
try { testExceptions(); } catch (IOException e) { e.printStackTrace(); }
אפשר לבחור כל אחת מהשיטות הללו על פי קוד בלוק התפיסה שלך.
-
מה הבעיה עם התוכנית שמטה?
package com.journaldev.exceptions; import java.io.FileNotFoundException; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException1 { public static void main(String[] args) { try { go(); } catch (IOException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (JAXBException e) { e.printStackTrace(); } } public static void go() throws IOException, JAXBException, FileNotFoundException{ } }
התוכנית לא תהיה מסודרת מראש מאחר וFileNotFoundException הוא מחלקה משנה של IOException, לכן בלוק התפיסה של FileNotFoundException הוא לא נגיש ותקבל הודעת שגיאה "בלוק התפיסה לא נגיש עבור FileNotFoundException. זה כבר טופל על ידי בלוק התפיסה ל IOException". עליך לתקן את סדר בלוקי התפיסה כדי לפתור את הבעיה.
try { go(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JAXBException e) { e.printStackTrace(); }
שים לב ש-JAXBException אינה קשורה ל IOException או FileNotFoundException וניתן להשתמש בה בכל מקום בהיררכיה של בלוקי התפיסה לעיל.
-
מה הבעיה עם התוכנית למטה?
package com.journaldev.exceptions; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException2 { public static void main(String[] args) { try { foo(); } catch (IOException e) { e.printStackTrace(); }catch(JAXBException e){ e.printStackTrace(); }catch(NullPointerException e){ e.printStackTrace(); }catch(Exception e){ e.printStackTrace(); } } public static void foo() throws IOException{ } }
התוכנית לא תהיה מזהה כי JAXBException היא חריגה בדיקה והמתודה foo() צריכה לזרוק את החריגה הזו כדי לתפוס אותה במתודה הקוראת. תקבל הודעת שגיאה "בלוק התפיסה של JAXBException אינו נגיש. החריגה הזו אף פעם לא מוזרקת מתוך גוף ההצהרה try". כדי לפתור את הבעיה, עליך להסיר את בלוק התפיסה של JAXBException. שים לב שתפיסת NullPointerException היא חוקית מאחר וזו חריגה בדקתית.
-
מה הבעיה עם התוכנית למטה?
package com.journaldev.exceptions; public class TestException3 { public static void main(String[] args) { try{ bar(); }catch(NullPointerException e){ e.printStackTrace(); }catch(Exception e){ e.printStackTrace(); } foo(); } public static void bar(){ } public static void foo() throws NullPointerException{ } }
זו שאלת טריק, אין בעיה עם הקוד והוא יהיה מהודר בהצלחה. תמיד אפשר לתפוס חריגה או כל חריגה שאינה בדיקה אפילו אם היא לא כתובה בתקן throws של השיטה. באופן דומה, אם שיטה (foo) מכריזה על חריגה שאינה בדיקה בתקן throws, זה לא חובה לטפל בה בתוכנית.
-
מה הבעיה עם התוכנית למטה?
package com.journaldev.exceptions; import java.io.IOException; public class TestException4 { public void start() throws IOException{ } public void foo() throws NullPointerException{ } } class TestException5 extends TestException4{ public void start() throws Exception{ } public void foo() throws RuntimeException{ } }
התוכנית לעיל לא תיתרגם משום שחתימת השיטה start() אינה זהה בתת-מחלקה. כדי לתקן את הבעיה, אפשר לשנות את חתימת השיטה בתת-המחלקה כך שתהיה בדיוק זהה לזו במחלקת האב או להסיר את ההצהרת throws משיטת התת-המחלקה כפי שמוצג למטה.
@Override public void start(){ }
-
מה הבעיה עם התוכנית למטה?
package com.journaldev.exceptions; import java.io.IOException; import javax.xml.bind.JAXBException; public class TestException6 { public static void main(String[] args) { try { foo(); } catch (IOException | JAXBException e) { e = new Exception(""); e.printStackTrace(); }catch(Exception e){ e = new Exception(""); e.printStackTrace(); } } public static void foo() throws IOException, JAXBException{ } }
התוכנית לעיל לא תידרוג מאחר ואובייקט החריגה בבלוק multi-catch הוא סופי ואין אפשרות לשנות את ערכו. תקבלו שגיאת זמן קומפילציה "לא ניתן להקצות לפרמטר e של בלוק multi-catch". עלינו להסיר את ההקצאה של "e" לאובייקט חריגה חדש כדי לפתור את השגיאה הזו. קרא עוד ב- בלוק multi-catch של Java 7.
זהו כל מה שיש לי לשאלות הראיונות בנושא שגיאות ג'אווה, אני מקווה שתאהב אותן. אני אוסיף עוד שאלות לרשימה בעתיד, ואנא וודא שתסמן אותה כסימנייה לשימוש עתידי.
Source:
https://www.digitalocean.com/community/tutorials/java-exception-interview-questions-and-answers