Java print流簡介

接下來這篇博文介紹java另1種Stream, print 流.  亦有人稱其爲打印流.


介紹這個print流之前有必要明確兩點:

1. print 流是輸出流, 只能用於輸出到外部設備不能用於輸入.

2. print 流是包裹流(處理流), 必須包裹在另1個流之上.


一, 其他輸出流介紹

要了解print流的由來, 有必要明白print流和其他輸出流的區別.

在這裏首先重新go through一次一起博文介紹過的若干常用的輸出流.


1.1 Writer

Writer是1個流的上層抽象類,  我們常用的FileWriter, CharArrayWriter 都是繼承自這個類.

利用writer的 各種重載的write()方法可以寫入

1. 1個字符 write(int) //字符二進制數據存放在int類型中

2. 1個字符數組 write(char[])

3. 1個字符數組的一部分 write(char[],int,int)

4. 1個字符串 或者 一個字符串的一部分 write(String,0,len)


Wirter的優點就是方便地處理字符外部設備(例如文本文件) 但是不能處理二進制外部設備(類如mp3文件).


1.2 OutputStream

OutputStream也是1個上層抽象類, 相對於Writer,OutputStream一次只能寫入1個字節, 而不是字符.

我們常用的 FileOutputStream, ByteArrayOutputStream 都是繼承自這個流.


利用OutputStream 可以的write方法可以寫入

1. 1個字節 write(int)

2. 1個字節數組 write(byte[])

3. 1個字節數組的一部分 write(byte[],int,int)


OutputStream的優點就是可以處理一切文件, 包括文本文件和二進制文件.



1.3 DataOutputStream

基本上所有作爲原始流的輸出流都是繼承子上面的兩個抽象類(Writer 和 OutputStream)

而這個DataOutputStream 則是1個繼承自OutputStream的1個包裹流, 它必須包裹在1個OutputStream的原始流之上.


利用DataOutputSream, 可以寫入

1. 1個字節 write(int)

2. 1個字節數組 write(byte[])

3. 1個字節數組的一部分 write(byte[],int,int)


前面這3個都是繼承自OutputStream, 沒什麼特別. 關鍵是下面這個

所有基本類型的二進制代碼.

例如:

4. 1個int類型的二進制數據 writeInt(int)

5. 1個foat類型的二進制數據 writeFloat(float)

....


簡單地講,

如果執行 DataOutputStream 的 writeInt(100) 就會把100的二進制代碼 1100100, 也就是 00 00 00 64(16進製表示) 寫入到外部設備. 共佔4個字節


1.3.1 DataOuputStream 1個例子

這個例子步驟很簡單.

就是利用DataOutputStream 往1個文件(/home/gateman/tmp/testStream1.txt) 寫入1個int類型 值是: 1229935882.

然後查看這個文件的內容.


代碼如下:

import java.io.*;

