JDK

java.lang.Throwableクラスのソースコード

今回はjava.lang.Throwableクラスのソースコードを読んでみる。これはエラーでスローされる例外の最上位のクラスである。try-carhでThrowableをキャッチすれば,すべてのエラーをキャッチできる(JVMのエラーまでキャッチしてしまうため,適切ではない)。

 publicなコンストラクタが4種類定義されているので,インスタンスを作成することは可能であるが,やはりするべきではない書き方と言えよう。ユーザ例外を定義する場合は,java.lang.Exception,あるいは,java.lang.RuntimeExceptionを継承して作成する。

 定義されているメソッドは,エラーメッセージを管理するためのメソッドと,スタックトレースを管理するためのメソッドである。例外クラスの実質的な機能は,ほぼjava.lang.Throwableクラスで実装されていると言える。

/*
 * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the “Classpath” exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.lang;

import  java.io.*;
import  java.util.*;

public class Throwable implements Serializable {
    private transient Object backtrace;
    private String detailMessage;
    private static final StackTraceElement[] UNASSIGNED_STACK = new StackTraceElement[0];
    private Throwable cause = this;
    private StackTraceElement[] stackTrace = UNASSIGNED_STACK;
    private transient int depth;
    private static final List<Throwable> SUPPRESSED_SENTINEL = Collections.emptyList();
    private List<Throwable> suppressedExceptions = SUPPRESSED_SENTINEL;
    private static final String NULL_CAUSE_MESSAGE = “Cannot suppress a null exception.”;
    private static final String SELF_SUPPRESSION_MESSAGE = “Self-suppression not permitted”;
    private static final String CAUSE_CAPTION = “Caused by: “;
    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;
    }
    final void setCause(Throwable t) {
        this.cause = t;
    }
    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) {
        Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<>());
        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);
        }
    }
    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);
            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;
            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”);
            for (Throwable se : getSuppressed())
                se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION,
                                           prefix +”\t”, dejaVu);
            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 {
        abstract Object lock();
        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() {
        if (stackTrace == UNASSIGNED_STACK ||
            (stackTrace == null && backtrace != null) /* Out of protocol state */) {
            stackTrace = StackTraceElement.of(this, depth);
        } else if (stackTrace == null) {
            return UNASSIGNED_STACK;
        }
        return stackTrace;
    }
    public void setStackTrace(StackTraceElement[] stackTrace) {
        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;
        }
    }
    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.
                    Objects.requireNonNull(t, 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) {
                    Objects.requireNonNull(ste, “null StackTraceElement in serial stream.”);
                }
            }
        } else {
            stackTrace = UNASSIGNED_STACK.clone();
        }
    }
    private synchronized void writeObject(ObjectOutputStream s)
        throws IOException {
        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);
        Objects.requireNonNull(exception, 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);
    }
}