深入扒一扒System.out.println()!

什麼是System.out.println()

System.out.println是一個Java語句,一般情況下是將傳遞的參數,打印到控制檯。

System:是 java.lang包中的一個final類。根據javadoc,“java.lang.System該類提供的設施包括標準輸入,標準輸出和錯誤輸出流; 訪問外部定義的屬性和環境變量; 一種加載文件和庫的方法; 以及用於快速複製數組等一部分的實用方法… ”

out:是System類的靜態成員字段,類型為PrintStream。

<code>    public static final PrintStream out/<code>

他在啟動時就會被實例化,並與主機的標準輸出控制檯進行映射。該流在實例化之後立即打開,並準備接受數據。

println:是PrintStream類的一個方法。println打印(參數內容+換行符) 到控制檯。

PrintStream類中有多個重載的println方法。每個println是通過調用print方法並添加一個換行符實現的。print方法是通過調用write方法實現的。

System.out.println() 結構圖如下:

深入扒一扒System.out.println()!

以下是JDK源中System.out.println的骨架結構,和代碼片段。

UML圖

深入扒一扒System.out.println()!

部分代碼段:

<code>public final class System {
    static PrintStream out;
    static PrintStream err;
    static InputStream in;
    ...
}

public class PrintStream extends FilterOutputStream {
    //out object is inherited from FilterOutputStream class
    public void println() {
    ...
}/<code>

輸出重定向——改變輸出路徑

out對象可以自定義的。在啟動時由java運行時環境初始化,並且可以在執行期間由開發人員更改。代替在默認情況下的標準輸出。當您通過命令行運行程序時,輸出將打印在同一個命令窗口中。我們可以使用setOut方法來改變這種行為。在以下示例中,我將輸出重定向到同一目錄中的文本文件。

<code>    
public

class

ChangeOut
{


public

static

void
main(

String
args[]) {


try
{


System
.setOut(
new

PrintStream
(
new

FileOutputStream
(
"log.txt"
)));


System
.
out
.println(
"Now the output is redirected!"
);

}
catch
(
Exception
e) {}

}

}/<code>

System.out.println性能分析

有一個普遍的觀念需要大家知道——System.out.println性能並不好。當我們深入分析時,其調用順序如下println - > print - > write()+ newLine()。這個順序流是Sun / Oracle JDK的實現。write()和newLine()都包含一個synchronized塊。同步有一點開銷,但更多的是添加字符到緩衝區和打印的開銷更大。

當我們運行性能分析時,運行多個System.out.println並記錄時間,執行時間會按比例增加。當打印超過50個字符並打印超過50,000行時,性能下降。

當然這一切都取決於我們使用的場景。不過無論如何請勿使用System.out.println打印日誌( logging)到stdout。

System.out.println VS 日誌記錄組件(Log4j等)

Log4J具有多種記錄級別。如果我們正在編寫一個小程序,只是為了實驗/學習目的那麼使用 System.out.println 就很不錯。但當我們開發生產質量軟件時,我們應該注意到應該使用記錄組件(log4j等),並且應該避免使用System.out.println。為什麼?

  • 靈活性:log4j的記錄器提供了多種記錄級別。我們可以相應地分隔日誌信息。例如,X消息只能在PRODUCTION上打印,Y消息應打印在ERROR等上。
  • 可重構性:log4j只需一個參數更改即可關閉所有日誌記錄。
  • 可維護性:想象一下,如果我們有數百個System.out.println全部通過應用程序散落,那麼在一段時間內將難以維護程序。
    粒度:在應用程序中,每個類都可以有不同的記錄器並相應地進行控制。
  • 實用性:在System.out中限制重定向消息的選項,但是如果是記錄器(like log4j),則可以提供多種選項。我們甚至可以創建自定義輸出選項並將其重定向。

所以我們不應該使用System.out.println進行日誌記錄和調試(logging and debugging)

靜態導入來縮短System.out.println

有時我們覺得System.out.println是一個很長的語句要打印。靜態導入可能會縮短一點,但不推薦使用,因為它導致可讀性差。我只是使用這種情況來解釋靜態導入,並避免在下面的情況下使用它。

<code>    import static java.lang.System.out;
public class ShortSOP {   public static void main(String[] args) {     out.println("Hello, world");   } }/<code>

不靜態導入的話直接寫out.println會提示編譯錯誤的。

System.err和System.in

作為相關部分,接下來介紹一下“err”和“in”。'in'與InputStream相關聯。與“out”相對,“in”用於從標準控制檯通用鍵盤獲取輸入。

'err'與PrintStream相關聯,並將參數打印到標準錯誤輸出流。當您使用等的IDE時,可以看到“out”和“err”之間的輸出差異。

<code>    public class InOutErr {
public static void main(String args[]) {
try {

System.out.println("請輸入一段話");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in ));
String filename = reader.readLine();
InputStream input = new FileInputStream(filename);
System.out.println("File opened...");
} catch (IOException e){
System.err.println("Where is that file?");
}
}
}/<code>
深入扒一扒System.out.println()!


分享到:


相關文章: