System.outの深淵

Javaでは、System.outを用いて標準出力を行う。このSystem.outはリファレンスを見るとPrintStream型の変数であることがわかる。PrintStreamは任意のOutputStreamをラッピングして、表示を簡単にするための機能(printlnとか)を提供する出力ストリームだ。つまり、内部になんらかのOutputStreamを保持していることになる。


では、System.outが内部に保持しているOutputStreamは一体どのようなOutputStreamなのだろうか?StandardOutputStreamのような隠れたクラスが存在しているのではないか?もしくは、デフォルトでは画面出力用のDisplayOutputStreamのようなものが用意されており、設定によってそれを切り替えて(例えば、サーブレットのときはHttpOutputStreamのようなものに)使われているのだろうか?

そのような期待を持ってSystem.outのお腹の中をさぐってみた。



まずは、JDKに付属しているSystemクラスのソースコードを見てみる。System.outは次のように定義されていた。

public final static PrintStream out = nullPrintStream();

どうやら、nullPrintStream()というメソッドによって初期化されているらしい。Systemクラスに、そんな名前の可視なメソッドはないので、おそらくprivateメソッドなのだろう。


Systemクラスの中を引き続き探してみると、やはりprivateなnullPrintStreamメソッドが見つかる。

private static PrintStream nullPrintStream() throws NullPointerException {
    if (currentTimeMillis() > 0)
        return null;
    throw new NullPointerException();
}

!!!!!!!!!!!!!!!!!
nullをreturnしている??!



このメソッドの中に書かれているのは、nullをreturnするか、もしくはNullPointerExceptionをthrowする、ということだけだ。System.outはfinalで定義されているので、このコードを見ている限り、System.outがnull以外のものになる可能性はない。

しかし、もちろんそんなわけはない。System.outがnullならSystem.out.println()はぬるぽだ。



ソースコードからの解析は無理っぽいので、デバッガを使って調べてみることにしてみた。


デバッガを起動させてSystem.outの中身を覗いてみると、内部にoutという変数を保持していることがわかる。どうやらこのoutはBufferedOutputStreamのようだ。BufferedOutputStreamも、任意のOutputStreamにバッファリング機能を加える出力ストリームなので、まだその腹の中にOutputStreamを抱えているわけだ。


当然、次はBufferedOutputStreamの中を覗いてみる。そこにはやはり、変数outが用意されている。これこそが、System.outの奥深くに隠された、標準出力ストリームの真の姿!!!その正体は?!




















え?FileOutputStream?





そっかー、そーだよなー。PHPでも

<?php
$out=fopen('php://stdout', 'w');
?>

とかやるもんなー。



・・・。
なんておもしろみのない結果だ・・・。





調べてみた結果、FileDescriptor.outが標準出力ストリームのファイル記述子となっているようだ。つまり、

// 最後のtrueはautoFlushをtrueにするため。
PrintStream out = new PrintStream(new BufferedOutputStream(
        new FileOutputStream(FileDescriptor.out)), true);

out.println("Hello World!!");

とすることで、System.outと同じPrintStreamのoutを生成し、用いることができる。これを使わなければならないようなシチュエーションが思いつかないけど・・・。





ちなみに、FileDescriptor.outの正体をソースコードから探ってみると、

public static final FileDescriptor out = standardStream(1);

で定義されており、standardStreamメソッドは、

private static FileDescriptor standardStream(int fd) {
    FileDescriptor desc = new FileDescriptor();
    desc.handle = set(fd);
    return desc;
}

となっている。このsetメソッドを見ると、

private static native long set(int d);

となり、nativeメソッドの壁に阻まれてしまったことも併せて報告しておく。


(J2SE5での話です。他のバージョンではソースコードなどが異なるかもしれません)