Java中IO流解析及代碼實例
I/O簡介
I/O是Input/output的縮寫,在java中,對於數據的輸入和輸出以流的方式進行。java.io包下提供瞭各種“流”類和接口,用以獲取不同種類的數據,並通過標準的方法輸入或輸出數據。
輸入輸出都是基於內存的角度來說的。輸入:讀取外部數據(磁盤、光盤等存儲設備的數據)到程序(內存)中。 輸出:輸出output:將程序(內存)數據輸出到磁盤、光盤等存儲設備中。
流的分類
按操作數據單位不同分為:字節流(8 bit),字符流(16 bit)
按數據流的流向不同分為:輸入流,輸出流
按流的角色的不同分為:節點流,處理流
節點流:直接從數據源或目的地讀寫數據。
處理流:不直接連接到數據源或目的地,而是“連接”在已存在的流(節點流或處理流)之上,通過對數據的處理為程序提供更為強大的讀寫功能。 相當於是二次包裝。
抽象基類 | 字節流 | 字符流 |
---|---|---|
輸入流 | InputStream | Reader |
輸出流 | OutputStream | Writer |
Java的IO流共涉及40多個類,實際上非常規則,都是從這4個抽象基類派生的。由這四個類派生出來的子類名稱都是以其父類名作為子類名後綴。
字節流和字符流常用API
InputStream
- int read():從輸入流中讀取數據的下一個字節。返回0到255范圍內的int字節值。如果讀到流的末尾,則返回 -1。 此方法一次隻讀一個字節,效率太低,一般不用
- int read(byte[] b):從此輸入流中將最多b.length個字節的數據保存到一個byte數組中。返回實際讀取字節長度,如果讀到流的末尾,也返回 -1。
- int read(byte[] b,int off,int len):將輸入流中最多len個數據字節讀入byte數組。嘗試讀取len個字節,但讀取的字節也可能小於該值,實際讀取個數以讀取到的為準,比如長度為23個字節的內容,每次讀取5個字節,則第五次讀取流並沒處於末尾,還剩長度是3,故返回3。第六次讀取發現是末尾,則返回 -1 。一般都用此方法。
- public void close() throws IOException:關閉此輸入流並釋放與該流關聯的所有系統資源,一定要關閉。
Reader
- Reader和InputStream類似,就是將字節數組換成瞭字符數組
- int read(); 效果與字節流一致
- int read(char[] cbuf); 效果與字節流一致
- int read(char[] cbuf,int off,int len); 效果與字節流一致
- public void close() throws IOException;關閉此輸入流並釋放與該流關聯的所有系統資源,字符流的必須要關閉,不然會出問題
OutputStream
- void write(int b) :將指定的字節寫入此輸出流。
- void write( byte[] b) :將b.length個字節從指定的byte數組寫入此輸出流。
- void write(byte[] b,int off,int len):將指定byte數組中從偏移量off開始的len個字節寫入此輸出流。
- public void flush() throws IOException:刷新此輸出流並強制寫出所有緩沖的輸出字節,調用此方法指示應將這些字節立即寫入它們預期的目標。就是將緩沖的字節全部寫出到字節或字符數組中。
- public void close() throws IOException:關閉此輸入流並釋放與該流關聯的所有系統資源
Writer
- void write(int c):寫入單個字符。
- void write(char[] cbuf):寫入字符數組。
- void write(char[] cbuf,int off,int len) : 寫入字符數組的某一部分。從off開始,寫入len個字符
- void write(String str):寫入字符串。
- void write(String str,int off,int len):寫入字符串的某一部分。
- void flush():刷新該流的緩沖,則立即將它們寫入預期目標。
- public void close() throws IOException:關閉此輸入流並釋放與該流關聯的所有系統資源
字節字符流相關操作
字節流讀取文本內容
// 讀取文本文件內容,用字節流去讀,可能會亂碼 public void test1() { InputStream inputStream = null; try { inputStream = new FileInputStream("hello.txt");//相對路徑是工程路徑下 int len; byte[] bytes = new byte[5];// 定義5字節長度的byte數組 while ((len = inputStream.read(bytes)) != -1) {// 將數據讀到byte數組中 System.out.println(new String(bytes, 0, len)); //打印到控制臺 } } catch (Exception e) { e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } }
字符流讀取文本內容
// 讀取文本文件內容,用字符流去讀,不會亂碼,最多重復 public void test2() { Reader reader = null; try { reader = new FileReader("hello.txt"); int len; char[] charBuff = new char[5]; while ((len = reader.read(charBuff)) != -1) { // System.out.println(new String(charBuff)); // 註意這兩行的代碼區別 System.out.println(new String(charBuff, 0, len)); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (reader != null) { reader.close(); } } catch (IOException e) { e.printStackTrace(); } } }
字節流讀取文件到輸出到指定位置
public void test3() { InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = new FileInputStream("hello.txt"); // 讀取文件 outputStream = new FileOutputStream("hello2.txt"); // 相對路徑,默認是工程路徑下 int len; byte[] bytes = new byte[5]; while ((len = inputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } if (outputStream != null){ outputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } }
字符流讀取文件到輸出到指定位置
字符流讀取文件到輸出到指定位置時,如果沒有手動關閉流,則不會輸出到指定位置,需要手動flush。但是如果在finally塊中關閉瞭流,則會自動flush。在close()操作中,會幫我們flush。
public void test4() { Reader reader = null; Writer writer = null; try { reader = new FileReader("hello2.txt"); writer = new FileWriter("hello3.txt"); int len; char[] charBuff = new char[5]; while ((len = reader.read(charBuff)) != -1) { writer.write(charBuff, 0, len); // writer.flush(); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (reader != null) { reader.close(); } if (writer != null) { // 一定要關閉字符流,否則要手動flush writer.close(); } } catch (IOException e) { e.printStackTrace(); } } }
註意,字符流用來處理字符串很便捷,並且隻能操作普通的文本文件,例如:.txt,.java,.c,.cpp 等語言的源代碼。尤其註意.doc,excel,ppt這些不是文本文件。字節流既可以操做文本文件,也可以操作字節文件,比如.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt。如果用字符流來操作圖片等字節文件,生成的文件是無法打開的!
緩沖流
為瞭提高數據讀寫的速度,Java API提供瞭帶緩沖功能的流類,在使用這些流類時,會創建一個內部緩沖區數組,缺省使用 8192個字節(8Kb) 的緩沖區。
緩沖流要“套接”在相應的節點流之上,根據數據操作單位可以把緩沖流分為BufferedInputStream和ufferedOutputStream以及BufferedReader和BufferedWriter。分別對應字節緩沖流和字符緩沖流。相當於在字節和字符流上包裝瞭一下。
BufferedInputStream和BufferedOutputStream
public void test() { InputStream inputStream ; OutputStream outputStream ; BufferedInputStream bis = null; BufferedOutputStream bos = null; try { inputStream = new FileInputStream("modify.txt"); //小於8KB outputStream = new FileOutputStream("modify2.txt"); bis = new BufferedInputStream(inputStream); bos = new BufferedOutputStream(outputStream); int len; byte[] bytes = new byte[5]; while ((len = bis.read(bytes)) != -1) { bos.write(bytes, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (bis != null) { bis.close(); } if (bos != null){ bos.close(); } } catch (IOException e) { e.printStackTrace(); } } }
BufferedReader和BufferedWriter
public void test2() { Reader reader ; Writer writer ; BufferedReader br = null; BufferedWriter bw = null; try { reader = new FileReader("modify.txt"); writer = new FileWriter("modify2.txt"); br = new BufferedReader(reader); bw = new BufferedWriter(writer); int len; char[] bytes = new char[5]; while ((len = br.read(bytes)) != -1) { bw.write(bytes, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } if (bw != null){ bw.close(); } } catch (IOException e) { e.printStackTrace(); } } }
**緩沖流如果沒有手動關閉流,且讀取的文件小於底層緩存大小8KB,是不會自動寫到目標中去的,需要手動flush。**在關閉流時,隻需關閉緩沖流即可,它會自動關閉它包裝的底層節點流。
數據流
為瞭方便地操作Java語言的基本數據類型和String的數據,可以使用數據流。數據流有兩個類:DataInputStream和DataOutputStream, 別“套接”在InputStream和OutputStream子類的流上。
使用如下:
public void test() { DataOutputStream dos = null; try { OutputStream outputStream = new FileOutputStream("data.txt"); dos = new DataOutputStream(outputStream); dos.writeUTF("熱烈慶祝中國共產黨成立一百周年"); dos.writeInt(100); dos.writeBoolean(true); System.out.println("成功!"); } catch (IOException e) { e.printStackTrace(); } finally { try { if (dos != null) { dos.close(); } } catch (IOException e) { e.printStackTrace(); } } } public void test2(){ DataInputStream dis = null; try { InputStream inputStream = new FileInputStream("data.txt"); dis = new DataInputStream(inputStream); System.out.println(dis.readUTF()); //讀取時要按照寫入順序讀取 System.out.println(dis.readInt()); System.out.println(dis.readBoolean()); System.out.println("成功!"); } catch (IOException e) { e.printStackTrace(); } finally { try { if (dis != null) { dis.close(); } } catch (IOException e) { e.printStackTrace(); } } }
對象流
用於存儲和讀取基本數據類型數據或對象的處理流。它的強大之處就是可以把Java中的對象寫入到數據源中,也能把對象從數據源中還原回來。
- 序列化:用ObjectOutputStream類保存基本類型數據或對象的機制
- 反序列化:用ObjectInputStream類讀取基本類型數據或對象的機制
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變量
序列化與反序列化的演示
先定義一個Person類,該類必須實現Serializable接口,否則序列化時會報java.io.NotSerializableException 的錯誤
package day07; import java.io.Serializable; import java.util.Date; public class Person implements Serializable { private static final long serialVersionUID = -5858950242987134591L; private String name; private Integer age; private Date date; public Person(){} public Person(String name, Integer age, Date date) { this.name = name; this.age = age; this.date = date; } // getter、setter略 @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", date=" + date + '}'; } }
序列化
public void test() { ObjectOutputStream oos = null; try { OutputStream outputStream = new FileOutputStream("person.txt"); // 創建輸出流對象,指定輸出位置 oos = new ObjectOutputStream(outputStream); // 包裝輸出流 oos.writeObject(new Person("張三",22,new Date())); // 序列化對象 System.out.println("序列化成功!"); } catch (Exception e) { e.printStackTrace(); } finally { if (oos!=null){ try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
反序列化
public void test2() { ObjectInputStream ois = null; try { InputStream inputStream = new FileInputStream("person.txt"); ois = new ObjectInputStream(inputStream); Person person = (Person) ois.readObject(); System.out.println("person姓名為:" + person.getName()); System.out.println("person年齡為:" + person.getAge()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh時mm分ss秒"); System.out.println("創建時間為:" + sdf.format(person.getDate())); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if (ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } }
無論如何反序列化,時間都是不變的。
總結
本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!