Java基礎(IO流)


IO流概述

一、IO即Input、Ouput組合;

二、IO流特點:

1、IO流用來處理設備之間的數據傳輸

2、Java對數據的操作是通過流的方式

3、Java用於操作流的對象都在IO包中

三、IO流分類

1、按照操作數據分:字符流和字節流

       字節流:字節流可以操作任何數據,因爲在計算機中任何數據都是以字節的形式存儲的(如圖片,音頻)

       字符流:字符流只能操作純字符數據,比較方便(不能操作音頻和圖片)。

2、按照流向分爲:輸入流和輸出流

四、字節流和字符流

1、字節流的抽象基類

        InputStram(輸入流),OutputSteam(輸出流)

2、字符流的抽象基類

        Reader(輸入流)、Writer(輸出流)

住:由這四個類派生出來的子類的名稱都是以其父類名稱作爲後綴來命名的

如:InputStream的子類FileInputSteam

      OutputStram的子類FileOutputStram

       Reader的子類FileReader

       Writer的子類FileWriter

字符流

一、字符流是什麼?

      1、字符流是可以直接讀取字符的IO流

      2、字符流讀取字符, 就要先讀取到字節數據, 然後轉爲字符. 如果要寫出字符, 需要把字符轉爲字節再寫出.

二、FielReader和FileWriter

          FileReader

<span style="font-size:14px;">/*FileReader
	步驟:1、創建文件讀取流對象,並指定路徑下的文件名稱,如果沒指定路徑,
	         則默認是讀取該源文件所在的路徑下的文件
	      2、調用讀取流對象的read方法,返回讀取的字符在ASCII中對應的數字,
		     如果讀取到則返回-1
		  3、因爲沒調用一次read方法,僅僅只是讀取一個字符,所以要使用循環
		  4、關閉流
*/
import java.io.*;
class  FileReaderDemo
{
	public static void main(String[] args) 
	{
		/*FileReader是IO流中的字符輸入流,是從硬盤上讀取數據用的*/
		//步驟1
		FileReader fr = null;
		try{
			fr = new FileReader("d:\\file.txt");  //會拋出FileNotFoundException異常
			/*因爲是讀取文件,所以要指定文件以及文件所在的路徑,必須保證文件存在
			  如果要讀取得文件不存在,則會發生FileNotFoundException文件找不到異常
			*/
			//步驟2
			int charNum = 0;
			//調用read方法,循環讀取文件
			while((charNum = fr.read())!=-1){  //read會拋出IOExcepion異常
				System.out.print((char)charNum);
			}
		}
		catch(IOException e){
			System.out.println(e.toString());
		}
		finally{
			//流是必須要關閉的,所以放在finally語句塊中
			try
			{
				if(fr!=null)
				fr.close();     //會拋出IOException異常
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
		}

	}
}
</span>

注:輸出流中flush和close的區別:

       flush是用來刷新緩衝區的,刷完之後還可以寫出

