חריגים ב- Java, חלק 2: תכונות וסוגים מתקדמים

JDK 1.0 הציג מסגרת של מאפייני שפה וסוגי ספריות להתמודדות עם חריגים , שהם סטייה מהתנהגות התוכנית הצפויה. המחצית הראשונה של הדרכה זו כיסתה את יכולות הטיפול החריגות הבסיסיות של Java. מחצית שנייה זו מציגה יכולות מתקדמות יותר המסופקות על ידי JDK 1.0 ויורשיה: JDK 1.4, JDK 7 ו- JDK 9. למד כיצד לצפות ולנהל חריגים בתוכניות Java שלך באמצעות תכונות מתקדמות כגון עקבות מחסניות, גורמים ושרשור חריג. נסה -עם משאבים, רב תפיסה, זריקה חוזרת אחרונה והליכה בערימה.

שים לב כי דוגמאות קוד במדריך זה תואמות ל- JDK 12.

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

טיפול בחריגים ב- JDK 1.0 וב- 1.4: עקבות ערימה

כל שרשור JVM (נתיב ביצוע) משויך לערימה שנוצרת בעת יצירת השרשור. מבנה נתונים זה מחולק למסגרות , שהם מבני נתונים המשויכים לשיחות שיטה. מסיבה זו מכונה לעתים קרובות מחסנית של כל שרשור כערימת שיחה .

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

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

הדפסת עקבות מחסנית

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

רישום 1. PrintStackTraceDemo.java(גרסה 1)

import java.io.IOException; public class PrintStackTraceDemo { public static void main(String[] args) throws IOException { throw new IOException(); } }

הדוגמה המתוכננת של רישום 1 יוצרת java.io.IOExceptionאובייקט ומשליכה את האובייקט הזה main()מהשיטה. מכיוון main()שאינו מטפל בזריקה זו, ומכיוון שזו main()השיטה ברמה העליונה, ה- JVM מסתיים במסר מתאים. עבור יישום זה תראה את ההודעה הבאה:

Exception in thread "main" java.io.IOException at PrintStackTraceDemo.main(PrintStackTraceDemo.java:7)

JVM פלטי הודעה זו על ידי התקשרות Throwableשל" void printStackTrace()השיטה, אשר מדפיסה מעקב ערימות של הפנייה Throwableאובייקט על זרם שגיאת התקן. השורה הראשונה מציגה את התוצאה של הפעלת toString()שיטת הזריקה . השורה הבאה מציגה נתונים שנרשמו בעבר fillInStackTrace()(נדון בהקדם).

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

Throwable"s עמוס void printStackTrace(PrintStream ps)ואת void printStackTrace(PrintWriter pw)שיטות פלט אחר מחסנית לזרם מסוים או הסופר.

עקבות הערימה חושפת את קובץ המקור ומספר השורה שבו נוצר הנגן. במקרה זה, הוא נוצר בשורה 7 של PrintStackTrace.javaקובץ המקור.

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

רישום 2. PrintStackTraceDemo.java(גרסה 2)

import java.io.IOException; public class PrintStackTraceDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { ioe.printStackTrace(); } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

רישום 2 מגלה main()שיטה המכנה שיטה a(), המכנה שיטה b(). השיטה b()זורקת IOExceptionאובייקט ל- JVM, שמבטל את ערימת קריאת השיטה עד שהוא מוצא main()את catchהחסימה, שיכולה להתמודד עם החריג. החריג מטופל באמצעות printStackTrace()קריאה לזריקה. שיטה זו מייצרת את הפלט הבא:

java.io.IOException at PrintStackTraceDemo.b(PrintStackTraceDemo.java:24) at PrintStackTraceDemo.a(PrintStackTraceDemo.java:19) at PrintStackTraceDemo.main(PrintStackTraceDemo.java:9)

printStackTrace()אינו פולט את שם השרשור. במקום זאת, היא toString()קוראת לזרוק להחזיר את שם הכיתה המושלם של הזריקה ( java.io.IOException), המוצג בשורה הראשונה. לאחר מכן היא מוציאה את ההיררכיה של קריאת המתודה: השיטה האחרונה ( b()) שנקראה לאחרונה main()נמצאת בחלק התחתון.

איזה קו מזהה עקבות הערימה?

מעקב הערימה מזהה את השורה שבה נוצר זריקה. זה לא מזהה את הקו אליו נזרק הזריקה (דרך throw), אלא אם כן הזריקה נזרקת על אותו קו שבו נוצר.

מילוי עקבות מחסנית

Throwableמצהיר על Throwable fillInStackTrace()שיטה הממלאת את עקבות ערימת הביצוע. Throwableבאובייקט המזמין , הוא מתעד מידע אודות המצב הנוכחי של מסגרות הערימה הנוכחיות של השרשור. שקול רישום 3.

רישום 3. FillInStackTraceDemo.java(גרסה 1)

import java.io.IOException; public class FillInStackTraceDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { ioe.printStackTrace(); System.out.println(); throw (IOException) ioe.fillInStackTrace(); } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

ההבדל העיקרי בין רישום 3 לרישום 2 הוא הצהרת catchהבלוק throw (IOException) ioe.fillInStackTrace();. הצהרה זו מחליפה ioeאת עקבות הערימה, ולאחריה נזרק הזריקה. עליכם להתבונן בפלט זה:

java.io.IOException at FillInStackTraceDemo.b(FillInStackTraceDemo.java:26) at FillInStackTraceDemo.a(FillInStackTraceDemo.java:21) at FillInStackTraceDemo.main(FillInStackTraceDemo.java:9) Exception in thread "main" java.io.IOException at FillInStackTraceDemo.main(FillInStackTraceDemo.java:15)

במקום לחזור על עקבות הערימה הראשונית, המזהה את המיקום בו IOExceptionנוצר האובייקט, עקבות הערימה השנייה חושפות את מיקומו של ioe.fillInStackTrace().

קונסטרוקטורים זורקים ו fillInStackTrace()

כל אחד Throwableמהבונים קורא fillInStackTrace(). עם זאת, הבנאי הבא (שהוצג ב- JDK 7) לא יפעיל שיטה זו כשעוברים falseאל writableStackTrace:

Throwable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)

fillInStackTrace()קורא לשיטה מקורית שמעבירה את ערימת המתודה-קריאה של השרשור הנוכחי כדי לבנות את עקבות הערימה. הליכה זו יקרה ויכולה להשפיע על הביצועים אם היא מתרחשת לעיתים קרובות מדי.

אם נקלעת למצב (אולי כרוך בהתקן משובץ) שבו הביצועים הם קריטיים, אתה יכול למנוע את בניית עקבות הערימה על ידי עקיפה fillInStackTrace(). בדוק את הרישום 4.

רישום 4. FillInStackTraceDemo.java(גרסה 2)

{ public static void main(String[] args) throws NoStackTraceException { try { a(); } catch (NoStackTraceException nste) { nste.printStackTrace(); } } static void a() throws NoStackTraceException { b(); } static void b() throws NoStackTraceException { throw new NoStackTraceException(); } } class NoStackTraceException extends Exception { @Override public synchronized Throwable fillInStackTrace() { return this; } }

רישום 4 מציג NoStackTraceException. מחלקת חריגים שנבדקה מותאמת אישית זו עוקפת fillInStackTrace()להחזרה this- התייחסות להנפקה Throwable. תוכנית זו מייצרת את הפלט הבא:

NoStackTraceException

הגב על fillInStackTrace()שיטת החלוף ותבחין בפלט הבא:

NoStackTraceException at FillInStackTraceDemo.b(FillInStackTraceDemo.java:22) at FillInStackTraceDemo.a(FillInStackTraceDemo.java:17) at FillInStackTraceDemo.main(FillInStackTraceDemo.java:7)

גישה לאלמנטים של עקבות מחסנית

לעיתים תצטרך לגשת לאלמנטים של עקבות מחסנית על מנת לחלץ פרטים הנדרשים לרישום, זיהוי המקור לדליפת משאבים ומטרות אחרות. printStackTrace()ואת fillInStackTrace()השיטות אינן תומכות משימה זו, אבל JDK 1.4 הציגה java.lang.StackTraceElementושיטותיו למטרה זו.

The java.lang.StackTraceElement class describes an element representing a stack frame in a stack trace. Its methods can be used to return the fully-qualified name of the class containing the execution point represented by this stack trace element along with other useful information. Here are the main methods:

  • String getClassName() returns the fully-qualified name of the class containing the execution point represented by this stack trace element.
  • String getFileName() returns the name of the source file containing the execution point represented by this stack trace element.
  • int getLineNumber() returns the line number of the source line containing the execution point represented by this stack trace element.
  • String getMethodName() returns the name of the method containing the execution point represented by this stack trace element.
  • boolean isNativeMethod() returns true when the method containing the execution point represented by this stack trace element is a native method.

JDK 1.4 also introduced the StackTraceElement[] getStackTrace() method to the java.lang.Thread and Throwable classes. This method respectively returns an array of stack trace elements representing the invoking thread's stack dump and provides programmatic access to the stack trace information printed by printStackTrace().

Listing 5 demonstrates StackTraceElement and getStackTrace().

Listing 5. StackTraceElementDemo.java (version 1)

import java.io.IOException; public class StackTraceElementDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { StackTraceElement[] stackTrace = ioe.getStackTrace(); for (int i = 0; i < stackTrace.length; i++) { System.err.println("Exception thrown from " + stackTrace[i].getMethodName() + " in class " + stackTrace[i].getClassName() + " on line " + stackTrace[i].getLineNumber() + " of file " + stackTrace[i].getFileName()); System.err.println(); } } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

When you run this application, you'll observe the following output:

Exception thrown from b in class StackTraceElementDemo on line 33 of file StackTraceElementDemo.java Exception thrown from a in class StackTraceElementDemo on line 28 of file StackTraceElementDemo.java Exception thrown from main in class StackTraceElementDemo on line 9 of file StackTraceElementDemo.java

לבסוף, JDK 1.4 הציגה את setStackTrace()השיטה ל- Throwable. שיטה זו נועדה לשימוש על ידי מסגרות קריאה לפרוצדורה מרחוק (RPC) ומערכות מתקדמות אחרות, המאפשרת ללקוח לעקוף את מעקב fillInStackTrace()המחסניות המוגדר כברירת מחדל שנוצר בעת בניית זרקור.

בעבר הראיתי כיצד לעקוף fillInStackTrace()כדי למנוע בניית עקבות מחסנית. במקום זאת, תוכל להתקין מעקב מחסנית חדש באמצעות StackTraceElementו- setStackTrace(). צור מערך של StackTraceElementאובייקטים שעברו אתחול באמצעות הבנאי הבא והעביר מערך זה אל setStackTrace():

StackTraceElement(String declaringClass, String methodName, String fileName, int lineNumber)

רישום 6 מדגים StackTraceElementו setStackTrace().