舉例講解Java中的Stream流概念
1、基本的輸入流和輸出流
流是 Java 中最重要的基本概念之一。文件讀寫(xiě)、網(wǎng)絡(luò)收發(fā)、進(jìn)程通信,幾乎所有需要輸入輸出的地方,都要用到流。
流是做什么用的呢?就是做輸入輸出用的。為什么輸入輸出要用“流”這種方式呢?因?yàn)槌绦蜉斎胼敵龅幕締挝皇亲止?jié),輸入就是獲取一串字節(jié),輸出就是發(fā)送一串字節(jié)。但是很多情況下,程序不可能接收所有的字節(jié)之后再進(jìn)行處理,而是接收一點(diǎn)處理一點(diǎn)。比方你下載魔獸世界,不可能全部下載到內(nèi)存里再保存到硬盤(pán)上,而是下載一點(diǎn)就保存一點(diǎn)。這時(shí),流這種方式就非常適合。
在 Java 中,每個(gè)流都是一個(gè)對(duì)象。流分為兩種:輸入流(InputStream)和輸出流(OutputStream)。對(duì)于輸入流,你只要從流當(dāng)中不停地把字節(jié)取出來(lái)就是了;而對(duì)于輸出流,你只要把準(zhǔn)備好的字節(jié)串傳給它就行。
流對(duì)象是怎么獲得的呢?不同的外部系統(tǒng),獲取流的方式也不同。例如,文件讀寫(xiě)就要?jiǎng)?chuàng)建 FileInputStream/FileOutputStream 對(duì)象,而網(wǎng)絡(luò)通信是通過(guò) Socket 對(duì)象來(lái)獲取輸入輸出流的。一般來(lái)說(shuō),如果一個(gè)類(lèi)有 getInputStream() 或 getOutputStream() 這樣的方法,就表明它是通過(guò)流對(duì)象來(lái)進(jìn)行輸入輸出的。
InputStream 是輸入流,下面是一個(gè)通過(guò) 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; /** * 通過(guò)流讀取文件 */ public class ReadFileDemo { // 程序入口 public static void main(String[] args) { String path = "c:/boot.ini"; File file = new File(path); // 創(chuàng)建輸入流 InputStream is; try { is = new FileInputStream(file); } catch (FileNotFoundException e) { System.err.println("文件 " + path + " 不存在。"); return; } // 開(kāi)始讀取 byte[] content = new byte[0]; // 保存讀取出來(lái)的文件內(nèi)容 byte[] buffer = new byte[10240]; // 定義緩存 try { int eachTime = is.read(buffer); // 第一次讀取。如果返回值為 -1 就表示沒(méi)有內(nèi)容可讀了。 while (eachTime != -1) { // 讀取出來(lái)的內(nèi)容放在 buffer 中,現(xiàn)在將其合并到 content。 content = concatByteArrays(content, buffer, eachTime); eachTime = is.read(buffer); // 繼續(xù)讀取 } } catch (IOException e) { System.err.println("讀取文件內(nèi)容失敗。"); e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { // 這里的異??梢院雎圆惶幚? } } // 輸出文件內(nèi)容 String contentStr = new String(content); System.out.println(contentStr); } /** * 合并兩個(gè)字節(jié)串 * * @param bytes1 字節(jié)串1 * @param bytes2 字節(jié)串2 * @param sizeOfBytes2 需要從 bytes2 中取出的長(zhǎng)度 * * @return bytes1 和 bytes2 中的前 sizeOfBytes2 個(gè)字節(jié)合并后的結(jié)果 */ 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; } }
雖然寫(xiě)得很啰嗦,但這確實(shí)是 InputStream 的基本用法。注意,這只是一個(gè)例子,說(shuō)明如何從輸入流中讀取字節(jié)串。實(shí)際上,Java 提供更簡(jiǎn)單的方式來(lái)讀取文本文件。以后將會(huì)介紹。
相比從流中讀取,使用 OutputStream 輸出則是非常簡(jiǎn)單的事情。下面是一個(gè)例子:
import java.io.OutputStream; import java.io.FileOutputStream; import java.io.File; import java.io.IOException; import java.util.Date; /** * 將當(dāng)前日期保存到文件 */ 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("無(wú)法創(chuàng)建文件。"); return; } OutputStream os = new FileOutputStream(file); // 創(chuàng)建輸出流(前提是文件存在) os.write(new Date().toString().getBytes()); // 將當(dāng)前時(shí)間寫(xiě)入文件 os.close(); // 必須關(guān)閉流,內(nèi)容才會(huì)寫(xiě)入文件。 System.out.println("文件寫(xiě)入完成。"); } }
Java 還提供其它的流操作方式,但它們都是對(duì) InputStream 和 OutputStream 進(jìn)行擴(kuò)展或包裝。所以這兩個(gè)類(lèi)是基礎(chǔ),必須要理解它們的使用。
2、Reader 和 Writer
介紹了 InputStream 和 OutputStream,接下來(lái)介紹 Reader 和 Writer。這兩個(gè)類(lèi)其實(shí)就是將 InputStream 和 OutputStream 包裝了一下。不過(guò)它們處理的不是字節(jié)(byte),而是字符(char)。如果一個(gè)流當(dāng)中的內(nèi)容都是文本,那么用 Reader/Writer 處理起來(lái)會(huì)簡(jiǎn)單些。下面是一個(gè)用 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 將文本內(nèi)容寫(xiě)入文件,這里就不給出例子了,看官自己試著寫(xiě)一下吧。
上面這個(gè)例子,仍然不是讀取文本文件最常用的方式。Java 提供 BufferedReader,我們通常用它來(lái)讀取文本文件。下面是一個(gè)例子:
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、對(duì)象序列化
對(duì)象序列化也是流應(yīng)用的一個(gè)重要方面。序列化就是把一個(gè)對(duì)象轉(zhuǎn)換成一串字節(jié),既可以保存起來(lái),也可以傳給另外的 Java 程序使用。ObjectOutputStream 和 ObjectInputStream 就是專(zhuān)門(mén)用來(lái)進(jìn)行序列化和反序列化的。下面就是一個(gè)簡(jiǎn)單的例子:
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 示例。 * 這兩個(gè)類(lèi)分別用于序列化和反序列化。 */ 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 對(duì)象 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 對(duì)象保存到指定的文件中 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 + '}'; } } }
這個(gè)例子里面看不到字節(jié)和字符的讀寫(xiě),因?yàn)檫@兩個(gè)類(lèi)都包裝好了。上面只是一個(gè)簡(jiǎn)單的例子,序列化要寫(xiě)好的話還是有不少講究的。想深入了解序列化,可以看看這里。本文只關(guān)注跟流有關(guān)的部分。其實(shí)序列化用的很少,因?yàn)樾蛄谢档土遂`活性,所以可以不用的話一般都不會(huì)用。
- java8 Stream流逐行處理文本文件
- Java使用FileInputStream流讀取文件示例詳解
- Java實(shí)現(xiàn)inputstream流的復(fù)制代碼實(shí)例
- Java8中的Stream流式操作教程之王者歸來(lái)
- Java中FileOutputStream流的write方法
- Java8新特性Stream流實(shí)例詳解
- BufferedInputStream(緩沖輸入流)詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- Java的DataInputStream和DataOutputStream數(shù)據(jù)輸入輸出流
- Java中的stream流的概念解析及實(shí)際運(yùn)用總結(jié)
- 簡(jiǎn)單總結(jié)Java IO中stream流的使用方法
- 詳解JAVA Stream流
相關(guān)文章
Java使用原型模式展現(xiàn)每日生活應(yīng)用案例詳解
這篇文章主要介紹了Java使用原型模式展現(xiàn)每日生活應(yīng)用案例,較為詳細(xì)的分析了原型模式的概念、原理及Java使用原型模式展現(xiàn)每日生活案例的相關(guān)操作步驟與注意事項(xiàng),需要的朋友可以參考下2018-05-05java從字符串中提取數(shù)字的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇java從字符串中提取數(shù)字的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10Spring實(shí)戰(zhàn)之ResourceLoaderAware加載資源用法示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之ResourceLoaderAware加載資源用法,結(jié)合實(shí)例形式分析了spring使用ResourceLoaderAware加載資源相關(guān)配置與操作技巧,需要的朋友可以參考下2020-01-01java中SpringBoot?自動(dòng)裝配的原理分析
這篇文章主要介紹了SpringBoot?自動(dòng)裝配的原理分析的相關(guān)資料,需要的朋友可以參考下2022-12-12Java服務(wù)中的大文件上傳和下載優(yōu)化技巧分享
在Java服務(wù)中處理大文件的上傳和下載是一項(xiàng)常見(jiàn)但復(fù)雜的任務(wù),為了提供優(yōu)秀的用戶體驗(yàn)和高效的系統(tǒng)性能,我們將探索多種策略和技術(shù),并在每一點(diǎn)上都提供代碼示例以便實(shí)戰(zhàn)應(yīng)用,需要的朋友可以參考下2023-10-10java 使用URLDecoder和URLEncoder對(duì)中文進(jìn)行處理
這篇文章主要介紹了java 使用URLDecoder和URLEncoder對(duì)中文進(jìn)行處理的相關(guān)資料,需要的朋友可以參考下2017-02-02Java內(nèi)存泄漏問(wèn)題處理方法經(jīng)驗(yàn)總結(jié)
今天小編就為大家分享一篇關(guān)于Java內(nèi)存泄漏問(wèn)題處理方法經(jīng)驗(yàn)總結(jié),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01Java簡(jiǎn)化復(fù)雜系統(tǒng)調(diào)用的門(mén)面設(shè)計(jì)模式
Java門(mén)面模式是一種結(jié)構(gòu)性設(shè)計(jì)模式,它為復(fù)雜系統(tǒng)提供了一個(gè)簡(jiǎn)單的接口,使得系統(tǒng)的客戶端能夠更加方便地使用系統(tǒng)功能。門(mén)面模式通過(guò)封裝復(fù)雜的子系統(tǒng),隱藏系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié),提高了系統(tǒng)的易用性和靈活性2023-04-04