       close方法是用來關閉流的,在關閉之前會刷新一次緩衝區,刷完之後關閉,不可以再寫出

FileWriter

<span style="font-size:14px;">/*FileReader
	步驟:1、創建文件寫入流對象,並指定路徑下的文件名稱,如果該路徑下沒有指定文件,
	         則會在該路徑下創建一個文件,如果有則會覆蓋
	      2、調用輸入流對象的write方法,將數據寫入文件中
		  3、清空緩存或關閉流
*/
import java.io.*;
class  FileWriterDemo
{
	public static void main(String[] args) 
	{
		/*FileWriter是IO流中的字符輸出流,是將讀取到的數據寫入指定文件中*/
		//步驟1
		FileWriter fw = null;
		try{
			fw = new FileWriter("d:\\file_Copy.txt");  //會拋出IOException異常
			//步驟2
			fw.write("abcdefg");
		}
		catch(IOException e){
			System.out.println(e.toString());
		}
		finally{
			//流是必須要關閉的,所以放在finally語句塊中
			try
			{
				if(fw!=null)
					fw.close();     //會拋出IOException異常
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
		}
	}
}
</span>

三、什麼情況下使用字符流

1、字符流也可以拷貝文本文件,但不推薦使用,因爲讀取時會把字節轉換成字符,寫入時會把字符轉回字節

2、程序需要讀取一段文本,或者需要寫出一段文件的時候可以使用字符流

四、字符流是否可以拷貝非純文本的文件

1、不可以拷貝非純文本的文件

2、因爲在讀的時候會將字節轉換爲字符,在轉換過程中,可能找不到對應的字符,就會用?代替,寫出的時候會將字符轉換成字節寫出去

3、如果是?,直接寫出,這樣寫出之後的文件就亂了,看不了了

五、自定義的數組拷貝

<span style="font-size:14px;">/*自定義數組拷貝  提高效率
思路:當使用FileReader的read方法時,一次只讀取一個字符調用一次輸出一次,
比較麻煩,所以我們能不能把讀取的數據存入數組中,等達到一定數量後,在輸出呢?
步驟:1、定義輸入輸出流
      2、定義數組,用於保存讀取到的數據
	  3、將數組中的數據寫入到指定文件中
*/
import java.io.*;
class FileReaderFileWriterDemo 
{
	public static void main(String[] args) 
	{
		//定義輸入流
		FileReader fr = null;
		//定義輸出流
		FileWriter fw = null;
		try
		{
			fr = new FileReader("d:\\file.txt");
			fw = new FileWriter("d:\\file_copy.txt");
			//定義數組存放字符
			char [] ch = new char[10];	//這裏一般把數組的長度定義成1024也就是2kB
			int index = 0;
			//把讀到的字符存入數組
			while((index = fr.read(ch))!=-1){
				//從字符中讀取數據寫入文件中
				fw.write(ch,0,index);
				fw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally{
			//分別關閉輸入輸出流
			try
			{
				if(fr!=null)
					fr.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
			finally{
				try
				{
					if(fw!=null)
						fw.close();
				}
				catch (IOException e)
				{
					System.out.println(e.toString());
				}
			}
		}
	}
}
</span>

六、帶緩衝的字符流

1、BufferedReader的read()方法讀取字符時會一次讀取若干字符到緩衝區, 然後逐個返回給程序, 降低讀取文件的次數, 提高效率

2、BufferedWriter的write()方法寫出字符時會先寫到緩衝區, 緩衝區寫滿時纔會寫到文件, 降低寫文件的次數, 提高效率

注:1、BufferedReader中有一個特有的方法   readerLine()   用於讀取一行文本,還有就是用readerLine讀取的數據不會自動換行,那是因爲readerLine方法返回的時候只返回回車符之前的數據內容,並不返回回車符

       2、 BufferedWriter中也有一個特有的方法   newLine()   是一個跨平臺的換行符

<span style="font-size:14px;">/*帶緩衝的字符流
*/
import java.io.*;
class BufferedReaderBufferedWriterDemo 
{
	public static void main(String[] args) 
	{
		BufferedReader br = null;
		BufferedWriter bw = null;
		try
		{
			br = new BufferedReader(new FileReader("d:\\file.txt"));
			bw = new BufferedWriter(new FileWriter("d:\\file_copy.txt"));
			/*緩衝區的作用是爲了提高流的操作效率而出現的,所以在創建緩衝區
			之前,必須要先有流對象,所以要把流對象作爲實際參數傳遞給緩衝區
			的構造函數*/
			String str = "";
			while((str=br.readLine())!=null){
				bw.write(str);
				bw.newLine();
				bw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally{
			//分別關閉輸入輸出流
			try
			{
				if(br!=null)
					br.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
			finally{
				try
				{
					if(bw!=null)
						bw.close();
				}
				catch (IOException e)
				{
					System.out.println(e.toString());
				}
			}
		}
	}
}
</span>

七、寫一個自己的緩衝區

<span style="font-size:14px;">/*
明白了BufferedReader類中特有方法readLine的原理後,
可以自定義一個類中包含一個功能和readLine一致的方法。
來模擬一下BufferedReader
*/
import java.io.*;
class MyBufferedReader
{
	private FileReader fr;
	MyBufferedReader(FileReader fr){
		this.fr = fr;
	}
	public String readLine()throws IOException{
		int charNum = 0;
		String str = "";
		while((charNum = fr.read())!=-1){
			if(charNum=='\r')
				continue;
			if(charNum=='\n')
				return str;
			else
				str+=(char)charNum;
		}
		if(str.length()!=0)
			return str;
		return null;
	}
	public void close()throws IOException{
		fr.close();
	}
}
class MyBufferedReaderMyBufferedWriterDemo 
{
	public static void main(String[] args) 
	{
		MyBufferedReader br = null;
		BufferedWriter bw = null;
		try
		{
			br = new MyBufferedReader(new FileReader("d:\\file.txt"));
			bw = new BufferedWriter(new FileWriter("d:\\file_copy.txt"));
			String str = "";
			while((str=br.readLine())!=null){
				bw.write(str);
				bw.newLine();
				bw.flush();
			}
		}
		catch (IOException e)
		{
			System.out.println(e.toString());
		}
		finally{
			//分別關閉輸入輸出流
			try
			{
				if(br!=null)
					br.close();
			}
			catch (IOException e)
			{
				System.out.println(e.toString());
			}
			finally{
				try
				{
					if(bw!=null)
						bw.close();
				}
				catch (IOException e)
				{
					System.out.println(e.toString());
				}
			}
		}
	}
}
</span>

八、裝飾類

1、定義:裝飾類其實和繼承的複寫差不多,但是有一點是裝飾並不是繼承這個類複寫該類方法,而是繼承這個類的父類,把需要增強功能的類作爲實際參數傳遞給裝飾類的構造函數

如:

使用繼承完成的功能增強後體系表現

 MyReader//專門用於讀取數據的類。
    |--MyTextReader(需要增強的類)
        |--MyBufferTextReader(子類繼承父類,增強功能)
    |--MyMediaReader(需要增強的類)
        |--MyBufferMediaReader(子類繼承父類,增強功能)
    |--MyDataReader(需要增強的類)
        |--MyBufferDataReader(子類繼承父類,增強功能)

發現:當超類下面有多個子類時,而子類複寫父類方法後共性方法還需要增強時,可以在定義一個裝飾類繼承超類,把子類傳入,挺高功能增強,通過多態的形式。可以提高擴展性。

如:
MyReader//專門用於讀取數據的類。
    |--MyTextReader(需要增強的類)
    |--MyMediaReader(需要增強的類)
    |--MyDataReader(需要增強的類)
    |--MyBufferReader(用於增強子類)

以前是通過繼承將每一個子類都具備緩衝功能。
那麼繼承體系會複雜,並不利於擴展。

現在優化思想。單獨描述一下緩衝內容。
將需要被緩衝的對象。傳遞進來。也就是,誰需要被緩衝,誰就作爲參數傳遞給緩衝區。
這樣繼承體系就變得很簡單。優化了體系結構。

裝飾模式比繼承要靈活。避免了繼承體系臃腫。
而且降低了類於類之間的關係。

裝飾類因爲增強已有對象,具備的功能和已有的是相同的,只不過提供了更強功能。
所以裝飾類和被裝飾類通常是都屬於一個體系中的。

寫一個自己的裝飾類

<span style="font-size:14px;">//基礎Reader類   同時必須複寫抽象方法close和read(char[] cbuf, int off, int len)方法
class MyBufferedReader extends Reader
{
	
	private Reader r;
	MyBufferedReader(Reader r)
	{
		this.r = r;
	}

	//可以一次讀一行數據的方法。
	public String myReadLine()throws IOException
	{
		//定義一個臨時容器。原BufferReader封裝的是字符數組。
		//爲了演示方便。定義一個StringBuilder容器。因爲最終還是要將數據變成字符串。
		StringBuilder sb = new StringBuilder();
		int ch = 0;
		while((ch=r.read())!=-1)
		{
			if(ch=='\r')
				continue;
			if(ch=='\n')
				return sb.toString();
			else
				sb.append((char)ch);
		}

		if(sb.length()!=0)
			return sb.toString();
		return null;		
	}

	/*
	覆蓋Reader類中的抽象方法。

	*/
	public int read(char[] cbuf, int off, int len) throws IOException
	{
		return r.read(cbuf,off,len) ;
	}

	public void close()throws IOException
	{
		r.close();
	}
}</span>


九、.LineNumberReader

特有方法:獲取行號      getLineNumber()

                  設置行號      setLineNumber()

<span style="font-size:14px;">import java.io.*;

class LineNumberReaderDemo 
{
	public static void main(String[] args)throws IOException 
	{
		FileReader fr = new FileReader("PersonDemo.java");

		LineNumberReader lnr = new LineNumberReader(fr);

		String line = null;
               //設置行號
              //lnr.setLineNumber(100);
		while((line=lnr.readLine())!=null)
		{
			//給拷貝的文件添加行號
			System.out.println(lnr.getLineNumber()+":"+line);
		}

		lnr.close();
	}
}
</span>
運行結果:


十、寫一個自己的LineNumberReader

<span style="font-size:14px;">import java.io.*;
//繼承BufferedReader是爲了方便調用BufferedReader的readline方法
class MyLineNumberReader extends BufferedReader
{
	private int lineNumber;
	MyLineNumberReader(Reader r)
	{
		super(r);
	}
	//沒從硬盤上讀取一行數據就讓lineNumber+1
	public String myReadLine()throws IOException
	{

		lineNumber++;
		return super.readLine();
	}
	public void setLineNumber(int lineNumber)
	{
		this.lineNumber = lineNumber;
	}
	public int getLineNumber()
	{
		return lineNumber;
	}
}

class  MyLineNumberReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		FileReader fr = new FileReader("copyTextByBuf.java");

		MyLineNumberReader mylnr = new MyLineNumberReader(fr);

		String line = null;
		mylnr.setLineNumber(100);
		while((line=mylnr.myReadLine())!=null)
		{
			System.out.println(mylnr.getLineNumber()+"::"+line);
		}

		mylnr.myClose();
	}
}
</span>

字節流

一、概述

1、字節流和字符流的基本操作是相同的,但字節流還可以操作其他媒體文件。

2、由於媒體文件數據中都是以字節存儲的,所以,字節流對象可直接對媒體文件的數據寫入到文件中,而可以不用再進行刷流動作。

3、讀寫字節流:InputStream   輸入流(讀)

                             OutputStream  輸出流(寫)

4、常用方法:

InputStream:

1、read()        讀取一個字節,返回 0 255 範圍內的 int 字節值

2、read(byte[] b)   從輸入流中讀取一定數量的字節,並將其存儲在緩衝區數組 b

3、available()       可以獲取讀取文件中的所有字節數

4、close()                             關閉流,釋放資源  

OutputStream:

1、write(int b)      寫出一個字節

2、write(byte[])     寫出數組中的所有字節

3、writer(byte[],int strat,int end)   從指定位置寫出數組中的字節

4、close()    關閉流,釋放資源   

5、讀取文件

1、創建FileINputStream對象,指定一個文件,該文件必須存在,不存在則會拋出FileNotFoundException異常
2、使用read方法從文件中讀取一個字節,如果讀取到文件末尾則會返回-1
3、讀取完畢後,使用close方法釋放資源
<span style="font-size:14px;">import java.io.*;
class  FileInputStreamDemo
{
	public static void main(String[] args) 
	{
                //創建流對象,關聯file.txt文件
                FileInputStream fis = new FileInputStream("D:\\file.txt");
		int by = 0;
                //循環讀取
                while((by=fis.read())!=-1){
		System.out.println(char(by));
                fis.close();
        }
}
</span>
爲什麼read方法返回的是int而不是byte類型呢?
原因:因爲如果讀取的是視頻文件或音頻文件或圖片文件等,在讀取過程中很有可能會遇到11111111,也就是byte類型的-1,那麼遇到-1程序就會停止讀取,會漏掉文件,爲了防止這種情況出現,把byte類型提升爲int類型,在這個字節前補上24個0,把遇到的-1變成255,這樣可以保證將整個文件讀完
6、寫出文件
1、創建FileOutputStream對象,指定一個文件,如果文件不存在,會自動創建該文件,如果需要在該文件後面追加,則需要在創建FileOutputStream對象時傳入true
2、使用write方法進行寫入
3、使用close方法關閉資源
<span style="font-size:14px;">import java.io.*;
class  FileOutputStreamDemo
{
	public static void main(String[] args) 
	{
		//創建輸出流對象,與文件關聯
		FileOutputStream fos = new FileOutputStream("D:\\file.txt");
		fos.write(100);    //寫出數據的時候會將前面24個0去掉,寫出的是一個字節
		fos.write(65);
		fos.write(97);
		fos.write("AABBBCCCDDDEEEFFFF".getBytes());  //寫出字符串時,需要轉換
                fos.close();     //關閉
        }
}</span>
7、拷貝文件
1、琢個字節拷貝
<span style="font-size:14px;">import java.io.*;
class  CopyMp3
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		FileInputStream fis =null;
		FileOutputStream fos =null;
		try
		{
			fis = new FileInputStream("D:\\KuGou\\陳奕迅 - 鬥戰神.mp3");
			fos = new FileOutputStream("D:\\KuGou\\陳奕迅 - 鬥戰神_copy.mp3");
			int by = 0;
			while((by=fis.read())!=-1){
				fos.write(by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件複製失敗");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件讀取流關閉失敗");
			}
			finally{
				try
				{
					if(fos!=null)
						fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件輸出流關閉失敗");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷貝使用的毫秒數:"+(start-end));
	}
}
</span>
2、自定義數組拷貝
<span style="font-size:14px;">import java.io.*;
class  BytesCopyMp3
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		FileInputStream fis =null;
		FileOutputStream fos =null;
		try
		{
			fis = new FileInputStream("D:\\KuGou\\陳奕迅 - 鬥戰神.mp3");
			fos = new FileOutputStream("D:\\KuGou\\陳奕迅 - 鬥戰神_copy.mp3");
			byte [] bys = new byte[1024];
			int by = 0;
			while((by=fis.read(bys))!=-1){
				fos.write(bys,0,by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件複製失敗");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件讀取流關閉失敗");
			}
			finally{
				try
				{
					if(fos!=null)
						fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件輸出流關閉失敗");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷貝使用的毫秒數:"+(end-start));
	}
}
</span>


3、定義一個和文件字符數相同的數組拷貝import java.io.*;
<span style="font-size:14px;">import java.io.*;
class  CopyMp3_available
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		FileInputStream fis =null;
		FileOutputStream fos =null;
		try
		{
			fis = new FileInputStream("D:\\KuGou\\陳奕迅 - 鬥戰神.mp3");
			fos = new FileOutputStream("D:\\KuGou\\陳奕迅 - 鬥戰神_copy.mp3");
			//因爲數組的長度和文件字節長度是一樣的,所以不需要使用循環了
			byte [] bys = new byte[fis.available()];
			fis.read(bys);
			fos.write(bys);
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件複製失敗");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件讀取流關閉失敗");
			}
			finally{
				try
				{
					if(fos!=null)
						fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件輸出流關閉失敗");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷貝使用的毫秒數:"+(end-start));
	}
}
</span>
4、帶緩衝流拷貝
<span style="font-size:14px;">import java.io.*;
class  CopyMp3Buffered
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		FileInputStream fis =null;
		FileOutputStream fos =null;
		try
		{
			fis = new FileInputStream("D:\\KuGou\\陳奕迅 - 鬥戰神.mp3");
			fos = new FileOutputStream("D:\\KuGou\\陳奕迅 - 鬥戰神_copy.mp3");
			//創建緩衝區
			BufferedInputStream bis = new BufferedInputStream(fis);
			BufferedOutputStream bos = new BufferedOutputStream(fos);
			int by = 0;
			while((by=bis.read())!=-1){
				bos.write(by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件複製失敗");
		}
		finally{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件讀取流關閉失敗");
			}
			finally{
				try
				{
					if(fos!=null)
						fos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件輸出流關閉失敗");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷貝使用的毫秒數:"+(end-start));
	}
}
</span>

8、自定義字節流緩衝區

BufferedInputStream的特點:

BufferedInputStream內置了一個緩衝區(數組),從BufferedInputStream中讀取一個字節時,BufferedInputStream會一次性從文件中讀取8192個,存在緩衝區中,返回給程序一個,當程序再次讀取時,就不用找文件了,直接從緩衝區讀取,知道緩衝區中的所有的都被使用過,才重新從文件中讀取8192個

BufferedOutputStream的特點:

BufferedOutputStream也內置了一個緩衝區(數組),當程序向流中寫出字節時,不會直接寫到文件,先寫到緩衝區,知道緩衝區寫滿,BufferedOutputStream纔會把緩衝區中的數據一次性寫到文件中

自定義BufferedOutputStream

/*自定義BufferedInputStream緩衝區
原理:BufferedInputStream內置了一個緩衝區(數組),從BufferedInputStream中
讀取一個字節時,BufferedInputStream會一次性從文件中讀取8192個,存在緩衝區
中,返回給程序一個,當程序再次讀取時,就不用找文件了,直接從緩衝區讀取,
直到緩衝區中的所有的都被使用過,才重新從文件中讀取8192個


	思路:1、既然是緩衝區,肯定要有底層讀取流,所以要有FileInputStream
	      2、緩衝區肯定要有緩存的地方,即數組   類型byte
		  3、由於已經讀取了一批數據並存入了數組中,所以需要把數組中的數據讀取完畢後,
		     才能從文件中再次讀取一批文件存入數組,所以要先把數組中的數據寫出去
		  4、要想把數組中的數據清空,只能是當調用緩衝區的read方法時,利用指針來獲取

*/
import java.io.*;
class MyBufferedInputStream
{
	private byte [] bys = new byte[1024*4];   //緩存區
	private int count = 0;   //用於記錄數組中數據的長度
	private int pos=0;    //指針
	private FileInputStream fis;
	public MyBufferedInputStream(FileInputStream fis){
		this.fis = fis;
	}
	public int myRead()throws IOException{
		if(count==0)	//如果count爲0,從文件中讀取一批數據存入數組中
		{
			count = fis.read(bys);    //給count賦值
			if((count==-1))   //如果成立表示沒有文件中沒有數據,直接返回-1
				return -1;
			pos = 0;		//數組中的元素被讀完,把pos初始化
			byte b = bys[pos];  //用b記錄bys數組中的pos位置上的元素
			count--;      //每調用一次myRead方法,條件成立,讓count-1,當count等於0時,再從文件中讀取一批數據
			pos++;       //每調用一次myRead方法,條件成立,讓pos+1,知道把bys數組中的元素讀完
			return b&255;   //因爲read方法返回的是int類型,而b是byte類型,類型提升
			                //由原先的8位變成了32位,而b的二進制有可能是11111111的情況
							//也就是byte類型的-1,那麼遇到-1程序就會停止讀取,會漏掉文件,
							//爲了防止這種情況出現,把byte類型提升爲int類型,需要在這個字節
							//前補上24個0,把遇到的-1變成255,所以需要&255

		}
		else if(count>0)
		{
			byte b = bys[pos];
			count--;
			pos++;
			return b&0xff;
		}
		return -1;
	}
	public void myClose()throws IOException{
		fis.close();
	}
}
class  MyBufferedInputStreamDemo
{
	public static void main(String[] args) 
	{
		long start = System.currentTimeMillis();
		MyBufferedInputStream bis =null;
		BufferedOutputStream bos =null;
		try
		{
			//創建緩衝區
			bis = new MyBufferedInputStream(new FileInputStream("D:\\KuGou\\陳奕迅 - 鬥戰神.mp3"));
			bos = new BufferedOutputStream(new FileOutputStream("D:\\KuGou\\陳奕迅 - 鬥戰神_copy.mp3"));
			int by = 0;
			while((by=bis.myRead())!=-1){
				bos.write(by);
			}
		}
		catch (IOException e)
		{
			throw new RuntimeException("文件複製失敗");
		}
		finally{
			try
			{
				if(bis!=null)
					bis.myClose();
			}
			catch (IOException e)
			{
				throw new RuntimeException("文件讀取流關閉失敗");
			}
			finally{
				try
				{
					if(bos!=null)
						bos.close();
				}
				catch (IOException e)
				{
					throw new RuntimeException("文件輸出流關閉失敗");
				}
			}
		}
		long end = System.currentTimeMillis();
		System.out.println("拷貝使用的毫秒數:"+(end-start));
	}
}

流操作規律

一、鍵盤錄入

1、標準輸入輸出流

        System.in:對應的標準輸入設備,鍵盤。

        Ssytem.out:對應的是標準的輸出設備,控制檯。

        System.in的返回值類型是InputStream.

        System.out的返回值類型是PrintStream,它是OutputStream的子類FilterOutputStream的子類。

2、整行錄入

       當使用輸入流進行鍵盤錄入時,只能一個字節一個字節進行錄入。爲了提高效率,可以自定義一個數組將一行字節進行存儲。當一行錄入完畢,再將一行數據進行顯示。這種正行錄入的方式,和字符流讀一行數據的原理是一樣的。也就是readLine方法。

      那麼能不能直接使用readLine方法來完成鍵盤錄入的一行數據的讀取呢?readLine方法是字符流BufferedReader類中方法。而鍵盤錄入的read方法是字節流InputStream的方法。

      那麼能不能將字節流轉成字符流再使用字符流緩衝區的readLine方法呢?這就需要用到轉換流了。

3、轉換流

3.1 轉換流的由來:

       a、字符流與字節流之間的橋樑

       b、方便了字符流與字節流之間的操作

轉換流的應用:

      字節流中的數據都是字符時,轉成字符流操作更高效。

3.2   InputStreamReader將字節流通向字符流

       a、獲取鍵盤錄入對象。

              InputStream in=System.in;

       b、將字節流對象轉成字符流對象,使用轉換流。

              InputStreamReade  risr = new InputStreamReader(in);

       c、爲了提高效率,將字符串進行緩衝區技術高效操作。使用BufferedReader

              BufferedReaderbr=new BufferedReader(isr);

       //鍵盤錄入最常見寫法

              BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));

3.3   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();  
    }  
}  

二、流操作規律

1

