JavaRush /בלוג Java /Random-HE /סטאק טרייס ועם מה הוא נאכל
Alukard
רָמָה
London

סטאק טרייס ועם מה הוא נאכל

פורסם בקבוצה
במאמר זה תלמדו ותבינו כיצד פועלת תופעת Java StackTrace, הידועה גם בשם Call Stack Tracing. מידע זה נבנה עבור מתחילים שנתקלו במושג זה בתחילת תחביר Java Level 9. אני חושב שכולכם, לפחות פעם אחת, נתקלתם בשגיאות דומות בעת העבודה ב-IDE שלכם, ללא קשר אם זה היה Idea , Eclipse או משהו אחר.
Exception in thread "main" java.lang.ArithmeticException
	at com.example.task01.Test.division(Test.java:10)
	at com.example.task01.Test.main(Test.java:6)
זה, כפי שאולי ניחשתם, המעקב שלנו. אבל אל תמהרו להיכנס לפאניקה, עכשיו נשבור עבורכם את הדוגמה הזו. ראשית עליך להבין את העובדה שהוא StackTraceעובד כפי Стэкשהשם שלו מרמז. בשלב זה נתעכב קצת יותר בפירוט. כיצד פועל אוסף Stack ברמה השמינית כבר התוודעתם לאוספים ויודעים שהם מחולקים לשלוש קבוצות Set- סט, List- רשימה, Map- מילון (או מפה). על פי JavaRush (ג). שלנו Stackהוא חלק מהקבוצה List. ניתן לתאר את עקרון פעולתו כ- LIFO , אשר מייצג Last In First Out. כלומר, זוהי רשימה דומה לערימה של ספרים; כדי לקחת את האלמנט שהכנסנו Stackראשון, עלינו לחלץ תחילה את כל האלמנטים שהוספנו לרשימה שלנו לאחר מכן. כפי שצוין בתמונה למעלה, בניגוד למשל לרשימה רגילה ArrayListשבה נוכל לקבל כל אלמנט מהרשימה לפי אינדקס. שוב לחיזוק. השגת אלמנט Стэкаמתאפשרת רק מהסוף! בעוד שהאלמנט הראשון שנוסף לו הוא בהתחלה (או בתחתית, לפי מה שנוח יותר). אלו השיטות שיש Stack לאובייקט שלנו push()- מוסיף אלמנט לראש הערימה. אובייקט pop()- מחזיר את האלמנט בחלק העליון של הערימה, מסיר אותו תוך כדי. אובייקט peek()- מחזיר את האלמנט בראש הערימה, אך לא מסיר אותו. int search()- מחפש אלמנט בערימה. אם נמצא, ההיסט שלו מהחלק העליון של הערימה מוחזר. אחרת -1 מוחזר. boolean empty()- בודק אם הערימה ריקה. מחזירה true אם הערימה ריקה. מחזירה false אם המחסנית מכילה אלמנטים. אז למה אתה Javaצריך StackTraceאחד שמבוסס על עקרונות הפעולה Stack? הבה נסתכל על הדוגמה של שגיאה להלן שהתרחשה במהלך ביצוע תוכנית כה פשוטה.
public class Test {

    public static void main(String[] args) {
        System.out.println(convertStringToInt(null));
    }

    public static int convertStringToInt(String s) {
        int x = Integer.parseInt(s);
        return x;
    }
}
יש לנו שיעור Testעם שתי שיטות. כולם מכירים mainוההיגיון convertStringToIntשל זה הוא להמיר ולהחזיר מחרוזת שהתקבלה מבחוץ (כלומר מהשיטה main) למספר שלם מסוג int. כפי שאתה יכול לראות, העברנו בכוונה את הפרמטר במקום מחרוזת עם מספר כלשהו null. השיטה שלנו לא הצליחה לעבד את הפרמטר הזה בצורה נכונה וגרמה לשגיאה NumberFormatException. כידוע, התוכנית מתחילה לחשב את עבודתה מהשיטה mainוברגע זה היא יוצרת אחת חדשה Стэкעם השם StackTraceשבה היא מכניסה את הערך הנוכחי של העבודה שלה תחת מספר 1 , ואז נעבור שוב לשיטה convertStringToIntולתוכנית מכניס את הפרמטרים של המיקום שלנו לזה שנוצר קודם לכן StackTraceתחת מספר 2 , אז זה נקרא שיטה בלתי נראית לעינינו parseIntהממוקמת במחלקה Integerוזה כבר יהיה אלמנט מספר 3 שלנו StackTrace, בשיטה הזו תתווסף עוד קריאה פנימית למספר StackTrace4 כדי לבדוק את האלמנט עבור null שיוביל לשגיאה. התוכנית צריכה להציג את השגיאה שלנו המציינת את כל שרשרת המעברים שלנו עד שהשגיאה התרחשה. זה המקום שבו נוצרה בעבר עם נתוני המעברים שלנו באה לעזרתה StackTrace.
Exception in thread "main" java.lang.NumberFormatException: null
	at java.base/java.lang.Integer.parseInt(Integer.java:614)
	at java.base/java.lang.Integer.parseInt(Integer.java:770)
	at com.example.task01.Test.convertStringToInt(Solution.java:10)
	at com.example.task01.Test.main(Solution.java:6)
