Java IO最詳解
初學java,一直搞不懂java裡面的io關係,在網上找了很多大多都是給個結構圖草草描述也看的不是很懂。而且沒有結合到java7 的最新技術,所以自己來整理一下,有錯的話請指正,也希望大家提出寶貴意見。
首先看個圖:(如果你也是初學者,我相信你看了真個人都不好了,想想java設計者真是煞費苦心啊!)
這是java io 比較基本的一些處理流,除此之外我們還會提到一些比較深入的基於io的處理類,比如console類,SteamTokenzier,Externalizable介面,Serializable介面等等一些高階用法極其原理。
一、java io的開始:檔案
1. 我們主要講的是流,流的本質也是對檔案的處理,我們循序漸進一步一步從檔案將到流去。
2. java 處理檔案的類 File,java提供了十分詳細的檔案處理方法,舉了其中幾個例子,其餘的可以去
Java程式碼- package com.hxw.io;
- import java.io.*;
- public class FileExample{
- public static void main(String[] args) {
- createFile();
- }
- /**
-
* 檔案處理示例
- */
- public static void createFile() {
- File f=new File("E:/電腦桌面/jar/files/create.txt");
- try{
- f.createNewFile(); //當且僅當不存在具有此抽象路徑名指定名稱的檔案時,不可分地建立一個新的空檔案。
- System.out.println("該分割槽大小"+f.getTotalSpace()/(1024*1024*1024)+"G"); //返回由此抽象路徑名錶示的檔案或目錄的名稱。
-
f.mkdirs(); //建立此抽象路徑名指定的目錄,包括所有必需但不存在的父目錄。
- // f.delete(); // 刪除此抽象路徑名錶示的檔案或目錄
- System.out.println("檔名 "+f.getName()); // 返回由此抽象路徑名錶示的檔案或目錄的名稱。
- System.out.println("檔案父目錄字串 "+f.getParent());// 返回此抽象路徑名父目錄的路徑名字串;如果此路徑名沒有指定父目錄,則返回 null。
- }catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
二、位元組流:
1.位元組流有輸入和輸出流,我們首先看輸入流InputStream,我們首先解析一個例子(FileInputStream)。
Java程式碼- package com.hxw.io;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- public class FileCount {
- /**
- * 我們寫一個檢測檔案長度的小程式,別看這個程式挺長的,你忽略try catch塊後發現也就那麼幾行而已。
- */
- publicstatic void main(String[] args) {
- //TODO 自動生成的方法存根
- int count=0; //統計檔案位元組長度
- InputStreamstreamReader = null; //檔案輸入流
- try{
- streamReader=newFileInputStream(new File("D:/David/Java/java 高階進階/files/tiger.jpg"));
- /*1.new File()裡面的檔案地址也可以寫成D:\\David\\Java\\java 高階進階\\files\\tiger.jpg,前一個\是用來對後一個
- * 進行轉換的,FileInputStream是有緩衝區的,所以用完之後必須關閉,否則可能導致記憶體佔滿,資料丟失。
- */
- while(streamReader.read()!=-1) { //讀取檔案位元組,並遞增指標到下一個位元組
- count++;
- }
- System.out.println("---長度是: "+count+" 位元組");
- }catch (final IOException e) {
- //TODO 自動生成的 catch 塊
- e.printStackTrace();
- }finally{
- try{
- streamReader.close();
- }catch (IOException e) {
- //TODO 自動生成的 catch 塊
- e.printStackTrace();
- }
- }
- }
- }
我們一步一步來,首先,上面的程式存在問題是,每讀取一個自己我都要去用到FileInputStream,我輸出的結果是“---長度是: 64982 位元組”,那麼進行了64982次操作!可能想象如果檔案十分龐大,這樣的操作肯定會出大問題,所以引出了緩衝區的概念。可以將streamReader.read()改成streamReader.read(byte[]b)此方法讀取的位元組數目等於位元組陣列的長度,讀取的資料被儲存在位元組陣列中,返回讀取的位元組數,InputStream還有其他方法mark,reset,markSupported方法,例如:
markSupported 判斷該輸入流能支援mark
和 reset
方法。
mark用於標記當前位置;在讀取一定數量的資料(小於readlimit的資料)後使用reset可以回到mark標記的位置。
FileInputStream不支援mark/reset操作;BufferedInputStream支援此操作;
mark(readlimit)的含義是在當前位置作一個標記,制定可以重新讀取的最大位元組數,也就是說你如果標記後讀取的位元組數大於readlimit,你就再也回不到回來的位置了。
通常InputStream的read()返回-1後,說明到達檔案尾,不能再讀取。除非使用了mark/reset。
2.FileOutputStream 循序漸進版, InputStream是所有位元組輸出流的父類,子類有ByteArrayOutputStream,FileOutputStream,ObjectOutputStreanm,這些我們在後面都會一一說到。先說FileOutputStream
我以一個檔案複製程式來說,順便演示一下快取區的使用。(Java I/O預設是不緩衝流的,所謂“緩衝”就是先把從流中得到的一塊位元組序列暫存在一個被稱為buffer的內部位元組數組裡,然後你可以一下子取到這一整塊的位元組資料,沒有緩衝的流只能一個位元組一個位元組讀,效率孰高孰低一目瞭然。有兩個特殊的輸入流實現了緩衝功能,一個是我們常用的BufferedInputStream.)
Java程式碼- package com.hxw.io;
- import java.io.*;
- public class FileCopy {
- public static void main(String[] args) {
- // TODO自動生成的方法存根
- byte[] buffer=new byte[512]; //一次取出的位元組數大小,緩衝區大小
- int numberRead=0;
- FileInputStream input=null;
- FileOutputStream out =null;
- try {
- input=new FileInputStream("D:/David/Java/java 高階進階/files/tiger.jpg");
- out=new FileOutputStream("D:/David/Java/java 高階進階/files/tiger2.jpg"); //如果檔案不存在會自動建立
- while ((numberRead=input.read(buffer))!=-1) { //numberRead的目的在於防止最後一次讀取的位元組小於buffer長度,
- out.write(buffer, 0, numberRead); //否則會自動被填充0
- }
- } catch (final IOException e) {
- // TODO自動生成的 catch 塊
- e.printStackTrace();
- }finally{
- try {
- input.close();
- out.close();
- } catch (IOException e) {
- // TODO自動生成的 catch 塊
- e.printStackTrace();
- }
- }
- }
- }
3.讀寫物件:ObjectInputStream 和ObjectOutputStream ,該流允許讀取或寫入使用者自定義的類,但是要實現這種功能,被讀取和寫入的類必須實現Serializable介面,其實該介面並沒有什麼方法,可能相當於一個標記而已,但是確實不合缺少的。例項程式碼如下:
Java程式碼- package com.hxw.io;
- import java.io.*;
- public class ObjetStream {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO自動生成的方法存根
- ObjectOutputStream objectwriter=null;
- ObjectInputStream objectreader=null;
- try {
- objectwriter=new ObjectOutputStream(new FileOutputStream("D:/David/Java/java 高階進階/files/student.txt"));
- objectwriter.writeObject(new Student("gg", 22));
- objectwriter.writeObject(new Student("tt", 18));
- objectwriter.writeObject(new Student("rr", 17));
- objectreader=new ObjectInputStream(new FileInputStream("D:/David/Java/java 高階進階/files/student.txt"));
- for (int i = 0; i < 3; i++) {
- System.out.println(objectreader.readObject());
- }
- } catch (IOException | ClassNotFoundException e) {
- // TODO自動生成的 catch 塊
- e.printStackTrace();
- }finally{
- try {
- objectreader.close();
- objectwriter.close();
- } catch (IOException e) {
- // TODO自動生成的 catch 塊
- e.printStackTrace();
- }
- }
- }
- }
- class Student implements Serializable{
- private String name;
- private int age;
- public Student(String name, int age) {
- super();
- this.name = name;
- this.age = age;
- }
- @Override
- public String toString() {
- return "Student [name=" + name + ", age=" + age + "]";
- }
- }
執行後系統輸出:
Student [name=gg, age=22]
Student [name=tt, age=18]
Student [name=rr, age=17]
4.有時沒有必要儲存整個物件的資訊,而只是要儲存一個物件的成員資料,成員資料的型別假設都是Java的基本資料型別,這樣的需求不必使用到與Object輸入、輸出相關的流物件,可以使用DataInputStream、DataOutputStream來寫入或讀出資料。下面是一個例子:(DataInputStream的好處在於在從檔案讀出資料時,不用費心地自行判斷讀入字串時或讀入int型別時何時將停止,使用對應的readUTF()和readInt()方法就可以正確地讀入完整的型別資料。)