        源:鍵盤錄入。

        目的:控制檯。

2、需求:想把鍵盤錄入的數據存儲到一個文件中。

        源:鍵盤

        目的:文件。

        使用字節流通向字符流的轉換流(橋樑):InputStreamReader

3、需求:想要將一個文件的數據打印在控制檯上。

        源:文件

        目的:控制檯

        使用字符流通向字節流的轉換流(橋樑):OutputStreamWriter

4、流操作的基本規律:

        最痛苦的就是流對象有很多,不知道該用哪一個。

通過三個明確來完成:

4.1   明確源和目的。

       源:輸入流。InputStream  Reader

       目的:輸出流。OutputStream  Writer

4.2   操作的數據是否是純文本。

       是:字符流

       否:字節流

4.3   當體系明確後,再明確要使用哪個具體的對象。通過設備來進行區分:

       源設備:內存,硬盤,鍵盤

       目的設備:內存,硬盤,控制檯

5、規律體現

5.1 將一個文本文件中數據存儲到另一個文件中。複製文件。

        1)源:因爲是源,所以使用讀取流:InputStreamReader

             明確體系:是否操作文本:是,Reader

              明確設備:明確要使用該體系中的哪個對象:硬盤上的一個文件。Reader體系中可以操作文件的對象是FileReader

