JDK

java.io.OutputStreamクラス

OutputStreamクラスもByteArrayOutputStreamクラス,FileOutputStreamクラス,ObjectOutputStreamクラスなどのスーパークラスとなる抽象クラスである。

 InputStreamクラスの場合と同じように,何らの出力もしない無名の実装クラスを返すnullOutputStream()メソッドが定義されている。

抽象メソッドとしては,void write(int b)だけが宣言されている。flush()メソッドや,close()メソッドは実装はされているものの,中身は空である。

write(byte b[], int off, int len)メソッドはオフセットと長さを指定して,バイト配列を出力する。forループで配列の内容を繰り返しながら,抽象メソッドであるwrite(int)メソッドを呼び出す実装である。write(byte b[])メソッドは,渡された配列を先頭からすべて出力するようにwrite(b, 0, b.length)を実行するだけである。

/*
 * Copyright (c) 1994, 2018, 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.io;
import java.util.Objects;
public abstract class OutputStream implements Closeable, Flushable {
    public static OutputStream nullOutputStream() {
        return new OutputStream() {
            private volatile boolean closed;
            private void ensureOpen() throws IOException {
                if (closed) {
                    throw new IOException("Stream closed");
                }
            }
            @Override
            public void write(int b) throws IOException {
                ensureOpen();
            }
            @Override
            public void write(byte b[], int off, int len) throws IOException {
                Objects.checkFromIndexSize(off, len, b.length);
                ensureOpen();
            }
            @Override
            public void close() {
                closed = true;
            }
        };
    }
    public abstract void write(int b) throws IOException;
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }
    public void write(byte b[], int off, int len) throws IOException {
        Objects.checkFromIndexSize(off, len, b.length);
        // len == 0 condition implicitly handled by loop bounds
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }
    public void flush() throws IOException {
    }
    public void close() throws IOException {
    }
}
JDK

java.io. InputStreamクラス

InputStreamクラスは抽象クラスであり,FileInputStreamクラス,ByteArrayInputStreamクラス,ObjectInputStreamクラス等のスーパークラスとなっている。内部にダミーの実装クラスを返すnullInputStream()メソッドが実装されている。

 抽象メソッドとしては,public abstract int read() throws IOExceptionメソッドが定義されているのみである。

read(byte b[], int off, int len)メソッドはバイト配列から指定したオフセットと長さを読み込む。このメソッド内で抽象メソッドであるread()を呼び出している。

また,readNBytes(int len)メソッドはファイルの先頭から指定した長さだけを読み込む。内部では指定のバッファサイズごとに読み込むように実装されている。読み込んだ結果はbyte[]型で返却する。これはJava 11から実装されたメソッドのようだ。readNBytes(byte[] b, int off, int len)メソッドはオフセットと長さを指定できる。こちらも比較的新しくJava9から実装されたメソッドのようである。

 特筆すべきメソッドとして,transferTo(OutputStream out)が挙げられる。これは自身のストリームの内容をOutputStreamオブジェクトに書き込むというものである。Java 9から実装されたもののようである。close()メソッドやmark(int readlimit)close()メソッドや

close()メソッドやmark(int readlimit)メソッドは抽象メソッドではないが,実装コードは空である。

/*
 * 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.io;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public abstract class InputStream implements Closeable {
    // MAX_SKIP_BUFFER_SIZE is used to determine the maximum buffer size to
    // use when skipping.
    private static final int MAX_SKIP_BUFFER_SIZE = 2048;
    private static final int DEFAULT_BUFFER_SIZE = 8192;
    public static InputStream nullInputStream() {
        return new InputStream() {
            private volatile boolean closed;
            private void ensureOpen() throws IOException {
                if (closed) {
                    throw new IOException(“Stream closed”);
                }
            }
            @Override
            public int available () throws IOException {
                ensureOpen();
                return 0;
            }
            @Override
            public int read() throws IOException {
                ensureOpen();
                return -1;
            }
            @Override
            public int read(byte[] b, int off, int len) throws IOException {
                Objects.checkFromIndexSize(off, len, b.length);
                if (len == 0) {
                    return 0;
                }
                ensureOpen();
                return -1;
            }
            @Override
            public byte[] readAllBytes() throws IOException {
                ensureOpen();
                return new byte[0];
            }
            @Override
            public int readNBytes(byte[] b, int off, int len)
                throws IOException {
                Objects.checkFromIndexSize(off, len, b.length);
                ensureOpen();
                return 0;
            }
            @Override
            public byte[] readNBytes(int len) throws IOException {
                if (len < 0) {
                    throw new IllegalArgumentException(“len < 0”);
                }
                ensureOpen();
                return new byte[0];
            }
            @Override
            public long skip(long n) throws IOException {
                ensureOpen();
                return 0L;
            }
            @Override
            public void skipNBytes(long n) throws IOException {
                ensureOpen();
                if (n > 0) {
                    throw new EOFException();
                }
            }
            @Override
            public long transferTo(OutputStream out) throws IOException {
                Objects.requireNonNull(out);
                ensureOpen();
                return 0L;
            }
            @Override
            public void close() throws IOException {
                closed = true;
            }
        };
    }
    public abstract int read() throws IOException;
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
    public int read(byte b[], int off, int len) throws IOException {
        Objects.checkFromIndexSize(off, len, b.length);
        if (len == 0) {
            return 0;
        }
        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;
        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }
    private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE – 8;
    public byte[] readAllBytes() throws IOException {
        return readNBytes(Integer.MAX_VALUE);
    }
    public byte[] readNBytes(int len) throws IOException {
        if (len < 0) {
            throw new IllegalArgumentException(“len < 0”);
        }
        List<byte[]> bufs = null;
        byte[] result = null;
        int total = 0;
        int remaining = len;
        int n;
        do {
            byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];
            int nread = 0;
            // read to EOF which may read more or less than buffer size
            while ((n = read(buf, nread,
                    Math.min(buf.length – nread, remaining))) > 0) {
                nread += n;
                remaining -= n;
            }
            if (nread > 0) {
                if (MAX_BUFFER_SIZE – total < nread) {
                    throw new OutOfMemoryError(“Required array size too large”);
                }
                total += nread;
                if (result == null) {
                    result = buf;
                } else {
                    if (bufs == null) {
                        bufs = new ArrayList<>();
                        bufs.add(result);
                    }
                    bufs.add(buf);
                }
            }
            // if the last call to read returned -1 or the number of bytes
            // requested have been read then break
        } while (n >= 0 && remaining > 0);
        if (bufs == null) {
            if (result == null) {
                return new byte[0];
            }
            return result.length == total ?
                result : Arrays.copyOf(result, total);
        }
        result = new byte[total];
        int offset = 0;
        remaining = total;
        for (byte[] b : bufs) {
            int count = Math.min(b.length, remaining);
            System.arraycopy(b, 0, result, offset, count);
            offset += count;
            remaining -= count;
        }
        return result;
    }
    public int readNBytes(byte[] b, int off, int len) throws IOException {
        Objects.checkFromIndexSize(off, len, b.length);
        int n = 0;
        while (n < len) {
            int count = read(b, off + n, len – n);
            if (count < 0)
                break;
            n += count;
        }
        return n;
    }
    public long skip(long n) throws IOException {
        long remaining = n;
        int nr;
        if (n <= 0) {
            return 0;
        }
        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        while (remaining > 0) {
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            if (nr < 0) {
                break;
            }
            remaining -= nr;
        }
        return n – remaining;
    }
    public void skipNBytes(long n) throws IOException {
        if (n > 0) {
            long ns = skip(n);
            if (ns >= 0 && ns < n) { // skipped too few bytes
                // adjust number to skip
                n -= ns;
                // read until requested number skipped or EOS reached
                while (n > 0 && read() != -1) {
                    n–;
                }
                // if not enough skipped, then EOFE
                if (n != 0) {
                    throw new EOFException();
                }
            } else if (ns != n) { // skipped negative or too many bytes
                throw new IOException(“Unable to skip exactly”);
            }
        }
    }
    public int available() throws IOException {
        return 0;
    }
    public void close() throws IOException {}
    public synchronized void mark(int readlimit) {}
    public synchronized void reset() throws IOException {
        throw new IOException(“mark/reset not supported”);
    }
    public boolean markSupported() {
        return false;
    }
    public long transferTo(OutputStream out) throws IOException {
        Objects.requireNonNull(out, “out”);
        long transferred = 0;
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        int read;
        while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
            out.write(buffer, 0, read);
            transferred += read;
        }
        return transferred;
    }
}
JDK

java.io.BufferedWriterクラス

 ファイル等を書き込む際に,バッファリングを行って高速化するWriterクラスである。Writerクラスのインスタンスoutをラップしている。バッファはchar型の配列でディフォルトのサイズは8192と定義されている。

 コンストラクタは次のようなものが定義されている。一つ目のコンストラクタは,引数を上述のディフォルトバッファサイズと共に2つ目のコンストラクタへ渡している。そして,バッファサイズはnCharsというインスタンス変数に保存される。また,次に書き込む配列インデックスを表すnextCharというインスタンス変数に0がセットされる。

  • BufferedWriter(Writer out)
  • BufferedWriter(Writer out, int sz)

write(int c)メソッドはこのクラスの中では低レベルのメソッドで,int型の値をバッファに書き出す。そして,次に書き込むバッファ位置がバッファサイズを超えていたら,flushBuffer()メソッドを呼び出して,バッファをフラッシュする。

  flushBuffer() メソッドでは,ラップしているWriterクラスのwrite()メソッドを用いてバッファの内容を書き込み,次に書き込むバッファ位置を示すnextCharを0に戻す。

write(char cbuf[], int off, int len)メソッドはchar型の配列で渡されたデータをオフセットと長さを指定してバッファに書き込む。この時,長さがバッファサイズを超えている場合には,直接ラップしているWriterクラスのwriteメソッドを呼び出している。バッファサイズより大きいものを書き込もうとしたら,バッファの効果がなくなるので合理的な処理である。一方,バッファサイズより書き込むデータが短い場合はSystem.arraycopyメソッドを使って,データをバッファにコピーする。

public void write(String s, int off, int len)メソッドは,文字列をバッファに書き込む。StringクラスのgetChars()メソッドでchar型の配列に変換したものを書き込んでいるに過ぎない。

/*
 * Copyright (c) 1996, 2016, 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.io;
public class BufferedWriter extends Writer {
    private Writer out;
    private char cb[];
    private int nChars, nextChar;
    private static int defaultCharBufferSize = 8192;
    public BufferedWriter(Writer out) {
        this(out, defaultCharBufferSize);
    }
    public BufferedWriter(Writer out, int sz) {
        super(out);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        this.out = out;
        cb = new char[sz];
        nChars = sz;
        nextChar = 0;
    }
    private void ensureOpen() throws IOException {
        if (out == null)
            throw new IOException("Stream closed");
    }
    void flushBuffer() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar == 0)
                return;
            out.write(cb, 0, nextChar);
            nextChar = 0;
        }
    }
    public void write(int c) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (nextChar >= nChars)
                flushBuffer();
            cb[nextChar++] = (char) c;
        }
    }
    private int min(int a, int b) {
        if (a < b) return a;
        return b;
    }
    public void write(char cbuf[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return;
            }
            if (len >= nChars) {
                /* If the request length exceeds the size of the output buffer,
                   flush the buffer and then write the data directly.  In this
                   way buffered streams will cascade harmlessly. */
                flushBuffer();
                out.write(cbuf, off, len);
                return;
            }
            int b = off, t = off + len;
            while (b < t) {
                int d = min(nChars – nextChar, t – b);
                System.arraycopy(cbuf, b, cb, nextChar, d);
                b += d;
                nextChar += d;
                if (nextChar >= nChars)
                    flushBuffer();
            }
        }
    }
    public void write(String s, int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            int b = off, t = off + len;
            while (b < t) {
                int d = min(nChars – nextChar, t – b);
                s.getChars(b, b + d, cb, nextChar);
                b += d;
                nextChar += d;
                if (nextChar >= nChars)
                    flushBuffer();
            }
        }
    }
    public void newLine() throws IOException {
        write(System.lineSeparator());
    }
    public void flush() throws IOException {
        synchronized (lock) {
            flushBuffer();
            out.flush();
        }
    }
    @SuppressWarnings("try")
    public void close() throws IOException {
        synchronized (lock) {
            if (out == null) {
                return;
            }
            try (Writer w = out) {
                flushBuffer();
            } finally {
                out = null;
                cb = null;
            }
        }
    }
}
JDK

java.io.BufferedReaderクラス

Readerクラスをバッファリングして高速化するためのBufferedReaderクラスである。privateな Reader 型のインスタンス変数inがバッファリングする対象の入力である。ディフォルトのバッファーサイズとしてdefaultCharBufferSizeという変数に8192が設定されており,初期値は8KBであることがわかる。また,defaultExpectedLineLengthとしてディフォルトの1行の長さが80であることがわかる。

コンストラクタとして,次のようなものが定義されている。2番目のコンストラクタは第2引数にdefaultCharBufferSizeを与えて,1つめのコンストラクタを呼び出している。

  • BufferedReader(Reader in, int sz)
  • public BufferedReader(Reader in)

public int read() メソッドは,バッファから1文字を読込んで,int型で返す。もし読み込んだバッファが無効な領域に達した場合は-1を返す。public int read(char cbuf[], int off, int len)メソッドは指定した位置から指定した長さだけ読み込んで,第1引数の配列へ格納していく。

 public String readLine()メソッドはテキストファイルの読み込みではもっともよく使用するメソッドの一つだろう。改行までを読み込む。内部ではさらにパッケージプライベートなreadLine(boolean ignoreLF)メソッドを呼び出している。処理内容は改行コードが現れるまでループで読込み続けるという単純なものである。

このほか,読み飛ばすためのskip(long n)メソッドや,Readerをクローズするclose()メソッドが定義されている。

/*
 * Copyright (c) 1996, 2017, 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.io;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class BufferedReader extends Reader {
    private Reader in;
    private char cb[];
    private int nChars, nextChar;
    private static final int INVALIDATED = -2;
    private static final int UNMARKED = -1;
    private int markedChar = UNMARKED;
    private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
    private boolean skipLF = false;
    private boolean markedSkipLF = false;
    private static int defaultCharBufferSize = 8192;
    private static int defaultExpectedLineLength = 80;
    public BufferedReader(Reader in, int sz) {
        super(in);
        if (sz <= 0)
            throw new IllegalArgumentException(“Buffer size <= 0”);
        this.in = in;
        cb = new char[sz];
        nextChar = nChars = 0;
    }
    public BufferedReader(Reader in) {
        this(in, defaultCharBufferSize);
    }
    private void ensureOpen() throws IOException {
        if (in == null)
            throw new IOException(“Stream closed”);
    }
    private void fill() throws IOException {
        int dst;
        if (markedChar <= UNMARKED) {
            /* No mark */
            dst = 0;
        } else {
            /* Marked */
            int delta = nextChar – markedChar;
            if (delta >= readAheadLimit) {
                /* Gone past read-ahead limit: Invalidate mark */
                markedChar = INVALIDATED;
                readAheadLimit = 0;
                dst = 0;
            } else {
                if (readAheadLimit <= cb.length) {
                    /* Shuffle in the current buffer */
                    System.arraycopy(cb, markedChar, cb, 0, delta);
                    markedChar = 0;
                    dst = delta;
                } else {
                    /* Reallocate buffer to accommodate read-ahead limit */
                    char ncb[] = new char[readAheadLimit];
                    System.arraycopy(cb, markedChar, ncb, 0, delta);
                    cb = ncb;
                    markedChar = 0;
                    dst = delta;
                }
                nextChar = nChars = delta;
            }
        }
        int n;
        do {
            n = in.read(cb, dst, cb.length – dst);
        } while (n == 0);
        if (n > 0) {
            nChars = dst + n;
            nextChar = dst;
        }
    }
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            for (;;) {
                if (nextChar >= nChars) {
                    fill();
                    if (nextChar >= nChars)
                        return -1;
                }
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == ‘\n’) {
                        nextChar++;
                        continue;
                    }
                }
                return cb[nextChar++];
            }
        }
    }
    private int read1(char[] cbuf, int off, int len) throws IOException {
        if (nextChar >= nChars) {
            /* If the requested length is at least as large as the buffer, and
               if there is no mark/reset activity, and if line feeds are not
               being skipped, do not bother to copy the characters into the
               local buffer.  In this way buffered streams will cascade
               harmlessly. */
            if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
                return in.read(cbuf, off, len);
            }
            fill();
        }
        if (nextChar >= nChars) return -1;
        if (skipLF) {
            skipLF = false;
            if (cb[nextChar] == ‘\n’) {
                nextChar++;
                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars)
                    return -1;
            }
        }
        int n = Math.min(len, nChars – nextChar);
        System.arraycopy(cb, nextChar, cbuf, off, n);
        nextChar += n;
        return n;
    }
    public int read(char cbuf[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }
            int n = read1(cbuf, off, len);
            if (n <= 0) return n;
            while ((n < len) && in.ready()) {
                int n1 = read1(cbuf, off + n, len – n);
                if (n1 <= 0) break;
                n += n1;
            }
            return n;
        }
    }
    String readLine(boolean ignoreLF) throws IOException {
        StringBuffer s = null;
        int startChar;
        synchronized (lock) {
            ensureOpen();
            boolean omitLF = ignoreLF || skipLF;
        bufferLoop:
            for (;;) {
                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars) { /* EOF */
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
                boolean eol = false;
                char c = 0;
                int i;
                /* Skip a leftover ‘\n’, if necessary */
                if (omitLF && (cb[nextChar] == ‘\n’))
                    nextChar++;
                skipLF = false;
                omitLF = false;
            charLoop:
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    if ((c == ‘\n’) || (c == ‘\r’)) {
                        eol = true;
                        break charLoop;
                    }
                }
                startChar = nextChar;
                nextChar = i;
                if (eol) {
                    String str;
                    if (s == null) {
                        str = new String(cb, startChar, i – startChar);
                    } else {
                        s.append(cb, startChar, i – startChar);
                        str = s.toString();
                    }
                    nextChar++;
                    if (c == ‘\r’) {
                        skipLF = true;
                    }
                    return str;
                }
                if (s == null)
                    s = new StringBuffer(defaultExpectedLineLength);
                s.append(cb, startChar, i – startChar);
            }
        }
    }
    public String readLine() throws IOException {
        return readLine(false);
    }
    public long skip(long n) throws IOException {
        if (n < 0L) {
            throw new IllegalArgumentException(“skip value is negative”);
        }
        synchronized (lock) {
            ensureOpen();
            long r = n;
            while (r > 0) {
                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars) /* EOF */
                    break;
                if (skipLF) {
                    skipLF = false;
                    if (cb[nextChar] == ‘\n’) {
                        nextChar++;
                    }
                }
                long d = nChars – nextChar;
                if (r <= d) {
                    nextChar += r;
                    r = 0;
                    break;
                }
                else {
                    r -= d;
                    nextChar = nChars;
                }
            }
            return n – r;
        }
    }
    public boolean ready() throws IOException {
        synchronized (lock) {
            ensureOpen();
            /*
             * If newline needs to be skipped and the next char to be read
             * is a newline character, then just skip it right away.
             */
            if (skipLF) {
                /* Note that in.ready() will return true if and only if the next
                 * read on the stream will not block.
                 */
                if (nextChar >= nChars && in.ready()) {
                    fill();
                }
                if (nextChar < nChars) {
                    if (cb[nextChar] == ‘\n’)
                        nextChar++;
                    skipLF = false;
                }
            }
            return (nextChar < nChars) || in.ready();
        }
    }
    public boolean markSupported() {
        return true;
    }
    public void mark(int readAheadLimit) throws IOException {
        if (readAheadLimit < 0) {
            throw new IllegalArgumentException(“Read-ahead limit < 0”);
        }
        synchronized (lock) {
            ensureOpen();
            this.readAheadLimit = readAheadLimit;
            markedChar = nextChar;
            markedSkipLF = skipLF;
        }
    }
    public void reset() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (markedChar < 0)
                throw new IOException((markedChar == INVALIDATED)
                                      ? “Mark invalid”
                                      : “Stream not marked”);
            nextChar = markedChar;
            skipLF = markedSkipLF;
        }
    }
    public void close() throws IOException {
        synchronized (lock) {
            if (in == null)
                return;
            try {
                in.close();
            } finally {
                in = null;
                cb = null;
            }
        }
    }
    public Stream<String> lines() {
        Iterator<String> iter = new Iterator<>() {
            String nextLine = null;
            @Override
            public boolean hasNext() {
                if (nextLine != null) {
                    return true;
                } else {
                    try {
                        nextLine = readLine();
                        return (nextLine != null);
                    } catch (IOException e) {
                        throw new UncheckedIOException(e);
                    }
                }
            }
            @Override
            public String next() {
                if (nextLine != null || hasNext()) {
                    String line = nextLine;
                    nextLine = null;
                    return line;
                } else {
                    throw new NoSuchElementException();
                }
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                iter, Spliterator.ORDERED | Spliterator.NONNULL), false);
    }
}

JDK

java.io.Writerクラス

java.io.Writerクラスのソースコードを取り上げる。まず目につくのが,writeBufferというbyte配列のバッファが用意されていることである。バッファリングを行っている。そしてその大きさは1024となっている。

 続いて,Readerクラスの場合と同様にnullWriterという何を出力してもどこにも出力しない「ブラックホール」のようなWriterクラスが実装されている。

 そして,中段にはlockというObject型のロックオブジェクトがあり,排他制御に用いられている。コンストラクタはprotectedのものが2種類定義されている。

 このクラスの本質的な機能を果たすwrite()メソッドは多数オーバーロードされている。これらのメソッドはいずれも抽象メソッドであるwrite(char cbuf[], int off, int len)を呼び出す。

  • public void write(int c) throws IOException
  • public void write(char cbuf[]) throws IOException
  • public void write(String str) throws IOException
  • public void write(String str, int off, int len) throws IOException

 このほかにReaderクラスにはないものとして,バッファにコンテンツを追加するためのappend()メソッドやバッファの内容をストレージ等に書き込むためのflush()メソッドが定義されている。

/*
 * Copyright (c) 1996, 2018, 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.io;
import java.util.Objects;
public abstract class Writer implements Appendable, Closeable, Flushable {
    private char[] writeBuffer;
    private static final int WRITE_BUFFER_SIZE = 1024;
    public static Writer nullWriter() {
        return new Writer() {
            private volatile boolean closed;
            private void ensureOpen() throws IOException {
                if (closed) {
                    throw new IOException("Stream closed");
                }
            }
            @Override
            public Writer append(char c) throws IOException {
                ensureOpen();
                return this;
            }
            @Override
            public Writer append(CharSequence csq) throws IOException {
                ensureOpen();
                return this;
            }
            @Override
            public Writer append(CharSequence csq, int start, int end) throws IOException {
                ensureOpen();
                if (csq != null) {
                    Objects.checkFromToIndex(start, end, csq.length());
                }
                return this;
            }
            @Override
            public void write(int c) throws IOException {
                ensureOpen();
            }
            @Override
            public void write(char[] cbuf, int off, int len) throws IOException {
                Objects.checkFromIndexSize(off, len, cbuf.length);
                ensureOpen();
            }
            @Override
            public void write(String str) throws IOException {
                Objects.requireNonNull(str);
                ensureOpen();
            }
            @Override
            public void write(String str, int off, int len) throws IOException {
                Objects.checkFromIndexSize(off, len, str.length());
                ensureOpen();
            }
            @Override
            public void flush() throws IOException {
                ensureOpen();
            }
            @Override
            public void close() throws IOException {
                closed = true;
            }
        };
    }
    protected Object lock;
    protected Writer() {
        this.lock = this;
    }
    protected Writer(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }
    public void write(int c) throws IOException {
        synchronized (lock) {
            if (writeBuffer == null){
                writeBuffer = new char[WRITE_BUFFER_SIZE];
            }
            writeBuffer[0] = (char) c;
            write(writeBuffer, 0, 1);
        }
    }
    public void write(char cbuf[]) throws IOException {
        write(cbuf, 0, cbuf.length);
    }
    public abstract void write(char cbuf[], int off, int len) throws IOException;
    public void write(String str) throws IOException {
        write(str, 0, str.length());
    }
    public void write(String str, int off, int len) throws IOException {
        synchronized (lock) {
            char cbuf[];
            if (len <= WRITE_BUFFER_SIZE) {
                if (writeBuffer == null) {
                    writeBuffer = new char[WRITE_BUFFER_SIZE];
                }
                cbuf = writeBuffer;
            } else {    // Don't permanently allocate very large buffers.
                cbuf = new char[len];
            }
            str.getChars(off, (off + len), cbuf, 0);
            write(cbuf, 0, len);
        }
    }
    public Writer append(CharSequence csq) throws IOException {
        write(String.valueOf(csq));
        return this;
    }
    public Writer append(CharSequence csq, int start, int end) throws IOException {
        if (csq == null) csq = "null";
        return append(csq.subSequence(start, end));
    }
    public Writer append(char c) throws IOException {
        write(c);
        return this;
    }
    public abstract void flush() throws IOException;
    public abstract void close() throws IOException;
}
JDK

java.io.Readerクラス

java.io.Readerクラスは入力関係を扱うクラスであるBufferedReaderクラス,InputStreamReaderクラス,StringReaderクラスなどのスーパークラスとなる抽象クラスである。

 まず,nullReader()メソッドでほぼ何もしないReadderクラスのインスタンスを作成する。.

 中段ではlockというprotectedなインスタンス変数で,排他制御を行っている。

read()メソッドはいくつかオーバーロードされており,read(char cbuf[], inf off, int len)は抽象メソッドになっている。オーバーロードされた他のメソッドはこの抽象メソッドを呼び出すようになっている。
  • read(java.nio.CharBuffer target) throws IOException
  • public int read() throws IOException
  • public int read(char cbuf[]) throws IOException
  • public int read(char cbuf[]) throws IOException
  • public abstract int read(char cbuf[], int off, int len) throws IOException

 このほか,読み飛ばすためのskip()メソッドや,リソースをクローズするためのclose()メソッド,そしてWriterへそのまま転送するtrnasfer(Writer)メソッドが定義されている。

/*
 * Copyright (c) 1996, 2018, 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.io;
import java.nio.CharBuffer;
import java.util.Objects;
public abstract class Reader implements Readable, Closeable {
    private static final int TRANSFER_BUFFER_SIZE = 8192;
    public static Reader nullReader() {
        return new Reader() {
            private volatile boolean closed;
            private void ensureOpen() throws IOException {
                if (closed) {
                    throw new IOException("Stream closed");
                }
            }
            @Override
            public int read() throws IOException {
                ensureOpen();
                return -1;
            }
            @Override
            public int read(char[] cbuf, int off, int len) throws IOException {
                Objects.checkFromIndexSize(off, len, cbuf.length);
                ensureOpen();
                if (len == 0) {
                    return 0;
                }
                return -1;
            }
            @Override
            public int read(CharBuffer target) throws IOException {
                Objects.requireNonNull(target);
                ensureOpen();
                if (target.hasRemaining()) {
                    return -1;
                }
                return 0;
            }
            @Override
            public boolean ready() throws IOException {
                ensureOpen();
                return false;
            }
            @Override
            public long skip(long n) throws IOException {
                ensureOpen();
                return 0L;
            }
            @Override
            public long transferTo(Writer out) throws IOException {
                Objects.requireNonNull(out);
                ensureOpen();
                return 0L;
            }
            @Override
            public void close() {
                closed = true;
            }
        };
    }
    protected Object lock;
    protected Reader() {
        this.lock = this;
    }
    protected Reader(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }
    public int read(java.nio.CharBuffer target) throws IOException {
        int len = target.remaining();
        char[] cbuf = new char[len];
        int n = read(cbuf, 0, len);
        if (n > 0)
            target.put(cbuf, 0, n);
        return n;
    }
    public int read() throws IOException {
        char cb[] = new char[1];
        if (read(cb, 0, 1) == -1)
            return -1;
        else
            return cb[0];
    }
    public int read(char cbuf[]) throws IOException {
        return read(cbuf, 0, cbuf.length);
    }
    public abstract int read(char cbuf[], int off, int len) throws IOException;
    private static final int maxSkipBufferSize = 8192;
    private char skipBuffer[] = null;
    public long skip(long n) throws IOException {
        if (n < 0L)
            throw new IllegalArgumentException("skip value is negative");
        int nn = (int) Math.min(n, maxSkipBufferSize);
        synchronized (lock) {
            if ((skipBuffer == null) || (skipBuffer.length < nn))
                skipBuffer = new char[nn];
            long r = n;
            while (r > 0) {
                int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
                if (nc == -1)
                    break;
                r -= nc;
            }
            return n – r;
        }
    }
    public boolean ready() throws IOException {
        return false;
    }
    public boolean markSupported() {
        return false;
    }
    public void mark(int readAheadLimit) throws IOException {
        throw new IOException("mark() not supported");
    }
    public void reset() throws IOException {
        throw new IOException("reset() not supported");
    }
     public abstract void close() throws IOException;
    public long transferTo(Writer out) throws IOException {
        Objects.requireNonNull(out, "out");
        long transferred = 0;
        char[] buffer = new char[TRANSFER_BUFFER_SIZE];
        int nRead;
        while ((nRead = read(buffer, 0, TRANSFER_BUFFER_SIZE)) >= 0) {
            out.write(buffer, 0, nRead);
            transferred += nRead;
        }
        return transferred;
    }
}
JDK

java.lang.StackTraceElementクラス

java.lang.StackTraceElementクラスは,Exceptionをキャッチした際に,printStackTrace()で表示されるスタックトレースの一行を表すものである。フィールドとして,クラスローダ名,モジュール名,モジュールバージョン,宣言されているクラス,メソッド名,ファイル名,行番号,フォーマットが宣言されており,これらのアクセッサメソッドがある。下部を見ると,一部にnativeメソッド呼出が存在することがわかる。

/*
 * Copyright (c) 2000, 2016, 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 jdk.internal.loader.BuiltinClassLoader;
import jdk.internal.misc.VM;
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleReferenceImpl;
import java.lang.module.ModuleDescriptor.Version;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
public final class StackTraceElement implements java.io.Serializable {
    // For Throwables and StackWalker, the VM initially sets this field to a
    // reference to the declaring Class.  The Class reference is used to
    // construct the 'format' bitmap, and then is cleared.
    //
    // For STEs constructed using the public constructors, this field is not used.
    private transient Class<?> declaringClassObject;
    // Normally initialized by VM
    private String classLoaderName;
    private String moduleName;
    private String moduleVersion;
    private String declaringClass;
    private String methodName;
    private String fileName;
    private int    lineNumber;
    private byte   format = 0; // Default to show all
    public StackTraceElement(String declaringClass, String methodName,
                             String fileName, int lineNumber) {
        this(null, null, null, declaringClass, methodName, fileName, lineNumber);
    }
    public StackTraceElement(String classLoaderName,
                             String moduleName, String moduleVersion,
                             String declaringClass, String methodName,
                             String fileName, int lineNumber) {
        this.classLoaderName = classLoaderName;
        this.moduleName      = moduleName;
        this.moduleVersion   = moduleVersion;
        this.declaringClass  = Objects.requireNonNull(declaringClass, "Declaring class is null");
        this.methodName      = Objects.requireNonNull(methodName, "Method name is null");
        this.fileName        = fileName;
        this.lineNumber      = lineNumber;
    }
    /*
     * Private constructor for the factory methods to create StackTraceElement
     * for Throwable and StackFrameInfo
     */
    private StackTraceElement() {}
    public String getFileName() {
        return fileName;
    }
    public int getLineNumber() {
        return lineNumber;
    }
    public String getModuleName() {
        return moduleName;
    }
    public String getModuleVersion() {
        return moduleVersion;
    }
    public String getClassLoaderName() {
        return classLoaderName;
    }
    public String getClassName() {
        return declaringClass;
    }
    public String getMethodName() {
        return methodName;
    }
    public boolean isNativeMethod() {
        return lineNumber == -2;
    }
    public String toString() {
        String s = "";
        if (!dropClassLoaderName() && classLoaderName != null &&
                !classLoaderName.isEmpty()) {
            s += classLoaderName + "/";
        }
        if (moduleName != null && !moduleName.isEmpty()) {
            s += moduleName;
            if (!dropModuleVersion() && moduleVersion != null &&
                    !moduleVersion.isEmpty()) {
                s += "@" + moduleVersion;
            }
        }
        s = s.isEmpty() ? declaringClass : s + "/" + declaringClass;
        return s + "." + 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 Objects.equals(classLoaderName, e.classLoaderName) &&
            Objects.equals(moduleName, e.moduleName) &&
            Objects.equals(moduleVersion, e.moduleVersion) &&
            e.declaringClass.equals(declaringClass) &&
            e.lineNumber == lineNumber &&
            Objects.equals(methodName, e.methodName) &&
            Objects.equals(fileName, e.fileName);
    }
    public int hashCode() {
        int result = 31*declaringClass.hashCode() + methodName.hashCode();
        result = 31*result + Objects.hashCode(classLoaderName);
        result = 31*result + Objects.hashCode(moduleName);
        result = 31*result + Objects.hashCode(moduleVersion);
        result = 31*result + Objects.hashCode(fileName);
        result = 31*result + lineNumber;
        return result;
    }
    private synchronized void computeFormat() {
        try {
            Class<?> cls = (Class<?>) declaringClassObject;
            ClassLoader loader = cls.getClassLoader0();
            Module m = cls.getModule();
            byte bits = 0;
            // First element – class loader name
            // Call package-private ClassLoader::name method
            if (loader instanceof BuiltinClassLoader) {
                bits |= BUILTIN_CLASS_LOADER;
            }
            // Second element – module name and version
            // Omit if is a JDK non-upgradeable module (recorded in the hashes
            // in java.base)
            if (isHashedInJavaBase(m)) {
                bits |= JDK_NON_UPGRADEABLE_MODULE;
            }
            format = bits;
        } finally {
            // Class reference no longer needed, clear it
            declaringClassObject = null;
        }
    }
    private static final byte BUILTIN_CLASS_LOADER       = 0x1;
    private static final byte JDK_NON_UPGRADEABLE_MODULE = 0x2;
    private boolean dropClassLoaderName() {
        return (format & BUILTIN_CLASS_LOADER) == BUILTIN_CLASS_LOADER;
    }
    private boolean dropModuleVersion() {
        return (format & JDK_NON_UPGRADEABLE_MODULE) == JDK_NON_UPGRADEABLE_MODULE;
    }
    private static boolean isHashedInJavaBase(Module m) {
        // return true if module system is not initialized as the code
        // must be in java.base
        if (!VM.isModuleSystemInited())
            return true;
        return ModuleLayer.boot() == m.getLayer() && HashedModules.contains(m);
    }
    /*
     * Finds JDK non-upgradeable modules, i.e. the modules that are
     * included in the hashes in java.base.
     */
    private static class HashedModules {
        static Set<String> HASHED_MODULES = hashedModules();
        static Set<String> hashedModules() {
            Optional<ResolvedModule> resolvedModule = ModuleLayer.boot()
                    .configuration()
                    .findModule("java.base");
            assert resolvedModule.isPresent();
            ModuleReference mref = resolvedModule.get().reference();
            assert mref instanceof ModuleReferenceImpl;
            ModuleHashes hashes = ((ModuleReferenceImpl)mref).recordedHashes();
            if (hashes != null) {
                Set<String> names = new HashSet<>(hashes.names());
                names.add("java.base");
                return names;
            }
            return Set.of();
        }
        static boolean contains(Module m) {
            return HASHED_MODULES.contains(m.getName());
        }
    }
    /*
     * Returns an array of StackTraceElements of the given depth
     * filled from the backtrace of a given Throwable.
     */
    static StackTraceElement[] of(Throwable x, int depth) {
        StackTraceElement[] stackTrace = new StackTraceElement[depth];
        for (int i = 0; i < depth; i++) {
            stackTrace[i] = new StackTraceElement();
        }
        // VM to fill in StackTraceElement
        initStackTraceElements(stackTrace, x);
        // ensure the proper StackTraceElement initialization
        for (StackTraceElement ste : stackTrace) {
            ste.computeFormat();
        }
        return stackTrace;
    }
    /*
     * Returns a StackTraceElement from a given StackFrameInfo.
     */
    static StackTraceElement of(StackFrameInfo sfi) {
        StackTraceElement ste = new StackTraceElement();
        initStackTraceElement(ste, sfi);
        ste.computeFormat();
        return ste;
    }
    /*
     * Sets the given stack trace elements with the backtrace
     * of the given Throwable.
     */
    private static native void initStackTraceElements(StackTraceElement[] elements,
                                                      Throwable x);
    /*
     * Sets the given stack trace element with the given StackFrameInfo
     */
    private static native void initStackTraceElement(StackTraceElement element,
                                                     StackFrameInfo sfi);
    private static final long serialVersionUID = 6992337162326171013L;
}