首先要知道:這四個類是抽象類,是一切字符字節輸入輸出流的父類,因為是抽象類,所以要通過子類來實例化,不能直接實例化;
public abstract class InputStream implements Closeable; public abstract class OutputStream implements Closeable, Flushable; public abstract class Reader implements Readable, Closeable; public abstract class Writer implements Appendable, Closeable, Flushable;
1)FileInputStream:
public static void demo1() throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream("xxx.txt"); //創建流對象,個人認為,相當於一根管道 int x = fis.read(); //從硬盤上讀取一個字節,硬盤上都是以二進制的形式存儲 System.out.println(x); int y = fis.read(); System.out.println(y); int z = fis.read(); System.out.println(z); int a = fis.read(); System.out.println(a);//文件的結束標志:-1 int b = fis.read(); System.out.println(b); fis.close(); //關流釋放資源 }
打印的結果為:97、98、99、-1、-1
/** * Reads a byte of data from this input stream. This method blocks * if no input is yet available.
* * * @return the next byte of data, or <code>-1</code> if the end of the file is reached. * 返回值為0到255的int類型的值,返回值為字符的ACSII值(如a就返回97,n就返回110). * @exception IOException if an I/O error occurs. */ public native int read() throws IOException; //空參構造,返回讀到的內容(第五點闡述有參構造)
從最基本的開始,假如說相關路徑下有文件"xxx.txt",文件上面有abc三個字母,從上面源碼可以看出,調用一次read()方法,就讀一個字母,返回下一個。結束的時候,就返回-1。
所以可以利用循環來判斷:
private static void demo2() throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream("xxx.txt"); //創建流對象 int b; //文件的結束標志:-1,所以定義-1就結束了 while((b = fis.read()) != -1) { System.out.println(b); } fis.close(); }
read()方法讀取的是一個字節,為什么返回是int,而不是byte(復制的,其實我看不懂)
因為字節輸入流可以操作任意類型的文件,比如圖片音頻等,這些文件底層都是以二進制形式的存儲的,如果每次讀取都返回byte,有可能在讀到中間的時候遇到111111111,
那么這11111111是byte類型的-1,我們的程序是遇到-1就會停止不讀了,后面的數據就讀不到了,所以在讀取的時候用int類型接收,如果11111111會在其前面補上,
24個0湊足4個字節,那么byte類型的-1就變成int類型的255了這樣可以保證整個數據讀完,而結束標記的-1就是int類型。
2)FileOutputStream:
public static void demo1() throws FileNotFoundException, IOException { //創建字節輸出流對象,如果沒有就自動創建一個 FileOutputStream fos = new FileOutputStream("yyy.txt"); fos.write(97); //雖然寫出的是一個int數,但是到文件上的是一個字節,會自動去除前三個8位 fos.write(98); fos.write(99); fos.close(); }
相關路徑的文件"yyy.txt"就在里面寫出了一個abc內容。
如果繼續在上面寫一個,如下:
public static void demo1() throws FileNotFoundException, IOException { //創建字節輸出流對象,如果沒有就自動創建一個 FileOutputStream fos = new FileOutputStream("yyy.txt"); // fos.write(97); //雖然寫出的是一個int數,但是到文件上的是一個字節,會自動去除前三個8位 // fos.write(98); // fos.write(99); fos.write(100); fos.close(); }
則文件"yyy.txt"上面只會顯示d內容,因為其會在原來的文件上面進行清空,再重新寫。
******在創建對象的時候是如果沒有這個文件會幫我創建出來
******如果有這個文件就會先將文件清空,是將里面的內容清空,再寫入
*****如果不想文件清空,想續寫,則在后面加一個布爾值就可以了。
private static void demo2() throws FileNotFoundException, IOException { FileOutputStream fos = new FileOutputStream("yyy.txt",true); //如果想續寫就在第二個參數傳true fos.write(97); fos.write(98); fos.close(); }
3)拷貝:核心代碼就是下面幾行
/* * 復制文件,圖片,讀一次一個字節,寫一次一個字節 */ public static void demo1() throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream("雙元.jpg"); //創建輸入流對象,關聯雙元.jpg FileOutputStream fos = new FileOutputStream("copy.jpg"); //創建輸出流對象,關聯copy.jpg int b; while((b = fis.read()) != -1) { //在不斷的讀取每一個字節 fos.write(b); //將每一個字節寫出 } fis.close(); //關流釋放資源 fos.close(); }
一個一個字節去讀,去寫(去拷貝),所以這種方法特別耗時。讀一次寫一次,一共要讀寫900多萬次,所以特別耗時。

4)fis.available() 得到輸入流文件的全部字節數
/* * 不推薦使用,因為有可能會導致內存溢出 */ public static void demo3() throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream("致青春.mp3"); FileOutputStream fos = new FileOutputStream("copy.mp3"); byte[] arr = new byte[fis.available()]; fis.read(arr); fos.write(arr); fis.close(); fos.close(); }
5)
private native int readBytes(byte b[], int off, int len) throws IOException; //這個方法使用一個byte的數組作為一個緩沖區,每次從數據源中讀取和緩沖區大小(二進制位)相同的數據並將其存在緩沖區中。 //定義的數組長度為10,每次寫進去的也是10 /* * 1.從讀取流讀取一定數量的字節,如果比如文件總共是102個字節 * 2.我們定義的數組長度是10,那么默認前面10次都是讀取10個長度 * 3.最后一次不夠十個,那么讀取的是2個 * 4.這十一次,每次都是放入10個長度的數組. */ public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } /* * 1.從讀取流讀取一定數量的字節,如果比如文件總共是102個字節 * 2.我們定義的數組長度是10,但是這里我們寫read(bytes,0,9)那么每次往里面添加的(將只會是9個長度),就要讀12次,最后一次放入3個. * 3.所以一般讀取流都不用這個而是用上一個方法:read(byte[]); */ public int read(byte b[], int off, int len) throws IOException { return readBytes(b, off, len); } 注意:空參構造和有參構造返回的值內容是不一樣
關於read(byte[] buffer,int off, int len):可以看出:參數buffer表示建了多大長度的緩沖區;off表示把東東往第幾個緩沖區放,所以就一般為0,要知道,為什么不在第0個放,而要浪費第0個的位置呢;len就表示每次往緩沖區放多少個東東;
public static void main(String[] args) throws Exception { InputStream is = null; byte[] buffer = new byte[5]; char c; try { is = new FileInputStream("test.txt");// 里面的內容為:ABCDEFGHI is.read(buffer, 1, 2); for (byte b : buffer) { if (b == 0) { c = '-'; } else { c = (char) b; } System.out.print(c); } System.out.println(); is.read(buffer, 1, 3); for (byte b : buffer) { if (b == 0) { c = '-'; } else { c = (char) b; } System.out.print(c); } } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) is.close(); } }
輸出:
-AB--
-CDE-
public static void demo1() throws FileNotFoundException, IOException { //xxx.txt 文件內容上面為 97,98,99 FileInputStream fis = new FileInputStream("xxx.txt"); byte[] arr = new byte[2]; int a = fis.read(arr); //將文件上的字節讀取到字節數組中, //返回的是讀到的數組的長度,也就是讀到的字節的個數 System.out.println("a:"+a); //讀到的有效字節個數 for (byte b : arr) { //第一次獲取到文件上的a和b System.out.println(b); } int c = fis.read(arr); System.out.println(c); for (byte b : arr) { System.out.println(b); } fis.close(); }
打印出:
2、97、98
1、99、98

標准代碼:
public static void demo2() throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream("xxx.txt"); FileOutputStream fos = new FileOutputStream("yyy.txt"); byte[] arr = new byte[2]; int len; while((len = fis.read(arr)) != -1) { //結合上面的,其實第二次返回的長度等於1了 fos.write(arr,0,len); //這次每兩個字節兩個字節地讀,比一開始一個個字節讀快多了 } //但是在實際中,字節數組長度設置得更大 fis.close(); fos.close(); }
我們設置數組長度,就等於我們自己拿着籃子去買菜了,但是實際上在java中,java已經幫我們備好籃子了
private static int defaultBufferSize = 8192; //每次讀取8192個字節 public BufferedInputStream(InputStream in) { //包裝,裝飾者模式 this(in, defaultBufferSize); } public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; }

* E.小數組的讀寫和帶Buffered的讀取哪個更快?
* 定義小數組如果是8192個字節大小和Buffered比較的話
* 定義小數組會略勝一籌,因為讀和寫操作的是同一個數組
* 而Buffered操作的是兩個數組

* close方法
* 具備刷新的功能,在關閉流之前,就會先刷新一次緩沖區,將緩沖區的字節全都刷新到文件上,再關閉,
* close方法刷完之后就能寫了
* 只有滿了才刷新,有點不厚道
*
* flush方法?
* 具備刷新的功能,刷完之后還可以繼續寫,自動刷新啊 。。用戶體驗
字節數組處理中文:
public static void demo1() throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream("yyy.txt"); byte[] arr = new byte[4]; int len; while((len = fis.read(arr)) != -1) { //寫中文隨時出現亂碼 System.out.println(new String(arr,0,len));//碼表轉中文,string的構造方法 } fis.close(); }
public static void main(String[] args) throws IOException { //demo1(); FileOutputStream fos = new FileOutputStream("zzz.txt"); fos.write("我讀書少,你不要騙我".getBytes()); //字節流寫出中文,要轉換 fos.write("\r\n".getBytes()); //換行 fos.close(); }
標准代碼:但是實際上一般都不會寫得那么麻煩(1.6版本)
public static void demo1() throws FileNotFoundException, IOException { FileInputStream fis = null; //作用域問題,所以要放在外面 FileOutputStream fos = null; //如果下面兩兩端代碼初始化失敗 try { fis = new FileInputStream("xxx.txt"); fos = new FileOutputStream("yyy.txt"); int b; while((b = fis.read()) != -1) { fos.write(b); } }finally { //一定要關流,所以finally try{ //如果初始化失敗,沒有開啟,則不用關閉 if(fis != null) fis.close(); }finally { //try fianlly的嵌套目的是能關一個盡量關一個 if(fos != null) fos.close(); } } }
1.7 版本之后流自動關閉,全實現了AutoCloseable接口,自動調用里面的close方法(這種方式開發中用得比較少)
//當把流寫在小括號里面,流就會自動關閉,為什么請見下面
public static void main(String[] args) throws IOException { try( FileInputStream fis = new FileInputStream("xxx.txt"); FileOutputStream fos = new FileOutputStream("yyy.txt"); ){ int b; while((b = fis.read()) != -1) { fos.write(b); } } }
public class FileInputStream extends InputStream; public abstract class InputStream implements Closeable; public interface Closeable extends AutoCloseable; /** * A resource that must be closed when it is no longer needed. * * @author Josh Bloch * @since 1.7 */ public interface AutoCloseable;
END!
END!