לפני שהתרחשה השגיאה, התוכנית נכנסה עמוק לשיטות, אך ברגע שהתרחשה השגיאה, הכל מתחיל לקרות בסדר הפוך. מודפסת שורה המתארת ​​את הבעיה (מס' 1 בדוגמה), ואז נלקח הערך האחרון (ובחלק העליון) שנוסף לשלנו, Стэкהוא היה מספר ארבע והודפס לקונסולה (מס' 2 בדוגמה) ו אנו רואים שהבעיה התעוררה במחלקה Integerבשורת קוד 614 וקראה לשורה זו, שורה 770 של שיטה parseIntמאותה מחלקה (מס' 3 בדוגמה) שכאשר נוספה אליה, Стэкהייתה מספר שלוש ושיטת המחלקה הזו, Integerעדיין לא נראה לנו, נקרא בשיטה שלנו convertStringToIntשנמצאת בשורה 10 של התוכנית שלנו (מס' 4 בדוגמה, וכשהוספה היה שני), וזה, בתורו, נקרא mainבשורה 6 (מס' 5 ב- הדוגמה, וכאשר מוסיפים, בהתאמה, את הראשונה). לכן, על ידי אחסון Стекהשיטות הנקראות שלנו צעד אחר צעד, הצלחנו לחזור להדפסה mainמקבילה של מידע שהוביל אותנו בדיוק לשגיאה. אבל StackTraceזה לא רק עבודה עם שגיאות, זה מאפשר לנו לקבל הרבה מידע מעניין על תהליך היישום שלנו. בואו נסתכל על דוגמה פופולרית נוספת בתגובות להרצאה הראשית של רמה 9. יש לנו את הקוד ומיד אצרף לו תמונה הממחישה את תהליך התוכנית:
public class Test {
    public static void main(String[] args) {
        method1();
        method2();
    }
    public static void method1() {
        //не вызывает ничего
    }
    public static void method2() {
        method3();
        method4();
    }
    public static void method3() {
        //не вызывает ничего
    }
    public static void method4() {
        method5();
    }
    public static void method5() {
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
        for (StackTraceElement element:stackTraceElements) {
            System.out.println(element.getMethodName());
        }
    }
}
Stack Trace ועם מה הוא נאכל - 2 כאן התוכנית שלנו עושה את עבודתה ללא רבב ומסתיימת. זה מה שנראה בפלט הקונסולה:
getStackTrace
method5
method4
method2
main

Process finished with exit code 0
איך הגענו למסקנה הזו ומה קרה בשיטה החמישית, החל משורה 20? אני חושש שהדבר הטוב ביותר שאני יכול לעשות הוא להוסיף את ההסבר הפופולרי ביותר (בקיצור) של המשתמש Kirill מתוך ההערות להרצאה. בואו נפנה לקו היצירה StackTraceוננתח אותו אלמנט אחר אלמנט:
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement[]- אינדיקציה לסוג המערך (ברמות מוקדמות כבר למדת על מערכים כמו int[], String[], כאן זה אותו הדבר). stackTraceElements- השם של המערך יכול להיות כל דבר, תוך התחשבות בכללי השמות הכלליים, זה לא משפיע על העבודה. Thread.currentThread()- השגת קישור לשרשור הנוכחי בו מבוצעות השיטות שאנו רוצים לעקוב אחריהם (בינתיים זה לא חשוב, תנתח שרשורים ביתר פירוט ברמה 16 ב-Java Core Quest) getStackTrace()- נקבל את כל Стэкהמתודות שנקראות (זהו מגבר רגיל עבור StackTrace) עכשיו בואו נראה מה המערך שנוצר עשוי להיות שימושי עבורנו. אנו מבינים שהמערך מאחסן מידע על שיטות שבוצעו. (ג) ולשם כך, בשורה ה-21, אנו משיקים מחזור שונה forבשם forEach(אגב, למי שעדיין לא למד את המחזור הזה, אני ממליץ לך לקרוא על זה) ומוציא נתונים מהמערך לקונסולה כלומר מידע על אילו שיטות בוצעו במהלך העבודה באמצעות הקונסטרוקציה element.getMethodName(). שימו לב, כפי שאנו רואים, אלמנט האפס של המערך התברר כעצמו, getStackTrace()בהתאמה, שכן ברגע קבלת מערך הנתונים זו הייתה השיטה האחרונה שבוצעה ובכך הגיעה לראש Стэка, וזוכרים את הבנייה שלנו " Last in, first out ” הוא מיד הראשון שמתווסף למערך מתחת לאלמנט האפס. הנה מה עוד אנחנו יכולים לקבל מ StackTraceElement: מחרוזת getClassName()- מחזירה את שם המחלקה. מחרוזת getMethodName()- מחזירה את שם השיטה. מחרוזת getFileName()- מחזירה את שם הקובץ (יכולות להיות מחלקות רבות בקובץ אחד). מחרוזת getModuleName()- מחזירה את שם המודול (יכול להיות null). מחרוזת getModuleVersion()- מחזירה את גרסת המודול (יכול להיות null). int getLineNumber()- מחזירה את מספר השורה בקובץ שבו נקראה השיטה. עכשיו כשאתה מבין את העיקרון הכללי של הפעולה, אני ממליץ לך לנסות שיטות שונות בעצמך StackTraceב- Ide שלך . גם אם לא שלטת לגמרי בהכל, תמשיך ללמוד והפאזל יתברר כמו שזה יצא לי בעניין הזה. אני מאחל לכולכם הצלחה! נ.ב. אם אהבתם את החומר הזה, אנא תמכו בו בלייק. זה לא קשה לך, אני מרוצה. תודה ולהתראות ברמה 41 ;)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION