Java入門
1 流的概念
stream代表的是任何有能力產出數據的數據源,或是任何有能力接收數據的接收源。流是一個很形象的概念,當程序需要讀取數據的時候,就會開啟一個通向數據源的流,這個數據源可以是文件,內存,或是網絡連接。類似的,當程序需要寫入數據的時候,就會開啟一個通向目的地的流。這時候你就可以想象數據好像在這其中“流”動一樣,如下圖:
在Java的IO中,所有的stream(包括Inputstream和Out stream)都包括兩種類型:
(1)字節流
表示以字節為單位從stream中讀取或往stream中寫入信息,即io包中的inputstream類和outputstream類的派生類。通常用來讀取二進制數據,如圖象和聲音。
(2)字符流
以Unicode字符為導向的stream,表示以Unicode字符為單位從stream中讀取或往stream中寫入信息。
區別:
Reader和Writer要解決的,最主要的問題就是國際化。原先的I/O類庫只支持8位的字節流,因此不可能很好地處理16位的Unicode字符流。Unicode是國際化的字符集(更何況Java內置的char就是16位的Unicode字符),這樣加了Reader和Writer之后,所有的I/O就都支持Unicode了。此外新類庫的性能也比舊的好。
但是,Read和Write并不是取代InputStream和OutputStream,有時,你還必須同時使用"基于byte的類"和"基于字符的類"。為此,它還提供了兩個"適配器(adapter)"類。InputStreamReader負責將InputStream轉化成Reader,而OutputStreamWriter則將OutputStream轉化成Writer。實際上是通過byte[]和String來關聯。在實際開發中出現的漢字問題實際上都是在字符流和字節流之間轉化不統一而造成的。
以字符為導向的stream基本上對有與之相對應的以字節為導向的stream。兩個對應類實現的功能相同,只是在操作時的導向不同。如 CharArrayReader和ByteArrayInputStream的作用都是把內存中的一個緩沖區作為InputStream使用,所不同的是前者每次從內存中讀取一個字節的信息,而后者每次從內存中讀取一個字符。
字符流處理的單元為2個字節的Unicode字符,分別操作字符、字符數組或字符串;而字節流處理單元為1個字節,操作字節和字節數組,可用于任何類型的對象,包括二進制對象,但是不能直接處理Unicode字符。字符流是由Java虛擬機將字節轉化為2個字節的Unicode字符為單位的字符而成的,所以它對多國語言支持性比較好。如果是音頻文件、圖片、歌曲,就用字節流好點,如果是關系到中文(文本)的,用字符流好點。
2 流的層次結構
java將讀取數據對象稱為輸入流,能向其寫入的對象叫輸出流。
1) 基于字節的輸入流
l FileInputStream:把一個文件作為InputStream,從本地文件系統中讀取數據字節,實現對文件的讀取操作
l ByteArrayInputStream:把內存中的一個緩沖區作為InputStream使用,從內存數組中讀取數據字節
l ObjectInputStream:對象輸入流。從文件中把對象讀出來重新建立。對象必須要實現Serializable接口。對象中的transient和static類型的成員變量不會被讀取和寫入。
l PipedInputStream:實現了pipe的概念,從線程管道中讀取數據字節,主要在線程中使用。管道輸入流是指一個通訊管道的接收端。一個線程通過管道輸出流發送數據,而另一個線程通過管道輸入流讀取數據,這樣可實現兩個線程間的通訊
l SequenceInputStream:把多個InputStream合并為一個InputStream,當到達流的末尾時從一個流轉到另一個流,“序列輸入流”類允許應用程序把幾個輸入流連續地合并起來,并且使它們像單個輸入流一樣出現。
l StringBufferInputStream:把一個String對象作為InputStream,從字符串中讀取數據字節
l FilterInputStream:過濾器流java.io.FilterInputStream,過濾器流即能把基本流包裹起來,提供更多方便的用法。類的構造方法為FilterInputStream(InputStream),在指定的輸入流之上,創建一個輸入流過濾器。常用的子類如下:
u BufferedInputStream:緩沖區對數據的訪問,以提高效率
u DataInputStream:從輸入流中讀取基本數據類型,如int、float、double或者甚至一行文本
u LineNumberInputStream:在翻譯行結束符的基礎上,維護一個計數器,該計數器表明正在讀取的是哪一行。
u PushbackInputStream:允許把數據字節向后推到流的首部
l System.in
從用戶控制臺讀取數據字節
在System類中, in是InputStream類的靜態對象,因此,out和err可以引用PrintStream類的成員方法。如:System.in.read()。
2) 基于字節的輸出流
l FileOutputStream:把信息存入文件中
l ByteArrayOutputStream:把信息存入內存中的一個緩沖區中,該類實現一個以字節數組形式寫入數據的輸出流
l PipedOutputStream:實現了pipe的概念,主要在線程中使用。管道輸出流是指一個通訊管道的發送端。 一個線程通過管道輸出流發送數據, 而另一個線程通過管道輸入流讀取數據,這樣可實現兩個線程間的通訊。
l SequenceOutputStream:把多個OutStream合并為一個OutStream
l FilterOutputStream:類似于FilterInputStream,OutputStream也提供了過濾器輸出流。
l ObjectOutputStream:對象輸出流。對象必須要實現Serializable接口。對象中的transient和static類型的成員變量不會被讀取和寫入。
l System.out
輸出數據字節到用戶控制臺
在System類中,out和err是PrintStream類的靜態對象,因此,out和err可以引用PrintStream類的成員方法。如:System.out.write (int a)。
3) 基于字符的輸入流
l CharArrayReader:與ByteArrayInputStream對應,從字符數組中讀取數據
l StringReader:與StringBufferInputStream對應,從字符數組中讀取數據
l FileReader:與FileInputStream對應,從本地文件系統中讀取字符序列
l PipedReader:與PipedInputStream對應,從線程管道中讀取字符序列
l InputStreamReader:InputStreamReader是從輸入流中讀取數據,連接輸入流于讀取器。如: new InputStreamReader(System.in)
l BufferedReader:緩沖數據的訪問,以提高效率
u LineNumberReader(BufferedReader的子類):維護一個計數器,該計數器表明正在讀取的是哪一行。
l FilterReader(抽象類):提供一個類創建過濾器時可以擴展這個類
u PushbackReader(FilterReader的子類):允許把文本數據推回到讀取器的流中。
這些過濾器讀取器都可以傳入一個Reader作為構造方法的參數。
4) 基于字符的輸出流
l CharArrayWrite:與ByteArrayOutputStream對應
l StringWrite:無與之對應的以字節為導向的stream
l FileWrite:與FileOutputStream對應
l PipedWrite:與PipedOutputStream對應
3 InputStream類
Inputstream類和Outputstream類都為抽象類,不能創建對象,可以通過子類來實例化。
InputStream是輸入字節數據用的類,所以InputStream類提供了3種重載的read方法。Reader也有完全相同的3個read接口。Inputstream類中的常用方法:
1) public abstract int read( ):讀取一個byte的數據,返回讀到的數據(高位補0的int類型值),如果返回-1,表示讀到了輸入流的末尾。
2) public int read(byte b[ ]):讀取b.length個字節的數據放到b數組中。返回值是讀取的字節數, 如果返回-1,表示讀到了輸入流的末尾。該方法實際上是調用下一個方法實現的。
3) public int read(byte b[ ], int off, int len):從輸入流中最多讀取len個字節的數據,存放到數組b中,返回實際讀取的字節數。如果返回-1,表示讀到了輸入流的末尾。off指定在 數組b中存放數據的起始偏移位置。
4) public int available( ):返回輸入流中可以讀取的字節數。注意:若輸入阻塞,當前線程將被掛起,如果InputStream對象調用這個方法的話,它只會返回0,這個方法必須由繼承InputStream類的子類對象調用才有用。
5) public long skip(long n):忽略輸入流中的n個字節,返回值是實際忽略的字節數, 跳過一些字節來讀取。
6) public int close( ) :我們在使用完后,必須對我們打開的流進行關閉。
7) void mark(int readlimit) :在輸入流的當前位置放置一個 標記,如果讀取的字節數多于readlimit設置的值,則流忽略這個標記。在IutputStream類中實際是一個空實現。
8) void reset() :返回到上一個標記。
9) boolean markSupported() :測試當前流是否支持mark和reset方法。如果支持,返回true,否則返回false。在IutputStream類中實際是一個空實現。
4 OutputSteam類
OutputStream提供了3個write方法來做數據的輸出,這個是和InputStream是相對應的,Writer同樣提供了相同的三個write方法。
1) public abstract void write(int b) :先將int轉換為byte類型,把低字節寫入到輸出流中。
2) public void write(byte b[ ]):將參數b中的字節寫到輸出流。
3) public void write(byte b[ ], int off, int len) :將參數b的從偏移量off開始的len個字節寫到輸出流。
4) public void flush( ) : 將數據緩沖區中數據全部輸出,并清空緩沖區。只對使用了緩沖的流類起作用。在OutputStream類中實際是一個空實現。
5) public void close( ) : 關閉輸出流并釋放與流相關的系統資源。
注意:
1) 上述各方法都有可能引起異常。
2) InputStream和OutputStream都是抽象類,不能創建這種類型的對象。
5 FileInputStream和FileOutputStream
5.1FileInputStream
FileInputStream類是InputStream類的子類,用來處理以文件作為數據輸入源的數據流。使用方法:
方式1:
File fin=new File("d:/abc.txt");
FileInputStream in=new FileInputStream(fin);
方式2:
FileInputStream in=new FileInputStream("d: /abc.txt");
方式3:
構造函數將 FileDescriptor()對象作為其參數。
FileDescriptor() fd=new FileDescriptor();
FileInputStream f2=new FileInputStream(fd);
從指定文件中讀出數據:
view plaincopy to clipboardprint?
1. import java.io.*;
2. lass StreamTest{
3. public static void main(String[] args)throws Exception{
4. FileInputStream fis=new FileInputStream("testoutput.txt");
5. byte[] buf=new byte [100];
6. int len=fis.read(buf);
7. System.out.println(new String(buf,0,len));//由于buf是大小為100的緩沖區,為 了輸出有效字符使用String類的構造方法String(byte[] bytes, int offset, int length)實現輸出
8. fis.close();
9. }
10. }
5.2FileOutputStream
FileOutputStream類用來處理以文件作為數據輸出目的數據流;一個表示文件名的字符串,也可以是File或FileDescriptor對象。
創建一個文件流對象有兩種方法:
方式1:
File f=new File("d:/abc.txt");
FileOutputStream out=new FileOutputStream (f);
方式2:
FileOutputStream out=new
FileOutputStream("d:/abc.txt");
方式3:
構造函數將 FileDescriptor()對象作為其參數。
FileDescriptor() fd=new FileDescriptor();
FileOutputStream f2=new FileOutputStream(fd);
方式4:
構造函數將文件名作為其第一參數,將布爾值作為第二參數。
FileOutputStream f=new FileOutputStream("d:/abc.txt",true);
注意:
1) 文件中寫數據時,若文件已經存在,則覆蓋存在的文件;
2) 文件的讀/寫操作結束時,應調用close方法關閉流。
向指定文件寫入數據:
view plaincopy to clipboardprint?
1. import java.io.*;
2. class StreamTest{
3. public static void main(String[] args)throws Exception{
4. FileOutputStream fos=new FileOutputStream("testoutput.txt");
5. fos.write("Hello World".getBytes ());
6. fos.close();
7. }
8. }
程序中使用到FileOutputStream類的方法void write(byte[] b) ,所以從寫入字符串" Hello World "通過String類的方法getBytes()得到字節數組b,成功進行寫入。
6 ObjectOutputStream和ObjectInputStream
ObjectOutputStream和ObjectInputStream可以利用對象流對對象進行序列化。
在一個程序運行的時候,其中的變量數據是保存在內存中的,一旦程序結束這些數據將不會被保存,一種解決的辦法是將數據寫入文件,而Java中提供了一種機制,它可以將程序中的對象寫入文件,之后再從文件中把對象讀出來重新建立。這就是所謂的對象序列化Java中引入它主要是為了RMI(Remote Method Invocation)和Java Bean所用,不過在平時應用中,它也是很有用的一種技術。
所有需要實現對象序列化的對象必須首先實現Serializable接口。下面看一個例子:
view plaincopy to clipboardprint?
1. import java.io.*;
2. import java.util.*;
3. public class Logon implements Serializable {
4. private Date date = new Date();
5. private String username;
6. private transient String password;
7. Logon(String name, String pwd){
8. username = name;
9. password = pwd;
10. }
11. public String toString(){
12. String pwd = (password == null) ? "(n/a)":password;
13. return "logon info: \n " + "username: " + username + "\n date: " + date + "\n password: " + pwd;
14. }
15. public static void main(String[] args)throws IOException, ClassNotFoundException {
16. Logon a = new Logon("Morgan", "morgan83");
17. System.out.println("logon a = " + a);
18. ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Logon.out"));
19. o.writeObject(a);
20. o.close();
21. int seconds = 5;
22. long t = System.currentTimeMillis() + seconds * 1000;
23. while(System.currentTimeMillis() <t) ;
24. ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logon.out"));
25. System.out.println("Recovering object at " + new Date());
26. a = (Logon)in.readObject();
27. System.out.println("logon a = " + a);
28. }
29. }
類Logon是一個記錄登錄信息的類,包括用戶名和密碼。首先它實現了接口Serializable,這就標志著它可以被序列化。之后再main方法里ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("Logon.out"));新建一個對象輸出流包裝一個文件流,表示對象序列化的目的地是文件Logon.out。然后用方法writeObject開始寫入。想要還原的時候也很簡單ObjectInputStream in = new ObjectInputStream(new FileInputStream("Logon.out"));新建一個對象輸入流以文件流Logon.out為參數,之后調用readObject方法就可以了。
需要說明一點,對象序列化有一個神奇之處就是,它建立了一張對象網,將當前要序列化的對象中所持有的引用指向的對象都包含起來一起寫入到文件,更為奇妙的是,如果你一次序列化了好幾個對象,它們中相同的內容將會被共享寫入。這的確是一個非常好的機制。它可以用來實現深層拷貝。
關鍵字transient在這里表示當前內容將不被序列化,比如例子中的密碼,需要保密,所以沒有被寫入文件。
7 PipedInputStream和PipedOutputStream
PipedInputStream和PipedOutputStream分別從InputStream類和OutputStream類繼承,因此它們并不是過濾流類。
管道流,用于線程間的通信。一個線程的PipedInputStream對象從另一個線程的 PipedOutputStream對象讀取。要使管道流有用,必須同時構造管道輸入流和管道輸出流。
利用線程生產者和消費者問題:
view plaincopy to clipboardprint?
1. import java.io.*;
2. class PipedStreamTest{
3. public static void main(String[] args){
4. PipedOutputStream pos=new PipedOutputStream();
5. PipedInputStream pis=new PipedInputStream();
6. try{
7. pos.connect(pis);
8. new Producer(pos).start();
9. new Consumer(pis).start();
10. }catch(Exception e){
11. e.printStackTrace();
12. }
13. }
14. }
15.
16. class Producer extends Thread{
17. private PipedOutputStream pos;
18. Producer(PipedOutputStream pos){
19. this.pos=pos;
20. }
21. public void run(){
22. try{
23. pos.write("Hello,My friends!".getBytes());
24. pos.close();
25. }catch(Exception e){
26. e.printStackTrace();
27. }
28. }
29. }
30. class Consumer extends Thread{
31. private PipedInputStream pis;
32. Consumer(PipedInputStream pis){
33. this.pis=pis;
34. }
35. public void run(){
36. try{
37. byte[] buf=new byte[100];
38. int len=pis.read(buf);
39. System.out.println(new String(buf,0,len));
40. pis.close();
41. }catch(Exception e){
42. e.printStackTrace();
43. }
44. }
45. }
8 FilterInputStream和FilterOutputStream
java的流類提供了結構化方法,如,底層流和高層過濾流。
而高層流不是從輸入設備讀取,而是從其他流讀取。同樣高層輸出流也不是寫入輸出設備,而是寫入其他流。
使用“分層對象(layered objects)”,為單個對象動態地,透明地添加功能的做法,被稱為Decorator Pattern。Decorator模式要求所有包覆在原始對象之外的對象,都必須具有與之完全相同的接口。這使得decorator的用法變得非常的透明--無論對象是否被decorate過,傳給它的消息總是相同的。這也是Java I/O類庫要有“filter(過濾器)”類的原因:抽象的“filter”類是所有decorator的基類。Decorator模式常用于如下的情形:如果用繼承來解決各種需求的話,類的數量會多到不切實際的地步。Java的I/O類庫需要提供很多功能的組合,于是decorator模式就有了用武之地。
為InputStream和OutputStream定義decorator類接口的類,分別是FilterInputStream和FilterOutputStream。
通過FilterInputStream和FilterOutStream的子類,我們可以為stream添加屬性。下面以一個例子來說明這種功能的作用。
如果我們要往一個文件中寫入數據,我們可以這樣操作:
FileOutStream fs = new FileOutStream(“test.txt”);
然后就可以通過產生的fs對象調用write()函數來往test.txt文件中寫入數據了。但是,如果我們想實現“先把要寫入文件的數據先緩存到內存中,再把緩存中的數據寫入文件中”的功能時,上面的API就沒有一個能滿足我們的需求了。但是通過FilterInputStream和 FilterOutStream的子類,為FileOutStream添加我們所需要的功能。
8.1FilterInputStream
類 |
功能 |
構造函數參數 |
用法 |
DataInputStream |
與DataOutputStream配合使用,這樣你就能以一種"可攜帶的方式(portable fashion)"從流里讀取primitives了(int,char,long等) |
InputStream |
包含了一整套讀取primitive數據的接口 |
BufferedInputStream |
使用緩沖區解決"每次要用數據的時候都要進行物理讀取"的問題。 |
InputStream,以及可選的緩沖區的容量 |
它本身并不提供接口,只是提供一個緩沖區。需要連到一個"有接口的對象(interface object)"。 |
LineNumberInputStream |
跟蹤輸入流的行號;有getLineNumber( )和setLineNumber(int)方法 |
InputStream |
只是加一個行號,所以還得連一個"有接口的對象"。 |
PushbackInputStream |
有一個"彈壓單字節"的緩沖區(has a one byte push-back buffer),這樣你就能把最后讀到的那個字節再壓回去了。 |
InputStream |
主要用于編譯器的掃描程序。可能是為支持Java的編譯器而設計的。用的機會不多。 |
8.2FilterOutputStream
類 |
功能 |
構造函數參數 |
用法 |
DataOutputStream |
與DataInputStream配合使用,這樣你就可以用一種"可攜帶的方式(portable fashion)"往流里寫primitive了(int, char, long,等) |
OutputStream |
包含了一整套寫入primitive數據的接口 |
PrintStream |
負責生成帶格式的輸出(formatted output)。DataOutputStrem負責數據的存儲,而PrintStream負責數據的顯示。 |
一個OutputStream以及一個可選的boolean值。這個boolean值表示,要不要清空換行符后面的緩沖區。 |
應該是OutputStream對象的最終包覆層。用的機會很多。 |
BufferedOutputStream |
用 這個類解決"每次往流里寫數據,都要進行物理操作"的問題。也就是說"用緩沖區"。用flush( )清空緩沖區。 |
OutputStream, 以及一個可選的緩沖區大小 |
本身并不提供接口,只是加了一個緩沖區。需要鏈接一個有接口的對象。 |
8.3DataInputStream和DataOutputStream
DataInputStream類對象可以讀取各種類型的數據。
DataOutputStream類對象可以寫各種類型的數據;
創建這兩類對象時,必須使新建立的對象指向構造函數中的參數對象。例如:
FileInputStream in=new FileInputStream("d:/test.txt");
DataInputStream din=new DataInputStream(in);
向指定文件中寫入數據:
view plaincopy to clipboardprint?
1. import java.io.*;
2. class StreamTest{
3. public static void main(String[] args)throws Exception{
4. FileOutputStream fos=new FileOutputStream("d:/test.txt");
5. BufferedOutputStream bos=new BufferedOutputStream(fos);
6. DataOutputStream dos=new DataOutputStream(bos);
7. byte b=3;
8. int i=100;
9. boolean bool=false;
10. char c='k';
11. float f=45.77f;
12. dos.writeByte(b);
13. dos.writeInt(i);
14. dos.writeBoolean(bool);
15. dos.writeChar(c);
16. dos.writeFloat(f);
17. dos.close();
18. }
19. }
從指定文件中讀出數據:
view plaincopy to clipboardprint?
1. import java.io.*;
2. class StreamTest{
3. public static void main(String[] args)throws Exception{
4. FileInputStream fis=new FileInputStream("d:/test.txt");
5. BufferedInputStream bis=new BufferedInputStream(fis);
6. DataInputStream dis=new DataInputStream(bis);
7. System.out.println(dis.readByte());
8. System.out.println(dis.readInt());
9. System.out.println(dis.readBoolean());
10. System.out.println(dis.readChar());
11. System.out.println(dis.readFloat());
12. dis.close();
13. }
14. }
8.4BufferInputStream和BufferOutputStream
允許程序在不降低系統性能的情況下一次一個字節的從流中讀取數據。
BufferInputstream定義了兩種構造函數
(1)BufferInputStream b= new BufferInputstream(in);
(2)BufferInputStream b=new BufferInputStream(in,size)
第二個參數表示指定緩沖器的大小。
同樣BufferOutputStream也有兩種構造函數。一次一個字節的向流中寫數據。
向指定文件中寫入數據:
view plaincopy to clipboardprint?
1. import java.io.*;
2. class StreamTest{
3. public static void main(String[] args)throws Exception{
4. FileOutputStream fos=new FileOutputStream("testBuffer.txt");
5. BufferedOutputStream bos=new BufferedOutputStream(fos);
6. bos.write("Hello BufferedOutputStream!".getBytes());
7. bos.close();
8. }
9. }
如果不加bos.close();語句,則運行后打開testBuffer.txt文件會查看不到內容,這是因為我們將數據寫入了緩沖區,只有結束程序時才寫入到指定文件中。bos.close();實現立即寫入到指定文件,不同的是這樣如果想要繼續寫入就不用再次打開文件了。
從指定文件中讀出數據:
view plaincopy to clipboardprint?
1. import java.io.*;
2. class StreamTest{
3. public static void main(String[] args)throws Exception{
4. FileInputStream fis=new FileInputStream("d:/test.txt");
5. BufferedInputStream bis=new BufferedInputStream(fis);
6. byte[] buf=new byte [100];
7. int len = bis.read(buf);
8. System.out.println(new String(buf,0,len));
9. bis.close();
10. }
11. }
8.5Printstream
用于寫入文本或基本類型
兩種構造函數方法:
PrintStream ps=new PrintStream(out);
PrintStream ps=new PrintStream(out, autoflush)
第二個參數為布爾值,控制每次輸出換行符時java是否刷新輸出流。
9 Java IO 的一般使用原則
一、按數據來源(去向)分類:
1 、是文件: FileInputStream, FileOutputStream(字節流),FileReader, FileWriter(字符流)
2 、是 byte[] : ByteArrayInputStream, ByteArrayOutputStream(字節流)
3 、是 Char[]: CharArrayReader, CharArrayWriter(字符流)
4 、是 String: StringBufferInputStream, StringBufferOuputStream (字節流)StringReader, StringWriter(字符流)
5 、網絡數據流: InputStream, OutputStream(字節流), Reader, Writer( 字符流 )
二、按是否格式化輸出分:
1 、要格式化輸出: PrintStream, PrintWriter
三、按是否要緩沖分:
1 、要緩沖: BufferedInputStream, BufferedOutputStream(字節流), BufferedReader, BufferedWriter(字符流)
四、按數據格式分:
1 、二進制格式(只要不能確定是純文本的): InputStream, OutputStream 及其所有帶 Stream 結束的子類
2 、純文本格式(含純英文與漢字或其他編碼方式):Reader, Writer 及其所有帶 Reader, Writer 的子類
五、按輸入輸出分:
1 、輸入: Reader, InputStream 類型的子類
2 、輸出: Writer, OutputStream 類型的子類
六、特殊需要:
1 、從 Stream 到 Reader,Writer 的轉換類: InputStreamReader, OutputStreamWriter
2 、對象輸入輸出: ObjectInputStream, ObjectOutputStream
3 、進程間通信: PipeInputStream, PipeOutputStream, PipeReader, PipeWriter
4 、合并輸入: SequenceInputStream
5 、更特殊的需要: PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
決定使用哪個類以及它的構造進程的一般準則如下(不考慮特殊需要):
首先,考慮最原始的數據格式是什么: 原則四
第二,是輸入還是輸出:原則五
第三,是否需要轉換流:原則六第 1 點
第四,數據來源(去向)是什么:原則一
第五,是否要緩沖:原則三 (特別注明:一定要注意的是 readLine() 是否有定義,有什么比 read, write 更特殊的輸入或輸出方法)
第六,是否要格式化輸出:原則二