15、IO流


十五、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. 流關閉以後不能,不能再調用write方法,否則會報異常錯誤:IOException。
       IO流的異常處理方式:爲防止代碼異常導致流無法關閉,因此在finally中對流進行關閉。如果構造函數中加入true,可以實現對文件進行續寫。


——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.小結

流的基本應用小結:
•流是用來處理數據的。
•處理數據時,一定要先明確數據源,與數據目的地(數據匯)。
•數據源可以是文件,可以是鍵盤。
•數據目的地可以是文件、顯示器或者其他設備。
•而流只是在幫助數據進行傳輸,並對傳輸的數據進行處理,比如過濾處理.轉換處理等。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章