              是否需要提高效率:是,加入Reader體系中緩衝區 BufferedReader.

              FileReader fr = new FileReader("a.txt");

              BufferedReader bufr = new BufferedReader(fr);

        2)目的:輸出流:OutputStreamWriter

             明確體系:是否操作文本:是,Writer

             明確設備:明確要使用該體系中的哪個對象:硬盤上的一個文件。Writer體系中可以操作文件的對象FileWriter

             是否需要提高效率:是,加入Writer體系中緩衝區 BufferedWriter

              FileWriter fw = new FileWriter("b.txt");

              BufferedWriter bufw = new BufferedWriter(fw);

練習:將一個圖片文件中數據存儲到另一個文件中。複製文件。要按照以上格式自己完成三個明確。

        1)源:輸入流,InputStreamReader

            是否是文本?否,InputStream

            源設備:硬盤上的一個文件。InputSteam體系中可以操作文件的對象是FileInputSteam

            是否需要提供效率:是,BufferedInputStream

              BufferedInputSteambis=newBufferedInputStream(newFileInputStream("c:/users/asus/desktop/1.jpg"));

        2)目的:輸出流,OutputStreamWriter

             是否是文本?否,OutputStream

             源設備:硬盤上的文件,FileOutputStream

             是否需要提高效率:是,加入BufferedOutputStream

               BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("c:/users/asus/desktop/2.jpg"));

5.2 需求:將鍵盤錄入的數據保存到一個文件中。

        1)源:InputStreamReader

             是不是純文本?是,Reader

              設備:鍵盤。對應的對象是System.in。——爲了操作鍵盤的文本數據方便。轉成字符流按照字符串操作是最方便的。所以既然明確了Reader,那麼就將System.in轉換成Reader。用Reader體系中轉換流,InputStreamReader

              InputStreamReaderisr = new InputStreamReader(System.in);

            需要提高效率嗎?需要,BufferedReader

             BufferedReaderbufr = new BufferedReader(isr);

       2)目的:OutputStream  Writer

            是否是存文本?是!Writer

            設備:硬盤。一個文件。使用 FileWriter

             FileWriter fw = new FileWriter("c.txt");

           需要提高效率嗎?需要。

            BufferedWriter bufw = new BufferedWriter(fw);

5.3   擴展:想要把錄入的數據按照指定的編碼表(UTF-8)(默認編碼表是GBK),將數據存到文件中。

        目的:OutputStream  Writer

       是否是存文本?是!Writer

        設備:硬盤上的一個文件。使用 FileWriter。——但是FileWriter是使用的默認編碼表:GBK。而存儲時,需要加入指定編碼表utf-8。而指定的編碼表只有轉換流可以指定。所以要使用的對象是OutputStreamWriter

        該轉換流對象要接收一個字節輸出流,而且還可以操作的文件的字節輸出流:FileOutputStream

        OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");

        需要高效嗎?需要,BufferedWriter

        BufferedWriter bufw = new BufferedWriter(osw);

記住:

       轉換流什麼使用?

       字符和字節之間的橋樑。通常,涉及到字符編碼轉換時,需要用到轉換流。

練習:將一個文本數據打印在控制檯上。要按照以上格式自己完成三個明確。

        1)源:InputStreamReader

            是文本?是:Reader

            設備:硬盤。上的文件:FileReader

            是否需要提高效率?是:BufferedReader

             BufferedReader br=new BufferedReader(newFileReader("1.txt"));

       2)目的:OutputStream Writer

            是文本?是:Writer

            設備:控制檯。對應對象System.out。由於System.out對應的是字節流,所以利用OutputSteamWriter轉換流

            是否提高效率?是:BufferedWriter

              BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out));

示例:

  /* 
    2、需求:想把鍵盤錄入的數據存儲到一個文件中。 
    源:鍵盤 
    目的:文件 
    把錄入的數據按照指定的編碼表(UTF-8),將數據存到文件中。 
     
     
    3、需求:想要將一個文件的數據打印在控制檯上。 
    源:文件 
    目的:控制檯 
     
     
    */  
    import java.io.*;  
    class  TransStreamDemo2  
    {  
        public static void main(String[] args)throws IOException  
        {  
              
            //鍵盤錄入  
            BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  
      
            //存入文件中,按照指定的編碼表(UTF-8)   
            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("readin1.txt"),"UTF-8"));  
      
            String line=null;  
            while((line=br.readLine())!=null)  
            {  
                if("over".equals(line))  
                    break;  
                bw.write(line);  
                bw.newLine();  
                bw.flush();  
            }  
              
            /* 
            //錄入文件數據 
            BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("TransStreamDemo2.java"))); 
     
            //顯示在控制檯 
            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out)); 
     
            String line=null; 
            while((line=br.readLine())!=null) 
            { 
                if("over".equals(line)) 
                    break; 
                bw.write(line); 
                bw.newLine(); 
                bw.flush(); 
            } 
            */  
        }  
    }  


小知識:

1、異常的日誌信息:

        當程序在執行的時候,出現的問題是不希望直接打印給用戶看的,是需要作爲文件存儲起來,方便程序員查看,並及時調整的。

示例:

    import java.io.*;  
    import java.text.*;  
    import java.util.*;  
    class  ExceptionInfo  
    {  
        public static void main(String[] args)   
        {  
            try  
            {  
                int[] arr =new int[2];  
                System.out.println(arr[3]);  
      
            }  
            catch (Exception e)  
            {  
                try  
                {  
                    Date d=new Date();//創建時間對象  
                //時間模塊格式對象  
                SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");  
                    String s=sdf.format(d);  
      
                    PrintStream ps=new PrintStream("info.log");//打印流對象  
                    System.setOut(ps);//修改輸出流設備  
                    ps.println(s);//輸出時間  
                      
                }  
                catch (IOException ex)  
                {  
                    throw new RuntimeException("文件創建失敗");  
                }  
                e.printStackTrace(System.out);//將異常信息輸出指定輸出流  
            }  
        }  
    }  


2、系統屬性信息存入文本

        獲取系統信息:

                 Properties getProperties()

        將信息輸出到指定輸出流中

                 void list(PrintStream out)

        將輸出流中數據存入指定文件中

                  new PrintStream("systeminfo.txt")

示例:

    //將系統屬性信息保存到指定文本中  
    import java.util.*;    
    import java.io.*;    
      
    class SystemInfo     
    {    
       public static void main(String[] args)     
       {     
           PrintStream ps = null;    
           try    
           {    
              //獲取系統信息:    
              Properties pop = System.getProperties();    
              //創建輸出流對象,將輸出流中數據存入指定文件中    
              ps = new PrintStream("systeminfo.txt");    
              //將屬性列表輸出到指定的輸出流    
              pop.list(ps);    
           }    
           catch (Exception e)    
           {    
                throw new RuntimeException("獲取系統信息失敗。");    
           }    
        }    
    } 


3、通過System類的setInsetOut方法可以對默認設備進行改變

        System.setIn(new InputStream(“1.txt”));//將源改成文件1.txt

        System.setOut(new OutputStream(“2.txt”));//將目的改成文件2.txt

流的基本應用小結:

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

字符流繼承體系簡圖

字節流繼承體系簡圖




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