Java基礎知識——IO流(一)


IO流(Input Output)

一、     概述

(一)IO流的概述

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

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

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

4. 流按操作數據分爲兩種:字節流和字符流

5. 流按方向分爲:輸入流和輸出流

 

(二)IO流常用基類

1.      字節流的抽象基類:InputStream、OutputStream

2.      字符流的抽象基類:Reader、Writer

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

 

字節流與和字符流的使用非常相似,兩者除了操作代碼上的不同之外,是否還有其他的不同呢?

實際上字節流在操作時本身不會用到緩衝區(內存),是文件本身直接操作的,而字符流在操作時使用了緩衝區,通過緩衝區再操作文件,如圖所示。

(三)字符流

在程序中一個字符等於兩個字節,Java爲我們提供了Reader和Writer兩個專門操作字符流的類

 

---------------------------------------------------------------------------------------

字符輸出流:Writer


Writer是一個字符流,它是一個抽象類,所以要使用它,也必須通過其子類來實例化它後才能使用它。
Writer類的常用方法
方法名稱
描述
public abstract void close() throws IOException
關閉輸出流
public void write(String str) throws IOException
將字符串輸出
public void write(char cbuf) throws IOException
將字符數組輸出
public abstract void flush() throws IOException
強制性清空緩存

----------------------------------------------------------------------------------------

字符輸入流:Reader

 

Reader本身也是一個抽象類,同樣,如果使用它,我們需要通過其子類來實例化它纔可以使用它。

Reader類的常用方法

方法名稱

描述

public abstract voidclose() throws IOException

 

public int read()throws IOException

 

 

public int read(char[] cbuf,int offset,int length)throwsIOException

 將字符讀入數組中的某一部分

----------------------------------------------------------------------------------------------

事例:

 

既然IO流是用於操作數據的,那麼數據的最常見體現形式是:文件。

那麼先以操作文件爲主來演示。

 

需求:在硬盤上,創建一個文件並寫入一些文字數據。找到一個專門用於操作文件的Writer子類對象:FileWriter。

import java.io.*;
class FileWriterDemo
{
       publicstatic void main(String[] args)throws IOException
       {
              //創建一個FileWriter對象。該對象一被初始化就必須要明確被操作的文件。
              //而且該文件會被創建到指定目錄下。如果該目錄下已有同名文件,將被覆蓋。
              //其實該步就是在明確數據要存放的目的地。
              FileWriterfw = newFileWriter("demo.txt");
 
              //調用write方法,將字符串寫入到流中。
              fw.write("abcde");
 
              //刷新流對象中的緩衝中的數據。
              //將數據刷到目的地中。
              //fw.flush();
 
 
              //關閉流資源,但是關閉之前會刷新一次內部的緩衝中的數據。
              //將數據刷到目的地中。
              //和flush區別:flush刷新後,流可以繼續使用,close刷新後,會將流關閉。
              fw.close();
       }
}
 

============================================================

 

二、IO的異常處理方式:

import java.io.*;
 
class FileWriterDemo2
{
       publicstatic void main(String[] args)
       {
              FileWriterfw = null;
              try
              {
                     fw= newFileWriter("demo.txt");
                    fw.write("abcdefg");
 
              }
              catch(IOException e)
              {
                    System.out.println("catch:"+e.toString());
              }
              finally
              {
                     try
                     {
                            if(fw!=null)
                                  fw.close();                         
                     }
                     catch(IOException e)
                     {
                           System.out.println(e.toString());
                     }
                   
              }          
 
       }
}
 


演示對已有文件的數據續寫。

 

import java.io.*;
class FileWriterDemo3
{
       publicstatic void main(String[] args)throws IOException
       {
 
              //傳遞一個true參數,代表不覆蓋已有的文件。並在已有文件的末尾處進行數據續寫。
              FileWriterfw = newFileWriter("demo.txt",true);
 
             fw.write("nihao\r\nxiexie");
 
              fw.close();
       }
}

=================================================================

 

 

三、文本的讀取——字符流讀取的兩種方式

 

(一)第一種方式:通過字符讀取

 

import java.io.*;
 
class FileReaderDemo
{
       publicstatic void main(String[] args)throws IOException
       {
              //創建一個文件讀取流對象,和指定名稱的文件相關聯。
              //要保證該文件是已經存在的,如果不存在,會發生異常FileNotFoundException
              FileReaderfr = newFileReader("demo.txt");
 
              //調用讀取流對象的read方法。
              //read():一次讀一個字符。而且會自動往下讀。
            
              intch = 0;
 
              while((ch=fr.read())!=-1)
              {
                     System.out.println(
              }
              fr.close();
 
       }
}

------------------------------------------------------------------------------------------

(二)第二種讀取方式:通過字符數組來讀取

 

import java.io.*;
 
class FileReaderDemo2
{
       publicstatic void main(String[] args)throws IOException
       {
              FileReaderfr = newFileReader("demo.txt");
            
              //定義一個字符數組。用於存儲讀到字符。
              //該read(char[])返回的是讀到字符個數。
              char[]buf = new char[1024];
 
              intnum = 0;
              while((num=fr.read(buf))!=-1)
              {
                    System.out.println(newString(buf,0,num));
              }
            
              fr.close();
       }
}
 

-----------------------------------------------------------------------------------------------------

 

(三)小練習:拷貝文本文件

 

//將C盤一個文本文件複製到D盤。
 
/*
複製的原理:
其實就是將C盤下的文件數據存儲到D盤的一個文件中。
 
步驟:
1,在D盤創建一個文件。用於存儲C盤文件中的數據。
2,定義讀取流和C盤文件關聯。
3,通過不斷的讀寫完成數據存儲。
4,關閉資源。
*/
 
import java.io.*;
 
class CopyText
{
       publicstatic void main(String[] args)throws IOException
       {
              copy_2();
       }
 
/*------------------利用緩衝區拷貝文件--------------------------*/
       publicstatic void copy_2()
       {
              FileWriterfw = null;
              FileReaderfr = null;
              try
              {
                     fw= newFileWriter("SystemDemo_copy.txt");//寫入流,目的
                     fr= newFileReader("SystemDemo.java");//讀取流,源
 
                     char[]buf = newchar[1024]; //定義1k的緩衝區
 
                     intlen = 0;
                     while((len=fr.read(buf))!=-1)//每次讀1k大小後寫入
                     {
                           fw.write(buf,0,len);//定義len是爲了確定最後一次不滿足1k時的長度
                     }
              }
              catch(IOException e)
              {
                     throw new RuntimeException("讀寫失敗");
 
              }
              finally  //close()語句是一定要執行的語句,放在finally中
              {
                     if(fr!=null)
                            try
                            {
                                  fr.close();//但是close()語句也會拋出異常,所以在此處理
                            }
                            catch(IOExceptione)
                            {
                            }
                     if(fw!=null)
                            try
                            {
                                   fw.close();
                            }
                            catch(IOExceptione)
                            {
                            }
              }
       }
/*--------------------------------一次一個字節的拷貝文件--------------------------*/
       //從C盤讀一個字符,就往D盤寫一個字符。
       publicstatic void copy_1()throwsIOException
       {
              //創建目的地。
              FileWriterfw = newFileWriter("RuntimeDemo_copy.txt");
 
              //與已有文件關聯。
              FileReaderfr = newFileReader("RuntimeDemo.java");
 
              intch = 0;
 
              while((ch=fr.read())!=-1)
              {
                     fw.write(ch);
              }
            
              fw.close();
              fr.close();
 
       }
 
}
 





==============================================================

四、字符流的緩衝區

(一)簡要介紹

1.      緩衝區的出現提高了對字符的讀寫效率

2.      對應類:BufferedReader  BufferedWriter

3.      緩衝區要結合流纔可以使用

4.      在流的基礎上對流的功能進行了增強(裝飾設計模式)

-----------------------------------------------------------------------------------------------------------

(二)BufferedWriter

 

 

緩衝區的出現是爲了提高流的操作效率而出現的。

 

所以在創建緩衝區之前,必須要先有流對象。

 

該緩衝區中提供了一個跨平臺的換行符。

newLine();

import java.io.*;
 
classBufferedWriterDemo
{
       publicstatic void main(String[] args)throws IOException
       {
              //創建一個字符寫入流對象。
              FileWriterfw = newFileWriter("buf.txt");
              //爲了提高字符寫入流效率。加入了緩衝技術。
              //只要將需要被提高效率的流對象作爲參數傳遞給緩衝區的構造函數即可。
              BufferedWriterbufw = newBufferedWriter(fw);
 
              for(intx=1; x<5; x++)
              {
                     bufw.write("abcd"+x);
                     bufw.newLine();
                     bufw.flush();
              }
 
              //記住,只要用到緩衝區,就要記得刷新。
              //bufw.flush();
 
              //其實關閉緩衝區,就是在關閉緩衝區中的流對象。
              bufw.close();
 
       }
}

---------------------------------------------------------------------------------------------------------

(三)BufferWriter

 

該緩衝區提供了一個一次讀一行的方法 readLine,方便於對文本數據的獲取。

當返回null時,表示讀到文件末尾。

 

readLine方法返回的時候只返回回車符之前的數據內容。並不返回回車符。

 

import java.io.*;
 
classBufferedReaderDemo
{
       publicstatic void main(String[] args)throws IOException
       {
              //創建一個讀取流對象和文件相關聯。
              FileReaderfr = newFileReader("buf.txt");
 
              //爲了提高效率。加入緩衝技術。將字符讀取流對象作爲參數傳遞給緩衝對象的構造函數。
              BufferedReaderbufr = newBufferedReader(fr);
            
              Stringline = null;
 
             while((line=bufr.readLine())!=null)
              {
                     System.out.print(line);
              }
 
              bufr.close();
       }
 
}

--------------------------------------------------------------------------------------------------------------------

 

(四)小練習一

通過緩衝區複製文本文件

 

import java.io.*;
 
class CopyTextByBuf
{
       publicstatic void main(String[] args)
       {
              BufferedReaderbufr = null;
              BufferedWriterbufw = null;
 
              try
              {
                     bufr= new BufferedReader(newFileReader("BufferedWriterDemo.java"));
                     bufw= newBufferedWriter(new FileWriter("bufWriter_Copy.txt"));
 
                     Stringline = null;
 
                    while((line=bufr.readLine())!=null)
                     {
                            bufw.write(line);
                            bufw.newLine();
                            bufw.flush();
 
                     }
              }
              catch(IOException e)
              {
                     thrownew RuntimeException("讀寫失敗");
              }
              finally
              {
                     try
                     {
                            if(bufr!=null)
                                  bufr.close();
                     }
                     catch(IOException e)
                     {
                            thrownewRuntimeException("讀取關閉失敗");
                     }
                     try
                     {
                            if(bufw!=null)
                                   bufw.close();
                     }
                     catch(IOException e)
                     {
                            thrownewRuntimeException("寫入關閉失敗");
                     }
              }
       }
}


 

--------------------------------------------------------------------------------------------------------------------------

(五)小練習二

自定義一個類中包含一個功能和readLine一致的方法。

來模擬一下BufferedReader

 

import java.io.*;
classMyBufferedReader extends Reader//繼承Reader類,父類方法直接用
{
      
       privateReader r;
       MyBufferedReader(Readerr)
       {
              this.r= r;
       }
 
       //可以一次讀一行數據的方法。
       publicString myReadLine()throwsIOException
       {
              //定義一個臨時容器。原BufferReader封裝的是字符數組。
              //定義一個StringBuilder容器。因爲最終還是要將數據變成字符串。
              StringBuildersb = newStringBuilder();
              intch = 0;
              while((ch=r.read())!=-1)
              {
                     if(ch=='\r')
                            continue;
                     if(ch=='\n')
                            returnsb.toString(); //讀到”\r\n”換行符後一次將StringBuilder中數據寫出
                     else
                           sb.append((char)ch);
              }
 
              if(sb.length()!=0)
                     returnsb.toString();
              returnnull;          
       }
 
       /*
       覆蓋Reader類中的抽象方法。
 
       */
       publicint read(char[] cbuf, int off, intlen) throws IOException
       {
              returnr.read(cbuf,off,len) ;
       }
 
       publicvoid close()throws IOException
       {
              r.close();
       }
       publicvoid myClose()throws IOException
       {
              r.close();
       }
}
 
 
classMyBufferedReaderDemo
{
       publicstatic void main(String[] args)throws IOException
       {
              FileReaderfr = newFileReader("buf.txt");
 
              MyBufferedReadermyBuf = newMyBufferedReader(fr);
 
              Stringline = null;
 
             while((line=myBuf.myReadLine())!=null)
              {
                     System.out.println(line);
              }
 
              myBuf.myClose();
       }
}


 



===================================================================

五、裝飾設計模式

(一)簡單介紹

當想要對已有的對象進行功能增強時,

可以定義類,將已有對象傳入,基於已有的功能,並提供加強功能。

那麼自定義的該類稱爲裝飾類。

 

裝飾類通常會通過構造方法接收被裝飾的對象。

並基於被裝飾的對象的功能,提供更強的功能。

 

------------------------------------------------------------------------------------------------

(二)舉例說明

class Person
{
       publicvoid chifan()
       {
              System.out.println("吃飯");
       }
}
 
class SuperPerson
{
       privatePerson p ;
       SuperPerson(Personp)
       {
              this.p= p;
       }
       publicvoid superChifan()
       {
              System.out.println("開胃酒");
              p.chifan();
              System.out.println("甜點");
              System.out.println("來一根");
       }
}
 
 
 
class PersonDemo
{
       publicstatic void main(String[] args)
       {
              Personp = new Person();
 
              //p.chifan();
 
              SuperPersonsp = newSuperPerson(p);
              sp.superChifan();
 
       }
}


(三)LineNumberReader——帶行號的Reader

 

import java.io.*;
 
classLineNumberReaderDemo
{
       publicstatic void main(String[]args)throws IOException
       {
              FileReaderfr = newFileReader("PersonDemo.java");
 
              LineNumberReaderlnr = newLineNumberReader(fr);
 
              Stringline = null;
              lnr.setLineNumber(100);//從100開始計算行號
             while((line=lnr.readLine())!=null)
              {
                     System.out.println(lnr.getLineNumber()+":"+line);
              }
 
              lnr.close();
       }
}

 

(四)練習:模擬一個帶行號的緩衝區對象

 

import java.io.*;
 
classMyLineNumberReader extends MyBufferedReader
{
       privateint lineNumber;//定義行號變量
 
       MyLineNumberReader(Readerr) //構造函數
       {
              super(r);
       }
 
       publicString myReadLine()throwsIOException
       {
 
              lineNumber++;
              returnsuper.myReadLine();
       }
       publicvoid setLineNumber(int lineNumber)//設置行號
       {
              this.lineNumber= lineNumber;
       }
       publicint getLineNumber()//獲得行號
       {
              returnlineNumber;
       }
}
 
classMyLineNumberReaderDemo
{
       publicstatic void main(String[] args)throws IOException
       {
              FileReaderfr = newFileReader("copyTextByBuf.java");
 
              MyLineNumberReadermylnr = newMyLineNumberReader(fr);
 
              Stringline = null;
              mylnr.setLineNumber(100);//從100開始輸出行號
 
              while((line=mylnr.myReadLine())!=null)
              {
                    System.out.println(mylnr.getLineNumber()+"::"+line);
              }
 
              mylnr.myClose();
       }
}
/*
classMyLineNumberReader
{
       privateReader r;
       privateint lineNumber;
       MyLineNumberReader(Readerr)
       {
              this.r= r;
       }
 
       publicString myReadLine()throwsIOException
       {
 
              lineNumber++;
              StringBuildersb = newStringBuilder();
 
              intch = 0;
 
              while((ch=r.read())!=-1)
              {
                     if(ch=='\r')
                            continue;
                     if(ch=='\n')
                           returnsb.toString();
                     else
                            sb.append((char)ch);
              }
              if(sb.length()!=0)
                     returnsb.toString();
              returnnull;
       }
       publicvoid setLineNumber(int lineNumber)
       {
              this.lineNumber= lineNumber;
       }
       publicint getLineNumber()
       {
              returnlineNumber;
       }
 
       publicvoid myClose()throws IOException
       {
              r.close();
       }
}
*/





===============================================================

六、字節流文件的讀寫操作

(一)字節流(bytestream)

不包含邊界數據的連續流

字節流是由字節組成的,字符流是由字符組成的. Java裏字符由兩個字節組成.字節流是最基本的,所有的InputStream和OutputStream的子類都是,主要用在處理二進制數據,它是按字節來處理的但實際中很多的數據是文本,又提出了字符流的概念,它是按虛擬機的encode來處理,也就是要進行字符集的轉化。在從字節流轉化爲字符流時,實際上就是byte[]轉化爲String時,public String(byte bytes[], String charsetName)有一個關鍵的參數字符集編碼,通常我們都省略了,那系統就用操作系統默認的lang

------------------------------------------------------------------------------------------------

 

 

(二)三種讀取方式

 

字符流:

FileReader

FileWriter。

 

BufferedReader

BufferedWriter

 

字節流:

InputStream OutputStream

 

需求,想要操作圖片數據。這時就要用到字節流。

複製一個圖片.


import java.io.*;
class FileStream
{
       publicstatic void main(String[] args)throws IOException
       {
              readFile_3();
       }
 
//方法三:定義一個剛剛好的緩衝區
       publicstatic void readFile_3()throwsIOException
       {
              FileInputStreamfis = newFileInputStream("fos.txt");
            
//            intnum = fis.available();
              byte[]buf = newbyte[fis.available()];//定義一個剛剛好的緩衝區。不用在循環了。
 
              fis.read(buf);
 
             System.out.println(newString(buf));
 
              fis.close();
       }
 
/*定義一個剛剛好的緩衝區也有弊端,那就是很佔內存,因爲必須一次性將整個文件讀入,而過大的文件不能用這樣的方法*/
//------------------------------------------------------------------------------------------
 
//方法二:定義一個1024整數倍的緩衝區
 
       publicstatic void readFile_2()throwsIOException
       {
              FileInputStreamfis = newFileInputStream("fos.txt");
 
              byte[]buf = new byte[1024];
              intlen = 0;
              while((len=fis.read(buf))!=-1)
              {
                    System.out.println(newString(buf,0,len));
              }
 
              fis.close();
            
       }
 
/*方法二更爲常用,可以根據文件大小動態調整緩衝區大小*/
 
//-----------------------------------------------------------------------------------
 
//方法一:一次一個字符的讀取
 
       publicstatic void readFile_1()throwsIOException
       {
              FileInputStreamfis = newFileInputStream("fos.txt");
 
              intch = 0;
 
              while((ch=fis.read())!=-1)
              {
                    System.out.println((char)ch);
              }
 
              fis.close();
       }
/*這種讀寫方式非常慢,不建議使用*/
 
//-----------------------------------------------------------------------------------
       publicstatic void writeFile()throwsIOException
       {
              FileOutputStreamfos = newFileOutputStream("fos.txt");
            
 
             fos.write("abcde".getBytes());
 
              fos.close();
}
}


===========================================================

(三)練習:複製一個圖片

 

思路:

1,用字節讀取流對象和圖片關聯。

2,用字節寫入流對象創建一個圖片文件。用於存儲獲取到的圖片數據。

3,通過循環讀寫,完成數據的存儲。

4,關閉資源。

 

 
import java.io.*;
class CopyPic
{
       publicstatic void main(String[] args)
       {
              FileOutputStreamfos = null;
              FileInputStreamfis = null;
              try
              {
                     fos= newFileOutputStream("c:\\2.bmp"); //目的
                     fis= newFileInputStream("c:\\1.bmp"); //源
 
                     byte[]buf = new byte[1024];
 
                     intlen = 0;
 
                    while((len=fis.read(buf))!=-1)
                     {
                           fos.write(buf,0,len);
                     }
              }
              catch(IOException e)
              {
                     thrownewRuntimeException("複製文件失敗");
              }
              finally
              {
                     try
                     {
                            if(fis!=null)
                                   fis.close();
                     }
                     catch(IOException e)
                     {
                            thrownewRuntimeException("讀取關閉失敗");
                     }
                     try
                     {
                            if(fos!=null)
                                   fos.close();
                     }
                     catch(IOException e)
                     {
                            thrownewRuntimeException("寫入關閉失敗");
                     }
              }
       }
}


------------------------------------------------------------------------------------------------

(四)通過緩衝區複製MP3

 

/*
演示mp3的複製。通過緩衝區。
BufferedOutputStream
BufferedInputStream
*/
import java.io.*;
class CopyMp3
{
       publicstatic void main(String[] args)throws IOException
       {
              longstart =System.currentTimeMillis();
              copy_2();
              longend =System.currentTimeMillis();
 
             System.out.println((end-start)+"毫秒");
       }
 
       publicstatic void copy_2()throwsIOException
       {
              BufferedInputStreambufis = newBufferedInputStream(new FileInputStream("c:\\9.mp3"));
              BufferedOutputStreambufos = newBufferedOutputStream(new FileOutputStream("c:\\3.mp3"));
            
              intby = 0;
 
              //System.out.println("第一個字節:"+bufis.myRead());
 
              while((by=bufis.myRead())!=-1)
              {
                     bufos.write(by);
              }
 
              bufos.close();
              bufis.myClose();
       }
 
       //通過字節流的緩衝區完成複製。
       publicstatic void copy_1()throwsIOException
       {
              BufferedInputStreambufis = newBufferedInputStream(new FileInputStream("c:\\0.mp3"));
              BufferedOutputStreambufos = newBufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
            
              intby = 0;
 
              while((by=bufis.read())!=-1)
              {
                     bufos.write(by);
              }
 
              bufos.close();
              bufis.close();
 
            
       }
}


--------------------------------------------------------------------------------------------------

(五)自定義字節流的緩衝區

 
import java.io.*;
 
classMyBufferedInputStream
{
       privateInputStream in;
 
       privatebyte[] buf = new byte[1024*4];
            
       privateint pos = 0,count = 0;
     
       MyBufferedInputStream(InputStreamin)
       {
              this.in= in;
       }
 
       //一次讀一個字節,從緩衝區(字節數組)獲取。
       publicint myRead()throws IOException
       {
              //通過in對象讀取硬盤上數據,並存儲buf中。
              if(count==0)
              {
                     count= in.read(buf);
                     if(count<0)
                            return-1;
                     pos= 0;
                     byteb = buf[pos];
 
                     count--;
                     pos++;
                     returnb&255;//取最低八位
              }
              elseif(count>0)
              {
                     byteb = buf[pos];
 
                     count--;
                     pos++;
                     returnb&0xff; //十六進制的255
              }
              return-1;
 
       }
       publicvoid myClose()throws IOException
       {
              in.close();
       }
}


11111111-111111110000000000101001001010100101010010101001010

 

byte: -1 --->  int : -1;

00000000 00000000 00000000 11111111  255

 

11111111 11111111 11111111 11111111

 

11111111 -->提升了一個int類型 那不還是-1嗎?是-1的原因是因爲在8個1前面補的是1導致的。

那麼我只要在前面補0,即可以保留原字節數據不變,又可以避免-1的出現。

怎麼補0呢?

 

 11111111 11111111 1111111111111111                       

&00000000 00000000 00000000 11111111

------------------------------------

 00000000 00000000 00000000 11111111

 

0000-0001

1111-1110

000000001

1111-1111 -1

 

結論:

字節流的讀一個字節的read方法爲什麼返回值類型不是byte,而是int。

因爲有可能會讀到連續8個二進制1的情況,8個二進制1對應的十進制是-1.

那麼就會數據還沒有讀完,就結束的情況。因爲我們判斷讀取結束是通過結尾標記-1來確定的。

所以,爲了避免這種情況將讀到的字節進行int類型的提升。

並在保留原字節數據的情況前面了補了24個0,變成了int類型的數值。

 

而在寫入數據時,只寫該int類型數據的最低8位。

 

 

(六)讀取鍵盤錄入

 

讀取鍵盤錄入。

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

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

 

 

需求:

通過鍵盤錄入數據。

當錄入一行數據後,就將該行數據進行打印。

如果錄入的數據是over,那麼停止錄入。

 

import java.io.*;
class ReadIn
{
       publicstatic void main(String[] args)throws IOException
       {
              InputStreamin = System.in;
              StringBuildersb = newStringBuilder();
 
              while(true)
              {
                     intch = in.read();
                     if(ch=='\r')
                            continue;
                     if(ch=='\n')
                     {
                            Strings = sb.toString();
                           if("over".equals(s))
                                   break;
                           System.out.println(s.toUpperCase());
                           sb.delete(0,sb.length());
                     }
                     else
                           sb.append((char)ch);
 
              }
       }
}


===================================================================

七、讀取轉換流和寫入轉換流

(一)概述

讀取轉換流InputStreamReader

InputStreamReader是字節流通向字符流的橋樑:它使用指定的 charset 讀取字節並將其解碼爲字符。它使用的字符集可以由名稱指定或顯式給定,或者可以接受平臺默認的字符集。

每次調用InputStreamReader 中的一個 read() 方法都會導致從底層輸入流讀取一個或多個字節。要啓用從字節到字符的有效轉換,可以提前從底層流讀取更多的字節,使其超過滿足當前讀取操作所需的字節。

爲了達到最高效率,可要考慮在BufferedReader 內包裝InputStreamReader。例如:

 BufferedReader in

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

----------------------------------------------------------------------------------------------------

 

寫入轉換流OutputStreamWriter

OutputStreamWriter 是字符流通向字節流的橋樑:可使用指定的 charset 將要寫入流中的字符編碼成字節。它使用的字符集可以由名稱指定或顯式給定,否則將接受平臺默認的字符集。

每次調用 write() 方法都會導致在給定字符(或字符集)上調用編碼轉換器。在寫入底層輸出流之前,得到的這些字節將在緩衝區中累積。可以指定此緩衝區的大小,不過,默認的緩衝區對多數用途來說已足夠大。注意,傳遞給 write() 方法的字符沒有緩衝。

爲了獲得最高效率,可考慮將OutputStreamWriter 包裝到 BufferedWriter 中,以避免頻繁調用轉換器。例如:

 Writer out

   = new BufferedWriter(new OutputStreamWriter(System.out));

 

--------------------------------------------------------------------------------------------------------------

(二)舉例說明

 

通過剛纔的鍵盤錄入一行數據並打印其大寫,發現其實就是讀一行數據的原理。

也就是readLine方法。

 

能不能直接使用readLine方法來完成鍵盤錄入的一行數據的讀取呢?

 

 

readLine方法是字符流BufferedReader類中的方法。

 

而鍵盤錄入的read方法是字節流InputStream的方法。

 

那麼能不能將字節流轉成字符流在使用字符流緩衝去的readLine方法呢?

 

import java.io.*;
 
class TransStreamDemo
{
       publicstatic void main(String[] args)throws IOException
       {
              //獲取鍵盤錄入對象。
              //InputStreamin = System.in;
 
              //將字節流對象轉成字符流對象,使用轉換流。InputStreamReader
              //InputStreamReaderisr = newInputStreamReader(in);
 
              //爲了提高效率,將字符串進行緩衝區技術高效操作。使用BufferedReader
 
              //BufferedReaderbufr = newBufferedReader(isr);
 
 
              //鍵盤的最常見寫法。
              BufferedReaderbufr =
                           newBufferedReader(new InputStreamReader(System.in));
 
            
//            OutputStreamout = System.out;
//            OutputStreamWriterosw = newOutputStreamWriter(out);
//            BufferedWriterbufw = newBufferedWriter(osw);
              BufferedWriterbufw = newBufferedWriter(new OutputStreamWriter(System.out));
 
              Stringline = null;
 
             while((line=bufr.readLine())!=null)
              {
                    if("over".equals(line))
                            break;
                    bufw.write(line.toUpperCase());
                     bufw.newLine();
                     bufw.flush();
              }
 
              bufr.close();
 
       }
}


=======================================================================

八、總結——流操作規律

 

 

(一)流操作的基本規律

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

 

通過三個明確來完成。

 

1,明確源和目的。

       源:輸入流。InputStream  Reader

       目的:輸出流。OutputStream  Writer。

 

2,操作的數據是否是純文本。

       是:字符流。

       不是:字節流。

 

3,當體系明確後,在明確要使用哪個具體的對象。

       通過設備來進行區分:

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

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

 

 

舉例1:

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

      

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

       是不是操作文本文件。

       是!這時就可以選擇Reader

       這樣體系就明確了。

 

       接下來明確要使用該體系中的哪個對象。

       明確設備:硬盤。上一個文件。

       Reader體系中可以操作文件的對象是 FileReader

 

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

 

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

      BufferedReaderbufr = new BufferedReader(fr);

 

 

目的:OutputStreamWriter

       是否是純文本。

       是!Writer。

       設備:硬盤,一個文件。

       Writer體系中可以操作文件的對象FileWriter。

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

      

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

      BufferedWriterbufw = new BufferedWriter(fw);

 

 

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

 

 

------------------------------------------------------------------

 

 

舉例2:

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

       這個需求中有源和目的都存在。

       那麼分別分析

      

源:InputStreamReader

       是不是純文本?是!Reader

      

       設備:鍵盤。對應的對象是System.in.

       不是選擇Reader嗎?System.in對應的不是字節流嗎?

       爲了操作鍵盤的文本數據方便。轉成字符流按照字符串操作是最方便的。

       所以既然明確了Reader,那麼就將System.in轉換成Reader。

       用了Reader體系中轉換流,InputStreamReader

 

      InputStreamReaderisr = new InputStreamReader(System.in);

 

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

      BufferedReaderbufr = new BufferedReader(isr);

 

      

目的:OutputStream Writer

       是否是存文本?是!Writer。

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

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

       需要提高效率嗎?需要。

      BufferedWriterbufw = new BufferedWriter(fw);

 

 

      ************************************************************

擴展      

想要把錄入的數據按照指定的編碼表(utf-8),將數據存到文件中。

      

       目的:OutputStream  Writer

       是否是存文本?是!Writer。

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

       但是FileWriter是使用的默認編碼表。GBK.

      

       但是存儲時,需要加入指定編碼表utf-8。而指定的編碼表只有轉換流可以指定。

       所以要使用的對象是OutputStreamWriter。

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

 

      OutputStreamWriterosw = newOutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");

 

       需要高效嗎?需要。

      BufferedWriterbufw = new BufferedWriter(osw);

 

       所以,記住。轉換流什麼使用。字符和字節之間的橋樑,通常,涉及到字符編碼轉換時,

       需要用到轉換流。

 

----------------------------------------------------------------------------------------------------------------------

(二)練習

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

 

import java.io.*;
 
classTransStreamDemo2
{
       publicstatic void main(String[] args)throws IOException
       {
             System.setIn(newFileInputStream("PersonDemo.java"));//改變標準輸入設備
 
             System.setOut(newPrintStream("zzz.txt"));//改變標準輸出設備
 
              //鍵盤的最常見寫法。一定要記住!!!
              BufferedReaderbufr =
                           newBufferedReader(new InputStreamReader(System.in));
 
            
              BufferedWriterbufw = newBufferedWriter(new OutputStreamWriter(System.out));
 
              Stringline = null;
 
             while((line=bufr.readLine())!=null)
              {
                    if("over".equals(line))
                            break;
                    bufw.write(line.toUpperCase());
                     bufw.newLine();
                     bufw.flush();
              }
 
              bufr.close();
 
       }
}


---------------------------------------------------------------------------------------------

(三)異常的日誌信息

 

import java.io.*;
import java.util.*;
import java.text.*;
class ExceptionInfo
{
       publicstatic void main(String[]args)throws IOException
       {
              try
              {
                     int[]arr = new int[2];
                    System.out.println(arr[3]);
              }
              catch(Exception e)
              {
                   
                     try
                     {
                            Dated = new Date();
                            SimpleDateFormatsdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            Strings =sdf.format(d);
 
                            PrintStreamps = newPrintStream("exeception.log");
                            ps.println(s);
                            System.setOut(ps);
 
                           
                     }
                     catch(IOException ex)
                     {
                            thrownewRuntimeException("日誌文件創建失敗");
                     }
                    e.printStackTrace(System.out);
              }
       }
}
//實際開發當中我們使用的是log4j

 

保存系統信息:

import java.util.*;
import java.io.*;
class SystemInfo
{
       publicstatic void main(String[] args)throws IOException
       {
              Propertiesprop =System.getProperties();
 
              //System.out.println(prop);
             prop.list(newPrintStream("sysinfo.txt"));
       }
}


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