當程序需要讀取或寫入數據的時候,就會開啓一個數據源的流,這個數據源可以是文件,內存,或是網絡連接。
流的分類:
1,按數據方向分:輸入流和輸出流
輸入流:Reader / InputStream
輸出流:Writer / OutputStream
2,按數據類型分:字節流和字符流
字節流:InputStream / OutputStream
|-- FileOutputStream
|-- FileInputStream
緩衝技術
|-- BufferedInputStream
|-- BufferedOutputStream
字符流:Reader / Writer
|-- FileReader
|-- FileWriter
緩衝技術
|-- BufferedReader 字符讀取流緩衝區
|-- BufferedWriter 字符寫入流緩衝區
轉換流
OutputStreamWriter :將輸出的字符流轉化爲字節流
InputStreamReader :將輸入的字節流轉換爲字符流
例://源,鍵盤
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
注意:不管如何操作,最後都是以字節的形式保存在文件中的。
轉換流什麼時候使用?
是字符和字節之間的橋樑,通常,涉及到字符編碼轉換時,需要用到轉換流。
例:
按照指定的編碼表(UTF-8)存到文件中使用下面的方法
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:\\123\\d.txt","UTF-8")));
管道流
管道輸出流:PiPedOutputStream
管道輸入流:PipedInputStream
打印流
PrintStream
爲其他輸出流添加了功能,使它們能夠方便地打印各種數據值表示形式。它還提供其他兩項功能。與其他輸出流不同,PrintStream 永遠不會拋出 IOException;
合併流
SequenceInputStream :(Enumeration<? extends InputStream> e)
將多個流合併成一個,然後再輸出到指定位置
例:
將三個文本文件中內容合併成一個文件。
思路:讀取一個文本就會創建一個流。把這些流放在集合中。
Vector<FileInputStream> v = new Vector<FileInputStream>();
v.add(new FileInputStream("d:\\123\\1.txt"));
v.add(new FileInputStream("d:\\123\\2.txt"));
v.add(new FileInputStream("d:\\123\\3.txt"));
Enumeration<FileInputStream> en = v.elements();
//SequenceInputStream 只接收 Enumeration(枚舉)所以在選用集合時選能實現枚舉的
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("d:\\123\\123.txt");
byte[] buf = new byte[1024];
int len = 0;
while ((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
操作字節數組的流對象
ByteArrayInputStream
在構造的時候,需要接收數據源,而且數據源是一個字節數組。
例:
ByteArrayInputStream bis = new ByteArrayInputStream("abcd".getBytes());
ByteArrayOutputStream
在構造時,不用定義數據目的,因爲該對象中已經內部封裝了可變長度的字節數組。
例:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while ((by=bis.read())!=-1)
{
bos.write(by);
}
System.out.println(bos.toString());
操作字符數組
CharArrayReader
CharArrayWriter 用法一樣
操作字符串
StringReader
StringWriter
操作基本數據類型的流對象
DataInputStreamgn
DataOutputStream
Properties
操作於集合和IO流之間, Properties 繼承於 Hashtable。
緩衝區技術
緩衝區的出現是爲了提高流的操作效率而出現的。所以在創建緩衝區之前,必須要先有流對象。
例:
使用寫入流緩衝區技術
//1,創建一個字符寫入流對象
FileWriter fw = new FileWriter("buf.txt");
//2,爲了提高寫入流效率,加入緩衝技術
BufferedWriter bufw = new BufferedWriter(fw);
這樣就可以使用緩衝技術的方法,如:newLine() 換行,使用這個具有跨平臺性。
注意: bufw.close(); //底層操作其實就是fw流,所以只要關閉緩衝區就是關閉fw流
例:
複製一個Mp3,通過字節流的 緩衝區完成複製
public void method() throws IOException
{
BufferedInputStream bfis = new BufferedInputStream(new FileInputStream("D:\\123\\Honey.mp3"));
BufferedOutputStream bfos = new BufferedOutputStream(new FileOutputStream("D:\\123\\123.mp3"));
int by = 0;
while ( (by=bfis.read()) !=-1 )
{
bfos.write(by);
}
bfis.close();
bfos.close();
}
例:複製一個圖片,不用緩衝區,使用數組完成,
public void method2() throws IOException
{
FileInputStream fis = new FileInputStream("D:\\123\\1.jpg");
FileOutputStream fos = new FileOutputStream("D:\\123\\2.jpg");
byte[] buf = new byte[1024]; // 這裏數組其實就相當於是緩衝區
int len = 0;
while ((len=fis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fis.close();
fos.close();
}
自定義緩衝區技術:
實現原理:
使用底層InputStream read 方法,將數據存入到緩衝區(數組)中,我們從緩衝區讀數據是從數組中讀取數據,當該數組中讀完後,讓read方法在讀一次,把讀到的數據存入到緩衝區中,。。。這樣循環,直到,把數據源中的數據讀完。
這裏以定義 字節流緩衝區 爲例
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024]; //緩衝區,
private int pos = 0,count = 0; //計數器,指針
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//調用一次 從緩衝區(字節數組)中讀一個字節
public int myRead() throws IOException
{
//通過in對象讀取硬盤上數據,並存儲在緩衝區buf中
if(count==0)
{
count = in.read(buf); //採用底層方法讀取一批數據存入緩衝區,並返回其個數
if(count<0)
return -1;
pos = 0; //指針歸位,指向數組0角標開始
byte b = buf[pos]; // 指針指向的當前數據
count--;
pos++;
return b&255; //返回讀取到的數據
}
else if(count>0) //如果緩衝區還有數據,則取緩衝區中的下一個數據,如緩衝區無數據(count=0時)則讓底層read在去讀一批數據存入緩衝區,直到讀完
{
byte b = buf[pos];
count--;
pos++;
return b&255;
}
return -1;
}
public void myClose() throws IOException //提供關閉流的方法
{
in.close();
}
}
/*
爲什麼要與255呢?
因爲返回類型爲int 而byte會自動提升
byte 1111 (-1)提升爲:
int 1111 1111 1111 1111 (還是-1)
前面填充了1使得一開始while ((by=bfis.myRead())!=-1)不滿足沒有進行循環
所以得填充0 只要與 0000 0000 0000 1111 進行與運算就能保證填充爲0
1111 1111 1111 1111
& 0000 0000 0000 1111
----- -------------------------
0000 0000 0000 1111
*/
流對象很多,使用時該用那一個呢?
流操作規律:通過兩個明確來完成。
1,明確源和目的。
源:輸入流。 InputStream(字節流) Reader(字符流)
目的:輸出流。 OutputStream(字節流) Writer(字符流)
2,操作的數據是否是純文本。
是:使用字符流。
不是:使用字節流。
例1:
將一個文本文件中的數據存儲到別一個文件中(複製文件)
源:
用讀取流。InputStream Reader
是文本文件 選Reader
操作文件對象的是 FileReader
要提高效率用:加入緩衝區
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
目的:
用 OutputStream Writer
是純文本 用Writer
操作文件 FileWriter
提高效率用:加入緩衝區
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
例2:
把鍵盤輸入的數據保存到一個文件中。
源: 是純文本,用Reader
設備:鍵盤,對應的對象是System.in(字節流)
爲了操作鍵盤的文本數據方便,轉成字符流按照字符串操作是最方便的
所以將System.in轉換成Reader 即轉換流InputStreamReader
InputStreadmReader isr = new InputStreamReader(System.in);
提高效率!->緩衝區
BufferedReader bufr = new BufferedReader(isr);
目的:是純文本,用Writer
一個文件,使用FileWriter,當然也可以用OutputStreamWriter
FileWriter fw = new FileWriter("c.txt");
BufferedWriter bufw = new BufferedWriter(fw);
*************************
擴展一下。
想要把錄入的數據按照指定的編碼表(UTF-8)存到文件中
目的:存儲時,需要加入指定編碼表。而指定的編碼表只有轉換流可以指定。
所以要使用的對象是OutputStreamWriter
而該轉換流對象要接收一個字節輸出流。而且還可以操作誰的的字節輸出流FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("d.txt"),"utf-8");
BufferedWriter bufw = new BufferedWriter(osw);
Scanner
可以使用正則表達式來解析基本類型和字符串的簡單文本掃描器
Scanner 使用分隔符模式將其輸入分解爲標記,默認情況下該分隔符模式與空白匹配。然後可以使用不同的 next 方法將得到的標記轉換爲不同類型的值。
例:
Scanner sc = new Scanner(System.in);
int i = sc.nextInt();
File
用於表示文件(目錄),通過File類操作硬盤上的文件和目錄。File類只用於表示文件的信息(名稱,大小等),不能對文件的內容進行訪問。
注意:File類的兩個常量,如果想實現跨平臺,那麼最好使用下面的方法來代替分隔符
\ : File.separator
; : File.pathSeparator
在創建文件時,如果該目錄下已有同名文件,將被覆蓋。在寫入數據的時候記得要將緩衝區的數據刷新到目的地中。
fw.flush(); //刷新流對象中的緩衝區中的數據,將數據刷新到目的地中
fw.close(); //關閉流資源,但是關閉之前會刷新一次內部的緩衝區中的數據。
//將數據刷到目的地中。流關閉後,不能在寫入數據。
close和flush區別:flush刷新後,流可以繼續使用,close刷新後,會將流關閉
例:
//列出指定目錄下的所有文件名稱及其路徑
public static void method_2()
{
File f = new File("D:\\123");
File s[] = f.listFiles();
for(File fi : s)
{
System.out.println(fi);
}
}
//列出指定文件類型的文件(如:列出後綴名爲.java的文件)
public static void method_3()
{
File f = new File("D:\\123");
File s[] = f.listFiles(new FilenameFilter()
{
public boolean accept(File dir,String name)
{
return name.endsWith(".txt");
}
});
for(File fi : s)
{
System.out.println(fi);
}
}
注意:在建立文件時,如果文件存在,繼續往裏面寫入數據,
則傳遞一個true參數,代表不覆蓋已有的文件 ,並在已有文件的末尾進行數據續寫
如:fw = new FileWriter("D:\\123\\demo.txt",true);
文本文件中的換行語句 “ \r\n ”
FileDialog :選擇文件對話框窗口
System
System 類包含一些有用的類字段和方法。它不能被實例化。
在 System 類提供的設施中,有標準輸入、標準輸出和錯誤輸出流;對外部定義的屬性和環境變量的訪問;加載文件和庫的方法;還有快速複製數組的一部分的實用方法
System.in 默認是指鍵盤錄入,還可以自己指定位置
使用: System.setIn(InputStream in) ;
如:System.setIn(new FileReader("123.txt")) ;
源:就被設置成了一個文件
System.out 輸出位置也可設置
使用:setOut(PrintStream out) ;
如:System.setOut(new FileWriter("222.txt")) ;
目的:輸出位置設置成了一個文件
System.currentTimeMillis(); 返回當前系統時間,以毫秒計算。
常見的編碼表
ASCII : 美國標準信息交換碼。用一個字節的7位可以表示
ISO8859-1 :拉丁碼錶。歐洲碼錶。用一個字節的8位表示
GB2312 : 中國的中文編碼表。
GBK :中國的中文編碼表升級,融合了更多的中文文字符號。
Unicode : 國際標準碼,融合了多種文字。
所有文字都用兩個字節來表示,Java語言使用的就是unicode
UTF-8 : 最多用三個字節來表示一個字符。
注意:聯通兩個字,這兩個字的GBK編碼,和UTF-8的規律相似