舉例講解Java中的Stream流概念_java

來源:互聯網
上載者:User

1、基本的輸入資料流和輸出資料流
流是 Java 中最重要的基本概念之一。檔案讀寫、網路收發、進程通訊,幾乎所有需要輸入輸出的地方,都要用到流。

流是做什麼用的呢?就是做輸入輸出用的。為什麼輸入輸出要用“流”這種方式呢?因為程式輸入輸出的基本單位是位元組,輸入就是擷取一串位元組,輸出就是發送一串位元組。但是很多情況下,程式不可能接收所有的位元組之後再進行處理,而是接收一點處理一點。比方你下載魔獸世界,不可能全部下載到記憶體裡再儲存到硬碟上,而是下載一點就儲存一點。這時,流這種方式就非常適合。

在 Java 中,每個流都是一個對象。流分為兩種:輸入資料流(InputStream)和輸出資料流(OutputStream)。對於輸入資料流,你只要從流當中不停地把位元組取出來就是了;而對於輸出資料流,你只要把準備好的位元組串傳給它就行。

流對象是怎麼獲得的呢?不同的外部系統,擷取流的方式也不同。例如,檔案讀寫就要建立 FileInputStream/FileOutputStream 對象,而網路通訊是通過 Socket 對象來擷取輸入輸出資料流的。一般來說,如果一個類有 getInputStream() 或 getOutputStream() 這樣的方法,就表明它是通過流對象來進行輸入輸出的。
 
InputStream 是輸入資料流,下面是一個通過 InputStream 讀取檔案的例子:

