JavaRush /จาวาบล็อก /Random-TH /Stack Trace และสิ่งที่กินเข้าไปด้วย
Alukard
ระดับ
London

Stack Trace และสิ่งที่กินเข้าไปด้วย

เผยแพร่ในกลุ่ม
ในบทความนี้ คุณจะได้เรียนรู้และทำความเข้าใจว่าปรากฏการณ์ Java StackTrace หรือที่เรียกว่า Call Stack Tracing ทำงานอย่างไร ข้อมูลนี้จัดทำขึ้นสำหรับผู้เริ่มต้นที่พบแนวคิดนี้เมื่อเริ่มต้น Java Syntax ระดับ 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 (c) ของเรา เป็นStackส่วนหนึ่งของกลุ่ม Listหลักการทำงานของมันสามารถอธิบายได้ว่าLIFOซึ่งย่อมาจากLast In First Out กล่าวคือ นี่คือรายการที่คล้ายกับกองหนังสือ เพื่อที่จะนำองค์ประกอบที่เราใส่ไว้Stackก่อน เราต้องแยกองค์ประกอบทั้งหมดที่เราเพิ่มเข้าไปในรายการของเราหลังจากนั้นก่อน ตามที่ระบุไว้ในภาพด้านบน ซึ่งแตกต่างจากรายการปกติArrayListที่เราสามารถรับองค์ประกอบใดๆ จากรายการตามดัชนี อีกครั้งสำหรับการเสริมกำลัง การได้รับองค์ประกอบนั้นСтэкаเป็นไปได้ตั้งแต่ตอนท้ายเท่านั้น! ในขณะที่องค์ประกอบแรกที่เพิ่มเข้าไปนั้นอยู่ที่จุดเริ่มต้น (หรือที่ด้านล่างตามที่สะดวกกว่า) นี่คือวิธีที่Stack Object ของเรามี push()- เพิ่มองค์ประกอบที่ด้านบนของสแต็ก วัตถุpop()- ส่งคืนองค์ประกอบที่ด้านบนของสแต็ก โดยลบออกในกระบวนการ วัตถุpeek()- ส่งคืนองค์ประกอบที่ด้านบนของสแต็ก แต่ไม่ได้ลบออก int search()- ค้นหาองค์ประกอบบนสแต็ก หากพบ ระบบจะส่งคืนออฟเซ็ตจากด้านบนของสแต็ก มิฉะนั้น -1 จะถูกส่งกลับ boolean 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ตรรกะคือการแปลงและส่งกลับสตริงที่ได้รับจากภายนอก (กล่าวคือจาก method 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 ในตัวอย่าง) ซึ่งเมื่อบวกเข้าไปแล้วСтэкคือหมายเลข 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
เราได้ข้อสรุปนี้มาได้อย่างไร และเกิดอะไรขึ้นในวิธีที่ 5 เริ่มจากบรรทัดที่ 20? ฉันเกรงว่าสิ่งที่ดีที่สุดที่ฉันสามารถทำได้คือเพิ่มคำอธิบายยอดนิยม (ตัวย่อ) โดยผู้ใช้Kirillจากความคิดเห็นไปยังการบรรยาย มาดูบรรทัดการสร้างStackTraceแล้ววิเคราะห์ทีละองค์ประกอบ:
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement[]- การระบุประเภทของอาร์เรย์ (ในระดับแรกๆ คุณได้เรียนรู้เกี่ยวกับอาร์เรย์เช่น int[], String[] แล้ว นี่ก็เหมือนกัน) stackTraceElements- ชื่อของอาร์เรย์สามารถเป็นอะไรก็ได้โดยคำนึงถึงกฎการตั้งชื่อทั่วไปซึ่งจะไม่ส่งผลกระทบต่องาน Thread.currentThread()- รับลิงก์ไปยังเธรดปัจจุบันซึ่งมีการดำเนินการวิธีการที่เราต้องการติดตาม (สำหรับตอนนี้สิ่งนี้ไม่สำคัญ คุณจะวิเคราะห์เธรดโดยละเอียดเพิ่มเติมที่ระดับ 16 ในภารกิจ Java Core) getStackTrace()- เราได้รับСтэкวิธีการที่เรียกว่า ทั้งหมด (นี่คือ getter ปกติสำหรับStackTrace) ทีนี้มาดูกันว่าอาร์เรย์ที่สร้างขึ้นอาจมีประโยชน์กับเราอย่างไร เราเข้าใจว่าอาเรย์เก็บข้อมูลเกี่ยวกับวิธีการดำเนินการ (c) และสำหรับสิ่งนี้ในบรรทัดที่ 21 เราเปิดตัววงจรที่แก้ไขforที่เรียกว่าforEach(โดยวิธีการสำหรับผู้ที่ยังไม่ได้ศึกษาวงจรนี้ฉันแนะนำให้คุณอ่านเกี่ยวกับมัน) และส่งออกข้อมูลจากอาเรย์ไปยังคอนโซล คือข้อมูลเกี่ยวกับวิธีการดำเนินการระหว่างการทำงานโดยใช้การelement.getMethodName()ก่อสร้าง ตามที่เราเห็น องค์ประกอบศูนย์ของอาเรย์กลายเป็นตัวมันเองgetStackTrace()ตามลำดับ เนื่องจากในขณะที่รับอาเรย์ข้อมูล มันเป็นวิธีสุดท้ายที่ถูกดำเนินการและด้วยเหตุนี้จึงจบลงที่ด้านบนСтэкаและจดจำการก่อสร้างของเรา” Last in, first out ” คือค่าแรกที่จะถูกเพิ่มในอาร์เรย์ภายใต้องค์ประกอบศูนย์ทันที นี่คือสิ่งอื่นที่เราได้จากStackTraceElement: String getClassName()- ส่งกลับชื่อของคลาส สตริงgetMethodName()- ส่งกลับชื่อของวิธีการ สตริงgetFileName()- ส่งกลับชื่อไฟล์ (สามารถมีได้หลายคลาสในไฟล์เดียว) สตริงgetModuleName()- ส่งกลับชื่อโมดูล (อาจเป็นค่าว่างได้) String getModuleVersion()- ส่งกลับเวอร์ชันของโมดูล (อาจเป็นค่าว่างได้) int getLineNumber()- ส่งกลับหมายเลขบรรทัดในไฟล์ที่มีการเรียกใช้เมธอด เมื่อคุณเข้าใจหลักการทำงานทั่วไปแล้ว ฉันขอแนะนำให้คุณลองวิธีการต่างๆ ด้วยตนเองStackTraceในIdeของ คุณ แม้ว่าคุณจะยังไม่เชี่ยวชาญทุกอย่างอย่างสมบูรณ์ แต่ให้เรียนรู้ต่อไปแล้วปริศนาจะกลายเป็นแบบเดียวกับที่ฉันพบในเรื่องนี้ ฉันขอให้คุณประสบความสำเร็จ! Ps หากคุณชอบเนื้อหานี้โปรดสนับสนุนด้วยการกดไลค์ ไม่ใช่เรื่องยากสำหรับคุณ ฉันยินดี ขอบคุณ แล้วพบกันใหม่ตอนเลเวล 41 ;)
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION