في هذه المقالة، سوف تتعلم وتفهم كيفية عمل ظاهرة Java StackTrace، والمعروفة أيضًا باسم Call Stack Tracing. تم تنظيم هذه المعلومات للمبتدئين الذين واجهوا هذا المفهوم في بداية Java Syntax 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. منطقي empty()
- يتحقق مما إذا كان المكدس فارغًا. يُرجع صحيحًا إذا كان المكدس فارغًا. إرجاع خطأ إذا كانت المكدس يحتوي على عناصر. فلماذا تحتاج 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 للتحقق من وجود العنصر خاليًا مما سيؤدي إلى حدوث خطأ. يحتاج البرنامج إلى عرض الخطأ الخاص بنا مع الإشارة إلى سلسلة التحولات بأكملها حتى حدوث الخطأ. هذا هو المكان الذي يأتي لمساعدتها الذي تم إنشاؤه مسبقًا ببيانات التحولات لدينا 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
هذا لا يقتصر على التعامل مع الأخطاء فحسب، بل يتيح لنا الحصول على الكثير من المعلومات المثيرة للاهتمام حول عملية تقديم طلبنا. دعونا نلقي نظرة على مثال شائع آخر في التعليقات على المحاضرة الرئيسية للمستوى التاسع. لدينا الكود وسأرفق له على الفور صورة توضح عملية البرنامج:
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؟ أخشى أن أفضل ما يمكنني فعله هو إضافة الشرح الأكثر شيوعًا (المختصر) بواسطة المستخدم كيريل من التعليقات على المحاضرة. دعنا ننتقل إلى خط الإنشاء StackTrace
ونحلله عنصرًا بعنصر:
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement[]
- إشارة إلى نوع المصفوفة (في المستويات المبكرة، تعلمت بالفعل عن المصفوفات مثل int[]، String[]، وهنا نفس الشيء). stackTraceElements
- يمكن أن يكون اسم المصفوفة أي شيء، مع مراعاة قواعد التسمية العامة، وهذا لا يؤثر على العمل. Thread.currentThread()
- الحصول على رابط للسلسلة الحالية التي يتم فيها تنفيذ الطرق التي نريد تتبعها (في الوقت الحالي، هذا ليس مهمًا، سوف تقوم بتحليل سلاسل الرسائل بمزيد من التفصيل في المستوى 16 في مهمة Java Core) getStackTrace()
- نحصل على جميع Стэк
الطرق المسماة (هذا هو getter العادي لـ StackTrace
) الآن دعونا نرى ما قد تكون المصفوفة التي تم إنشاؤها مفيدة لنا. نحن ندرك أن المصفوفة تخزن معلومات حول الطرق المنفذة. (ج) ولهذا، في السطر الحادي والعشرين، نطلق دورة معدلة for
تسمى forEach
(بالمناسبة، لأولئك الذين لم يدرسوا هذه الدورة بعد، أنصحك بالقراءة عنها) ونخرج البيانات من المصفوفة إلى وحدة التحكم وهي معلومات حول الأساليب التي تم تنفيذها أثناء العمل باستخدام البناء element.getMethodName()
. انتبه، كما نرى، تبين أن العنصر الصفري للمصفوفة هو نفسه، getStackTrace()
على التوالي، لأنه في لحظة استلام مصفوفة البيانات كانت هي الطريقة الأخيرة التي تم تنفيذها وبالتالي انتهى بها الأمر في الأعلى Стэка
، وتذكر البناء الخاص بنا " "آخر ما يدخل، يخرج أولاً " هو على الفور أول ما تتم إضافته إلى المصفوفة تحت العنصر الصفري. إليك ما يمكننا الحصول عليه أيضًا StackTraceElement
: سلسلة getClassName()
- تُرجع اسم الفئة. سلسلة getMethodName()
- تقوم بإرجاع اسم الطريقة. سلسلة getFileName()
- تقوم بإرجاع اسم الملف (يمكن أن يكون هناك العديد من الفئات في ملف واحد). سلسلة getModuleName()
- تُرجع اسم الوحدة (يمكن أن يكون فارغًا). سلسلة getModuleVersion()
- تُرجع إصدار الوحدة (يمكن أن يكون فارغًا). int getLineNumber()
- يُرجع رقم السطر في الملف الذي تم استدعاء الطريقة فيه. الآن بعد أن فهمت المبدأ العام للتشغيل، أنصحك بتجربة طرق مختلفة بنفسك StackTrace
في Ide . حتى لو لم تتقن كل شيء تمامًا، واصل التعلم وسيظهر اللغز بنفس الطريقة التي ظهر بها بالنسبة لي في هذا الشأن. وأتمنى لكم كل النجاح والتوفيق! ملاحظة: إذا أعجبتك هذه المادة، فيرجى دعمها بإعجاب. ليس من الصعب عليك، أنا مسرور. شكرا ونراكم في المستوى 41 ;)
GO TO FULL VERSION