JavaRush /Blog Java /Random-VI /Stack Trace và nó được ăn với cái gì
Alukard
Mức độ
London

Stack Trace và nó được ăn với cái gì

Xuất bản trong nhóm
Trong bài viết này, bạn sẽ tìm hiểu và hiểu cách hoạt động của hiện tượng Java StackTrace, còn được gọi là Call Stack Tracing. Thông tin này được cấu trúc dành cho những người mới bắt đầu gặp khái niệm này khi bắt đầu Cú pháp Java cấp 9. Tôi nghĩ rằng tất cả các bạn, ít nhất một lần, đã gặp phải các lỗi tương tự khi làm việc trong IDE của mình, bất kể đó là Idea , Eclipse hay thứ gì khác.
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)
Đây, như bạn có thể đoán, là dấu vết của chúng tôi. Nhưng đừng vội hoảng sợ, bây giờ chúng tôi sẽ chia nhỏ ví dụ này cho bạn. Trước tiên, bạn cần hiểu thực tế là nó StackTracehoạt động như Стэкtên gọi của nó. Tại thời điểm này chúng ta sẽ nói chi tiết hơn một chút. Cách hoạt động của bộ sưu tập Stack Ở cấp độ thứ tám, bạn đã làm quen với các bộ sưu tập và biết rằng chúng được chia thành ba nhóm Set- bộ, List- danh sách, Map- từ điển (hoặc bản đồ). Theo JavaRush (c). Của chúng tôi Stacklà một phần của nhóm List. Nguyên lý hoạt động của nó có thể được mô tả là LIFO , viết tắt của Last In First Out. Cụ thể, đây là một danh sách tương tự như một chồng sách, để lấy phần tử mà chúng ta đưa vào Stackđầu tiên, trước tiên chúng ta cần trích xuất tất cả các phần tử mà chúng ta đã thêm vào danh sách sau đó. Như được chỉ ra trong hình trên, không giống như một danh sách thông thường ArrayListnơi chúng ta có thể lấy bất kỳ phần tử nào từ danh sách theo chỉ mục. Một lần nữa để tăng cường. Chỉ có thể lấy một phần tử Стэкаtừ cuối! Trong khi phần tử đầu tiên được thêm vào nó nằm ở đầu (hoặc ở cuối, như vậy sẽ thuận tiện hơn). Đây là các phương thức mà Stack Đối tượng của chúng tôi có push()- Thêm một phần tử vào đầu ngăn xếp. Đối tượng pop()- Trả về phần tử ở đầu ngăn xếp, loại bỏ phần tử đó trong quy trình. Đối tượng peek()- Trả về phần tử ở đầu ngăn xếp nhưng không xóa phần tử đó. int search()- Tìm kiếm một phần tử trên ngăn xếp. Nếu tìm thấy, phần bù của nó từ đỉnh ngăn xếp sẽ được trả về. Ngược lại -1 sẽ được trả về. boolean empty()- Kiểm tra xem ngăn xếp có trống không. Trả về true nếu ngăn xếp trống. Trả về false nếu ngăn xếp chứa các phần tử. Vậy tại sao bạn Javacần StackTracemột chiếc được xây dựng dựa trên nguyên tắc hoạt động Stack? Hãy xem ví dụ về lỗi xảy ra bên dưới trong quá trình thực thi một chương trình đơn giản như vậy.
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;
    }
}
Chúng tôi có một lớp học Testvới hai phương pháp. Mọi người đều quen thuộc mainconvertStringToIntlogic của nó là chuyển đổi và trả về một chuỗi nhận được từ bên ngoài (cụ thể là từ phương thức main) thành một số nguyên loại int. Như bạn có thể thấy, chúng tôi đã cố tình truyền tham số thay vì một chuỗi có số nào đó null. Phương pháp của chúng tôi không thể xử lý chính xác tham số này và đã gây ra lỗi NumberFormatException. Như bạn đã biết, chương trình bắt đầu thực hiện công việc của nó từ phương thức mainvà tại thời điểm này, nó tạo một Стэкcái mới có tên StackTracetrong đó nó đặt giá trị hiện tại của công việc của nó dưới số 1 , sau đó chúng ta quay lại phương thức convertStringToIntvà chương trình nhập các tham số vị trí của chúng ta vào tham số được tạo trước đó StackTracedưới số 2 , sau đó nó được gọi là một phương thức vô hình đối với mắt chúng ta parseIntnằm trong lớp Integervà đây sẽ là phần tử số 3 của chúng ta StackTrace, trong phương thức này sẽ có một lệnh gọi nội bộ khác được thêm vào đến StackTracesố 4 để kiểm tra phần tử có null không sẽ dẫn đến lỗi. Chương trình cần hiển thị lỗi của chúng tôi cho biết toàn bộ chuỗi chuyển đổi của chúng tôi cho đến khi xảy ra lỗi. Đây là nơi mà cái được tạo trước đó bằng dữ liệu chuyển đổi của chúng tôi sẽ hỗ trợ cô ấy 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)
Trước khi xảy ra lỗi, chương trình đã đi sâu vào các phương thức, nhưng ngay khi xảy ra lỗi, mọi thứ bắt đầu diễn ra theo trình tự ngược lại. Một dòng mô tả sự cố được in (số 1 trong ví dụ), sau đó lấy giá trị cuối cùng (và ở trên cùng) được thêm vào giá trị của chúng tôi, Стэкđó là số 4 và được in ra bảng điều khiển (số 2 trong ví dụ) và chúng ta thấy rằng vấn đề phát sinh trong lớp Integerở dòng mã 614 và được gọi là dòng này, dòng 770 của một phương thức parseIntcùng lớp (số 3 trong ví dụ), khi được thêm vào, Стэкlà số ba và phương thức lớp này, Integervẫn không hiển thị với chúng tôi, được gọi bằng phương thức convertStringToIntnằm ở dòng 10 của chương trình của chúng tôi (số 4 trong ví dụ và khi thêm nó là thứ hai), và đến lượt nó, được gọi mainở dòng 6 (số 5 trong ví dụ và khi thêm, tương ứng, lần đầu tiên). Vì vậy, bằng cách lưu trữ Стекtừng bước các phương thức được gọi, chúng tôi có thể quay lại mainin song song những thông tin chính xác đã dẫn đến lỗi. Nhưng StackTraceđiều này không chỉ giải quyết được lỗi mà còn cho phép chúng tôi nhận được nhiều thông tin thú vị về quá trình đăng ký của mình. Hãy xem một ví dụ phổ biến khác trong phần bình luận của bài giảng chính ở cấp độ 9. Chúng tôi có mã và tôi sẽ đính kèm ngay một bức ảnh vào đó để trực quan hóa quá trình của chương trình:
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 và nó ăn với cái gì - 2 Ở đây chương trình của chúng tôi thực hiện công việc của mình một cách hoàn hảo và kết thúc. Đây là những gì chúng ta sẽ thấy trong đầu ra của giao diện điều khiển:
getStackTrace
method5
method4
method2
main

