java是基于棧設計的語言,其實與c、c++語言相同。整個程序的運行表現在方法的執行是一系列入棧出棧的行為,棧是線程私有的。
在java語言中,我們可以跟蹤方法的調用關系,即當前棧幀(棧頂)和已經入棧的棧幀的層次關系。
從java1.4以后,java語言的throwable類提供了以下方法:
1
2
3
4
5
6
7
|
opendeclarationstacktraceelement[]java.lang.throwable.getstacktrace() providesprogrammaticaccesstothestacktraceinformationprintedbyprintstacktrace().returnsanarrayofstacktraceelements,eachrepresentingonestackframe.thezerothelementofthearray(assumingthearray 'slengthisnon-zero)representsthetopofthestack,whichisthelastmethodinvocationinthesequence.typically,thisisthepointatwhichthisthrowablewascreatedandthrown.thelastelementofthearray(assumingthearray' slengthisnon-zero)representsthebottomofthestack,whichisthefirstmethodinvocationinthesequence. somevirtualmachinesmay,undersomecircumstances,omitoneormorestackframesfromthestacktrace.intheextremecase,avirtualmachinethathasnostacktraceinformationconcerningthisthrowableispermittedtoreturnazero-lengtharrayfromthismethod.generallyspeaking,thearrayreturnedbythismethodwillcontainoneelementforeveryframethatwouldbeprintedbyprintstacktrace.writestothereturnedarraydonotaffectfuturecallstothismethod. returns: anarrayofstacktraceelementsrepresentingthestacktracepertainingtothisthrowable. since: 1.4 |
該方法返回的stacktraceelement[] 就是棧幀數組。數組下標0的元素代表當前棧頂棧幀,數組的最大下標代表調用棧序列中第一個棧幀,也就是第一個方法的調用。我們可以從stacktraceelement得到棧調用層級的關系、調用方法名及調用入口位置,代碼示例:
執行結果:
調用結果顯示的方法調用層級關系。
那我們得到這些信息有什么用呢。
1.日志:這些信息可以讓應用的日志系統得到信息更詳細。
2.安全:api可以決定調用者當前包或者類是否有權限進入。
3.流程控制:可以避免一些流程錯誤,比如無限遞歸調用。
實現一個簡單的日志系統:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
package com.doctor.reflect; import java.io.printwriter; import java.io.stringwriter; /** * call stack introspection * * @author sdcuike * * created at 2016年8月29日 下午9:40:35 */ public class callstackintrospectiondemo { private static final mylogger logger = new loggerimpl(); public static void main(string[] args) { logger.logrecord( "hello" ); illegalargumentexception exception = new illegalargumentexception( "illegalargumentexception" ); logger.logproblem( "throwable" , exception); } public interface mylogger { // types for log records int error = 0 ; int warning = 100 ; int status = 200 ; int debug = 300 ; int trace = 400 ; void logrecord(string message); void logproblem(string message, throwable throwable); } public static class loggerimpl implements mylogger { @override public void logrecord(string message) { throwable throwable = new throwable(); log(message, throwable.getstacktrace()[ 1 ]); } @override public void logproblem(string message, throwable throwable) { stringwriter out = new stringwriter(); printwriter writer = new printwriter(out); throwable.printstacktrace(writer); writer.flush(); log(message + out.tostring(), throwable.getstacktrace()[ 0 ]); } private void log(string message, stacktraceelement stacktraceelement) { string classname = stacktraceelement.getclassname(); string methodname = stacktraceelement.getmethodname(); int linenumber = stacktraceelement.getlinenumber(); system.out.println(string.join( " " , "模擬打印日志:" , methodname, classname, "" + linenumber, message)); } } } |
執行結果:
1
2
3
|
模擬打印日志: main com.doctor.reflect.callstackintrospectiondemo 36 hello 模擬打印日志: main com.doctor.reflect.callstackintrospectiondemo 38 throwablejava.lang.illegalargumentexception: illegalargumentexception at com.doctor.reflect.callstackintrospectiondemo.main(callstackintrospectiondemo.java: 38 ) |
上述日志,只是簡單的在控制臺打印一些信息。
總結
以上就是本文關于java反射之call stack introspection詳解的全部內容,希望對大家有所幫助。如有不足之處,歡迎留言指出。
原文鏈接:https://www.2cto.com/kf/201609/544142.html