一、OutputStream類
- 1.概述
OutputStream
類可以在硬盤上創建一個文件,並寫入或添加數據。該類的子類還能實現寫入過程中的不同功能。- 2.FileOutputStream類
FileOutputStream
類用於在硬盤上創建文件,並以字節的形式寫入數據。其使用方式和FileWriter
類相似,只是以字節的形式操作數據。下面的代碼在指定目錄創建一個文件並寫入自定義數據。示例代碼:
package com.heisejiuhuche.io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
/* 創建FileOutputStream對象並關聯文件 */
fos = new FileOutputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt");
/* 寫入數據,通過String類的getBytes方法將字符串轉換爲字節數組 */
fos.write("abcdefg".getBytes());
} catch(FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch(IOException e) {
throw new RuntimeException("操作失敗");
} finally {
try {
if(fos != null)
fos.close();
} catch(IOException e) {
throw new RuntimeException("輸出流關閉失敗");
}
}
}
}
該程序在指定目錄創建fos.txt
並寫入abcdefg
注意:
字節輸出流在直接使用的時候不需要調用flush()方法,與字符流不用。由於字符流底層操作的也是字節,同時用的是字節流的緩衝區;該緩衝區中有個數組,用於臨時存儲數據。要將數組中的數據寫入目的地文件,字符流就需要調用flush()方法進行刷新。而字節輸出流是對最小單位字節直接進行操作,沒有使用具體緩衝區,所以不需要刷新,直接往目的地文件寫入數據。
二、InputStream類
- 1.概述
InputStream
類以字節形式讀取硬盤上的文件數據。該類的子類還能實現讀取過程中的不同功能。- 2.FileInputStream類
FileInputStream
類用於以字節形式讀取文件數據。其特有的方法使創建字節數組有了明確的大小。該類的使用方式和FileReader
相似。下面的代碼讀取文件中的數據。示例代碼:
package com.heisejiuhuche.io;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
read_1();
System.out.println();
read_2();
read_3();
}
private static void read_3() throws IOException {
FileInputStream fis = new FileInputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt");
/* available方法返回文件的大小 */
byte[] buf = new byte[fis.available()];
fis.read(buf);
System.out.println("read_3: " + new String(buf));
}
private static void read_2() throws IOException {
FileInputStream fis = new FileInputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf)) != -1) {
System.out.println("read_2: " + new String(buf, 0, len));
}
}
private static void read_1() throws IOException {
FileInputStream fis = new FileInputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt");
int ch = 0;
System.out.print("read_1: ");
while((ch = fis.read()) != -1) {
System.out.print((char)ch);
}
}
}
程序輸出結果:
read_1: abcdefg
read_2: abcdefg
read_3: abcdefg
注意:
如果使用字節輸入流讀取的文件較大,建議使用1024字節整數倍方法讀入數據,而不要使用創建available()方法返回值大小的數組;後者可能造成內存溢出
三、字節流練習
- 1.拷貝圖片到指定目錄
示例代碼:
package com.heisejiuhuche.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class PicCopy {
public static void main(String[] args) {
picCopy();
}
private static void picCopy() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
/* 創建輸入輸出流並關聯文件 */
fos = new FileOutputStream("C:/Users/jeremy/Documents/me.jpg");
fis = new FileInputStream("C:/Users/jeremy/Documents/javaTmp/me.jpg");
/* 創建緩衝區 */
byte[] buf = new byte[1024];
int len = 0;
/* 複製文件 */
while((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
} catch(FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch(IOException e) {
throw new RuntimeException("複製失敗");
} finally {
try {
if(fis != null)
fis.close();
} catch(IOException e) {
throw new RuntimeException("輸入流關閉失敗");
}
try {
if(fos != null)
fos.close();
} catch(IOException e) {
throw new RuntimeException("輸出流關閉失敗");
}
}
}
}
四、字節流緩衝區
- 1.緩衝區對應的類
- 字節流緩衝區對應
BufferedOuputStream
類和BufferedInputStream
類。- 2.緩衝區應用
- 1)用緩衝區拷貝一個Mp3文件
示例代碼:
package com.heisejiuhuche.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyMp3Demo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
copy();
long end = System.currentTimeMillis();
System.out.println((end - start) + "毫秒");
}
private static void copy() throws IOException {
/* 創建Buffered緩衝區對象並關聯文件 */
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream(
"C:/Users/jeremy/Documents/javaTmp/back.mp3"));
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream(
"C:\\Users\\jeremy\\Music\\BaiduMusic\\Songs\\Backseat Serenade - All Time Low,Cassadee Pope.mp3"));
int ch = 0;
/* 複製文件 */
while((ch = bufis.read()) != -1) {
bufos.write(ch);
}
}
}
程序輸出結果:136毫秒
- 2)自定義緩衝區
假設內存中字節數組的大小定義爲1024
字節;那麼字節流緩衝區在工作時,首先由FileInputStream
從硬盤抓取1024
字節的數據存入字節數組,然後由BufferedInputStream
的read()
方法依次一個字節一個字節讀取。緩衝區中有兩個控制讀取過程的變量,分別是數組的索引指針,和一個計數器。下標用於控制不斷讀取下一個字節,計數器用於控制下一次從硬盤抓數據存入緩衝區數組的時間。read()
方法每讀取一個字節,指針右移一位,計數器自減1
;當計數器減至0的時候,意味着數組中已經沒有字節可讀,這時再由FileInputStream
從硬盤抓取1024
個字節存入數組,指針歸零,計數器回到1024
,再次進行以上步驟的循環,直至硬盤數據全部被抓取。
要自定義緩衝區,需要定義一個字節數組,兩個變量(指針和計數器)。
示例代碼:
package com.heisejiuhuche.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class MyBufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
copy();
long end = System.currentTimeMillis();
System.out.println((end - start) + "毫秒");
}
private static void copy() throws IOException {
/* 創建Buffered緩衝區對象並關聯文件 */
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream(
"C:/Users/jeremy/Documents/javaTmp/back.mp3"));
MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream(
"C:\\Users\\jeremy\\Music\\BaiduMusic\\Songs\\Backseat Serenade - All Time Low,Cassadee Pope.mp3"));
int ch = 0;
/* 複製文件 */
while((ch = bufis.read()) != -1) {
bufos.write(ch);
}
bufos.close();
bufis.close();
}
}
class MyBufferedInputStream {
private FileInputStream fis;
private int pos;
private int count;
private byte ch;
private byte[] buf = new byte[1024];
MyBufferedInputStream(FileInputStream fis) {
this.fis = fis;
}
public int read() throws IOException {
/* 如果count=0了,繼續抓取數據到緩衝區數組 */
if(count == 0) {
/* 抓取數據後,count回到1024 */
count = fis.read(buf);
/* 讀到文件末尾,fis的read()方法返回-1,必須判斷如果count < 0,說明讀到了最後 */
if(count < 0) {
return -1;
}
/* 抓取數據後指針歸0 */
pos = 0;
/* 取出第一個字節 */
ch = buf[pos];
/* 每取一個數據,指針+1,右移 */
pos++;
/* 每取一個數據,count-1 */
count--;
/* 返回第一個字節 */
return ch;
} else if(count > 0) {
ch = buf[pos];
pos++;
count--;
/* 返回第一個字節後的每一個字節 */
return ch;
}
/* 讀完文件,返回-1 */
return -1;
}
public void close() throws IOException {
fis.close();
}
}
問題:
上面的代碼運行結果只拷貝了8K
到目的文件。
原因:
是因爲媒體文件在硬盤上的數據以二進制形式存在;read()
方法在讀取第一個字節的時候,有可能會讀到:11111111
;這樣8個1的情況;而8個1的的二進制就是十進制的-1
;程序中while
循環的跳進等於-1
時,循環停止;因此只複製了8K大小。
理解BufferedInputStream的read()方法:
細看BufferedInputStream類的read()方法,其返回的是int類型。而方法中讀到的字節都是byte類型;這樣做的原因,就是爲了解決讀取字節讀到8個1的情況。read()方法在返回byte類型字節數據的時候,將byte類型提升爲int類型,存儲位數由1個8位,變爲4個8位。爲了確保返回的數據與原數據相同而不產生-1的情況,read()方法在類型提升之後,補了3個8位的0在原byte數據前面。過程如下圖:
在自定義緩衝區中,雖然方法返回了int
類型,進行了數據類型提升,但是沒有進行補0
的操作,意味着當讀到8個1組成的byte
數據時,返回了一個由32個1組成的int
類型數據,結果還是-1
。那麼,如果要完成相同功能,只需取32個1的最後8位
即可。取最後8位,將原數據與上255
。
將每個return ch;語句改爲return ch & 255;即可。
五、鍵盤錄入
- 1.System標準輸出輸入
System
類中對應的成員out
和in
分別是: - System.out-標準輸出流
System.in-標準輸入流
System.in
用於讀取鍵盤錄入。- 2.接收鍵盤錄入
- 從鍵盤接收輸入,並打印在控制檯。
示例代碼:
package com.heisejiuhuche.io;
import java.io.IOException;
import java.io.InputStream;
public class SysteminDemo {
public static void main(String[] args) {
/* 創建標準輸入對象 */
InputStream in = System.in;
try {
/* 錄入一個字符 */
int ch = in.read();
/* 打印該字符的ASCII碼 */
System.out.println(in.read());
} catch(IOException e) {
throw new RuntimeException("運行出錯~");
}
}
}
程序輸出結果:
a
97
- 3.練習
- 接收鍵盤錄入,當回車時打印整行內容;當輸入
over
回車時,結束輸入。示例代碼:
package com.heisejiuhuche.io;
import java.io.IOException;
import java.io.InputStream;
public class SysteminDemo {
public static void main(String[] args) {
InputStream in = System.in;
StringBuilder sb = new StringBuilder();
int ch = 0;
try {
/* 一直讀入字符,每輸入一個字符,就往StringBuilder添加一個;
* 如果讀到回車符,繼續讀入;
* 如果讀到換行符,將StringBuilder中的字符組成字符串
* 判斷該字符串是否等於over,如果是,結束程序;
* 如果不是,就打印該字符串,並將StringBuilder容器清空
*/
while(true) {
ch = in.read();
if(ch == 13)
continue;
if(ch == 10) {
String str = sb.toString();
if(str.equals("over")) {
break;
}
System.out.println(str);
sb.delete(0, sb.length());
} else
sb.append((char)ch);
}
} catch(IOException e) {
throw new RuntimeException("運行出錯~");
}
}
}
六、轉換流
- 1.InputStreamReader類
InputStreamReader
類用於將字節流轉換爲字符流。該類可以將讀取到的字節數據轉換爲字符數據。其使用的編碼表可以由開發者指定,也可以使用系統默認。利用轉換流將字節流轉換爲字符流,就意味着該字節流可以使用字符流的緩衝區技術,調用其readLine()
方法,使鍵盤錄入的讀取過程更加高效便捷。利用轉換流修改鍵盤錄入並打印的代碼:
package com.heisejiuhuche.io;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class InputStreamReaderDemo {
public static void main(String[] args) {
/* 創建標準輸入對象 */
InputStream in = System.in;
/* 利用轉換流將字節流轉換爲字符流 */
InputStreamReader isr = new InputStreamReader(in);
/* 轉換後的字節流,就可以像字符流一樣使用字符流的緩衝技術 */
BufferedReader bufr = new BufferedReader(isr);
/* 調用BufferedReader的readLine()方法整行獲取鍵盤錄入 */
String line = null;
try {
while((line = bufr.readLine()) != null) {
/* 如果輸入over 結束鍵盤錄入 */
if(line.equals("over"))
break;
System.out.println(line.toUpperCase());
}
} catch(IOException e) {
throw new RuntimeException("Exception occured...");
}
}
}
將字節流轉換爲字符流,相當於在字節流上套了兩根管子;一根使字節流變成字符流;另一根使字節流可以使用字符流的緩衝技術。
- 2.OutputStreamWriter類
OutputStreamWriter
類用於將字符流轉換爲字節流。該類的編碼表同樣可以指定或使用系統默認。用OutputStreamWriter類修改上面的代碼:
package com.heisejiuhuche.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class InputStreamReaderDemo {
public static void main(String[] args) {
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
BufferedReader bufr = new BufferedReader(isr);
/* 創建標準輸出對象 */
OutputStream out = System.out;
/* 用轉換流將字符流轉換爲字節流 */
OutputStreamWriter osw = new OutputStreamWriter(out);
/* 使用緩衝字符輸出流增強字符輸出流 */
BufferedWriter bufw = new BufferedWriter(osw);
String line = null;
try {
while((line = bufr.readLine()) != null) {
/* 如果輸入over 結束鍵盤錄入 */
if(line.equals("over"))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
} catch(IOException e) {
throw new RuntimeException("Exception occured...");
} finally {
try {
if(bufw != null)
bufw.close();
} catch(IOException e) {
throw new RuntimeException("資源關閉失敗");
}
}
}
}
可以將字節流轉字符流的三個步驟簡化爲一行代碼:
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
- 3.轉換流的作用
- 轉換流可以指定讀寫時使用的字符編碼集;如不指定,將使用系統默認的編碼集,本機默認使用
GBK
。下面的代碼演示了用UTF-8
寫入,用GBK
讀取會發生亂碼的情況。示例代碼:
package com.heisejiuhuche.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class CharSetDemo {
public static void main(String[] args) {
write();
}
private static void read() {
BufferedReader bufr = null;
try {
/* 使用默認GBK字符集讀取數據,結果會出現亂碼 */
bufr = new BufferedReader(new FileReader("C:/Users/jeremy/Documents/javaTmp/demo.txt"));
System.out.println(bufr.readLine());
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(bufr != null)
bufr.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
private static void write() {
InputStreamReader isr = null;
OutputStreamWriter osw = null;
BufferedReader bufr = null;
BufferedWriter bufw = null;
String line = null;
try {
bufr = new BufferedReader(new InputStreamReader(System.in));
/* 指定使用UTF-8字符集寫入數據 */
bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
"C:/Users/jeremy/Documents/javaTmp/demo.txt"),"UTF-8"));
while((line = bufr.readLine()) != null) {
if(line.equals("over")) {
break;
}
bufw.write(line);
bufw.newLine();
bufw.flush();
}
/* 將寫入的數據讀取打印在控制檯 */
read();
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(bufr != null)
bufr.close();
} catch(IOException e) {
e.printStackTrace();
}
try {
if(bufw != null)
bufw.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
程序輸出結果:
你好,這裏是虹橋鎮~
over
浣犲ソ錛岃繖閲屾槸鉶規ˉ闀噡
爲了正常顯示,用InputStreamReader指定讀取時編碼集爲UTF-8即可
bufr = new BufferedReader(new InputStreamReader(new FileInputStream(
"C:/Users/jeremy/Documents/javaTmp/demo.txt"),"UTF-8"));
程序輸出結果:
你好,這裏是虹橋鎮~
over
你好,這裏是虹橋鎮~
小擴展
使用System類的方法setIn()和setOut()改變標準輸入輸出。示例代碼:
package com.heisejiuhuche.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class SystemSetDemo {
public static void main(String[] args) throws IOException {
BufferedReader bufr = null;
BufferedWriter bufw = null;
/* 改變標準輸入源,從鍵盤變爲指定目錄的文件 */
System.setIn(new FileInputStream("C:/Users/jeremy/Documents/javaTmp/fos.txt"));
/* 改變標準輸出目的地,從控制檯變爲指定目錄的文件 */
System.setOut(new PrintStream("C:/Users/jeremy/Documents/javaTmp/fos1.txt"));
bufr = new BufferedReader(new InputStreamReader(System.in));
bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = bufr.readLine();
bufw.write(line);
bufw.flush();
bufr.close();
bufw.close();
}
}