Process finished with exit code 0
Làm thế nào chúng ta có được kết luận này và điều gì đã xảy ra trong phương pháp thứ năm, bắt đầu từ dòng 20? Tôi e rằng điều tốt nhất tôi có thể làm là thêm lời giải thích phổ biến nhất (viết tắt) của người dùng Kirill từ phần nhận xét vào bài giảng. Hãy chuyển sang dòng sáng tạo StackTracevà phân tích từng phần tử:
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
StackTraceElement[]- chỉ báo về loại mảng (Ở các cấp độ đầu, bạn đã học về các mảng như int[], String[], ở đây cũng vậy). stackTraceElements- Tên mảng có thể là bất kỳ tên nào, có tính đến quy tắc đặt tên chung, điều này không ảnh hưởng đến công việc. Thread.currentThread()- lấy liên kết đến luồng hiện tại trong đó các phương thức mà chúng tôi muốn theo dõi được thực thi (hiện tại điều này không quan trọng, bạn sẽ phân tích các luồng chi tiết hơn ở cấp 16 trong nhiệm vụ Java Core) getStackTrace()- chúng tôi nhận được tất cả Стэкcác phương thức được gọi (Đây là một getter thông thường cho StackTrace) Bây giờ hãy xem Mảng được tạo có thể hữu ích cho chúng ta như thế nào. Chúng tôi hiểu rằng mảng lưu trữ thông tin về các phương thức được thực thi. (c) Và đối với điều này, ở dòng thứ 21, chúng tôi khởi chạy một chu trình đã sửa đổi forcó tên forEach(nhân tiện, đối với những người chưa nghiên cứu về chu trình này, tôi khuyên bạn nên đọc về nó) và xuất dữ liệu từ mảng ra bảng điều khiển , cụ thể là thông tin về những phương pháp nào đã được thực hiện trong quá trình thực hiện công việc sử dụng công trình element.getMethodName(). Chú ý, như chúng ta thấy, phần tử 0 của mảng lần lượt là chính nó, getStackTrace()vì tại thời điểm nhận mảng dữ liệu, nó là phương thức cuối cùng được thực thi và do đó kết thúc ở trên cùng Стэкаvà ghi nhớ cấu trúc của chúng ta “ Vào sau, ra trước ” ngay lập tức là phần tử đầu tiên được thêm vào mảng bên dưới phần tử 0. Đây là những gì chúng ta có thể nhận được từ StackTraceElement: Chuỗi getClassName()- Trả về tên của lớp. Chuỗi getMethodName()- Trả về tên của phương thức. Chuỗi getFileName()- Trả về tên tệp (có thể có nhiều lớp trong một tệp). Chuỗi getModuleName()- Trả về tên mô-đun (có thể rỗng). Chuỗi getModuleVersion()- Trả về phiên bản mô-đun (có thể rỗng). int getLineNumber()- Trả về số dòng trong tệp mà phương thức được gọi. Bây giờ bạn đã hiểu nguyên lý hoạt động chung, tôi khuyên bạn nên tự mình thử các phương pháp khác nhau StackTracetrong Ide của mình . Ngay cả khi bạn chưa hoàn toàn nắm vững mọi thứ, hãy tiếp tục học và câu đố sẽ diễn ra giống như tôi trong vấn đề này. Tôi chúc tất cả các bạn thành công! Ps Nếu bạn thích tài liệu này, hãy ủng hộ nó bằng một lượt thích. Với bạn thì không khó đâu, tôi hài lòng. Cảm ơn và hẹn gặp lại ở cấp độ 41;)
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION