一、IO概述
(1)、概念:IO流是用於處理設備間的數據傳輸。
Java對數據的操作是通過流的方式。
Java中用於操作流的對象都在IO包中。
(2)、分類:
流按操作數據分爲兩種:字節流和字符流。
按流向分爲:輸入劉和輸出流。
(3)、IO流常用基類:
字節流的抽象基類:InpuStream、OutputStream。
字符流的抽象基類:Reader、Writer
注意:由以上四個類派生出的子類對象的名稱,都是以其父類名作爲子類名的後綴。
如:InputSream的子類FileInputStream
如:Reader的子類FileReader
二、文件的寫入Writer
(1)、FileeWriter用來寫入字符文件的便捷類。
import java.io.*; public class FileWriterDemo1 { public static void main(String[] args) throws IOException { // 創建一個FlieWriter對象。該對象一被初始化,就必須要明確被操作的文件。 // 而且該文件會被創建到指定的目錄下。如果該目錄下已有同名文件,將被覆蓋。 // 其實該句就是明確數據要存放的目的地。 FileWriter fw = new FileWriter("c:\\demo.txt"); // 用writer方法,將字符串寫入到流表中 fw.write("woshihuangwenkuigjghj"); // 刷新流對象中的緩衝中的數據,將數據刷到目的地中 // fw.flush(); // 關閉流資源,但是關閉前會刷新一次內部的緩衝數據,將數據刷到目的地。 // 和flush的區別:flush刷新後,流可以繼續使用。close刷新後,流將會關閉。 fw.close(); } }
(2)、 IO中標準的異常處理
//異常處理 import java.io.*; public class FileWriterDemo2 { public static void main(String[] args) { FileWriter fw = null; try { fw = new FileWriter("c:\\demo.txt"); fw.write("sdfhdsfsljkdfggg"); } catch (IOException e) { System.out.println(e.toString()); } finally { try { if (fw != null) fw.close(); } catch (IOException e) { System.out.println(e.toString()); } } } }
(3)、文件的續寫
FileWriter(String fileName, boolean append)
根據給定的文件名以及指示是否附加寫入數據的 boolean 值來構造 FileWriter 對象。
fileName - 一個字符串,表示與系統有關的文件名。
append - 一個 boolean 值,如果爲 true,則將數據寫入文件末尾處,而不是寫入文件開始處。
import java.io.*; public class FileWriterDemo3 { public static void main(String[] args) throws IOException { // 傳遞一個true參數,代表不覆蓋已有文件。並在原文件末尾處續寫 FileWriter fw = new FileWriter("c:\\demo.txt", true); fw.write("你好\r\n很高興見到你"); fw.close(); } }
三、文件的讀取Reader
(1)、文本文件讀取方式一:
read():一次讀一個 ,而且會自動往下讀
作爲整數讀取的字符,範圍在 0 到 65535 之間 (0x00-0xffff),如果已到達流的末尾,則返回 -1
import java.io.*; //讀取單個字符 public class FileReaderDemo { public static void main(String[] args) throws IOException { // 創建一個文件讀取流對象,和指定的文件名想關聯 // 要保證該文件是存在的,如果不存在,會發生異常FileNoteFoundException FileReader fr = new FileReader("c://demo.txt"); /* * // 調用讀取對象流的read方法 * // read():一次讀一個 ,而且會自動往下讀 * //作爲整數讀取的字符,範圍在 0 到 65535 之間 (<tt>0x00-0xffff</tt>),如果已到達流的末尾,則返回 -1 * int ch1 = fr.read(); * System.out.println("ch1=" + (char) ch1); * * int ch2 = fr.read(); * System.out.println("ch2=" + (char) ch2); */ //用true判斷來打印 /*while (true) { int ch = fr.read(); if (ch == -1) break; System.out.println("ch=" + (char) ch); }*/ //簡化寫法 int ch = 0; while ((ch = fr.read()) != -1) { System.out.println("ch=" + (char) ch); } fr.close(); } }
(2)、文本文件的讀取方式二
read(char[] cbuf) 將字符讀入數組。
讀取的字符數,如果已到達流的末尾,則返回 -1
//將字符讀入到數組的某一部分 import java.io.*; public class FileReaderDemo2 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("c:\\demo.txt"); /* * 這種形式將字符讀取到數組中,由於字符長度未知,無法定義數組長度 * * //定義一個字符數組。用於存儲讀取到的字符 * //該read(char[])返回的是讀取到字符的個數 * char[] buf = new char[3]; * * int num = fr.read(buf); * * System.out.println("num="+num+"...."+new String(buf)); */ // 定義一個字符數組,用於儲存獨到的字符 char[] buf = new char[1024]; int num = 0; while ((num = fr.read(buf)) != -1) { System.out.print(new String(buf, 0, num)); } fr.close(); } }
(3)、讀寫練習:將C盤下的一個文本文件複製到D盤下
/*將C盤下的一個文本文件複製到D盤下 * * 複製原理:就是將C盤下的文件數據儲存到d盤的一個文件下 * * 步驟: * 1.在D盤創建一個文件,用於儲存C盤文件下中的數據 * 2.定義讀取流和C盤文件相關連 * 3.通過不斷的讀取來完成數據的存儲 * 4.關閉資源 */ import java.io.*; public class CopyText { public static void main(String[] args) throws IOException { // copy_1(); copy_2(); } // 從C盤讀取一個字符,就往D盤寫一個字符 public static void copy_1() throws IOException { // 創建目的地。 FileWriter fw = new FileWriter("d:\\Demo.txt"); // 與已有文件相關聯 FileReader fr = new FileReader("c:\\Demo.java"); int ch = 0; while ((ch = fr.read()) != -1) { fw.write(ch); } // 關閉寫入流 fw.close(); // 關閉讀取流 fr.close(); } public static void copy_2() throws IOException { // 局部變量初始化 FileWriter fw = null; FileReader fr = null; try { fw = new FileWriter("d:\\Demo.txt"); fr = new FileReader("c:\\Demo.java"); char[] buf = new char[1024]; int len = 0; while ((len = fr.read(buf)) != -1) { fw.write(buf, 0, len); } } catch (IOException e) { throw new RuntimeException("讀寫失敗"); } finally { if (fr != null) try { fr.close(); } catch (IOException e) { } if (fw != null) try { fw.close(); } catch (IOException e) { } } } }
四、字符流緩衝區技術
(1)、緩衝區概述
緩衝區的出現提高了流對數據的讀寫率。
緩衝區要結合流猜可以使用。
緩衝區在流的基礎上對流的功能進行了增強。
(2)、緩衝區對應類。
1、------BufferedWriter 文本寫入字符輸出流,緩衝各個字符,從而提供單個字符、數組和字符串的高效寫入。
該緩衝區提供了一個跨平臺換行符newLine()
//字符寫入取緩衝區 import java.io.*; public class BufferedWriterDemo { public static void main(String[] args) throws IOException { // 創建一個字符流寫入對象 FileWriter fw = new FileWriter("c:\\buf.txt"); // 爲了提高字符寫入流效率,加入了緩存技術 // 只要將需要被提高效率的流對象作爲參數傳遞給緩衝區的構造函數即可 BufferedWriter bufw = new BufferedWriter(fw); for (int x = 1; x < 5; x++) { // bufw.write("你猜\r\n我是誰\r\n哈哈\r\n你猜不到吧"); bufw.write("abcf" + x); // 換行,可跨平臺 bufw.newLine(); // 刷新 bufw.flush(); } // 只要用到緩衝區,就要記得刷新 // bufw.flush(); // 其實關閉緩衝區,就是在關閉緩衝區中的流對象 bufw.close(); } }
2、-------BufferedReader 從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取。
/*字符流讀取緩衝區 該緩衝區提供了一個一次讀取一行的方法readLine,方便文本數據的讀取 當返回值爲null時,表示獨到末尾 */ import java.io.*; public class BufferedReaderDemo { public static void main(String[] args) throws IOException { // 創建一個讀取流對象和文件相關聯 FileReader fr = new FileReader("c:\\buf.txt"); // 爲了提高效率,加入緩衝技術,將字符讀取流對象作爲參數傳遞給緩衝對象的構造函數 BufferedReader bufr = new BufferedReader(fr); // 循環讀取,知道null String line = null; while ((line = bufr.readLine()) != null) { System.out.println(line); } /* * //readLine(); * //這是一行一行的讀取 * * String s1 = bufr.readLine(); * System.out.println("s1:" + s1); * * String s2 = bufr.readLine(); * System.out.println("s2:" + s2); */ // 其實關閉緩衝區,就是在關閉緩衝區中的流對象 bufr.close(); } }
(3)、緩衝區練習:通過緩衝區複製文本文件
//通過緩衝區複製一個.java文件 import java.io.*; public class CopyTextByBuf { public static void main(String[] args) { BufferedReader bufr = null; BufferedWriter bufw = null; try { bufr = new BufferedReader(new FileReader( "c:\\Test.java")); bufw = new BufferedWriter(new FileWriter( "d:\\Test.txt")); String line = null; while ((line = bufr.readLine()) != null) { bufw.write(line); bufw.newLine();// 換行 bufw.flush(); } } catch (IOException e) { throw new RuntimeException("讀取失敗"); } finally { try { if (bufr != null) bufr.close(); } catch (IOException e) { throw new RuntimeException("讀取關閉失敗"); } try { if (bufw != null) bufw.close(); } catch (IOException e) { throw new RuntimeException("寫入關閉失敗"); } } } }
(4)、readLine()方法的原理
無論是讀一行,獲取多個字符。其實最終都是在在硬盤上一個一個的讀取。所以最終使用的還是read方法一次讀一個的方法。讀到回車符好\r\n時,就返回該數據中的數
據。其實就是臨時存入一個緩衝數組中。 比read方法高效很多。
返回:包含該行內容的字符串,不包含任何行終止符,如果已到達流末尾,則返回null。
五、裝飾設計模式
(1)、定義:當想要對已有的對象進行功能增強時,可以定義類,將已有對象傳入,基於已有對象的功能,並提供加強功能,那麼自定義的該類,就稱爲裝飾類。
(2)、簡單的程序示例:(開始人只吃飯,後來又抽菸喝酒,通過裝飾對其加強)
/* * 裝飾設計模式: * 當想要對已有的對象進行功能增強時, 可以定義類,將已有的對象傳入,基於已有的功能,並提供加強功能。 * 那麼自定義的該類稱爲裝飾類。 * 裝飾類通常會通過構造方法接受被裝飾的對象。 * 並基於被裝飾的對象的功能,提供更強的功能。 */ public class PersonDemo { public static void main(String[] args) { Person p = new Person(); SuperPerson sp = new SuperPerson(p); sp.superEat(); } } class Person { public void eat() { System.out.println("吃飯"); } } class SuperPerson { private Person p; public SuperPerson(Person p) { this.p = p; } public void superEat() { System.out.println("開胃酒"); p.eat(); System.out.println("甜點"); System.out.println("抽菸"); } }
(3)、裝飾和繼承的區別
裝飾比繼承要靈活的多,避免了繼承體系的臃腫。
而且降低了類與類之間的關係。
裝飾類因爲增強了已有對象,具備的功能和已有的是相同的,只不過是對功能進行了增強。
所以裝飾類和被裝飾類都是屬於一個體系的。
(4)、 LineNumberReader的使用
package cn.hwk; import java.io.*; public class LineNumberReaderDemo { public static void main(String[] args) throws Exception { // 創建文本流對象 FileReader fr = new FileReader("c:\\Test.java"); // LineNumberReader關聯文本流 LineNumberReader lnr = new LineNumberReader(fr); String line = null; // 讀取一行 while ((line = lnr.readLine()) != null) { System.out.println(lnr.getLineNumber() + "::" + line); } // 關閉,其實也就是fr.close() lnr.close(); } }
(5)、模擬一個帶行號的緩衝區對象
import java.io.*; public class Test6 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("c:\\Test8.java"); MyLineReaderDemo frd = new MyLineReaderDemo(fr); String line = null; while ((line = frd.readLine()) != null) { System.out.println(frd.getLineNum() + ":" + line); } frd.close(); } } /** * 這裏可以繼承自定義的讀取緩衝區 */ class MyLineReaderDemo extends MyBufferedReader { private int linenum; public MyLineReaderDemo(FileReader fr) { super(fr);//調用父類的構造函數,都是FileReader對象 } public int getLineNum() { return linenum; } public String readLine() throws IOException { linenum++; return super.myReadLine();//代碼重複調用父類的myReadLine()就可 } public void setlineNum(int linenum) { this.linenum = linenum; } public void close() throws IOException { super.myClose(); } }
六、字節流
(1)、字節流的抽象基類:InpuStream、OutputStream。
(2)、字節流的讀寫
//字節流的讀寫 import java.io.*; public class FileStream { public static void main(String[] args) throws IOException { readFile_2(); // readFile_1(); // writerFile(); } public static void readFile_2() throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); // 定義一個剛剛好的緩衝區,不用再循環了 byte[] buf = new byte[fis.available()]; fis.read(buf); System.out.println(new String(buf)); fis.close(); } public static void readFile_1() throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); byte[] buf = new byte[1024]; int len = 0; while ((len = fis.read()) != -1) { System.out.println(new String(buf, 0, len)); } fis.close(); } public static void writerFile() throws IOException { FileOutputStream fos = new FileOutputStream("fos.txt"); fos.write("jdhfkjl".getBytes()); fos.close(); } }
(3)、練習(用字節流拷貝一個圖片)
package cn.hwk; /*複製一個圖片 * * 思路: * 1.用字節讀取流對象和圖片關聯。 * 2.用字節寫入流創建一個圖片文件。用於儲存獲取到的圖片數據。 * 3.通過循環讀寫,完成數據的存儲。 * 4.關閉資源 */ import java.io.*; public class CopyPic { public static void main(String[] args) { FileOutputStream fos = null; FileInputStream fis = null; try { // 讀取流對象和圖片關聯 fos = new FileOutputStream("c:\\2.jpg"); // 獲取圖片數據 fis = new FileInputStream("c:\\1.jpg"); byte[] buf = new byte[1024]; int len = 0; while ((len = fis.read(buf)) != -1) { fos.write(buf, 0, len); } } catch (IOException e) { throw new RuntimeException("複製文件失敗"); } finally { try { if (fos != null) fos.close(); } catch (IOException e) { throw new RuntimeException("讀取關閉失敗"); } try { if (fis != null) fis.close(); } catch (IOException e) { throw new RuntimeException("寫入關閉失敗"); } } } }
(4)、字節流緩衝技術
BufferedInputStream:爲另一個輸入流添加一些功能,即緩衝輸入以及支持 mark 和 reset 方法的能力。
BufferedOutputStream:該類實現緩衝的輸出流。
package cn.hwk; /*演示mp3的複製 */ import java.io.*; public class CopyMp3 { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); copy_1(); long end = System.currentTimeMillis(); System.out.println((end - start) + "毫秒"); } // 通過字節流緩衝去完成複製 public static void copy_1() throws IOException { BufferedInputStream bufis = new BufferedInputStream( new FileInputStream("c:\\1.mp3")); BufferedOutputStream bufos = new BufferedOutputStream( new FileOutputStream("c:\\2.mp3")); int by = 0; while ((by = bufis.read()) != -1) { bufos.write(by); } bufos.close(); bufis.close(); } public static void copy_2() { } }
七、鍵盤錄入
(1)、讀取鍵盤錄入:
System.out:對應的是標準輸出設備,控制檯。
System.in:對應的是標準輸入設備,鍵盤。
(2)、程序示例
package cn.hwk; /*需求: *通過鍵盤錄入數據。 *當錄入一行數據後,就將該數據進行打印 *如果錄入的數據是over,那麼就停止錄入。 */ import java.io.*; public class ReadIn { public static void main(String[] args) throws IOException { InputStream in = System.in; StringBuilder sb = new StringBuilder(); while (true) { int ch = in.read(); String s = null; // 讀到\r繼續下一次讀取 if ((char) ch == '\r') { continue; } // 讀到\n如果不是自定義結束標記的話,打印輸出 if ((char) ch == '\n') { s = sb.toString(); // 自定義over結束標記 if (s == "over") { break; } // 打印 System.out.println(s); // 這裏一定要重新清除,否則會自動追加到sb中。 sb.delete(0, sb.length()); } else { // 如果沒有讀取到換行標記就追加 sb.append((char) ch); } } } }
(3)、以上的例子,其實就是讀一行的原理,也就是readLine方法。
能不能直接用readLine方法來完成鍵盤錄入的一行數據的讀取呢?
readLine方法是BufferedReader類的方法,而鍵盤錄入的read方法是字節流InputStream的方法
能不能將字節流轉成字符流在使用字符流緩衝區的ReadLine方法呢?
package cn.hwk; import java.io.*; public class TransStreamDemo { public static void main(String[] args) throws IOException { // 獲取鍵盤錄入對象 InputStream in = System.in; // 將字節流對象轉換成字符流對象,使用轉換流。InputStreamReader InputStreamReader isr = new InputStreamReader(in); // 爲了提高效率,將字符流進行緩衝區技術的搞笑操作,使用BufferedReader BufferedReader bufr = new BufferedReader(isr); String line = null; while (((line = bufr.readLine())) != null) { if ("over".equals(line)) { break; } System.out.println(line.toUpperCase()); } } }
八、IO流總結
(1)、首先是四大基本抽象流:
字節流:InputStream、OutputStream,
字符流:Reader、Writer;
輸入流:InputStream、Reader,
輸出流:OutputStream、Writer;
後面的所有流都是這四個的子類;
(2)、文件流:
FileInputStream、FileOutputStream,FileReader、FileWriter,
文件流算是四大基本抽象流的最初級實現類,前面兩個是字節流,可以用於任何格式文件的讀寫(因爲文件的最基本存儲形式就是字節數據),後面兩個是字符流,只
能用來處理文本文件;
(3)、緩衝流:
字節流:BufferedInputStream、BufferedOutputStream,
字符流:BufferedReader、BufferedWriter,
這四個流用的是裝飾設計模式,在使用時要接收其它的流對象,比如文件流;同文件流一樣,前面兩個流可用於任何類型的文件讀寫,後面兩個流主要用於文本文件
的讀寫,由於這四個流在建立對象後內部會自建緩衝區,所以讀寫的效率比較高,尤其是BufferedReader中的readLine()方法讀取文本文
件特別合適,可以整行整行的讀取,遇到換行符算是一行的結束,但是它不會返回換行符,所以需要在調用write()方法後用newLine()
方法新建換行符;
(4)、數據流:
DataInputStream、DataOutputStream:
用於基本類型數據的讀寫,採用裝飾設計模式,構造方法的形參是流對象;
(5)、標準輸入輸出流:
java.lang.System下的兩個流,
public static final InputStream in:標準輸入流,此流已打開並準備提供輸入數據。通常,此流對應於鍵盤輸入或者由主機環境或用戶指定的另一個輸入源。
public static final PrintStream out:標準輸出流,此流已打開並準備接受輸出數據。通常,此流對應於顯示器輸出或者由主機環境或用戶指定的另一個輸出目標。
(6)、轉換流:
輸入流InputStreamReader,將讀取的字節數據轉換爲字符數據,
輸出流:OutputStreamWriter,將字符轉換爲字節輸出,
這兩個流都採用裝飾設計模式,構造方法的形參是流對象,都可以指定轉碼時的編碼表或者使用平臺默認的碼錶;
(7)、從鍵盤錄入:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
輸出到控制檯:
法一:BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
法二:PrintWriter pw = new PrintWriter(System.out, true);
PrintWriter流屬於Print流,加上true參數,表示自動刷新緩衝區,無需調用flush方法,而且這個流中的println方法會自動添加換行符,無需再調用newLine方法)
(8)、File類:
將用戶傳遞過來的文件名或者文件的路徑封裝爲類對象,然後在對其進行讀寫前進行一系列的判斷,
比如:exists()判斷文件或路徑是否存在,isFile()判斷是不是文件,isDirectory(,判斷是不是目錄,
如果結果爲false,直接讓程序return,這樣做豈不是更安全!
(9)、Print流:
PrintStream和PrintWriter,都用於寫出,
前者構造方法接收的參數類型爲String文件名、File對象、OutputStream對象,
後者構造方法接收的參數類型爲String文件名、File對象、OutputStream對象以及Writer對象,
由於後者接收的參數類型範圍廣,所以其使用的頻率更高。
public PrintWriter(Writer out, boolean autoFlush):
若autoFlush的值爲true,則每次輸出一行後會自動刷新,無需調用flush方法,
否則等遇到輸入結束標誌,纔會在關閉流的時候刷新,但此法僅限於使用println、printf或者format三個方法時有效;
public PrintStream(OutputStream out, boolean autoFlush):
功能類似於PrintWriter,但只有使用println方法或者遇到換行符時就會刷新;
(10)其它流:
ByteArrayInputStream:構造函數接收字節數組,也即數據源就是字節數組;
ByteArrayOutputStream:構造函數不需要數據目的地,因爲在該流內部已經封裝了一個字節數組作爲目的地,它會隨着寫入數據的增多而自動增長,可使用 toByteArray() 和 toString() 獲取數據;
這兩個流因爲並沒有調用底層系統資源,所以close方法無效,類中的方法在關閉此流後仍可被調用,它們也不會拋出IOException;
(11)、流操作中的數據源和目的地:
數據源:
鍵盤(System.in)、硬盤(FileInputStream(文件))、內存(ByteArrayInputStream);
數據目的地:
控制檯(System.out)、硬盤(FileOutputStream(文件))、內存(ByteArrayOutputStream);
(12)、流對象的操作規律:
流對象有很多,不知道該用哪一個。通過三個明確來完成。
1,明確源和目的
源:輸入流。 InputStream Reader
目的:輸出流。OutputStream Writer
2,操作的數據是否是存文本
是:字符流
不是:字節流
3,當體系明確後,再明確要使用哪個具體的對象。
通過設備來進行區分:
源 設備:內存,硬盤,鍵盤
目的設備:內存,硬盤,控制檯。
這衆多的流中,除了最早出場的文件流外,其它的都是裝飾設計模式,構造方法接收的形參都是流對象