十五、IO流
1、系統結構圖(xmind)
2、IO流繼承體系簡圖
3、tips
——1.字符流Writer
需求:將一些文字存儲到硬盤一個文件中。
如果要操作文字數據,建議優先考慮字符流。而且要將數據從內存寫到硬盤上,要使用字符流中的輸出流:Writer。硬盤的數據基本體現是文件,希望找到一個可以操作文件的Writer:FileWrite。
代碼:
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo
{
public static void main(String[] args) throws IOException
{
//創建一個可以往文件中寫入字符數據的字符輸出流對象
//既然是往一個文件中寫入文字數據,那麼在創建對象時,就必須明確該文件(用於存儲數據的目的地)
//如果文件不存在,則會自動創建
//如果文件存在,則會被覆蓋
FileWriter fw = new FileWriter("demo.txt" );
//調用Writer對象中的write(string)方法,寫入數據
//其實數據寫入到臨時存儲緩衝區中
fw.write( "abcde");
//進行刷新,將數據直接寫入到目的地中
fw.flush();
//關閉流,關閉資源,在關閉前會先調用flush刷新緩衝中的數據到目的地。
fw.close();
}
}
1.close方法只能用一次。
——2.字符流Reader
1.使用read()方法讀取文本文件數據。
慄:
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt" );
//用Reader中的read方法讀取字符
int ch = 0;
while((ch = fr.read()) != -1)
{
System.out.print(( char)ch+" ");
}
fr.close();
}
}
運行結果:
public int read()
throw IOException
讀取單個字符。在字符可用、發生I/O錯誤或者已達到流的末尾前,此方法一直阻塞。用於支持高效的單字符輸入的子類應重寫此方法。
返回:作爲整數讀取的字符,範圍在0到65535之間(0x00-0xffff),如果已達到流的末尾,則返回-1
拋出:IOException-如果發生I/O錯誤。
2.使用read(char[])方法讀取文本文件數據。
慄:
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo2
{
public static void main(String[] args)throws IOException
{
FileReader fr = new FileReader("demo.txt" );
//使用read(char[])讀取文本文件數據
//先創建字符數組
char[] buf = new char[3];
int len = 0;
while((len = fr.read(buf)) != -1)
{
System.out.println( new String(buf,0,len));
}
fr.close();
}
}
運行結果:
——3.字符流緩衝區
寫入換行使用BufferedWriter類中的newLine()方法。讀取一行數據使用BufferedReader類中的readLine()方法。bufr.read():這個read方法是從緩衝區中讀取字符數據,所以覆蓋了父類中的read方法。bufr.readLine():另外開闢了一個緩衝區,存儲的是原緩衝區一行的數據,不包含換行符。原理:使用了讀取緩衝區的read方法,將讀取到的字符進行緩衝並判斷換行標記,將標記前的緩衝數據變成字符串返回。
慄:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class CopyTextBufTest
{
public static void main(String[] args) throws Exception
{
FileReader fr = new FileReader("buf.txt" );
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FileWriter("buf_copy.txt" );
BufferedWriter bufw = new BufferedWriter(fw);
String line = null;
//方式一
while((line = bufr.readLine()) != null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
//方式二
/*
int ch = 0;
while((ch = bufr.read()) != -1)
{
bufw.write(ch);
}
*/
bufr.close();
bufw.close();
}
}
運行結果:
——4.裝飾設計模式
1.當想對已有對象進行功能增強時,可定義類:將已有對象傳入,基於已有對象的功能,並提供加強功能,那麼自定義的該類稱之爲裝飾類。
2.特點:裝飾類通常都會通過構造方法接收被裝飾的對象,並基於被裝飾的對象的功能,提供更強的功能。
3.裝飾和繼承的區別:
1)裝飾模式比繼承要靈活。避免了繼承體系的臃腫,且降低了類與類之間的關係。
2)裝飾類因爲增強已有對象,具備的功能和已有的是相同的,只不過提供了更強的功能,所以裝飾類和被裝飾的類通常都是屬於一個體系。
3)從繼承結構轉爲組合結構。
在定義類的時候,不要以繼承爲主;可通過裝飾設計模式進行增強類功能。靈活性較強,當裝飾類中的功能不適合,可再使用被裝飾類的功能。
——5.字節流
慄1:
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo
{
public static void main(String[] args) throws IOException
{
demo_write();
}
public static void demo_write() throws IOException
{
//1、創建字節輸出流對象,用於操作文件
FileOutputStream fos = new FileOutputStream( "bytedemo.txt");
//2、寫數據,直接寫入到了目的地中
fos.write( "abcdefg".getBytes());
//關閉資源動作要完成
fos.close();
}
}
運行結果:
慄2:
/*
練習:
複製一個圖片
思路:
1、用字節讀取流對象和圖片關聯。
2、用字節寫入流對象創建一個圖片文件。用於存儲獲取到的圖片數據。
3、通過循環讀寫,完成數據的存儲。
4、關閉資源。
*/
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
//常用方法複製
byteArrayCopy();
//利用輸入流的available方法進行復制
availableCopy();
//利用available方法對複製文件大小有限制,慎用
}
//使用available方法進行復制
public static void availableCopy()
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
//關聯要複製的文件
fis=new FileInputStream("D:/桌面/1.jpg");
//指定複製的路徑
fos=new FileOutputStream("D:/桌面/2.jpg");
//利用available方法指定數組長度
byte[] b=new byte[fis.available()];
fis.read(b);//複製關聯文件數據
fos.write(b);//粘貼到指定路徑
}
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("寫入字節流關閉失敗");
}
}
}
//使用讀數組方式進行復制
public static void byteArrayCopy()
{
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
//關聯要複製的文件
fis=new FileInputStream("D:/桌面/1.jpg");
//指定複製的路徑
fos=new FileOutputStream("D:/桌面/3.jpg");
//利用數組的讀取方式
byte[] b=new byte[1024];
int len=0;
while ((len=fis.read(b))!=-1)//複製文件的全部數據
{
fos.write(b,0,len);//粘貼到指定路徑
}
}
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("寫入字節流關閉失敗");
}
}
}
}
運行結果:
——6.字節流緩衝區
需求:根據字節流緩衝區的原理,自定義一個字節流緩衝區。
注意:
1、字節流的讀一個字節的read方法爲什麼返回值類型不是byte,而是int。因爲有可能會讀到連續8個二進制1的情況,8個二進制1對應的十進制是-1.那麼就會數據還沒有讀完,就結束的情況。因爲我們判斷讀取結束是通過結尾標記-1來確定的。所以,爲了避免這種情況將讀到的字節進行int類型的提升。並在保留原字節數據的情況前面了補了24個0,變成了int類型的數值。而在寫入數據時,只寫該int類型數據的最低8位。
2、byte類型的-1提升爲int類型時還是-1。原因:因爲在bit8個1前面補的全是1導致的。如果在bit8個1前面補0,即可以保留原字節數據不變,又可以避免-1的出現。這時將byte型數據&0xff即255即可。
慄:
/*
自定義字節流讀取緩衝區
思路:
1、定義一個固定長度的數組
2、定義一個指針和計數器用於讀取數組長度,和計數數組元素是否取完爲0
3、每次將字節數據存入元素要先將數組中的元素取完
*/
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] by=new byte[1024];
private int count=0,pos=0;
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//自定義讀方法,一次讀一個字節
public int myRead()throws IOException
{
//通過in對象讀取硬盤上數據,並存儲by中。
//存儲在數組中的數據被讀取完,再通過in對象從硬盤上讀取數據
if(count == 0)
{
count = in.read(by);
if(count < 0)//文件數據全部被讀取出來了
return -1;
pos = 0;//初始化指針
byte b = by[pos];
count--;//每被讀一個字節,表示數組中的字節數少一個
pos++;//指針加1
return b&255;//返回的byte類型提升爲int類型,字節數增加,且高24位被補1,原字節數據改變。
//通過與上255,主動將byte類型提升爲int類型,將高24位補0,原字節數據不變。
//而在輸出字節流寫入數據時,只寫該int類型數據的最低8位。
}
else if(count > 0)//如果數組中的數據沒被讀取完,則繼續讀取
{
byte b = by[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
//自定義關閉資源方法
public void close()throws IOException
{
in.close();
}
}
//測試自定義輸入字節流緩衝區
class MyBufferedCopyMp3
{
public static void main(String[] args)
{
long start = System.currentTimeMillis();
//利用字節流的緩衝區進行復制
copy_2();
long end=System.currentTimeMillis();
System.out.println("複製共用時:"+(end-start)+"毫秒");
}
//使用字節流的緩衝區進行復制
public static void copy_2()
{
BufferedOutputStream bout=null;
MyBufferedInputStream bin=null;
try
{
//關聯複製文件輸入流對象到緩衝區
bin = new MyBufferedInputStream(new FileInputStream("D:/桌面/Sugar.mp3"));
//指定文件粘貼位置的輸出流對象到緩衝區
bout = new BufferedOutputStream(new FileOutputStream("D:/桌面/Sugar2.mp3"));
int by=0;
while((by = bin.myRead())!=-1)
{
bout.write(by);//將緩衝區中的數據寫入指定文件中
}
}
catch(IOException e)
{
throw new RuntimeException("MP3複製失敗");
}
finally
{
try
{
if(bin != null)
bin.close();//關閉輸入字節流
}
catch(IOException e)
{
throw new RuntimeException("讀取字節流關閉失敗");
}
try
{
if(bout != null)
bout.close();//關閉輸出字節流
}
catch(IOException e)
{
throw new RuntimeException("寫入字節流關閉失敗");
}
}
}
}
運行結果:
——7.轉換流
1.InputStreamReader將字節流通向字符流
1、獲取鍵盤錄入對象。
InputStream in=System.in;
2、將字節流對象轉成字符流對象,使用轉換流。
InputStreamReaderisr=new InputStreamReader(in);
3、爲了提高效率,將字符串進行緩衝區技術高效操作。使用BufferedReader
BufferedReaderbr=new BufferedReader(isr);
//鍵盤錄入最常見寫法
BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));
2. OutputStreamWriter字符流通向字節流
字符通向字節:錄入的是字符,存到硬盤上的是字節。步驟和InputStreamReader轉換流一樣。
/*
需求:將鍵盤錄入的數據,顯示在控制檯,當輸入over時,表示結束
源:鍵盤錄入。
目的:控制檯。
*/
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)throws IOException
{
//獲取鍵盤錄入對象。
//InputStream in=System.in;
//將字節流對象轉成字符流對象,使用轉換流。
//InputStreamReader isr=new InputStreamReader(in);
//爲了提高效率,將字符串進行緩衝區技術高效操作。使用BufferedReader
//BufferedReader br=new BufferedReader(isr);
//鍵盤錄入最常見寫法
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
//字符流通向字節流
BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out));
String s=null;
while((s=in.readLine())!=null)
{
if("over".equals(s))
break;
bw.write(s.toUpperCase());//寫入數據
bw.newLine();//換行
bw.flush();//刷新
}
bw.close();//關閉流資源
in.close();
}
}
運行結果:
——8.Properties集合
Map
|--Hashtable
|--Properties
特點:
1. 該集合中的鍵和值都是字符串類型。
2. 集合中的數據可以保存到流中,或者從流中獲取。
3. 通常該集合用於操作以鍵值對形式存在的配置文件。
慄:
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo
{
public static void main(String[] args)
{
propertiesDemo();
}
public static void propertiesDemo()
{
//創建一個Properties集合
Properties prop = new Properties();
//存儲元素
prop.setProperty( "zhangsan","10" );
prop.setProperty( "lisi","20" );
prop.setProperty( "wangwu","30" );
prop.setProperty( "zhaoliu","40" );
//修改元素
prop.setProperty( "wangwu","26" );
//取出所有元素
Set<String> names = prop.stringPropertyNames();
for(String name : names)
{
String value = prop.getProperty(name);
System.out.println(name + ":" + value);
}
}
}
運行結果:
——9.實例
/*
需求:獲取指定目錄下,指定擴展名的文件(包含子目錄中的),並且將這些文件的絕對路徑寫入到
一個文本文件中。
簡單說:就是建立一個指定擴展名的文件的列表。
思路:
1. 必須進行深度遍歷。
2. 要在遍歷的過程中進行過濾,將符合條件的內容都存儲到容器中。
3. 對容器中的內容進行遍歷並將絕對路徑寫入到文件中。
*/
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Test
{
public static void main(String[] args) throws IOException
{
File dir = new File("E:/Java/day21" );
FilenameFilter filter = new FilenameFilter()
{
public boolean accept(File dir,String name)
{
return name.endsWith(".java" );
}
};
List<File> list = new ArrayList<File>();
getFiles(dir,filter,list);
File destFile = new File(dir,"javalist.txt" );
write2File(list,destFile);
}
/*
對指定目錄中的內容進行深度遍歷,並按照指定過濾器,進行過濾。
將過濾後的內容存儲到指定容器List中。
*/
public static void getFiles(File dir,FilenameFilter filter,List<File>list)
{
File[] files = dir.listFiles();
for(File file : files)
{
//遞歸
if(file.isDirectory())
{
getFiles(file,filter,list);
}
else
{
//對便利到的文件進行過濾器的過濾。將符合條件File對象,存儲到List集合中
if(filter.accept(dir,file.getName()))
{
list.add(file);
}
}
}
}
public static void write2File(List<File> list,File destFile) throws IOException
{
BufferedWriter bufw = null;
try
{
bufw = new BufferedWriter(new FileWriter(destFile));
for(File file : list)
{
bufw.write(file.getAbsolutePath());
bufw.newLine();
bufw.flush();
}
}
finally
{
if(bufw!=null)
try
{
bufw.close();
}
catch(IOException e)
{
throw new RuntimeException("關閉失敗");
}
}
}
}
運行結果:
——10.小結
流的基本應用小結:
•流是用來處理數據的。
•處理數據時,一定要先明確數據源,與數據目的地(數據匯)。
•數據源可以是文件,可以是鍵盤。
•數據目的地可以是文件、顯示器或者其他設備。
•而流只是在幫助數據進行傳輸,並對傳輸的數據進行處理,比如過濾處理.轉換處理等。