2013-08-01 15 views
5

Tôi đã thấy trong lớp System rằng đối tượng out (loại PrintStream) được khởi tạo với giá trị null. Làm thế nào chúng ta có thể gọi phương thức như System.out.prinln("");? Trong hệ thống lớp ra biến khởi tạo như theo cách này:PrintStream đối tượng ra được khởi tạo bằng null, làm thế nào chúng ta gọi phương pháp trên nó?

package java.lang; 

public final class System { 
    public final static PrintStream out = nullPrintStream(); 

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

Theo trình bày ở trên đang out biến khởi tạo bởi vô biến này là cuối cùng, vì vậy nó không thể khởi tạo hơn nữa thì làm sao chúng ta có thể sử dụng "out" biến.

+0

có thể giúp bạn http://stackoverflow.com/questions/9454866/out-in-system-out-println – Prabhaker

Trả lời

4

JVM gọi phương thức private static void initializeSystemClass() khởi tạo phương thức đó.

Xem hai dòng sau mã:

setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true)); 
setErr0(new PrintStream(new BufferedOutputStream(fdErr, 128), true)); 

Đây là hai phương pháp có nguồn gốc:

private static native void setOut0(PrintStream out); 
private static native void setErr0(PrintStream err); 

Có một nice article on it.

+1

Bài viết không giải thích mẹo 'currentTimeMillis'. Tại sao nó được sử dụng? Để tránh một số loại tối ưu hóa? – kan

+0

@kan: thật buồn cười (hoặc đôi khi khá đáng sợ) để xem mã bí truyền đó ngay cả trong các lớp Java cơ bản. [The 'nullInputStream()' counterpart] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/System.java#1063) có một bình luận nói về cấm inlining, nhưng 'null' không bao giờ được inlined tại biên dịch-thời gian và cách bạn nhận được để' null' sẽ không liên quan tại thời gian chạy. Do đó, [phiên bản Java 7] (http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/lang/System.java/#104) doesn ' t có phương pháp kỳ lạ đó nữa. – Holger

6

Lời giải thích là trong các ý kiến:

/** 
* The following two methods exist because in, out, and err must be 
* initialized to null. The compiler, however, cannot be permitted to 
* inline access to them, since they are later set to more sensible values 
* by initializeSystemClass(). 
*/ 

initializeSystemClass() sử dụng phương pháp tự nhiên để khởi tạo các dòng tiêu chuẩn để các giá trị khác null. Mã gốc có thể khởi tạo lại các biến được khai báo cuối cùng.

+0

tôi nghĩ rằng bạn đang cố gắng nói về mã dưới đây private static void initializeSystemClass() { \t setOut0 (new PrintStream (new BufferedOutputStream (fdOut, 128), true)); } riêng void tĩnh void setOut0 (PrintStream out); nhưng có một điều tôi không hiểu làm thế nào có thể biến cuối cùng khởi tạo hai lần ?? một từ phương pháp trên và lần thứ hai là mã câu hỏi của tôi. – Hits

+0

Vâng và tốt, nhưng sẽ không thất bại một khi tôi có một máy tính đó là quá nhanh mà nó đạt đến phương pháp khởi tạo này trong ít hơn 1 mili giây? Hoặc là currentTimeMillis() được bảo đảm để lấy lại một giá trị> 0? – Ingo

+1

Có. currentTimeMillis() trả về thời gian hiện tại? 0 nghĩa là ngày 1 tháng 1 năm 1970, và chúng ta đang ở năm 2013. Biến cuối cùng có thể được khởi tạo hai lần vì nó là mã gốc, được viết bằng C, khởi tạo biến đó. Mã nguồn gốc như vậy, một phần của chính JVM, tất nhiên có thể bỏ qua các lỗi JVM được áp dụng cho mã Java. –

1

Có một số gettersetter cho đối tượng out.

0

Khi lớp Hệ thống được khởi tạo, nó gọi phương pháp initializeSystemClass() của nó, đây là mã:

FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out); 
setOut0(new PrintStream(new BufferedOutputStream(fdOut, 128), true)); 

Trong mã setOut0 này() là một hàm có nguồn gốc thực hiện trong System.c:

JNIEXPORT void JNICALL 
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream) 
{ 
    jfieldID fid = 
     (*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;"); 
    if (fid == 0) 
     return; 
    (*env)->SetStaticObjectField(env,cla,fid,stream); 
} 

Đây là mã JNI tiêu chuẩn đặt số System.out cho đối số được truyền cho nó, phương thức này gọi phương thức gốc setOut0() đặt biến số ra thành giá trị thích hợp.

System.out là cuối cùng, điều đó có nghĩa là không thể đặt thành thứ khác trong initializeSystemClass() nhưng sử dụng mã gốc có thể sửa đổi biến cuối cùng.

Các vấn đề liên quan