本文主要關注在Java編程中涉及到的IO相關的類庫、方法。以及對各個層次(抽線、接口繼承)的流之間的關系進行梳理
相關學習資料
http://baike.baidu.com/view/1007958.htm?noadapt=1 http://blog.csdn.net/hguisu/article/details/7418161 https://www.ibm.com/developerworks/cn/java/j-lo-javaio/ http://docs.oracle.com/javase/7/docs/api/ http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/java/io/package-tree.html http://tutorials.jenkov.com/java-io/index.html
目錄
1. Java中IO簡介 2. Java中的流 3. Java中和IO相關的類庫層次結構
1. Java中IO簡介
IO(Input And Output)在編程中是一個很常見的需求,IO即意味着我們的java程序需要和"外部"進行通信,這個"外部"可以是很多介質
1) 本地磁盤文件、遠程磁盤文件 2) 數據庫連接 3) TCP、UDP、HTTP網絡通信 4) 進程間通信 5) 硬件設備(鍵盤、串口等)
...
2. Java中的流
IO是我們的目的,而要達到這一目的,我們需要一種機制來幫助我們完全,這種機制就是"流"、或者叫"數據流"。
數據流是一串連續不斷的數據的集合,就象水管里的水流,在水管的一端一點一點地供水,而在水管的另一端看到的是一股連續不斷的水流。數據寫入程序可以是一段、一段地向數據流管道中寫入數據,這些數據段會按先后順序形成一個長的數據流。對數據讀取程序來說,看不到數據流在寫入時的分段情況,每次可以讀取其中的任意長度的數據,但只能先讀取前面的數據后,再讀取后面的數據。不管寫入時是將數據分多次寫入,還是作為一個整體一次寫入,讀取時的效果都是完全一樣的。
Java的IO模型設計非常優秀,它使用Decorator模式,按功能划分Stream
記住這句話對我們在編程中選擇合適的類庫很重要,Java中按照功能提供不同類別的流,我們接下來深入學習一下java中的各個流、以及它們的層次結構關系
3. Java中和IO相關的類庫層次結構
首先,java中所有的對象(包括)流對象都從Object 超類繼承而來,所以,所有的流類的父類都是Object類
以下的縮進關閉表示的的類的繼承關系
Object(超類) 1. 基於"字節"操作的 I/O 接口: 1) InputStream InputStream類是一個abstract class(抽象父類),它不能被直接用於實例化進行流操作,我們在編程中使用的是它的子類 1.1) ByteArrayInputStream: 從字節數組(byte[])中進行以字節為單位的讀取 1.2) FileInputStream: 從文件中進行以字節為單位的讀取 1.2.1) SocketInputStream org.apache.commons.net.io.SocketInputStream: 封裝了對Socket的字節型流式讀取 1.3) FilterInputStream: 用來"封裝其它的輸入流,並為它們提供額外的功能" 1.3.1) InflaterInputStream java.util.zip.InflaterInputStream: 從壓縮數據源(zip)中以字節為單位讀取數據 1.3.1.1) ZipInputStream java.util.zip.ZipInputStream: 從zip文件中以字節為單位讀取數據 1.3.2) BufferedInputStream: 開辟"內部字節數組"對輸入流進行緩存,函數的返回也是一個字節數組 1.3.3) DataInputStream: DataInputStream 是用來裝飾其它輸入流,它"允許應用程序以與機器無關方式從底層輸入流中讀取基本 Java 數據類型"。應用程序可以使用DataOutputStream(數據輸出流)
寫入由DataInputStream(數據輸入流)讀取的數據。 1.4) ObjectInputStream: 從輸入流中讀取序列化后的數據,並進行反序列化(deserializes) 1.5) PipedInputStream: 從管道中讀取數據 2) OutputStream OutputStream類是一個abstract class(抽象父類),它不能被直接用於實例化進行流操作,我們在編程中使用的是它的子類 2.1) ByteArrayOutputStream: 以字節為單位將數據寫入到從字節數組(byte[])中 2.2) FileOutputStream: 以字節為單位將數據寫入到文件中 2.2.1) SocketOutputStream org.apache.commons.net.io.SocketOutputStream: 封裝了對Socket的字節型流式寫入 2.3) FilterOutputStream: 用來"封裝其它的輸出流,並為它們提供額外的功能" 2.3.1) ZipOutputStream: java.util.zip.ZipOutputStream: 以字節為單位向zip文件寫入數據 2.3.2) PrintStream: PrintStream 是用來裝飾其它輸出流。它能為其他輸出流添加了功能,使它們能夠方便地打印各種數據值表示形式 2.3.3) DataOutputStream: DataOutputStream 是用來裝飾其它輸入流,它"允許應用程序以與機器無關方式向底層輸出流中寫入基本 Java 數據類型"。應用程序可以使用DataInputStream(數據輸入流)
寫入由DataOutputStream(數據輸出流)寫入的數據()。有點類似管道、或者進程間通信的感覺
2.3.4) BufferedInputStream: 2.4) ObjectOutputStream: 對數據進行序列化(serializes),並向輸出流中寫入序列化后的數據 2.5) PipedOutputStream: 向管道中寫入數據 2. 基於"字符"操作的 I/O 接口 不管是磁盤還是網絡傳輸,最小的存儲單元都是字節,而不是字符,所以 I/O 操作的都是字節而不是字符,為了操作方便,java封裝了一個直接寫字符的 I/O 接口,這里就涉及到java的流機制
中的一個很重要的概念,包裝(裝飾)。即所有的流操作在底層實現都是字節流的形式,以這個底層字節流為基礎,在其上封裝了各種"附加功能"(緩存、字符、管道..) 1) Reader Reader類是一個abstract class(抽象父類),它不能被直接用於實例化進行流操作,我們在編程中使用的是它的子類 1.1) InputStreamReader: 我們知道,字符型的流接口是在字節型的流接口基礎之上進行了一次封裝,提供了一些額外的功能。所以,從名字上也可以看出來,InputStreamReader是字節流通向字符流的橋梁,
封裝了InputStream在里頭, 它以較高級的方式,一次讀取一個一個字符,以文本格式輸入/輸出,可以指定編碼格式。 1.1.1) FileReader: 提供對文本文件(保存字符的文件)進行以字符為單位的讀取 1.2) BufferedReader: BufferedReader會一次性從物理流中讀取8k(默認數值,可以設置)字節內容到內存,如果外界有請求,就會到這里存取,如果內存里沒有才到物理流里再去讀。即使讀,也是再8k
而直接讀物理流,是按字節來讀。對物理流的每次讀取,都有IO操作。IO操作是最耗費時間的。BufferedReader就是減少了大量IO操作,節省了時間 1.3) CharArrayReader: CharArrayReader 是字符數組輸入流。它和ByteArrayInputStream類似,只不過ByteArrayInputStream是字節數組輸入流,而CharArray是字符數組輸入流。
CharArrayReader 是用於讀取字符數組,它繼承於Reader。操作的數據是以字符為單位 1.4) FilterReader: 用來"封裝其它的字符輸入流,並為它們提供額外的功能" 1.5) PipedReader: PipedReader 是字符管道輸入流,它繼承於Reader。 1.6) StringReader: 以String作為數據源,進行以字符為單位的讀取 2) Writer Writer類是一個abstract class(抽象父類),它不能被直接用於實例化進行流操作,我們在編程中使用的是它的子類 2.1) OutputStreamWriter: 2.1.1) FileWriter: 提供對文本文件(保存字符的文件)進行以字符為單位的寫入 2.2) BufferedWriter 2.3) StringWriter 2.4) PipedWriter 2.5) PrintWriter 2.6) CharArrayWriter 3. 基於"磁盤"操作的 I/O 接口: 1) File: (文件特征與管理): 用於文件或者目錄的描述信息,例如生成新目錄,修改文件名,刪除文件,判斷文件所在路徑等,它不負責數據的輸入輸出,而專門用來管理磁盤文件與目錄 1) public boolean exists( ) 判斷文件或目錄是否存在 2) public boolean isFile( ) 判斷是文件還是目錄 3) public boolean isDirectory( ) 判斷是文件還是目錄 4) public String getName( ) 返回文件名或目錄名 5) public String getPath( ) 返回文件或目錄的路徑。 6) public long length( ) 獲取文件的長度 7) public String[] list( ) 將目錄中所有文件名保存在字符串數組中返回 8) public boolean renameTo( File newFile ); 重命名文件 9) public void delete( ); 刪除文件 10) public boolean mkdir( ); 創建目錄 4. 基於網絡操作的 I/O 接口: 1) Socket
以上是按照Java官方API文檔列出的總的目錄,我們接下來逐一學習一下它們的應用場景,因為輸入、輸出流在編程上具有對稱性,所以我們把它們合並在一起學習
0x1: InputStream: 字節輸入流
0x2: OutputStream: 字節輸出流
ByteArrayInputStream、ByteArrayOutputStream
ByteArrayOutputStream類是在創建它的實例時,程序內部創建一個byte型數組的緩沖區,然后利用ByteArrayOutputStream和ByteArrayInputStream的實例向數組中寫入或讀出byte型數據。在網絡傳輸中我們往往要傳輸很多變量,我們可以利用ByteArrayOutputStream把所有的變量收集到一起,然后一次性把數據發送出去
import java.io.*; public class circle { public static void main(String[] args) throws Exception { int a=0; int b=1; int c=2; /* * ByteArrayOutputStream() * Creates a new byte array output stream. */ ByteArrayOutputStream bout = new ByteArrayOutputStream(); /* * write(int b) * Writes the specified byte to this byte array output stream. */ bout.write(a); bout.write(b); bout.write(c); /* * toByteArray() * Creates a newly allocated byte array. * 返回內部保存的臨時byte緩存數組 */ byte[] buff = bout.toByteArray(); for(int i=0; i<buff.length; i++) { System.out.println(buff[i]); } System.out.println("***********************"); /* * ByteArrayInputStream(byte[] buf) * Creates a ByteArrayInputStream so that it uses buf as its buffer array. */ ByteArrayInputStream bin = new ByteArrayInputStream(buff); /* * read() * Reads the next byte of data from this input stream. */ while((b = bin.read()) != -1) { System.out.println(b); } } }
FileInputStream、FileOutputStream
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; public class circle { public static void main(String[] args) throws Exception { File f = new File("C:\1.txt"); //向文件里寫如"Hello"字符串. try { //要寫入的數據轉換成字節數組 byte[] buf = "Hello".getBytes(); /* * FileOutputStream(File file) * Creates a file output stream to write to the file represented by the specified File object. * 如果1.txt存在,則刪除1.txt里面的內容,文本所有內容變為Hello * 如果1.txt不存在,在新建1.txt文本,寫入Hello */ FileOutputStream out = new FileOutputStream(f); /* * write(byte[] b) * Writes b.length bytes from the specified byte array to this file output stream. */ out.write(buf); out.close(); } catch(Exception e) { System.out.println(e); } //讀取文件中的內容 try { /* * FileInputStream(File file) * Creates a FileInputStream by opening a connection to an actual file, the file named by the File object file in the file system. */ FileInputStream in = new FileInputStream(f); byte[] buf = new byte[1024]; /* * read(byte[] b) * Reads up to b.length bytes of data from this input stream into an array of bytes. * 從流中讀取內容 */ int len = in.read(buf); String str = new String(buf,0,len); //打印f文件的內容. System.out.println(str); } catch(Exception e) { System.out.println(e); } } }
ZipInputStream、ZipOutputStream(包裝流)
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import java.io.FileOutputStream; public class circle { public static void main(String[] args) throws Exception { // 定義要壓縮的文件 File file = new File("C:" + File.separator + "in.txt"); // 定義壓縮文件名稱 File zipFile = new File("C:" + File.separator + "in.zip"); /* * FileInputStream(File file) * Creates a FileInputStream by opening a connection to an actual file, the file named by the File object file in the file system. * 定義文件的輸入流 */ InputStream input = new FileInputStream(file); // 聲明壓縮流對象 ZipOutputStream zipOut = null; /* * ZipOutputStream(OutputStream out) * Creates a new ZIP output stream. * 再次體現了Java的流函數架構中的"裝飾器設計模式"的強大之處,我們可以根據我們對功能的 * 需要任意組合、加載我們需要的流,用通俗的話來說,把它們層層包裹在一起 */ zipOut = new ZipOutputStream(new FileOutputStream(zipFile)); /* * putNextEntry(ZipEntry e) * Begins writing a new ZIP file entry and positions the stream to the start of the entry data. * 設置ZipEntry對象 */ zipOut.putNextEntry(new ZipEntry(file.getName())); int temp = 0; /* * read() * Reads a byte of data from this input stream. * 從文件流中讀取內容 */ while((temp = input.read()) != -1) { // 壓縮輸出 zipOut.write(temp); } // 關閉輸入流 input.close(); // 關閉輸出流 zipOut.close(); // read zip file as input stream InputStream is= new FileInputStream("C:" + File.separator + "in.zip"); //zip is read by ZipInputStream ZipInputStream zis= new ZipInputStream(is); // now write zip file in extracted file ZipEntry ze; byte[] buff = new byte[1024]; /* * getNextEntry() * Reads the next ZIP file entry and positions the stream at the beginning of the entry data. */ while((ze = zis.getNextEntry()) != null) { // get file name FileOutputStream fos= new FileOutputStream("C:" + File.separator + "out.txt"); int l=0; // write buffer to file while((l = zis.read(buff)) > 0) { fos.write(buff,0, l); } } zis.close(); } }
BufferedInputStream、BufferedInputStream(包裝流)
import java.io.*; public class circle { public static void main(String[] args) throws Exception { File filein = new File("C:/1.png"); File fileout = new File("C:/out.png"); try { //前面說過,File類它不負責數據的輸入輸出,而專門用來管理磁盤文件與目錄 if (fileout.exists() == false) { fileout.createNewFile(); } //要使用文件流,自然要使用文件讀取流函數 FileInputStream in = new FileInputStream(filein); FileOutputStream out = new FileOutputStream(fileout); byte[] b = new byte[1]; //在文件流外面包一層緩沖流讀取函數 BufferedInputStream bin = new BufferedInputStream(in); BufferedOutputStream bout = new BufferedOutputStream(out); //緩存讀取、緩存寫入 while (bin.read(b) != -1) { bout.write(b); } bout.close(); bin.close(); out.close(); in.close(); } catch (Exception e) { e.printStackTrace(); } } }
DataInputStream、DataOutputStream(包裝流)
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class circle { public static void main(String[] args) throws Exception { writeDemo(); readDemo(); } public static void readDemo() throws IOException { DataInputStream dos = new DataInputStream(new FileInputStream("C:/in.txt")); String s = dos.readUTF(); System.out.println(s); } public static void writeDemo() throws IOException { /* * DataOutputStream(OutputStream out) * Creates a new data output stream to write data to the specified underlying output stream. */ DataOutputStream dos = new DataOutputStream(new FileOutputStream("C:/in.txt")); /* * writeUTF(String str) * Writes a string to the underlying output stream using modified UTF-8 encoding in a machine-independent manner. */ dos.writeUTF("你好啊");//UTF-8修改版 } }
ObjectInputStream、ObjectOutputStream
ObjectOutputStream可以把對象直接存入到文件中,然后利用ObjectInputStream讀取文件還原成對象,前提是該對象實現了Serializable接口
import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class circle { public static void main(String[] args) throws Exception { FileOutputStream fop = new FileOutputStream("C:/out.txt"); /* * ObjectOutputStream(OutputStream out) * Creates an ObjectOutputStream that writes to the specified OutputStream. */ ObjectOutputStream oos = new ObjectOutputStream(fop); People p = new People(1,"zhang"); /* * writeObject(Object obj) * Write the specified object to the ObjectOutputStream. */ oos.writeObject(p); p = new People(2,"li"); oos.writeObject(p); p = new People(3,"zhao"); oos.writeObject(p); //寫入三個對象 oos.close(); //關閉輸出流 FileInputStream fis=new FileInputStream("C:/out.txt"); /* * ObjectInputStream(InputStream in) * Creates an ObjectInputStream that reads from the specified InputStream. */ ObjectInputStream ois = new ObjectInputStream(fis); try { while(true) { /* * readObject() * Read an object from the ObjectInputStream. */ People p2=(People)ois.readObject(); System.out.println(p2); } //沒有辦法判斷文件中對象的數量,所以,只有通過EOFException異常來中斷 //或者在寫入的時候把所有的對象都放到一個ArrayLis里,這樣就不需要判斷了 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch(EOFException e) { System.out.println("讀取結束"); } } } class People implements Serializable { //必須實現Serializable接口 int id; String name; People(int id,String name) { this.id=id; this.name=name; } public String toString() { return "id:"+id+" name:"+name; } }
PipedInputStream、PipedOutputStream
管道流內部在實現時還有大量的對同步數據的處理。管道輸出流和管道輸入流執行時不能互相阻塞,所以一般要開啟獨立線程分別執行
import java.io.*; public class circle { public static void main(String[] args) throws Exception { /* * PipedInputStream() * Creates a PipedInputStream so that it is not yet connected. */ PipedInputStream pin = new PipedInputStream(); PipedOutputStream pout = new PipedOutputStream(); /* * connect(PipedOutputStream src) * Causes this piped input stream to be connected to the piped output stream src. * 輸入流與輸出流連接 */ pin.connect(pout); ReadThread readTh = new ReadThread(pin); WriteThread writeTh = new WriteThread(pout); new Thread(readTh).start(); new Thread(writeTh).start(); } public static void sop(Object obj) //打印 { System.out.println(obj); } } class ReadThread implements Runnable { private PipedInputStream pin; ReadThread(PipedInputStream pin) // { this.pin=pin; } //由於必須要覆蓋run方法,所以這里不能拋,只能try public void run() { try { sop("R:讀取前沒有數據,阻塞中...等待數據傳過來再輸出到控制台..."); byte[] buf = new byte[1024]; int len = pin.read(buf); //read阻塞 sop("R:讀取數據成功,阻塞解除..."); String s= new String(buf,0,len); sop(s); //將讀取的數據流用字符串以字符串打印出來 pin.close(); } catch(Exception e) { throw new RuntimeException("R:管道讀取流失敗!"); } } public static void sop(Object obj) //打印 { System.out.println(obj); } } class WriteThread implements Runnable { private PipedOutputStream pout; WriteThread(PipedOutputStream pout) { this.pout= pout; } public void run() { try { sop("W:開始將數據寫入:但等個5秒讓我們觀察..."); Thread.sleep(5000); //釋放cpu執行權5秒 /* * write(int b) * Writes the specified byte to the piped output stream. * 向管道寫入數據,同時解除管道上的阻塞狀態 */ pout.write("W: writePiped 數據...".getBytes()); pout.close(); } catch(Exception e) { throw new RuntimeException("W:WriteThread寫入失敗..."); } } //打印 public static void sop(Object obj) { System.out.println(obj); } }
在了解了"字節型"流處理函數的應用場景后,我們接下來繼續學習"字符型"流處理函數
0x3: Reader: 字符輸入流
0x4: Writer: 字符輸出流
在開始學習"字符型"流處理函數的應用場景之前,我們必須牢記一個概念,Java的流函數的整體架構是"裝飾器設計模式",也就是說,所有的流函數都可以按照所需的功能進行任意組合、互相嵌套、包裹。而我們的字符型流處理函數本質上也是對字節型流處理函數的一次包裹(或者說加載了字節型流處理函數的功能)
另外數據持久化或網絡傳輸都是以字節進行的,所以必須要有字符到字節或字節到字符的轉化。字符到字節需要轉化,其中讀的轉化過程如下圖所示:
InputStreamReader 類是字節到字符的轉化橋梁,InputStream 到 Reader 的過程要指定編碼字符集,否則將采用操作系統默認字符集,很可能會出現亂碼問題。StreamDecoder 正是完成字節到字符的解碼的實現類。
寫入也是類似的過程如下圖所示
通過 OutputStreamWriter 類完成,字符到字節的編碼過程,由StreamEncoder完成編碼過程
InputStreamReader、OutputStreamReader
import java.io.*; public class circle { public static void main(String[] args) throws Exception { InputStream inputStream = new FileInputStream("C:/in.txt"); /* * InputStreamReader(InputStream in) * Creates an InputStreamReader that uses the default charset. * 在字節型輸入流之上包裹一層字符型輸入流 */ Reader reader = new InputStreamReader(inputStream); /* * read() * Reads a single character. */ int data = reader.read(); while(data != -1) { char theChar = (char) data; System.out.print(theChar); data = reader.read(); } reader.close(); OutputStream outputStream = new FileOutputStream("C:/out.txt"); /* * OutputStreamWriter(OutputStream out) * Creates an OutputStreamWriter that uses the default character encoding. * 在字節型輸出流之上包裹一層字符型輸出流 */ Writer writer = new OutputStreamWriter(outputStream); /* * write(String str, int off, int len) * Writes a portion of a string. */ writer.write("Hello World"); writer.close(); } }
從上面的代碼我們可以看到,我們使用字節型的文件流讀取文件,然后再在上面包裹一層字符型流讀取函數。除此之外,還有另一種方法(嚴格來說不能算另一種方法,因為java的流函數架構是"裝飾器設計模式",功能之間可以任意組裝),直接之用字符型文件流讀取(本質上來說,字符型文件流讀取也是一些功能的組裝)。
FileReader、FileWriter
import java.io.*; public class circle { public static void main(String[] args) throws Exception { /* * FileReader(String fileName) * Creates a new FileReader, given the name of the file to read from. */ Reader reader = new FileReader("C:/in.txt"); int data = reader.read(); while(data != -1) { char theChar = (char) data; System.out.print(theChar); data = reader.read(); } reader.close(); Writer writer = new FileWriter("C:/out.txt"); writer.write("Hello World"); writer.close(); } }
BufferedReader、BufferedWriter
和BufferedInputStream、BufferedOutputStream不同的是,BufferedReader、BufferedWriter提供了ReadLine、newLine()這種以行為單位的字符讀寫機制
import java.io.*; public class circle { public static void main(String[] args) throws Exception { /* * FileReader(String fileName) * Creates a new FileReader, given the name of the file to read from. */ Reader reader = new FileReader("C:/in.txt"); /* * BufferedReader(Reader in) * Creates a buffering character-input stream that uses a default-sized input buffer. * 在Reader之上再包一層Buffer緩沖區的功能 */ BufferedReader brd = new BufferedReader(reader); String data = brd.readLine(); while(data != null) { System.out.print(data); data = brd.readLine(); } brd.close(); reader.close(); Writer writer = new FileWriter("C:/out.txt"); /* * BufferedWriter(Writer out) * Creates a buffered character-output stream that uses a default-sized output buffer. */ BufferedWriter bwd = new BufferedWriter(writer); /* * write(String s, int off, int len) * Writes a portion of a String. */ bwd.write("Hello World"); /* * newLine() * Writes a line separator. */ bwd.newLine(); bwd.close(); writer.close(); } }
CharArrayReader、CharArrayWriter
CharArrayReader 是字符數組輸入流。它和ByteArrayInputStream類似,只不過ByteArrayInputStream是字節數組輸入流,而CharArray是字符數組輸入流。CharArrayReader 是用於讀取字符數組,它繼承於Reader。操作的數據是以字符為單位
import java.io.*; public class circle { public static void main(String[] args) throws Exception { char a = 'a'; char b = 'b'; char c = 'c'; /* * CharArrayWriter() * Creates a new CharArrayWriter. */ CharArrayWriter carrWrt = new CharArrayWriter(); /* * write(int c) * Writes a character to the buffer. */ carrWrt.write(a); carrWrt.write(b); carrWrt.write(c); /* * toCharArray() * Returns a copy of the input data. */ char[] buff = carrWrt.toCharArray(); for(int i = 0; i < buff.length; i++) { System.out.println(buff[i]); } System.out.println("***********************"); /* * CharArrayReader(char[] buf) * Creates a CharArrayReader from the specified array of chars. */ CharArrayReader carrRed = new CharArrayReader(buff); /* * read() * Reads the next byte of data from this input stream. */ int data; while((data = carrRed.read()) != -1) { System.out.println(data); } } }
FilterReader、FilterWriter
用來"封裝其它的字符輸入流,並為它們提供額外的功能"
PipedReader、PipedWriter
字符管道流,原理上和PipedInputStream類似
PrintWriter
printwriter是向文本輸出流打印對象的格式化表示形式,它允許以一種格式化的方式進行數據流的寫入,類似C語言中的printf()函數
import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class circle { public static void main(String[] args) throws Exception { String filename = "C:/out.txt"; /* * PrintWriter(Writer out) * Creates a new PrintWriter, without automatic line flushing. */ PrintWriter pw = new PrintWriter(new FileWriter(filename)); String[] words = new String[]{"hello", "world", "!"}; for (int i = 0; i < words.length; i++) { /* * format(String format, Object... args) * Writes a formatted string to this writer using the specified format string and arguments. */ pw.format("words: %s\n", words[i]); } /* * flush() * Flushes the stream. */ pw.flush(); } }
0x5: File 文件、目錄操作接口
我們已經學習了關於流操作的各種函數接口,接下來我么繼續學習File類,要明確的一點是,雖然這個類名是File類,但是這個類卻不負責文件流的實際讀寫,在我本人看來,我更願意叫它"文件元數據Meta操作接口"。而File類常常和文件流讀寫函數配合起來,進行文件的操作
import java.io.*; public class circle { public void FileOperate() { } /** * 新建目錄 * @param folderPath String 如 c:/fqf * @return boolean */ public void newFolder(String folderPath) { try { String filePath = folderPath; filePath = filePath.toString(); java.io.File myFilePath = new java.io.File(filePath); if (!myFilePath.exists()) { myFilePath.mkdir(); } } catch (Exception e) { System.out.println("新建目錄操作出錯"); e.printStackTrace(); } } /** * 新建文件 * @param filePathAndName String 文件路徑及名稱 如c:/fqf.txt * @param fileContent String 文件內容 * @return boolean */ public void newFile(String filePathAndName, String fileContent) { try { String filePath = filePathAndName; filePath = filePath.toString(); File myFilePath = new File(filePath); if (!myFilePath.exists()) { myFilePath.createNewFile(); } FileWriter resultFile = new FileWriter(myFilePath); PrintWriter myFile = new PrintWriter(resultFile); String strContent = fileContent; myFile.println(strContent); resultFile.close(); } catch (Exception e) { System.out.println("新建目錄操作出錯"); e.printStackTrace(); } } /** * 刪除文件 * @param filePathAndName String 文件路徑及名稱 如c:/fqf.txt * @param fileContent String * @return boolean */ public void delFile(String filePathAndName) { try { String filePath = filePathAndName; filePath = filePath.toString(); java.io.File myDelFile = new java.io.File(filePath); myDelFile.delete(); } catch (Exception e) { System.out.println("刪除文件操作出錯"); e.printStackTrace(); } } /** * 刪除文件夾 * @param filePathAndName String 文件夾路徑及名稱 如c:/fqf * @param fileContent String * @return boolean */ public void delFolder(String folderPath) { try { delAllFile(folderPath); //刪除完里面所有內容 String filePath = folderPath; filePath = filePath.toString(); java.io.File myFilePath = new java.io.File(filePath); myFilePath.delete(); //刪除空文件夾 } catch (Exception e) { System.out.println("刪除文件夾操作出錯"); e.printStackTrace(); } } /** * 刪除文件夾里面的所有文件 * @param path String 文件夾路徑 如 c:/fqf */ public void delAllFile(String path) { File file = new File(path); if (!file.exists()) { return; } if (!file.isDirectory()) { return; } String[] tempList = file.list(); File temp = null; for (int i = 0; i < tempList.length; i++) { if (path.endsWith(File.separator)) { temp = new File(path + tempList[i]); } else { temp = new File(path + File.separator + tempList[i]); } if (temp.isFile()) { temp.delete(); } if (temp.isDirectory()) { delAllFile(path+"/"+ tempList[i]);//先刪除文件夾里面的文件 delFolder(path+"/"+ tempList[i]);//再刪除空文件夾 } } } /** * 復制單個文件 * @param oldPath String 原文件路徑 如:c:/fqf.txt * @param newPath String 復制后路徑 如:f:/fqf.txt * @return boolean */ public void copyFile(String oldPath, String newPath) { try { int bytesum = 0; int byteread = 0; File oldfile = new File(oldPath); if (oldfile.exists()) { //文件存在時 InputStream inStream = new FileInputStream(oldPath); //讀入原文件 FileOutputStream fs = new FileOutputStream(newPath); byte[] buffer = new byte[1444]; int length; while ( (byteread = inStream.read(buffer)) != -1) { bytesum += byteread; //字節數 文件大小 System.out.println(bytesum); fs.write(buffer, 0, byteread); } inStream.close(); } } catch (Exception e) { System.out.println("復制單個文件操作出錯"); e.printStackTrace(); } } /** * 復制整個文件夾內容 * @param oldPath String 原文件路徑 如:c:/fqf * @param newPath String 復制后路徑 如:f:/fqf/ff * @return boolean */ public void copyFolder(String oldPath, String newPath) { try { (new File(newPath)).mkdirs(); //如果文件夾不存在 則建立新文件夾 File a=new File(oldPath); String[] file=a.list(); File temp=null; for (int i = 0; i < file.length; i++) { if(oldPath.endsWith(File.separator)) { temp=new File(oldPath+file[i]); } else { temp=new File(oldPath+File.separator+file[i]); } if(temp.isFile()) { FileInputStream input = new FileInputStream(temp); FileOutputStream output = new FileOutputStream(newPath + "/" + (temp.getName()).toString()); byte[] b = new byte[1024 * 5]; int len; while ( (len = input.read(b)) != -1) { output.write(b, 0, len); } output.flush(); output.close(); input.close(); } if(temp.isDirectory()) {//如果是子文件夾 copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]); } } } catch (Exception e) { System.out.println("復制整個文件夾內容操作出錯"); e.printStackTrace(); } } /** * 移動文件到指定目錄 * @param oldPath String 如:c:/fqf.txt * @param newPath String 如:d:/fqf.txt */ public void moveFile(String oldPath, String newPath) { copyFile(oldPath, newPath); delFile(oldPath); } /** * 移動文件到指定目錄 * @param oldPath String 如:c:/fqf.txt * @param newPath String 如:d:/fqf.txt */ public void moveFolder(String oldPath, String newPath) { copyFolder(oldPath, newPath); delFolder(oldPath); } }
4. 后記
1. 什么時候改用什么流函數: 我在學習Java的的API的時候第一個問自己的問題就是這個,在我看來,可以遵循以下兩點: 1) 明確我們的目的,例如需要讀取文件,就使用文件流FileInputStream、需要緩存就包上一層BufferedInputStream、要進行字符型讀取就使用InputStreamReader 2) 理解"裝飾器設計模式"的概念,我們需要的功能可以通過流函數之間的包裹來進行實現,在編程的時候,注意參考API文檔,查明哪些流對象之間可以互相包裹很重要 2. 對流函數的深入理解: Java的源代碼是公開的,為了深入理解Java中的IO機制,下一步希望從源代碼角度去深入研究一下Java中的IO、流、緩存機制,以及優化方案
Copyright (c) 2014 LittleHann All rights reserved