深入学习java源码之PageException(String message)与PageException(Throwable cause)
长度为0的数组
长度为0的数组 int[] arr = new int[0],也称为空数组,虽然arr长度为0,但是依然是一个对象
null数组,int[] arr = null;arr是一个数组类型的空引用。
Effective Java第43条(返回零长度的数组或者集合,而不是null)清楚的说明了零长度或者集合的好处
为编写客户端程序的程序员可能会忘记写这种专门的代码来处理null返回值。这样的错误也许几年都不会被注意到,因为这样的方法通常返回一个或者多个对象。返回null而不是零长度的数组也会使返回数组或者集合的方法本身变得更加复杂,这一点虽然不是特别重要,但是也值得注意。
客户端必须有额外的代买来处理null的返回值。例如
Cheese[] cheeses = shop.getCheeses();
if(cheeses != null && Arrays.asList(cheeses).contains(Cheese.STILTON)){
System.out.println("Jolly good,just the thing.") ;
}
而不是下面这段代码:
if( Arrays.asList(cheeses).contains(Cheese.STILTON)){
System.out.println("Jolly good,just the thing.") ;
}
简而言之,返回类型为数组或集合的方法没理由返回null,而不是返回一个零长度的数组或者集合。
习惯用法中,零长度数组常量被传递给toArray方法,以指明所期望的返回类型.正常情况下,toArray方法分配了返回的数组,但是,如果集合是空的,它将使用零长度输入数组.
private final List<Cheese> cheeseInStock = …;
private static final Cheese[] EMPTY_CHEESE_ARRAY =new Cheese[0];
public Cheese[] getCheeses(){
return cheeseInStock.toArray(EMPTY_CHEESE_ARRAY);
}
集合值方法可以做成需要返回空集合时返回同一个不可变的空集合.Collections.emptySet(),
Collections.emptyList(),Collections.emptyMap()方法正是所需要的
private final List<Cheese> cheeseInStock = …;
public List<Cheese> getCheesesList(){
if (cheeseInStock.isEmpty())
rerurnCollections.emptyList();
else
return new ArrayList(cheeseInStock);
}
异常简介
Java中的所有不正常类都继承于Throwable类。Throwable主要包括两个大类,一个是Error类,另一个是Exception类;
其中Error类中包括虚拟机错误和线程死锁,一旦Error出现了,程序就彻底的挂了,被称为程序终结者;
Exception类,也就是通常所说的“异常”。主要指编码、环境、用户操作输入出现问题,Exception主要包括两大类,非检查异常(RuntimeException)和检查异常(其他的一些异常)
.RuntimeException异常主要包括以下四种异常(其实还有很多其他异常,这里不一一列出):空指针异常、数组下标越界异常、类型转换异常、算术异常。RuntimeException异常会由java虚拟机自动抛出并自动捕获(就算我们没写异常捕获语句运行时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题应该从逻辑上去解决并改进代码。
检查异常,引起该异常的原因多种多样,比如说文件不存在、或者是连接错误等等。跟它的“兄弟”RuntimeException运行异常不同,该异常我们必须手动在代码里添加捕获语句来处理该异常,这也是我们学习java异常语句中主要处理的异常对象。
try-catch-finally语句
try{
//一些会抛出的异常
}catch(Exception e){
//第一个catch
//处理该异常的代码块
}catch(Exception e){
//第二个catch,可以有多个catch
//处理该异常的代码块
}finally{
//最终要执行的代码
}
当异常出现时,程序将终止执行,交由异常处理程序(抛出提醒或记录日志等),异常代码块外代码正常执行。 try会抛出很多种类型的异常,由多个catch块捕获多钟错误。
多重异常处理代码块顺序问题:先子类再父类(顺序不对编译器会提醒错误),finally语句块处理最终将要执行的代码。
finally语句块中加上return,编译器就会提示警告:
public int test2(){
int divider=10;
int result=100;
try{
while(divider>-1){
divider--;
result=result+100/divider;
}
return result;
}catch(Exception e){
e.printStackTrace();
System.out.println("异常抛出了!!");
return result=999;
}finally{
System.out.println("这是finally,哈哈哈!!");
System.out.println("result的值为:"+result);
return result;//编译器警告
}
}
finally块中的return语句可能会覆盖try块、catch块中的return语句;如果finally块中包含了return语句,即使前面的catch块重新抛出了异常,则调用该方法的语句也不会获得catch块重新抛出的异常,而是会得到finally块的返回值,并且不会捕获异常。
解决问题:面对上述情况,其实更合理的做法是,既不在try block内部中使用return语句,也不在finally内部使用 return语句,而应该在 finally 语句之后使用return来表示函数的结束和返回。如:
throw和throws关键字
throw ----将产生的异常抛出,是抛出异常的一个动作。
一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。如:
语法:throw (异常对象),如:
public static void main(String[] args) {
String s = "abc";
if(s.equals("abc")) {
throw new PageException(s);
} else {
System.out.println(s);
}
//function();
}
throws----声明将要抛出何种类型的异常(声明)。
当某个方法可能会抛出某种异常时用于throws 声明可能抛出的异常,然后交给上层调用它的方法程序处理。如:
public static void function() throws NumberFormatException{
String s = "abc";
System.out.println(Double.parseDouble(s));
}
public static void main(String[] args) {
try {
function();
} catch (NumberFormatException e) {
System.err.println("非数据类型不能转换。");
//e.printStackTrace();
}
}
throw与throws的比较
1、throws出现在方法函数头;而throw出现在函数体。
2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
来看个例子:
throws e1,e2,e3只是告诉程序这个方法可能会抛出这些异常,方法的调用者可能要处理这些异常,而这些异常e1,e2,e3可能是该函数体产生的。
throw则是明确了这个地方要抛出这个异常。如:
void doA(int a) throws (Exception1,Exception2,Exception3){
try{
......
}catch(Exception1 e){
throw e;
}catch(Exception2 e){
System.out.println("出错了!");
}
if(a!=b)
throw new Exception3("自定义异常");
}
分析:
1.代码块中可能会产生3个异常,(Exception1,Exception2,Exception3)。
2.如果产生Exception1异常,则捕获之后再抛出,由该方法的调用者去处理。
3.如果产生Exception2异常,则该方法自己处理了(即System.out.println("出错了!");)。所以该方法就不会再向外抛出Exception2异常了,void doA() throws Exception1,Exception3 里面的Exception2也就不用写了。因为已经用try-catch语句捕获并处理了。
4.Exception3异常是该方法的某段逻辑出错,程序员自己做了处理,在该段逻辑错误的情况下抛出异常Exception3,则该方法的调用者也要处理此异常。这里用到了自定义异常,该异常下面会由解释。
使用throw和throws关键字需要注意以下几点:
1.throws的异常列表可以是抛出一条异常,也可以是抛出多条异常,每个类型的异常中间用逗号隔开
2.方法体中调用会抛出异常的方法或者是先抛出一个异常:用throw new Exception() throw写在方法体里,表示“抛出异常”这个动作。
3.如果某个方法调用了抛出异常的方法,那么必须添加try catch语句去尝试捕获这种异常, 或者添加声明,将异常抛出给更上一层的调用者进行处理
java中的异常链
异常需要封装,但是仅仅封装还是不够的,还需要传递异常。
异常链是一种面向对象编程技术,指将捕获的异常包装进一个新的异常中并重新抛出的异常处理方式。原异常被保存为新异常的一个属性(比如cause)。这样做的意义是一个方法应该抛出定义在相同的抽象层次上的异常,但不会丢弃更低层次的信息。
我可以这样理解异常链:
把捕获的异常包装成新的异常,在新异常里添加原始的异常,并将新异常抛出,它们就像是链式反应一样,一个导致(cause)另一个。这样在最后的顶层抛出的异常信息就包括了最底层的异常信息。
》场景
public class MyException extends Exception {
/**
* 错误编码
*/
private String errorCode;
public MyException(){}
/**
* 构造一个基本异常.
*
* @param message
* 信息描述
*/
public MyException(String message)
{
super(message);
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
}
public class Main {
public void test1() throws RuntimeException{
String[] sexs = {"男性","女性","中性"};
for(int i = 0; i < sexs.length; i++){
if("中性".equals(sexs[i])){
try {
throw new MyException("不存在中性的人!");
} catch (MyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
RuntimeException rte=new RuntimeException(e);//包装成RuntimeException异常
//rte.initCause(e);
throw rte;//抛出包装后的新的异常
}
}else{
System.out.println(sexs[i]);
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Main m =new Main();
try{
m.test1();
}catch (Exception e){
e.printStackTrace();
e.getCause();//获得原始异常
}
}
}
我们可以看到控制台先是输出了原始异常,这是由e.getCause()输出的;然后输出了e.printStackTrace(),在这里可以看到Caused by:原始异常和e.getCause()输出的一致。这样就是形成一个异常链。initCause()的作用是包装原始的异常,当想要知道底层发生了什么异常的时候调用getCause()就能获得原始异常。
》建议
异常需要封装和传递,我们在进行系统开发的时候,不要“吞噬”异常,也不要“赤裸裸”的抛出异常,封装后在抛出,或者通过异常链传递,可以达到系统更健壮、友好的目的。、
良好的编码习惯:
1、处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
2、在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常
3、对于不确定的代码,也可以加上try-catch,处理潜在的异常
4、尽量去处理异常,切记只是简单的调用printStackTrace()去打印
5、具体如何处理异常,要根据不同的业务需求和异常类型去决定
6、尽量添加finally语句块去释放占用的资源
Throwable类
Throwable类是整个异常体系类的父级类,当然最终追根溯源到底的父类当然要归于Object类。Throwable类实现了Serializable接口,表示Throwable可以进行序列化,继承自Object类,他的子类主要是Error和Exception类还有一个StackRecorder类(不是很常见)。
序列化问题
序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
三种情况:
1. 将对象存储再硬盘上。
2. 将对象通过网络传输。
3. 通过RMI远程调用等方式传输对象的时候。
在这三种情况下,是需要进行序列化然后传输的。
在Throwable类中使用输出流来进行输出,并把其对象作为输出流对象,这就需要必须实现序列化接口,使得其可以进行序列化,才能作为输出流中的对象进行输出。
private synchronized void writeObject(ObjectOutputStream s)
throws IOException {
// Ensure that the stackTrace field is initialized to a
// non-null value, if appropriate. As of JDK 7, a null stack
// trace field is a valid value indicating the stack trace
// should not be set.
getOurStackTrace();
StackTraceElement[] oldStackTrace = stackTrace;
try {
if (stackTrace == null)
stackTrace = SentinelHolder.STACK_TRACE_SENTINEL;
s.defaultWriteObject();
} finally {
stackTrace = oldStackTrace;
}
}
继承Object类
1. 在编译时,如果类没有继承的父类的话,会自动为其加入继承父类Object的相关的类的编译信息,这样在后面虚拟器进行解释执行的时候,按照存在父类进行处理就可以了。
2. 在虚拟机进行执行的时候,如果仍然存在没有父类的类,仍然会默认其父类为Object。
第一种情况属于再编译器进行处理,第二种情况属于在虚拟机上面做适当的处理。
子类Error和Exception
Error主要是用于表示java和虚拟机内部的异常信息,而Exception异常则是由于程序中可能存在各种的问题,是需要使用者去注意和捕获的异常。
从异常类的设计中体会到,设计者的抽象思维与设计水平令人叹服,通过一个类去抽象出所有异常中通用的方法与表示形式以及其表达的实体结构,而且通过继承的方式对异常这个领域做一个水平划分,将其切分为Error和Exception两种平行的异常类型,然后,这两者将再次作为各自类型的异常的父类,因为每一种异常同样是存在不同的分类,再次创建一系列的类去继承上面的两种异常派生出新的异常类型划分。这样不断的继承下去,逐步的细化到每一种具体的异常类型,形成一个丰富的异常类族。
从扩展性上而言,由于Throwable实现的是异常类中通用的部分,那么,如果再有特殊的异常分类的话,可以通过继承Throwable的方式去扩展该异常体系,当然,我们最常用的可能不会涉及到直接继承Throwable。
默认是空的StackTrace的节点数组初始化为空的stack,getOurStackTrace()方法实现的主要是获取当前节点异常的信息,获取栈上面的异常信息,遍历每一个异常信息,赋值给stackTrace进行保存。
private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
private synchronized StackTraceElement[] getOurStackTrace() {
if (stackTrace == UNASSIGNED_STACK ||
(stackTrace == null && backtrace != null) /* Out of protocol state */) {
int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth];
for (int i=0; i < depth; i++)
stackTrace[i] = getStackTraceElement(i);
} else if (stackTrace == null) {
return UNASSIGNED_STACK;
}
return stackTrace;
}
获取到栈异常信息以后,输出异常信息,对该数组进行遍历,输出异常的位置。
private void printStackTrace(PrintStreamOrWriter s) {
Set<Throwable> dejaVu =
Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
dejaVu.add(this);
synchronized (s.lock()) {
s.println(this);
StackTraceElement[] trace = getOurStackTrace();
for (StackTraceElement traceElement : trace)
s.println("\tat " + traceElement);
for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
}
}
其中,getSuppressed方法返回了被抑制(可能是try住的)的所有的异常信息,然后getCause()方法返回的是异常的原因,其中cause是一个Throwable属性,初始化为其本身this, 如果这个throwable中cause要么是抛出异常的类,要么是null,如果是Throwable类本身的话,那么只能说明Throwable类没有初始化完毕。
这个cause属性使得java可以通过链式的结构来组织异常信息,通过cause指向其下一个异常的抛出类。依次构成链状的结构。
通过递归的形式遍历并输出的过程。
private void printEnclosedStackTrace(PrintStreamOrWriter s,
StackTraceElement[] enclosingTrace,
String caption,
String prefix,
Set<Throwable> dejaVu) {
assert Thread.holdsLock(s.lock());
if (dejaVu.contains(this)) {
s.println("\t[CIRCULAR REFERENCE:" + this + "]");
} else {
dejaVu.add(this);
// Compute number of frames in common between this and enclosing trace
StackTraceElement[] trace = getOurStackTrace();
int m = trace.length - 1;
int n = enclosingTrace.length - 1;
while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) {
m--; n--;
}
int framesInCommon = trace.length - 1 - m;
// Print our stack trace
s.println(prefix + caption + this);
for (int i = 0; i <= m; i++)
s.println(prefix + "\tat " + trace[i]);
if (framesInCommon != 0)
s.println(prefix + "\t... " + framesInCommon + " more");
// Print suppressed exceptions, if any
for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
prefix +"\t", dejaVu);
// Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, prefix, dejaVu);
}
}
java源码
public class PageException extends RuntimeException {
public PageException() {
super();
}
public PageException(String message) {
super(message);
}
public PageException(String message, Throwable cause) {
super(message, cause);
}
public PageException(Throwable cause) {
super(cause);
}
RuntimeException
是在Java虚拟机的正常操作期间可以抛出的那些异常的超类。
RuntimeException
及其子类是未经检查的异常 。 unchecked异常不需要在方法或构造函数的拟申报throws
条款,如果他们可以通过该方法或构造函数的执行被抛出和方法或构造边界之外传播。
package java.lang;
public class RuntimeException extends Exception {
static final long serialVersionUID = -7034897190745766939L;
public RuntimeException() {
super();
}
public RuntimeException(String message) {
super(message);
}
public RuntimeException(String message, Throwable cause) {
super(message, cause);
}
public RuntimeException(Throwable cause) {
super(cause);
}
protected RuntimeException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
异常
类和任何不是RuntimeException
的子类的子类都是检查异常 。 检查的异常需要在方法或构造函数的throws
子句中声明,如果它们可以通过执行方法或构造函数抛出,并在方法或构造函数边界之外传播。
package java.lang;
public class Exception extends Throwable {
static final long serialVersionUID = -3387516993124229948L;
public Exception() {
super();
}
public Exception(String message) {
super(message);
}
public Exception(String message, Throwable cause) {
super(message, cause);
}
public Exception(Throwable cause) {
super(cause);
}
protected Exception(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
调用Thread.stop()方法时会抛出java.lang.ThreadDeath异常,但在通常的情况下,此异常不需要显式地捕捉。
方法Thread.stop()已经被作废,因为如果强制让线程停止则有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。
package java.lang;
public class ThreadDeath extends Error {
private static final long serialVersionUID = -4417128565033088268L;
}
一个Error
是的子类Throwable
表示严重的问题,合理的应用程序不应该试图捕获。 大多数这样的错误是异常情况。 ThreadDeath
错误虽然是“正常”的条件,但也是Error
一个子类,因为大多数应用程序不应该试图抓住它。
一个方法不需要在其throws
子句中声明Error的Error
,该子类可能在执行方法期间抛出,但未被捕获,因为这些错误是不应该发生的异常条件。 也就是说, Error
及其子类被视为未经检查的异常,用于编译时检查异常。
package java.lang;
public class Error extends Throwable {
static final long serialVersionUID = 4980196508277280342L;
public Error() {
super();
}
public Error(String message) {
super(message);
}
public Error(String message, Throwable cause) {
super(message, cause);
}
public Error(Throwable cause) {
super(cause);
}
protected Error(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
Modifier and Type | Method and Description |
---|---|
boolean |
equals(Object obj) 如果指定的对象是另一个 |
String |
getClassName() 返回包含由该堆栈跟踪元素表示的执行点的类的完全限定名称。 |
String |
getFileName() 返回包含由该堆栈跟踪元素表示的执行点的源文件的名称。 |
int |
getLineNumber() 返回包含由该堆栈跟踪元素表示的执行点的源行的行号。 |
String |
getMethodName() 返回包含由该堆栈跟踪元素表示的执行点的方法的名称。 |
int |
hashCode() 返回此堆栈跟踪元素的哈希码值。 |
boolean |
isNativeMethod() 如果包含由此堆栈跟踪元素表示的执行点的方法是本机方法,则返回true。 |
String |
toString() 返回此堆栈跟踪元素的字符串表示形式。 |
StackTrace
StackTrace(堆栈轨迹)存放的就是方法调用栈的信息,异常处理中常用的printStackTrace()实质就是打印异常调用的堆栈信息。
StackTraceElement表示StackTrace(堆栈轨迹)中的一个方法对象,属性包括方法的类名、方法名、文件名以及调用的行数。
StackTraceElement被定义为final,可见其作为一个java的基础类不允许被继承。
获取StackTraceElement的方法有两种,均返回StackTraceElement数组,也就是这个栈的信息。
1、Thread.currentThread().getStackTrace()
2、new Throwable().getStackTrace()
StackTraceElement数组包含了StackTrace(堆栈轨迹)的内容,通过遍历它可以得到方法间的调用过程,即可以得到当前方法以及其调用者的方法名、调用行数等信息
public class TestClass {
public static void main(String[] args)
{
new TestClass().methodA();
}
private void methodA(){
System.out.println("------进入methodA----------");
methodB();
}
private void methodB(){
System.out.println("------进入methodB----------");
StackTraceElement elements[] = Thread.currentThread().getStackTrace();
for (int i = 0; i < elements.length; i++) {
StackTraceElement stackTraceElement=elements[i];
String className=stackTraceElement.getClassName();
String methodName=stackTraceElement.getMethodName();
String fileName=stackTraceElement.getFileName();
int lineNumber=stackTraceElement.getLineNumber();
System.out.println("StackTraceElement数组下标 i="+i+",fileName="
+fileName+",className="+className+",methodName="+methodName+",lineNumber="+lineNumber);
}
}
}
我们可以封装一个日志库,在打印目标日志的时候,也可以通过这个调用栈打印出这个日志所在的行数,这样就可以迅速的定位到日志输出行,再也不要全局搜索去查找了。
public static void d(String tag, String msg, Object... params) {
StackTraceElement targetStackTraceElement = getTargetStackTraceElement();
Log.d(tag, "(" + targetStackTraceElement.getFileName() + ":"
+ targetStackTraceElement.getLineNumber() + ")");
Log.d(tag, String.format(msg, params));
}
package java.lang;
import java.util.Objects;
public final class StackTraceElement implements java.io.Serializable {
// Normally initialized by VM (public constructor added in 1.5)
private String declaringClass;
private String methodName;
private String fileName;
private int lineNumber;
public StackTraceElement(String declaringClass, String methodName,
String fileName, int lineNumber) {
this.declaringClass = Objects.requireNonNull(declaringClass, "Declaring class is null");
this.methodName = Objects.requireNonNull(methodName, "Method name is null");
this.fileName = fileName;
this.lineNumber = lineNumber;
}
public String getFileName() {
return fileName;
}
public int getLineNumber() {
return lineNumber;
}
public String getClassName() {
return declaringClass;
}
public String getMethodName() {
return methodName;
}
public boolean isNativeMethod() {
return lineNumber == -2;
}
public String toString() {
return getClassName() + "." + methodName +
(isNativeMethod() ? "(Native Method)" :
(fileName != null && lineNumber >= 0 ?
"(" + fileName + ":" + lineNumber + ")" :
(fileName != null ? "("+fileName+")" : "(Unknown Source)")));
}
public boolean equals(Object obj) {
if (obj==this)
return true;
if (!(obj instanceof StackTraceElement))
return false;
StackTraceElement e = (StackTraceElement)obj;
return e.declaringClass.equals(declaringClass) &&
e.lineNumber == lineNumber &&
Objects.equals(methodName, e.methodName) &&
Objects.equals(fileName, e.fileName);
}
/**
* Returns a hash code value for this stack trace element.
*/
public int hashCode() {
int result = 31*declaringClass.hashCode() + methodName.hashCode();
result = 31*result + Objects.hashCode(fileName);
result = 31*result + lineNumber;
return result;
}
private static final long serialVersionUID = 6992337162326171013L;
}
Modifier and Type | Method and Description |
---|---|
void |
addSuppressed(Throwable exception) 将指定的异常附加到为了传递此异常而被抑制的异常。 |
Throwable |
fillInStackTrace() 填写执行堆栈跟踪。 |
Throwable |
getCause() 如果原因不存在或未知,则返回此throwable的原因或 |
String |
getLocalizedMessage() 创建此可抛出的本地化描述。 |
String |
getMessage() 返回此throwable的详细消息字符串。 |
StackTraceElement[] |
getStackTrace() 提供对 |
Throwable[] |
getSuppressed() 返回一个包含所有被抑制的异常的数组,通常由 |
Throwable |
initCause(Throwable cause) 将此throwable的 原因初始化为指定值。 |
void |
printStackTrace() 将此throwable和其追溯打印到标准错误流。 |
void |
printStackTrace(PrintStream s) 将此throwable和其追溯打印到指定的打印流。 |
void |
printStackTrace(PrintWriter s) 将此throwable和其追溯打印到指定的打印作者。 |
void |
setStackTrace(StackTraceElement[] stackTrace) 设置将被返回的堆栈微量元素 |
String |
toString() 返回此可抛出的简短描述。 |
package java.lang;
import java.io.*;
import java.util.*;
public class Throwable implements Serializable {
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -3042686055658047285L;
private transient Object backtrace;
private String detailMessage;
private static class SentinelHolder {
public static final StackTraceElement STACK_TRACE_ELEMENT_SENTINEL =
new StackTraceElement("", "", null, Integer.MIN_VALUE);
public static final StackTraceElement[] STACK_TRACE_SENTINEL =
new StackTraceElement[] {STACK_TRACE_ELEMENT_SENTINEL};
}
private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
private Throwable cause = this;
private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
// Setting this static field introduces an acceptable
// initialization dependency on a few java.util classes.
private static final List<Throwable> SUPPRESSED_SENTINEL =
Collections.unmodifiableList(new ArrayList<Throwable>(0));
private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;
/** Message for trying to suppress a null exception. */
private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception.";
/** Message for trying to suppress oneself. */
private static final String SELF_SUPPRESSION_MESSAGE = "Self-suppression not permitted";
/** Caption for labeling causative exception stack traces */
private static final String CAUSE_CAPTION = "Caused by: ";
/** Caption for labeling suppressed exception stack traces */
private static final String SUPPRESSED_CAPTION = "Suppressed: ";
public Throwable() {
fillInStackTrace();
}
public Throwable(String message) {
fillInStackTrace();
detailMessage = message;
}
public Throwable(String message, Throwable cause) {
fillInStackTrace();
detailMessage = message;
this.cause = cause;
}
public Throwable(Throwable cause) {
fillInStackTrace();
detailMessage = (cause==null ? null : cause.toString());
this.cause = cause;
}
protected Throwable(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
if (writableStackTrace) {
fillInStackTrace();
} else {
stackTrace = null;
}
detailMessage = message;
this.cause = cause;
if (!enableSuppression)
suppressedExceptions = null;
}
public String getMessage() {
return detailMessage;
}
public String getLocalizedMessage() {
return getMessage();
}
public synchronized Throwable getCause() {
return (cause==this ? null : cause);
}
public synchronized Throwable initCause(Throwable cause) {
if (this.cause != this)
throw new IllegalStateException("Can't overwrite cause with " +
Objects.toString(cause, "a null"), this);
if (cause == this)
throw new IllegalArgumentException("Self-causation not permitted", this);
this.cause = cause;
return this;
}
public String toString() {
String s = getClass().getName();
String message = getLocalizedMessage();
return (message != null) ? (s + ": " + message) : s;
}
public void printStackTrace() {
printStackTrace(System.err);
}
public void printStackTrace(PrintStream s) {
printStackTrace(new WrappedPrintStream(s));
}
private void printStackTrace(PrintStreamOrWriter s) {
// Guard against malicious overrides of Throwable.equals by
// using a Set with identity equality semantics.
Set<Throwable> dejaVu =
Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
dejaVu.add(this);
synchronized (s.lock()) {
// Print our stack trace
s.println(this);
StackTraceElement[] trace = getOurStackTrace();
for (StackTraceElement traceElement : trace)
s.println("\tat " + traceElement);
// Print suppressed exceptions, if any
for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu);
// Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, "", dejaVu);
}
}
private void printEnclosedStackTrace(PrintStreamOrWriter s,
StackTraceElement[] enclosingTrace,
String caption,
String prefix,
Set<Throwable> dejaVu) {
assert Thread.holdsLock(s.lock());
if (dejaVu.contains(this)) {
s.println("\t[CIRCULAR REFERENCE:" + this + "]");
} else {
dejaVu.add(this);
// Compute number of frames in common between this and enclosing trace
StackTraceElement[] trace = getOurStackTrace();
int m = trace.length - 1;
int n = enclosingTrace.length - 1;
while (m >= 0 && n >=0 && trace[m].equals(enclosingTrace[n])) {
m--; n--;
}
int framesInCommon = trace.length - 1 - m;
// Print our stack trace
s.println(prefix + caption + this);
for (int i = 0; i <= m; i++)
s.println(prefix + "\tat " + trace[i]);
if (framesInCommon != 0)
s.println(prefix + "\t... " + framesInCommon + " more");
// Print suppressed exceptions, if any
for (Throwable se : getSuppressed())
se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
prefix +"\t", dejaVu);
// Print cause, if any
Throwable ourCause = getCause();
if (ourCause != null)
ourCause.printEnclosedStackTrace(s, trace, CAUSE_CAPTION, prefix, dejaVu);
}
}
public void printStackTrace(PrintWriter s) {
printStackTrace(new WrappedPrintWriter(s));
}
private abstract static class PrintStreamOrWriter {
/** Returns the object to be locked when using this StreamOrWriter */
abstract Object lock();
/** Prints the specified string as a line on this StreamOrWriter */
abstract void println(Object o);
}
private static class WrappedPrintStream extends PrintStreamOrWriter {
private final PrintStream printStream;
WrappedPrintStream(PrintStream printStream) {
this.printStream = printStream;
}
Object lock() {
return printStream;
}
void println(Object o) {
printStream.println(o);
}
}
private static class WrappedPrintWriter extends PrintStreamOrWriter {
private final PrintWriter printWriter;
WrappedPrintWriter(PrintWriter printWriter) {
this.printWriter = printWriter;
}
Object lock() {
return printWriter;
}
void println(Object o) {
printWriter.println(o);
}
}
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
private native Throwable fillInStackTrace(int dummy);
public StackTraceElement[] getStackTrace() {
return getOurStackTrace().clone();
}
private synchronized StackTraceElement[] getOurStackTrace() {
// Initialize stack trace field with information from
// backtrace if this is the first call to this method
if (stackTrace == UNASSIGNED_STACK ||
(stackTrace == null && backtrace != null) /* Out of protocol state */) {
int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth];
for (int i=0; i < depth; i++)
stackTrace[i] = getStackTraceElement(i);
} else if (stackTrace == null) {
return UNASSIGNED_STACK;
}
return stackTrace;
}
public void setStackTrace(StackTraceElement[] stackTrace) {
// Validate argument
StackTraceElement[] defensiveCopy = stackTrace.clone();
for (int i = 0; i < defensiveCopy.length; i++) {
if (defensiveCopy[i] == null)
throw new NullPointerException("stackTrace[" + i + "]");
}
synchronized (this) {
if (this.stackTrace == null && // Immutable stack
backtrace == null) // Test for out of protocol state
return;
this.stackTrace = defensiveCopy;
}
}
native int getStackTraceDepth();
native StackTraceElement getStackTraceElement(int index);
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject(); // read in all fields
if (suppressedExceptions != null) {
List<Throwable> suppressed = null;
if (suppressedExceptions.isEmpty()) {
// Use the sentinel for a zero-length list
suppressed = SUPPRESSED_SENTINEL;
} else { // Copy Throwables to new list
suppressed = new ArrayList<>(1);
for (Throwable t : suppressedExceptions) {
// Enforce constraints on suppressed exceptions in
// case of corrupt or malicious stream.
if (t == null)
throw new NullPointerException(NULL_CAUSE_MESSAGE);
if (t == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE);
suppressed.add(t);
}
}
suppressedExceptions = suppressed;
} // else a null suppressedExceptions field remains null
if (stackTrace != null) {
if (stackTrace.length == 0) {
stackTrace = UNASSIGNED_STACK.clone();
} else if (stackTrace.length == 1 &&
// Check for the marker of an immutable stack trace
SentinelHolder.STACK_TRACE_ELEMENT_SENTINEL.equals(stackTrace[0])) {
stackTrace = null;
} else { // Verify stack trace elements are non-null.
for(StackTraceElement ste : stackTrace) {
if (ste == null)
throw new NullPointerException("null StackTraceElement in serial stream. ");
}
}
} else {
// A null stackTrace field in the serial form can result
// from an exception serialized without that field in
// older JDK releases; treat such exceptions as having
// empty stack traces.
stackTrace = UNASSIGNED_STACK.clone();
}
}
private synchronized void writeObject(ObjectOutputStream s)
throws IOException {
// Ensure that the stackTrace field is initialized to a
// non-null value, if appropriate. As of JDK 7, a null stack
// trace field is a valid value indicating the stack trace
// should not be set.
getOurStackTrace();
StackTraceElement[] oldStackTrace = stackTrace;
try {
if (stackTrace == null)
stackTrace = SentinelHolder.STACK_TRACE_SENTINEL;
s.defaultWriteObject();
} finally {
stackTrace = oldStackTrace;
}
}
public final synchronized void addSuppressed(Throwable exception) {
if (exception == this)
throw new IllegalArgumentException(SELF_SUPPRESSION_MESSAGE, exception);
if (exception == null)
throw new NullPointerException(NULL_CAUSE_MESSAGE);
if (suppressedExceptions == null) // Suppressed exceptions not recorded
return;
if (suppressedExceptions == SUPPRESSED_SENTINEL)
suppressedExceptions = new ArrayList<>(1);
suppressedExceptions.add(exception);
}
private static final Throwable[] EMPTY_THROWABLE_ARRAY = new Throwable[0];
public final synchronized Throwable[] getSuppressed() {
if (suppressedExceptions == SUPPRESSED_SENTINEL ||
suppressedExceptions == null)
return EMPTY_THROWABLE_ARRAY;
else
return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
}
}