1. 程式人生 > >第十章學習筆記

第十章學習筆記

一,教材學習內容(10輸入輸出流)

如圖1所示,程式在執行期間,可能需要從外部的儲存媒介或其他程式中讀入所需要的資料,這就需要使用輸入流。另一方面,程式在處理資料後,可能就需要將處理的結果寫入到永久的儲存媒介或傳送給其他的應用程式,這就需要使用輸出流。

特別注意:。流是一組有順序的,有起點和終點的位元組集合,是對資料傳輸的總稱或抽象即資料在兩裝置間的傳輸稱為流,流的本質是資料傳輸,根據資料傳輸特性將流抽象為各種類,方便更直觀的進行資料操作。

 

2,字元流和位元組流的區別

2.1 IO流的分類

根據處理資料型別的不同分為:字元流和位元組流

根據資料流向不同分為:輸入流和輸出流

2.2字元流和位元組流

字元流的由來:因為資料編碼的不同,而有了對字元進行高效操作的流物件。本質其實就是基於位元組流讀取時,去查了指定的碼錶。位元組流和字元流的區別:

(1)讀寫單位不同:位元組流以位元組(8位)為單位,字元流以字元為單位,根據碼錶對映字元,一次可能讀多個位元組。

(2)處理物件不同:位元組流能處理所有型別的資料(如圖片,AVI等),而字元流只能處理字元型別的資料。

(3)位元組流在操作的時候本身是不會用到緩衝區的,是檔案本身的直接操作的;而字元流在操作的時候下後是會用到緩衝區的,是通過緩衝區來操作檔案,我們將在下面驗證這一點。

結論:。優先選用位元組流首先因為硬碟上的所有檔案都是以位元組的形式進行傳輸或者儲存的,包括圖片等內容但是字元只是在記憶體中才會形成的,所以在開發中,位元組流使用廣泛。

 

 

3,檔案類:在整理IO流的知識前,我們需要先了解往往作為流起點或者終點的檔案類特別注意的是,檔案類的物件主要用來獲取檔案本身的一些資訊,例如檔案所在的目錄,檔案的長度和檔案讀寫許可權等,不涉及對檔案的讀寫操作

建立一個檔案物件的構造方法有三種:

File(String filename);//利用檔名
File(String directoryPath,String filename);//利用檔案的路徑和檔名
File(File dir,String filename);//利用檔案所在的目錄和檔名

注意使用第三種構造方法時,檔案被認為與當前應用程式在同一目錄中。

另外也注意到,三種構造方法都需要使用到的字串型引數 - 檔名。

 

4,目錄

4.1目錄的建立:檔案物件呼叫方法public boolean mkdir()方法建立一個目錄,如果建立成功返回true,反之返回false(如果該目錄已經存在)。

4.2列出目錄中的檔案如果檔案物件是一個目錄,那麼該物件呼叫以下方法列出該目錄下的檔案和子目錄。

·public String [] list()用字串形式返回目錄下的全部檔案。

·public File [] listFile()用File物件形式返回目錄下的全部檔案。

 

5,檔案位元組輸入流(InputStream的使用類的子類:的FileInputStream類(檔案位元組輸入流))

5.1 使用輸入流通常包括以下四個基本步驟:

·設定輸入流的源

·建立指向源的輸入流

·讓輸入流讀取源中的資料

·關閉輸入流

5.2 構造FileInputStream類的物件

FileInputStream(String name)
FileInputStream(File file)

第一個構造方法使用給定名、第二個構造方法使用File物件建立FileInputStream流,其中引數name和File指定的檔案成為輸入流的源

特別需要注意,FileInputStream輸入流指定的檔案可能不存在,出現I/O錯誤(Java使用IOException(IO異常)物件來表示這個出錯訊號)。因此程式必須在try-catch語句塊中的try部分建立輸入流(FileInputStream),在catch部分中檢測並處理這個異常。

注意:當使用父類InputStream類宣告FileInputStream物件時(即FileInputStream的上轉型物件),可以不用try-catch語句塊

5.3使用輸入流讀取位元組

檔案位元組流可以呼叫從父類繼承的read方法順序地讀取檔案,只要不關閉流,每次呼叫read方法就順序地讀取檔案中的其餘內容,直到檔案的末尾或檔案位元組輸入流被關閉。

位元組輸入流(InputStream)的read方法以位元組為單位讀取源中的資料

·int read() 輸入流呼叫該方法從源中讀取單個位元組的資料,該方法返回位元組值,如果未讀出就返回-1。

·int read(byte b[]) 輸入流呼叫該方法從源中讀取b.length個位元組的資料,該方法返回實際讀取的位元組數目,如果未讀出就返回-1。

·int read(byte b[],int off,int len) 輸入流呼叫該方法從源中讀取len個位元組的資料到位元組陣列b中,該方法返回實際讀取的位元組數目,如果未讀出就返回-1,引數off指定開始讀取資料的偏移量。

import java.io.*;
public class Example10_4 {
   public static void main(String args[]) {
      int n=-1;
      byte [] a=new byte[100];
      try{  File f=new File("Example10_4.java");
            InputStream in = new FileInputStream(f);
            while((n=in.read(a,0,100))!=-1) {
               String s=new String (a,0,n);
               System.out.print(s);
            }
            in.close();
      }
      catch(IOException e) {
           System.out.println("File read Error"+e);
      }
   }
}

 

需要特別注意的是,當把位元組轉化為字串時,要把實際讀入的位元組轉化為字串。

5.4 關閉流

使用close()方法顯式地關閉開啟的流,如果沒有關閉那些被開啟的流,那麼就不可能不允許另一個程式操作這些流所用的資源。

 

6、檔案位元組輸出流(使用OutputStream類的子類:FileOutputStream類(檔案位元組輸出流))

6.1 使用輸出流通常包括以下四個基本步驟:

·設定輸出流的目的地

·建立指向目的地的輸出流

·讓輸出流讀取目的地中的資料

·關閉輸出流

6.2 構造FileOutputStream類的物件

FileOutputStream(String name);
FileOutputStream(File file);

第一個構造方法使用給定名、第二個構造方法使用File物件建立FileOutputStream流,其中引數name和File指定的檔案成為目的地

特別需要注意,FileOutputStream輸出流指向的檔案可能不存在,出現I/O錯誤(Java使用IOException(IO異常)物件來表示這個出錯訊號)。因此程式必須在try-catch語句塊中的try部分建立輸出流(FileOutputStream),在catch部分中檢測並處理這個異常。

 

可以使用FileOutputStream類的下列能選擇是否具有重新整理功能的構造方法建立指向檔案的輸出流。

FileOutputStream(String name,boolean append);
FileOutputStream(File file,boolean append);

其中引數append取值為true,輸出流不會重新整理所指向的檔案,輸出流write方法將從檔案的末尾開始向檔案寫入資料,若引數append去false,輸出流將重新整理所指向的檔案

 

注意:當使用父類OutputStream類宣告FileOutputStream物件時(即FileOutputStream的上轉型物件),可以不用try-catch語句塊

6.3 使用輸出流寫出位元組

檔案位元組流可以呼叫從父類繼承的write方法順序地寫檔案,只要不關閉流,每次呼叫write方法就順序地向檔案寫入內容,直到檔案位元組輸出流被關閉。

位元組輸入流(InputStream)的read方法以位元組為單位讀取源中的資料

·void write() 輸出流呼叫該方法向目的檔案寫入單個位元組的資料。

·void write(byte b[]) 輸出流呼叫該方法向目的地寫入一個位元組陣列b。

·void write(byte b[],int off,int len) 給定位元組陣列b中起始於偏移量off處取len個位元組寫入目的地。

·void close() 關閉輸出流

6.4 顯式關閉輸出流。

import java.io.*;
public class Example10_5 {
   public static void main(String args[]) {
      byte [] a = "新年快樂".getBytes();
      byte [] b = "Happy New Year".getBytes();
      File file = new File("a.txt");                         //輸出的目的地
      try{  
         OutputStream out=new FileOutputStream(file);      //指向目的地的輸出流
         System.out.println(file.getName()+"的大小:"+file.length()+"位元組");//a.txt的大小:0位元組
         out.write(a);                                    //向目的地寫資料
         out.close();
         out=new FileOutputStream(file,true);             //準備向檔案尾加內容
         System.out.println(file.getName()+"的大小:"+file.length()+"位元組");///a.txt的大小:8位元組
         out.write(b,0,b.length); 
         System.out.println(file.getName()+"的大小:"+file.length()+"位元組");///a.txt的大小:8位元組
         out.close();
      }
      catch(IOException e) {
          System.out.println("Error "+e);
      }
  }
}

 

7、檔案字元輸入、輸出流(使用Reader、Write類的子類FileReader、FileWrite(檔案字元輸入、輸出流))

7.1 位元組流存在不能很好地操作Unicode字元,因此,如果使用位元組流,讀取不當會出現“亂碼”的現象

7.2 構造方法和引數append的使用與位元組流類似,本處就不贅述。

7.3 對於Write流,write方法將資料首先寫入到緩衝區,每當緩衝區溢位時,緩衝區的內容被自動寫入到目的地,如果關閉流,緩衝區的內容會立刻被寫入到目的地。流呼叫flush()方法可以立刻沖洗當前緩衝區,即將當前緩衝區的內容寫入到目的地。

import java.io.*;
public class Example10_6 {
   public static void main(String args[]) {
      File sourceFile = new File("a.txt");  //讀取的檔案
      File targetFile = new File("b.txt");  //寫入的檔案
      char c[] =new char[19];               //char型陣列 
      try{  
         Writer out = new FileWriter(targetFile,true); //指向目的地的輸出流
         Reader in  = new FileReader(sourceFile);   //指向源的輸入流  
         int n = -1;
         while((n=in.read(c))!=-1) {
             out.write(c,0,n);
         }
         out.flush();
         out.close();
      }
      catch(IOException e) {
          System.out.println("Error "+e);
      }
  }
}

 

8、緩衝流(高階、上層流:BufferedReader、BufferedWrite類)

8.1 緩衝流能夠增強讀寫檔案的能力,需要注意的是,兩者的源和目的地必須是字元輸入流和字元輸出流,

8.2 構造方法

BufferedReader(Reader in);
BufferedWrite(Write out);

BufferedReader流能夠讀取文字行,方法是readLine();

BufferedWrite流有一個獨特的向檔案寫入一個回行符的方法:newLine();

import java.io.*;
import java.util.*;
public class Example10_7 {
   public static void main(String args[]) {
      File fRead = new File("english.txt");
      File fWrite = new File("englishCount.txt");
      try{  Writer out = new FileWriter(fWrite);
            BufferedWriter bufferWrite = new BufferedWriter(out);
            Reader in = new FileReader(fRead);
            BufferedReader bufferRead =new BufferedReader(in);
            String str = null;
            while((str=bufferRead.readLine())!=null) {
               StringTokenizer fenxi = new StringTokenizer(str);
               int count=fenxi.countTokens();
               str = str+" 句子中單詞個數:"+count;
               bufferWrite.write(str);
               bufferWrite.newLine();
            } 
            bufferWrite.close(); 
            out.close();
            in = new FileReader(fWrite);
            bufferRead =new BufferedReader(in);
            String s=null;
            System.out.println(fWrite.getName()+"內容:");
            while((s=bufferRead.readLine())!=null) {
              System.out.println(s);
           }  
           bufferRead.close();
           in.close();
      }
      catch(IOException e) {
          System.out.println(e.toString());
      }
   }
}

9、隨機流(RandomAccessFile類流)

9.1 RandomAccessFile類建立的流的指向既可以作為流的源,也可以作為流的目的地,換句話說,當準備對一個檔案進行讀寫操作時,建立一個指向該檔案的隨機流即可,這樣既可以從這個流中讀取檔案的資料,也可以通過這個流寫入資料到檔案中。

以下是RandomAccessFile的類的兩個構造方法:

RandomAccessFile(String name,String mode);
RandomAccessFile(File file,String mode);

引數名,檔案給出流的源,也是流向的目的地,引數模式取R(只讀)或RW(可讀寫),決定流對檔案的訪問許可權。

特別注意:RandomAccessFile流指向檔案時,不重新整理檔案

import java.io.*;
public class Example10_9 {
   public static void main(String args[]) {
      RandomAccessFile in=null;
      try{ in=new RandomAccessFile("Example10_9.java","rw");
           long length=in.length();  //獲取檔案的長度
           long position=0;
           in.seek(position);       //將讀取位置定位到檔案的起始 
           while(position<length) {
              String str=in.readLine();
              byte b[]=str.getBytes("iso-8859-1");
              str=new String(b);
              position=in.getFilePointer();
              System.out.println(str);
           } 
      }
      catch(IOException e){} 
   }
}

 

10,陣列流(ByteArrayInputStream的和ByteArrayOutputStream(位元組陣列輸入流,位元組陣列輸出流))

10.1流的源和目的地除了可以是檔案外,還可以是計算機記憶體。

import java.io.*;
public class Example10_10 {
   public static void main(String args[]) {
      try {
         ByteArrayOutputStream outByte=new ByteArrayOutputStream();
         byte [] byteContent="mid-autumn festival".getBytes(); 
         outByte.write(byteContent); 
         ByteArrayInputStream inByte=new ByteArrayInputStream(outByte.toByteArray());
         byte backByte []=new byte[outByte.toByteArray().length];
         inByte.read(backByte);
         System.out.println(new String(backByte));
         CharArrayWriter outChar=new CharArrayWriter();
         char [] charContent="中秋快樂".toCharArray(); 
         outChar.write(charContent); 
         CharArrayReader inChar=new CharArrayReader(outChar.toCharArray());
         char backChar []=new char[outChar.toCharArray().length];
         inChar.read(backChar);
         System.out.println(new String(backChar));
      }
      catch(IOException exp){}
  }
}

 

11,資料流(DataInputStream類和DataOutputStream類)

11.1它們允許程式按著機器無關的風格讀取Java原始資料。也就是說,讀取一個數據時,不必再關心這個數值應當是多少個位元組。

DataInputStream(InputStream in);
DataOutputStream(OutputSteam out);
import java.io.*;
public class Example10_11 {
   public static void main(String args[]) {
      File file=new File("apple.txt");
      try{
          FileOutputStream fos=new FileOutputStream(file);
          DataOutputStream outData=new DataOutputStream(fos);
          outData.writeInt(100);
          outData.writeLong(123456);  
          outData.writeFloat(3.1415926f);
          outData.writeDouble(987654321.1234);
          outData.writeBoolean(true);
          outData.writeChars("How are you doing ");
       } 
       catch(IOException e){}
       try{
          FileInputStream fis=new FileInputStream(file);
          DataInputStream inData=new DataInputStream(fis);
          System.out.println(inData.readInt());    //讀取int資料
          System.out.println(inData.readLong());   //讀取long資料 
          System.out.println(+inData.readFloat()); //讀取float資料
          System.out.println(inData.readDouble()); //讀取double資料
          System.out.println(inData.readBoolean());//讀取boolean資料
          char c = '\0';
          while((c=inData.readChar())!='\0') {       //'\0'表示空字元。
              System.out.print(c);
          } 
        }
        catch(IOException e){}
   }
}

 

11,物件流(ObjectInputStream的和ObjectOutputStrea)

11.1構建方法類似資料流

序列化:將物件的狀態和資訊轉化為可儲存的形式的過程。

11.2當使用物件流寫入或讀入物件時,要保證物件是序列化的,這是為了保證能把物件寫入到檔案,並能再把物件正確讀回到程式中。

import java.io.*;
public class Example10_13 {
   public static void main(String args[]) {
      TV changhong = new TV();
      changhong.setName("長虹電視");
      changhong.setPrice(5678); 
      File file=new File("television.txt");
      try{ 
          FileOutputStream fileOut=new FileOutputStream(file);
          ObjectOutputStream objectOut=new ObjectOutputStream(fileOut);
          objectOut.writeObject(changhong); 
          objectOut.close(); 
          FileInputStream fileIn=new FileInputStream(file);
          ObjectInputStream objectIn=new ObjectInputStream(fileIn);
          TV xinfei=(TV)objectIn.readObject(); 
          objectIn.close();
          xinfei.setName("新飛電視");
          xinfei.setPrice(6666); 
          System.out.println("changhong的名字:"+changhong.getName());
          System.out.println("changhong的價格:"+changhong.getPrice());
          System.out.println("xinfei的名字:"+xinfei.getName());
          System.out.println("xinfei的價格:"+xinfei.getPrice());
       }
       catch(ClassNotFoundException event) {
          System.out.println("不能讀出物件");
       }
       catch(IOException event) {
          System.out.println(event);
       }
   }
}

import java.io.*;
public class TV implements Serializable{
   String name;
   int price; 
   public void setName(String s) {
      name=s;
   }
   public void setPrice(int n) {
      price=n;
   }
   public String getName() {
      return name;
   }
   public int getPrice() {
      return price;
   }
}

 

12,序列化與物件克隆

12.1使用物件流很容易獲取一個序列化物件的克隆,只需將該物件寫入物件輸出流指向的目的地,然後將該目的地作為一個物件輸入流的源,那麼該物件輸入流從源中讀回的物件一定是原物件的一個​​克隆,即物件輸入流通過物件的序列化資訊來得到當前物件的一個​​克隆。

12.2當程式想以較快的速度得到一個物件的克隆時,可以用物件流將物件的序列化資訊寫入記憶體,而不是寫入到磁碟檔案中。物件流將陣列流作為底層流就可以將物件的序列化資訊寫入記憶體。

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.swing.*;
public class Example10_14 { 
   public static void main(String args[]) { 
      MyWin win=new MyWin();
   }
}
class MyWin extends JFrame implements ActionListener {
   JLabel label=null; 
   JButton 讀入=null,寫出=null;
   ByteArrayOutputStream out = null;
   MyWin() {
      setLayout(new FlowLayout()); 
      label=new JLabel("How are you");
      讀入=new JButton("讀入物件"); 
      寫出=new JButton("寫出物件");
      讀入.addActionListener(this);
      寫出.addActionListener(this);
      setVisible(true); 
      add(label);
      add(寫出);
      add(讀入);
      setSize(500,400);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      validate();
   }
   public void actionPerformed(ActionEvent e) {
      if(e.getSource()==寫出) {
          try{  out = new ByteArrayOutputStream();
                ObjectOutputStream objectOut = new ObjectOutputStream(out);
                objectOut.writeObject(label);               
                objectOut.close();
          }
          catch(IOException event){}
      }
      else if(e.getSource()==讀入) {
          try{  ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
                ObjectInputStream objectIn = new ObjectInputStream(in);
                JLabel temp=(JLabel)objectIn.readObject();
                temp.setText("你好"); 
                this.add(temp);
                this.validate();
                objectIn.close();
          }
          catch(Exception event){}
      }
   }
}