במאמר זה תלמדו ותבינו כיצד פועלת תופעת 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
עובד כפי Стэк
שהשם שלו מרמז. בשלב זה נתעכב קצת יותר בפירוט. ברמה השמינית כבר התוודעתם לאוספים ויודעים שהם מחולקים לשלוש קבוצות 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
, בשיטה הזו תתווסף עוד קריאה פנימית למספר StackTrace
4 כדי לבדוק את האלמנט עבור 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());
}
}
}
כאן התוכנית שלנו עושה את עבודתה ללא רבב ומסתיימת. זה מה שנראה בפלט הקונסולה:
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 ;)
GO TO FULL VERSION