JAva ioシリーズ17のSystem.out.println(「hello world」)原理

7090 ワード

Javaの最初のプログラムは「hello world」です
1 public class HelloWorld {
2     public static void main(String[] args) {
3         System.out.println("hello world");
4     }
5 }

上のプログラムはいったいどのようにスクリーンに「hello world」を出力しますか?これが本来説明すべき内容であるSystemです.out.println(「hello world」)の原理.
まずシステムを見てみましょうout.printlnのプロセス.まずシステムを見てみましょうJAvaのoutの定義は、次のようになります.
1 public final class System {
2     ...
3 
4     public final static PrintStream out = null;
5 
6     ...
7 }

ここから,(01)outはSystemであることが分かった.JAvaの静的変数.(02)そしてoutはPrintStreamオブジェクト、PrintStreamである.JAvaには、リロードされたprintln()メソッドがたくさんあります.
OK、outがPrintStreamオブジェクトであることがわかりました.次に、どのように初期化されたのか、画面出力にどのように関連付けられているのかを見てみましょう.まずシステムを見てみましょうJAvaのinitializeSystemClass()メソッド.1.initializeSystemClass()のソースコードは、out部分を赤色にマークする
 1 private static void initializeSystemClass() {
 2 
 3     props = new Properties();
 4     initProperties(props);  // initialized by the VM
 5 
 6     sun.misc.VM.saveAndRemoveProperties(props);
 7 
 8     lineSeparator = props.getProperty("line.separator");
 9     sun.misc.Version.init();
10 
11     FileInputStream fdIn = new FileInputStream(FileDescriptor.in);
12     FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);
13     FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);
14     setIn0(new BufferedInputStream(fdIn));
15     setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));
16     setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true));
17 
18     loadLibrary("zip");
19 
20     Terminator.setup();
21 
22     sun.misc.VM.initializeOSEnvironment();
23 
24     Thread current = Thread.currentThread();
25     current.getThreadGroup().add(current);
26 
27     setJavaLangAccess();
28 
29     sun.misc.VM.booted();
30 }

上の赤いコード部分、すなわちFileOutputStream fdOut=new FileOutputStream(FileDescriptor.out);setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true));この2つの言葉を細分化すると、第1ステップFileDescriptor fd=FileDescriptorに分けられる.out;ステップ2 FileOutputStream fdOut=new FileOutputStream(fd);ステップ3 BufferedOutputStream bufOut=new BufferedOutputStream(fdOut,128);ステップ4 PrintStreams ps=new PrintStream(bufout,true);ステップ5 setOut 0(ps);説明:(01)ステップ1、FileDescriptorを取得する.JAvaの静的メンバーout,outはFileDescriptorオブジェクトであり,実際には「標準出力(画面)」の識別子である.FileDescriptorの詳細については、ブログ「java ioシリーズ09のFileDescriptorまとめ」を参照してください.FileDescriptor.JAvaでFileDescriptorとout関連コードは以下の通りです.
 1 public final class FileDescriptor {
 2 
 3     private int fd;
 4 
 5     public static final FileDescriptor out = new FileDescriptor(1);
 6 
 7     private FileDescriptor(int fd) {
 8         this.fd = fd;
 9         useCount = new AtomicInteger();
10     }
11 
12     ...
13 }

(02)「標準出力(画面)」に対応する「ファイル出力ストリーム」を作成する.(03)ファイル出力ストリームに対応するバッファ出力ストリームを作成する.ファイル出力ストリームにバッファ機能を追加することを目的としています.(04)「バッファ出力ストリーム」に対応する「プリント出力ストリーム」を作成する.「バッファ出力ストリーム」にprint()、println()、printf()などの便利な印刷インタフェースを提供することを目的とする.印刷出力を容易かつ迅速に行うことができます.(05)setout 0(ps)を実行する.
次に,ステップ5のsetOut 0(ps)を解析する.システムを表示します.JAvaのsetOut 0()の宣言は、次のとおりです.
private static native void setOut0(PrintStream out);

ここからsetout 0()はnativeローカルメソッドであることが分かった.Openjdkでは、次のように対応するソースコードを見つけることができます.
1 JNIEXPORT void JNICALL
2 Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
3 {
4     jfieldID fid =
5         (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
6     if (fid == 0)
7         return;
8     (*env)->SetStaticObjectField(env,cla,fid,stream);
9 }

説明:これはJNI関数で、簡単な分析を行います.(01)関数名JNIEXPORT void JNICALL Java_java_lang_System_setOut 0(JNIEnv*env,jclass cla,jobject stream)これはJNIの静的登録方法、Java_java_lang_System_setOut 0(JNIEnv*env,jclass cla,jobject stream)とSystem.JAvaのsetOut 0(PrintStream out)関連付け;また、パラメータstreamはパラメータoutに対応する.簡単に言えばsetOut 0()を呼び出しますが、実際にはJava_を呼び出します.java_lang_System_setOut0().JNIの詳細については、ブログ「Android JNIとNDK学習(02)--静的にJNIを実現する」を参照してください.
(02) jfieldID fid = (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");この言葉の役割はSystemを取得することです.JAvaの静的メンバーoutのjfieldID、「Ljava/io/printStream;」説明outはjavaです.io.PrintStreamオブジェクト.outのjfieldIDを取得する役割は、「outのjfielID」を操作することでoutの値を変更する必要があることです.
(03) (*env)->SetStaticObjectField(env,cla,fid,stream);この文の役割はfid(fidはoutのjfieldID)に対応する静的メンバーの値をstreamに設定することである.streamは私たちがJavaに伝えたものです.java_lang_System_setout 0()のパラメータ、すなわちsetout 0に渡されるパラメータ.
上記の内容をまとめる.setout 0(PrintStreamps)の役割は、psをSystemに設定することであることを知っています.JAvaのout静的変数.
 
前にも言ったようにoutは機械の「標準出力(画面)」のファイル識別子である.ファイル識別子は、FileDescriptorとして一般的に理解できます.outは代表的な「標準出力」です.したがって、initializeSystemClass()では、上の5つのステップで「FileDescriptor.out」をカプセル化します.パッケージ化されたシステムIn既存のバッファ機能;print()、println()、printf()などの便利な操作インタフェースもあります.