public class DataStream2{
    public static void f(){
        try{
            g();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    
    public static void g() throws IOException{
        int i = 1229935882;

        File f = new File("/home/gateman/tmp/testStream1.txt");
        if (f.exists()){
            f.delete();
        }

        f.createNewFile();

        FileOutputStream fos = new FileOutputStream(f);
        DataOutputStream dos = new DataOutputStream(fos);
        
        dos.writeInt(i);
        dos.flush();
        dos.close();
        
        System.out.printf("done!!\n"); 
    }
}


代碼很容易讀懂, 這裏不解釋了.

當執行完這代碼後, 首先用cat命令來查看這個文件的內容

gateman@TPEOS Java_1 $ ant
Buildfile: /media/store1/Studies/Java/java_start/Java_1/build.xml

main:
    [javac] /media/store1/Studies/Java/java_start/Java_1/build.xml:10: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
     [java] done!!

BUILD SUCCESSFUL
Total time: 0 seconds
gateman@TPEOS Java_1 $ cat /home/gateman/tmp/testStream1.txt 
IOU

簡單文件的內容不是 數字1229935882,  而是三個字母 "IOU".

爲何呢?


原因就是DataOutputStream會將 int類型1229935882 轉換成 2進制

用16進制來表示就是 49 4f 55 0a. 

至於10進制如何轉換成16進制這裏就不深究了.


我們可以用xxd來查看1個文件的2進制(16進制)內容:

gateman@TPEOS Java_1 $ xxd -g 1 /home/gateman/tmp/testStream1.txt
0000000: 49 4f 55 0a                                      IOU.

可以見到.

0000000: 這些是文件頭部信息.

內容就是

49 4f 55 0a

那麼爲何輸出是IOU?


因爲cat命令不是輸出文件的2進制代碼命令, 而是將其視爲1個文本文件來輸出.

那麼就會利用ASCII碼來對文件的2進制數據進行編碼.


在ASCII表中

16進制 49 -> 10進制 73 -> 字符'I'

4f -> 79 -> O

55 -> 85 -> U

0a -> 10 -> 換行符


所以用cat轉碼後輸出就是"IOU\n"了

當然, 如果利用一般的文本編輯器打開, 例如vi, gedit等打開這個文件, 顯示的文件內容也是IOU哦.




二, Print流介紹

上面提過了, print流是輸出流的一種, 它也繼承自基本的抽象類OutputStream 或 Writer.

除了繼承過來(實際上是重寫)的基本的write()方法之外,  print 流更重要的是具有多種的重載的print(), printf() 和 println()方法.


這些print()方法能用於各種不同類型數據的格式話輸出.


例如DataOutputStream的 writeInt(int)能往外部設備寫入 int類型數值的二進制數據.

而print流的 print(int)則可以往外部設備寫入 Int類型數值的格式化輸出.


再簡單地講:

DataOutputStream的 writeFloat(8.8) 寫入的是8.8這個數值的二進制編碼.

而PrintStream(printWriter)的 print(8.8) 寫入的是'8', '.', '8' 這個3個字符.


2.1 一個例子

這個例子用於和上面例子做個對比.

我們利用PrintStream, 同樣往1個文件寫入 1229935882 這個int類型數據, 然後查看文件的內容.


代碼如下:

import java.io.*;

public class PrintStream1{
    public static void f(){
        try{
            g();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
    
    public static void g() throws IOException{
        int i = 1229935882;

        File f = new File("/home/gateman/tmp/testStream2.txt");
        if (f.exists()){
            f.delete();
        }

        f.createNewFile();

        FileOutputStream fos = new FileOutputStream(f);
        PrintStream ps = new PrintStream(fos);
        
        ps.print(i);
        ps.print('\n');
        ps.flush();
        ps.close();
        
        System.out.printf("done!!\n"); 
    }
}


可見上面DataOutputStream的改動很小, 只是把DataOutputStream 改成 PrintStream

輸出:

gateman@TPEOS Java_1 $ cat /home/gateman/tmp/testStream2.txt 
1229935882
gateman@TPEOS Java_1 $ 

可見 被寫入的外部文件中就只是 '1229935882\n' 這個幾個字符了.



三, Print流分類

我們常說的print流有兩個


其中1個就是上面例子中的PrintStream. 它繼承自OutputStream

另1個就是PrintWriter就是 Writer.


3.1 PrintStream

PrintStream繼承自OuputStream的子類FilterOutputStream

PrintStream的方法都不會拋出IOException異常, 而是使用另一種error handling機制.

PrintStream必須包裹在另1個OutputStream上使用.


PrintStream的println()方法會產生'\n'換行符.


3.2 PrintWriter

printWriter繼承自Writer

具有與PrintStream一樣的方法.

它的方法也不會拋出IOException.

PrintWriter既可以包裹在Writer原始流使用, 也可以包裹在另1個OutputStream上.


PrintWriter的println()方法會根據不同的操作系統產生對應的換行符, 例如在linux下換行符是'\n', 在windows下的換行符則是'\r\n'



3.3 PrintStream 和 PrintWriter的區別

它們兩者的使用方法基本上可以一樣的.

區別有兩個

1. PrintWriter可以包裹在另1個Writer上使用, 也可以包裹在另1個OutputStream上使用, 而PrintStream只能包裹在另1個OutputStream上使用.

2. PrintWriter的println()方法跨平臺性更加好.



四, 常用的Print流

看到了pint() println()這些方法是不是想起我們常用的輸出字符到屏幕的命令 System.out.println().

的確, System.out就是java系統封裝好的1個指向標準輸出設備的靜態print流.


實際上System這個類有3個靜態流

其中兩個是PrintStream, 分別是

System.out  標準輸出流(PrintStream), 默認指向屏幕

System.err   標準錯誤輸出流(PrintStream),默認指向屏幕

System.in    標準輸出流(InputStream), 默認指向鍵盤



4.1 System.out

首先, 要明白, System是1個類, 而不是1個包.

System: System是Java內部的類, 它包含了許多常用的靜態字段和靜態方法, 它不能實例化.


out: 而out是System的1個靜態屬性, 它指向java系統封裝好的指向標準輸出設備的一個靜態PrintStream(注意不是PrintWriter)

標準輸出設備: 所謂的標準設備就是計算的屏幕. 但是這個標準輸出設備可以被改變.


setOut(PrintStream out): 利用setOut()方法可以改變標準輸出設備, 例如我們可以將標準輸出設備設置爲1個FileOutputStream, 那麼以後我們執行System.out.println()方法時, 就不再把信息輸出到屏幕, 而是輸出到那個FileOutputStream, 也就是1個文件裏了.


簡單地講, A是1個PrintStream, 執行System.set(A)後,  System.out就等於A!



例子:

import java.io.*;

public class SystemOut1{

    public static void f(){
        try{
            g();
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    public static void g() throws IOException{
        PrintStream screenOS = System.out;//marked down
        System.out.println("It will be printed to monitor!");
        
        File f = new File("/home/gateman/tmp/testStream3.txt");

        if (f.exists()){
            f.delete();
        }

        FileOutputStream fos = new FileOutputStream(f);
        PrintStream ps = new PrintStream(fos,true); // autoflush = true;
        // PrintStream ps = new PrintStream(f); //another constructor;
        
        System.setOut(ps);
        System.out.println("It will be printed to file!");

        System.setOut(screenOS);// set to default value
        System.out.println("It will be printed to monitor again!");
        
        ps.close();
    }
}

上面代碼執行了三次System.out.println()方法.

第1次: 因爲System.out是指向標準輸出設備(屏幕)的PrintStream. 所以第一句被輸出到了屏幕.

第2次: 標準輸出設備被System.setOut()方法改變成了1個指向文件的PrintStream. 所以第二句被輸出到了文件.

第3次: 標準輸出設備再次被修改爲默認值, 所以第三句輸出到了屏幕.


執行結果:

gateman@TPEOS Java_1 $ ant
Buildfile: /media/store1/Studies/Java/java_start/Java_1/build.xml

main:
    [javac] /media/store1/Studies/Java/java_start/Java_1/build.xml:10: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 1 source file to /media/store1/Studies/Java/java_start/Java_1/build/classes
     [java] It will be printed to monitor!
     [java] It will be printed to monitor again!

BUILD SUCCESSFUL
Total time: 1 second
gateman@TPEOS Java_1 $ cat /home/gateman/tmp/testStream3.txt 
It will be printed to file!


4.2 System.err

System.err 也是1個靜態PrintStream.

作爲程序猿, 很多情況下需要處理程序的錯誤, 當錯誤發生時, 我們會把一些錯誤信息輸出.

System.err 實際上就是1個指向標準錯誤輸出設備的PrintStream.

所謂標準錯誤輸出設備是什麼? 默認也是屏幕, 但是很多情況下我們會修改其爲1個專門存儲錯誤信息的log文件.


有兩個常用的方法會利用System.err


1個就是直接執行System.err的printf/print/println()方法.

另1個就是Exception的方法printStackTrace(); 這個方法實際上也是輸出到標準錯誤輸出設備.


同理, 我們也可以利用System.setErr()方法來改變標準錯誤輸出設備的指向.


例子:

import java.io.*;

public class SystemErr1{

    public static void f(){
        try{
            g();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
    public static void h() throws IOException{
        throw new IOException("Shit !!! / by zero!");
    }

    public static void g() throws Exception{
        int i = 0;
        int j =0;
        File f = new File("/home/gateman/tmp/testStream4.txt");

        if (f.exists()){
            f.delete();
        }

        FileOutputStream fos = new FileOutputStream(f);
        PrintStream ps = new PrintStream(fos,true); // autoflush = true;
        // PrintStream ps = new PrintStream(f); //another constructor;
        
        try{
          j = 3 / i;  
        }catch(Exception e){
            System.err.println("this err msg will be printed to monitor!");
            e.printStackTrace();
        }
        
        System.setErr(ps);

        try{
           h(); 
        }catch(Exception e){
            System.err.println("this err msg will be printed to file!");
            e.printStackTrace();
        }
        
        ps.close();
    }
}

上面的代碼輸出次錯誤信息.

第一次輸出到屏幕

第二次輸出到1個文件


執行結果:

gateman@TPEOS Java_1 $ ant
Buildfile: /media/store1/Studies/Java/java_start/Java_1/build.xml

main:
    [javac] /media/store1/Studies/Java/java_start/Java_1/build.xml:10: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
     [java] this err msg will be printed to monitor!
     [java] java.lang.ArithmeticException: / by zero
     [java] 	at Stream_kng.PrintStream_kng.SystemErr1.g(SystemErr1.java:33)
     [java] 	at Stream_kng.PrintStream_kng.SystemErr1.f(SystemErr1.java:9)
     [java] 	at Enter_1.main(Enter_1.java:95)
     [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
     [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
     [java] 	at java.lang.reflect.Method.invoke(Method.java:601)
     [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:217)
     [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:152)
     [java] 	at org.apache.tools.ant.taskdefs.Java.run(Java.java:771)
     [java] 	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:221)
     [java] 	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:135)
     [java] 	at org.apache.tools.ant.taskdefs.Java.execute(Java.java:108)
     [java] 	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:292)
     [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
     [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
     [java] 	at java.lang.reflect.Method.invoke(Method.java:601)
     [java] 	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
     [java] 	at org.apache.tools.ant.Task.perform(Task.java:348)
     [java] 	at org.apache.tools.ant.Target.execute(Target.java:435)
     [java] 	at org.apache.tools.ant.Target.performTasks(Target.java:456)
     [java] 	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1393)
     [java] 	at org.apache.tools.ant.Project.executeTarget(Project.java:1364)
     [java] 	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
     [java] 	at org.apache.tools.ant.Project.executeTargets(Project.java:1248)
     [java] 	at org.apache.tools.ant.Main.runBuild(Main.java:851)
     [java] 	at org.apache.tools.ant.Main.startAnt(Main.java:235)
     [java] 	at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
     [java] 	at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109)

BUILD SUCCESSFUL
Total time: 0 seconds
gateman@TPEOS Java_1 $ cat /home/gateman/tmp/testStream4.txt 
this err msg will be printed to file!
java.io.IOException: Shit !!! / by zero!
	at Stream_kng.PrintStream_kng.SystemErr1.h(SystemErr1.java:16)
	at Stream_kng.PrintStream_kng.SystemErr1.g(SystemErr1.java:42)
	at Stream_kng.PrintStream_kng.SystemErr1.f(SystemErr1.java:9)
	at Enter_1.main(Enter_1.java:95)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:217)
	at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:152)
	at org.apache.tools.ant.taskdefs.Java.run(Java.java:771)
	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:221)
	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:135)
	at org.apache.tools.ant.taskdefs.Java.execute(Java.java:108)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:292)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.Target.execute(Target.java:435)
	at org.apache.tools.ant.Target.performTasks(Target.java:456)
	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1393)
	at org.apache.tools.ant.Project.executeTarget(Project.java:1364)
	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
	at org.apache.tools.ant.Project.executeTargets(Project.java:1248)
	at org.apache.tools.ant.Main.runBuild(Main.java:851)
	at org.apache.tools.ant.Main.startAnt(Main.java:235)
	at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
	at org.apache.to


4.2 標準輸出和標準錯誤輸出的重定向

在項目編程中, 有時我們需要把正常輸出到1個文件, 錯誤輸出到另外一個文件方便記錄和以後查看, 這時我們可以利用System.setOut() 和 System.setErr()方法來改變輸出的指向.

這個就是所謂的標準和標準錯誤的重定向輸出了.




















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章