import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.FileNotFoundException; import java.util.Arrays;  /**  * 通過流讀取檔案  */ public class ReadFileDemo {   // 程式入口  public static void main(String[] args) {   String path = "c:/boot.ini";   File file = new File(path);    // 建立輸入資料流   InputStream is;   try {    is = new FileInputStream(file);   } catch (FileNotFoundException e) {    System.err.println("檔案 " + path + " 不存在。");    return;   }    // 開始讀取   byte[] content = new byte[0];  // 儲存讀取出來的檔案內容   byte[] buffer = new byte[10240]; // 定義緩衝    try {    int eachTime = is.read(buffer); // 第一次讀取。如果傳回值為 -1 就表示沒有內容可讀了。    while (eachTime != -1) {     // 讀取出來的內容放在 buffer 中,現在將其合并到 content。     content = concatByteArrays(content, buffer, eachTime);     eachTime = is.read(buffer); // 繼續讀取    }   } catch (IOException e) {    System.err.println("讀取檔案內容失敗。");    e.printStackTrace();   } finally {    try {     is.close();    } catch (IOException e) {     // 這裡的異常可以忽略不處理    }   }    // 輸出檔案內容   String contentStr = new String(content);   System.out.println(contentStr);  }   /**   * 合并兩個位元組串   *   * @param bytes1  位元組串1   * @param bytes2  位元組串2   * @param sizeOfBytes2 需要從 bytes2 中取出的長度   *   * @return bytes1 和 bytes2 中的前 sizeOfBytes2 個位元組合并後的結果   */  private static byte[] concatByteArrays(byte[] bytes1, byte[] bytes2, int sizeOfBytes2) {   byte[] result = Arrays.copyOf(bytes1, (bytes1.length + sizeOfBytes2));   System.arraycopy(bytes2, 0, result, bytes1.length, sizeOfBytes2);   return result;  } } 

雖然寫得很囉嗦,但這確實是 InputStream 的基本用法。注意,這隻是一個例子,說明如何從輸入資料流中讀取位元組串。實際上,Java 提供更簡單的方式來讀取文字檔。以後將會介紹。

相比從流中讀取,使用 OutputStream 輸出則是非常簡單的事情。下面是一個例子:

import java.io.OutputStream; import java.io.FileOutputStream; import java.io.File; import java.io.IOException; import java.util.Date;  /**  * 將當前日期儲存到檔案  */ public class SaveFileDemo {   public static void main(String[] args) throws IOException {   String path = "c:/now.txt";    File file = new File(path);   if (!file.exists() && !file.createNewFile()) {    System.err.println("無法建立檔案。");    return;   }    OutputStream os = new FileOutputStream(file); // 建立輸出資料流(前提是檔案存在)   os.write(new Date().toString().getBytes());  // 將目前時間寫入檔案   os.close();          // 必須關閉流,內容才會寫入檔案。   System.out.println("檔案寫入完成。");  } } 

 
Java 還提供其它的流操作方式,但它們都是對 InputStream 和 OutputStream 進行擴充或封裝。所以這兩個類是基礎,必須要理解它們的使用。

2、Reader 和 Writer
介紹了 InputStream 和 OutputStream,接下來介紹 Reader 和 Writer。這兩個類其實就是將 InputStream 和 OutputStream 封裝了一下。不過它們處理的不是位元組(byte),而是字元(char)。如果一個流當中的內容都是文本,那麼用 Reader/Writer 處理起來會簡單些。下面是一個用 Reader 讀取文字檔的例子:

import java.io.FileReader; import java.io.IOException; import java.io.Reader;  /**  * 讀取文字檔  */ public class ReadTextFileDemo {   // 程式入口  public static void main(String[] args) {   String path = "c:/boot.ini";   String content = "";    try {    Reader reader = new FileReader(path);    char[] buffer = new char[10240];    int count;     while ((count = reader.read(buffer)) != -1) {     content += new String(buffer, 0, count);    }   } catch (IOException e) {    System.err.println("讀取檔案失敗。");    e.printStackTrace();   }    System.out.println(content);  }  } 

至於如何用 Writer 將常值內容寫入檔案,這裡就不給出例子了,看官自己試著寫一下吧。

上面這個例子,仍然不是讀取文字檔最常用的方式。Java 提供 BufferedReader,我們通常用它來讀取文字檔。下面是一個例子:

import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException;  /**  * 用 BufferedReader 讀取文字檔  */ public class ReadTextDemo2 {   public static void main(String[] args) {   String path = "c:/boot.ini";   String content = "";    try {    BufferedReader reader = new BufferedReader(new FileReader(path));    String line;    while ((line = reader.readLine()) != null) {     content += line + "/n";    }   } catch (IOException e) {    System.err.println("讀取檔案失敗。");    e.printStackTrace();   }    System.out.println(content);  } } 

3、對象序列化
對象序列化也是流應用的一個重要方面。序列化就是把一個對象轉換成一串位元組,既可以儲存起來,也可以傳給另外的 Java 程式使用。ObjectOutputStream 和 ObjectInputStream 就是專門用來進行序列化和還原序列化的。下面就是一個簡單的例子:

import java.io.ObjectOutputStream; import java.io.FileOutputStream; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.io.ObjectInputStream; import java.io.FileInputStream; import java.io.EOFException;   /**  * ObjectOutputStream/ObjectInputStream 樣本。  * 這兩個類分別用於序列化和還原序列化。  */ public class SerializationDemo {     public static void main(String[] args) throws Exception {     String path = "c:/persons.data";     File f = new File(path);     if (!f.exists()) {       f.createNewFile();     }       writePersons(path);     readPersons(path);   }     // 從指定的檔案中讀取 Person 對象   private static void readPersons(String path) throws IOException, ClassNotFoundException {     ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));       Person p;     while (true) {       try {         p = (Person) ois.readObject();         System.out.println(p);       } catch (EOFException e) {         break;       }     }   }     // 將 Person 對象儲存到指定的檔案中   private static void writePersons(String path) throws IOException {     Person[] persons = new Person[]{         new Person("張三", 23),         new Person("李四", 24)     };       ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));     for (Person person : persons) {       oos.writeObject(person);     }     oos.close();   }     ///////////////////////////////////////////////////////////     private static class Person implements Serializable {       private String name;       private int age;       public Person() {     }       public Person(String name, int age) {       this.name = name;       this.age = age;     }       public String getName() {       return name;     }       public void setName(String name) {       this.name = name;     }       public int getAge() {       return age;     }       public void setAge(int age) {       this.age = age;     }       @Override     public String toString() {       return "Person{" +           "name='" + name + '/'' +           ", age=" + age +           '}';     }   } } 

這個例子裡面看不到位元組和字元的讀寫,因為這兩個類都封裝好了。上面只是一個簡單的例子,序列化要寫好的話還是有不少講究的。想深入瞭解序列化,可以看看這裡。本文只關注跟流有關的部分。其實序列化用的很少,因為序列化降低了靈活性,所以可以不用的話一般都不會用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.