字節流顧名思義就是操作字節的流。字節流是可以操作任意數據的,比如說媒體數據,音樂,電影,圖片等,當然它也是可以操作字符的。字符流就是基於字節流來實現的,我們知道一個字節是8個二進制位,在UTF-8中一個字節對應一個英文字符,人們發現用單個字節操作文本比較麻煩,所以就對字節流進行封裝,當對文本進行操作的時候就不直接對字節流進行操作,而直接對封裝後的字節流也就是字符流進行操作,這樣就比較方便。
通過上述概述我們可以知道,使用字節流操作字符是完全行得通的。那麼字符流是專門針對字符進行操作的,但是字符流能不能對除了字符以外的數據進行操作呢?
如果你對除字符外的數據,比如說多媒體數據(音樂,電影,圖片)進行操作,比如說複製一個音樂文件,程序編譯時不會報錯的,但是操作過程中可能會出現數據丟失的情況,當你複製結束打開該文件的時候,可能會因爲數據的丟失而無法打開。所以強烈建議對字符文本的操作使用字符流,對字符以外的數據,使用字節流。
字節流的基類:
InputStream和OutputStream
二,字節流讀取文本文件:
需求:使用字節輸入流,讀取一個文本文件中的數據,打印該文件的文本內容到控制檯。
import java.io.*;
public class InputStreamDemo
{
public static void main(String[] args) throws IOException {
method_4();
}
//這種方法在讀取字節的時候,可以讀取剛好和文件大小相同的字節,用available獲取字節的大小
//但是這種方法要慎用,如果文件比較大,當在內存中建立字節數組緩衝區時,內存可能會無法裝這麼大,損壞內存
public static void method_4() throws IOException {
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[fis.available()];//available獲取文件中數據的大小,可以設置大小剛好點的字節數組緩衝區,該方法是字節流特有的。
fis.read(buf);
System.out.println(new String(buf));
}
//將多個字節存儲到字節數組緩衝區,然後打印
public static void method_3() throws IOException{
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[1024];
int len=0;
while ((len = fis.read(buf)) != -1)
{
System.out.println(new String(buf,0,len));
}
fis.close();
}
//一個一個的去讀取字節
public static void method_2() throws IOException {
FileInputStream fis = new FileInputStream("fos.txt");
int len=0;
while ((len=fis.read()) != -1)
{
System.out.println((char)len);
}
fis.close();
}
public static void method_1() throws IOException {
FileOutputStream fos = new FileOutputStream("fos.txt");
fos.write("asghkj".getBytes());
fos.close();
}
}
注意:字節流不需要刷新緩衝區,即沒有flush方法,close方法只關閉流,不刷新;
原因是字符流也是基於字節流的,字節流是可操作的最小單位,可以直接寫入一個字節操作一個,字符流之所以要刷新,是因爲字符不一定是一個字節,必須要先存入到緩衝區,才能完整的輸出,比如中文是兩個字節,如果讀取一個操作一個,那麼讀取到的一個字節只有半個漢字,根本沒法輸出,所以必須要先將字節存儲到緩衝區,然後字節到編碼表查找對應的文字,刷新後才能輸出。
三,字節流操作圖片文件
需求:通過字節流複製一個圖片文件。
思路:1,用字節讀取流對象和圖片文件關聯;
2,用字節寫入流對象創建一個文件用於存儲原圖片;
3,循環讀取字節寫入目的文件;
4,關閉流;
import java.io.*;
public class PictureCopy
{
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("hh.jpg");//原文件
FileOutputStream fos = new FileOutputStream("mu.jpg");//目的文件
int len = 0;
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fis.close();
fos.close();
}
}
四,字節流的緩衝區
字節流的緩衝區的原理和字符流的一樣,都是將原始流傳入到緩衝區的構造函數,增強原有流的功能。
public BufferedInputStream(InputStream in)
public BufferedOutputStream(OutputStream out)
通過複製圖片文件的程序我們知道多媒體文件的複製方法,原理如下:
import java.io.*;
public class MyCopyMp3
{
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("1.mp3");
FileOutputStream fod = new FileOutputStream("2.mp3");
byte[] buf = new byte[1024*1024];
int len=0;
while ((len = fis.read(buf)) != -1)
{
//System.out.println(new String(buf,0,len));
fod.write(buf);
}
fis.close();
}
}
那麼又該如何通過字節流緩衝區的方法複製一個MP3文件呢?
需求:通過字節流緩衝區複製MP3文件.
import java.io.*;
public class Mp3Copy
{
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 bi = new BufferedInputStream(new FileInputStream ("1.mp3"));
BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream("2.mp3"));
int len = 0;
while ((len=bi.read())!=-1)
{
bo.write(len);
}
bi.close();
bo.close();
}
}
總結:不使用緩衝區讀取字節文件的方法是直接建立字節輸入流文件對象和字節輸出流文件對象,創建字節數組,循環將字節輸入流中的字節值(int類型,範圍是0到255)讀取到字節數組,每循環一次就將字節數組寫入字節輸出流中,字節值爲-1就到了末尾,循環結束條件。
五,讀取鍵盤輸入
System.in是標準的輸入流,它的類型是InputStream;
System.out 標準輸出設備,控制檯
System.in 標準輸入設備,鍵盤
1,需求:通過鍵盤輸入一行數據,當一行輸入結束後,打印出來,當輸入over的時候退出;
import java.io.*;
public class SysInputStream
{
public static void main(String[] args) throws IOException {
InputStream in = System.in;//接受鍵盤輸入的字節輸入流;
//建立緩衝區,把一行的數據存儲到緩衝區中,當一行結束後,要清空緩衝區存儲下一行的數據;
StringBuilder sb = new StringBuilder();
//因爲不知道用戶什麼時候能輸入結束,用while(true)循環
while (true)
{
int ch = in.read();//將每次讀取到的一個字節存儲在整型ch中
if(ch == '\r')
continue;
if(ch == '\n')//windows平臺的回車是\r\n,兩個字節,遇到'\r'時跳出本次循環,不保存到緩衝區sb
//遇到'\n'時說明一行結束
{
String s = sb.toString();//將緩衝區的字符轉換成字符串toString();
if("over".equals(s)) //結束後的判斷,當over和緩衝區的字符串相等時退出,並打印字符串
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());//打印完一行後要清除緩衝區緩存,否則每次讀取到的字符都會存儲到之前的字符後面;
}
else
sb.append((char)ch);//沒有遇到換行符時,將讀取到的字符添加到緩衝區,直到遇到回車鍵後退出;
}
//int ch = in.read();讀取單個字符,當輸入的字符數小於讀取次數,會讀取到windows平臺下的回車'\r','\n'
//System.out.println(ch);
//int ch2 = in.read();
//System.out.println(ch2);
}
}
六,鍵盤錄入以及打印的綜合應用
需求:一下程序實現了鍵盤一次錄入一行數據,使用的是字節轉換流。需要特別注意,在使用字符輸出流的時候要注意刷新緩衝區,否則數據只是在內存緩衝區。
字節轉換流:
InputStreamReader是字節流通向字符流的橋樑;
對應的有:OutputStreamWriter是字符流通向字節流的橋樑;
import java.io.*;
public class OutputStreamWriterDemo
{
public static void main(String[] args) throws IOException {
/*InputStream in = System.in;
InputStreamReader inr = new InputStreamReader(in);
BufferedReader bufr = new BufferedReader(inr);*/
//最常用的鍵盤錄入的方法
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
/*
//建立字節輸出流對象
OutputStream out = System.out;
//將字符輸出流轉換成字節輸出流
OutputStreamWriter osw = new OutputStreamWriter(out);
//爲了提高輸出效率,採用緩衝區
BufferedWriter bufw = new BufferedWriter(osw); */
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while ((line=bufr.readLine())!=null)
{
if(line.equals("over"))
break;
bufw.write(line,0,line.length());
bufw.newLine();
bufw.flush();//字符輸出流必須要刷新緩衝區內存才能將流裏面的字符顯示到控制檯
}
bufw.close();
}
}
七,使用流的規律總結:
到目前爲止,所接觸到的幾個重點流有:
Writer,
FileWriter,
BufferedWriter,
OutputStreamWriter,
PrintWriter,
Reader,
FileReader,
BufferedReader,
InputStreamReader,
LineNumberReader,
InputStream,
FileInputStream,
BufferedInputStream,
System.in,
LineNumberInputStream,
OutputStream,
FileOutputStream,
BufferedOutputStream,
System.out,
這些列舉的流是最常用的幾個流對象,我們在使用時怎樣確定應該使用哪一個呢?其實在使用這些流的時候都是有規律的。
流操作的基本規律:
三個明確:
1,明確操作的源和目的:
源:輸入流 InputStream Reader
目的:輸出流 OutputStream Writer
2,明確操作的數據是否爲純文本:
是:字符流
否:字節流
3,明確要使用具體的那個對象,通過硬件設備進行區分
源設備:內存,硬盤,鍵盤;
目的設備:內存,硬盤,控制檯
實例分析一:
需求:將一個文本文件存儲到另一個文件中,文件的複製;
源:因爲是源,所以使用讀取流:InputStream或Reader
源的數據時文本,所以選用字符流Reader
源設備是硬盤上的一個文件,而Reader中可以對文件進行操作的是FileReader對象;
是否需要在原有基礎上提高對文件的操作效率?需要,加入文本讀取流的緩衝區
FileReader fr = new FileReader("tt.txt");
BufferedReader bufR = new BufferedReader(fr);
目的:因爲是目的,所以採用OutputStream或Write
操作的數據是純文本,所以選用字符流Writer
目的設備是硬盤上的一個文件,所以選用FileWriter對象;
FileWriter fw = new FileWriter("hah.txt");
BufferedWriter bufW = new BufferedWriter(fw);
實例分析二:
需求:將一個圖片文件存儲到另一個文件中;圖片的複製;
源:是輸入流,選擇InputStream或Reader
不是一個純文本文件,所以採用自節流,所以習題應選擇InputStream
它是硬盤上的一個文件,InputStream體系統可以操作文件的類是FileInputStream
是否需要提高文件的讀取效率,不需要
FileInputStream fis = new FileInoutStream("mm.jpg");
目的:
是輸出流,所以選擇OutputStream或Writer
圖片文件不是文本文件,所以選用字節流;OutputStream
對於硬盤上的一個文件,OutputStream體系中可以操作文件的只有FileOutputStream
FileOutputStream fos = new FileOutputStream("mu.jpg");
實例分析三:
需求:將鍵盤錄入的數據保存到一個文件中。
這個需求中有源和目的都存在。那麼分別分析:
源:InputStream Reader
是不是純文本?是!Reader
設備:鍵盤。對應的對象是System.in.
不是選擇Reader嗎?System.in對應的不是字節流嗎?
爲了操作鍵盤的文本數據方便。轉成字符流按照字符串操作是最方便的。
所以既然明確了Reader,那麼就將System.in轉換成Reader。
用了Reader體系中轉換流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率嗎?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是存文本?是!Writer。
設備:硬盤。一個文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率嗎?需要。
BufferedWriter bufw = new BufferedWriter(fw);
實例分析四:
實例分析四的擴展一下,想要把錄入的數據按照指定的編碼表(utf-8),將數據存到文件中。
目的:OutputStream Writer
是否是純文本?是!Writer。
設備:硬盤。一個文件。使用 FileWriter。
但是FileWriter是使用的默認編碼表。GBK.
但是存儲時,需要加入指定編碼表utf-8。而指定的編碼表只有轉換流可以指定。
所以要使用的對象是OutputStreamWriter。
而該轉換流對象要接收一個字節輸出流。而且還可以操作的文件的字節輸出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效嗎?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,記住。轉換流什麼使用。字符和字節之間的橋樑,通常,涉及到字符編碼轉換時,
需要用到轉換流。
實例分析五:
練習:將一個文本數據打印在控制檯上。要按照以上格式自己完成三個明確。
源:輸入流 --> InputStream或Reader
操作的數據是否爲純文本?是。Reader
設備:硬盤,一個文件,FileReader
FileReader fr = new FileReader("Demo.txt");
需要提高效率嗎?需要;BufferedReader
BufferedReader bugR = new BufferedReader(fr);
目的:輸出流-->OutputStream或Writer
操作數據是純文本;所以選擇Writer;
設備:控制檯。對應的對象時System.out,不是Writer嗎,System.out是字節流啊?怎麼辦?
將字節流轉換成字符流輸出到控制檯;
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要提高效率嗎?需要。BufferedWriter
BufferedWriter bufW = new BufferedWriter(osw);
八,其他知識點擴展
System類中還提供了兩個設置輸入和輸出的目的,通過這兩個方法,可以
static void setIn(InputStream in)
重新分配“標準”輸入流。
static void setOut(PrintStream out)
重新分配“標準”輸出流
這兩個方法可以將鍵盤輸入改成從某一個指定文件輸入,將控制檯輸出改爲輸出到一個文件中。
採用鍵盤錄入的方將文件複製到另外一個文件中;七,使用流的規律總結:
到目前爲止,所接觸到的幾個重點流有:
Writer,
FileWriter,
BufferedWriter,
OutputStreamWriter,
PrintWriter,
Reader,
FileReader,
BufferedReader,
InputStreamReader,
LineNumberReader,
InputStream,
FileInputStream,
BufferedInputStream,
System.in,
LineNumberInputStream,
OutputStream,
FileOutputStream,
BufferedOutputStream,
System.out,
這些列舉的流是最常用的幾個流對象,我們在使用時怎樣確定應該使用哪一個呢?其實在使用這些流的時候都是有規律的。
流操作的基本規律:
三個明確:
1,明確操作的源和目的:
源:輸入流 InputStream Reader
目的:輸出流 OutputStream Writer
2,明確操作的數據是否爲純文本:
是:字符流
否:字節流
3,明確要使用具體的那個對象,通過硬件設備進行區分
源設備:內存,硬盤,鍵盤;
目的設備:內存,硬盤,控制檯
實例分析一:
需求:將一個文本文件存儲到另一個文件中,文件的複製;
源:因爲是源,所以使用讀取流:InputStream或Reader
源的數據時文本,所以選用字符流Reader
源設備是硬盤上的一個文件,而Reader中可以對文件進行操作的是FileReader對象;
是否需要在原有基礎上提高對文件的操作效率?需要,加入文本讀取流的緩衝區
FileReader fr = new FileReader("tt.txt");
BufferedReader bufR = new BufferedReader(fr);
目的:因爲是目的,所以採用OutputStream或Write
操作的數據是純文本,所以選用字符流Writer
目的設備是硬盤上的一個文件,所以選用FileWriter對象;
FileWriter fw = new FileWriter("hah.txt");
BufferedWriter bufW = new BufferedWriter(fw);
實例分析二:
需求:將一個圖片文件存儲到另一個文件中;圖片的複製;
源:是輸入流,選擇InputStream或Reader
不是一個純文本文件,所以採用自節流,所以習題應選擇InputStream
它是硬盤上的一個文件,InputStream體系統可以操作文件的類是FileInputStream
是否需要提高文件的讀取效率,不需要
FileInputStream fis = new FileInoutStream("mm.jpg");
目的:
是輸出流,所以選擇OutputStream或Writer
圖片文件不是文本文件,所以選用字節流;OutputStream
對於硬盤上的一個文件,OutputStream體系中可以操作文件的只有FileOutputStream
FileOutputStream fos = new FileOutputStream("mu.jpg");
實例分析三:
需求:將鍵盤錄入的數據保存到一個文件中。
這個需求中有源和目的都存在。那麼分別分析:
源:InputStream Reader
是不是純文本?是!Reader
設備:鍵盤。對應的對象是System.in.
不是選擇Reader嗎?System.in對應的不是字節流嗎?
爲了操作鍵盤的文本數據方便。轉成字符流按照字符串操作是最方便的。
所以既然明確了Reader,那麼就將System.in轉換成Reader。
用了Reader體系中轉換流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率嗎?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是存文本?是!Writer。
設備:硬盤。一個文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率嗎?需要。
BufferedWriter bufw = new BufferedWriter(fw);
實例分析四:
實例分析四的擴展一下,想要把錄入的數據按照指定的編碼表(utf-8),將數據存到文件中。
目的:OutputStream Writer
是否是純文本?是!Writer。
設備:硬盤。一個文件。使用 FileWriter。
但是FileWriter是使用的默認編碼表。GBK.
但是存儲時,需要加入指定編碼表utf-8。而指定的編碼表只有轉換流可以指定。
所以要使用的對象是OutputStreamWriter。
而該轉換流對象要接收一個字節輸出流。而且還可以操作的文件的字節輸出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效嗎?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,記住。轉換流什麼使用。字符和字節之間的橋樑,通常,涉及到字符編碼轉換時,
需要用到轉換流。
實例分析五:
練習:將一個文本數據打印在控制檯上。要按照以上格式自己完成三個明確。
源:輸入流 --> InputStream或Reader
操作的數據是否爲純文本?是。Reader
設備:硬盤,一個文件,FileReader
FileReader fr = new FileReader("Demo.txt");
需要提高效率嗎?需要;BufferedReader
BufferedReader bugR = new BufferedReader(fr);
目的:輸出流-->OutputStream或Writer
操作數據是純文本;所以選擇Writer;
設備:控制檯。對應的對象時System.out,不是Writer嗎,System.out是字節流啊?怎麼辦?
將字節流轉換成字符流輸出到控制檯;
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要提高效率嗎?需要。BufferedWriter
BufferedWriter bufW = new BufferedWriter(osw);
八,其他知識點擴展
System類中還提供了兩個設置輸入和輸出的目的,通過這兩個方法,可以
static void setIn(InputStream in)
重新分配“標準”輸入流。
static void setOut(PrintStream out)
重新分配“標準”輸出流
這兩個方法可以將鍵盤輸入改成從某一個指定文件輸入,將控制檯輸出改爲輸出到一個文件中。
採用鍵盤錄入的方將文件複製到另外一個文件中;
import java.io.*;
public class Demo
{
public static void main(String[] args) throws IOException {
System.setIn(new FileInputStream("Demo.txt"));//默認的是鍵盤,改爲一個文件
System.setOut(new PrintStream("hehe.txt"));//默認的是控制檯,改爲文件;
//常用鍵盤輸入格式,這裏的System.in就是Demo.txt文件了,不是鍵盤了
BufferedReader bufR = new BufferedReader(new InputStreamReader(System.in));
//BufferedReader bufR = new BufferedReader(new InputStreamReader(new FileInputStream("Demo.txt")));
//這裏的System.out也不是控制檯了,而是hehe.txt文件
BufferedWriter bufW = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while ((line = bufR.readLine())!=null)
{
if("over".equals(line))
break;
bufW.write(line,0,line.length());
bufW.newLine();
bufW.flush();
}
bufW.close();
}
}
九,異常記錄原理以及用戶系信息獲取
需求:自定義一個類,當類中的某個方法出現異常的時候,記錄這個異常,將這個異常信息保存到一個文件中,並且要求記錄異常出現的時間。
import java.util.*;
import java.io.*;
import java.text.*;
public class Example {
public static void main(String[] args) {
try
{
int[] arr = new int[3];
System.out.println(arr[4]);
}
catch (Exception e)
{
try
{
//e.printStackTrace(new PrintStream("exception.log"));
//創建時間格式
PrintStream ps = new PrintStream("exception.log");
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
String time = sdf.format(d);
ps.println(time);
e.printStackTrace(ps);//異常默認是打印到控制檯上,其實內部就是使用的是e.printStackTrace(System.out),
//接收的是一個PrintStream
}
catch (Exception ex)
{
throw new RuntimeException("創建日誌失敗");
}
}
}
}
需求:獲取系統信息,保存到一個文件中。
import java.util.*;
import java.io.*;
public class PropertiesDemo {
public static void main(String[] args) throws Exception {
Properties pros =System.getProperties();
//System.out.println(pros);//輸出的結果沒有格式
//將系統屬性信息與流相結合,利用流中封裝的方法就可以讀取到屬性信息,並且格式化輸出
pros.list(System.out);
//pros.list(new PrintWriter(System.out,true));
//將系統屬性信息讀取到一個文件中,保存
PrintStream ps = new PrintStream("pros.info");
pros.list(ps);
}
}