Java文件I/O操作

    要把程序數據保存到文件中,就一定要使用哪個I/O輸入輸出技術。Java中提供的I/O操作可以把數據保存到多種類型的文件。大多數的應用程序都需要與外部設備進行數據交換,最常見的外部設備包含磁盤和網絡。IO就是指應用程序對這些設備的數據輸入和輸出。在程序中,鍵盤被用做文件輸出。Java語言定義了許多類專門負責各種方式的輸入輸出,這些類都被放在java.io包中。

一、File類
     File類是IO包中唯一代表磁盤文件本身的對象。File類定義了一些與平臺無關的方法來操縱文件,通過調用File類提供的各種方法,能夠完成創建、刪除、重命名文件、判斷讀寫權限等等。
     下面構造方法來生產File對象
     File(String directoryPath), directoryPath是文件的路徑名。
下面這個範例說明了File的幾個方法
範例1:
import java.io.*;
public class Test{
    public static void main(String args[]){
         File f = new File("/home/dingfeng/java_study/test.txt");
         if(f.exists()){
            f.delete();
         }else{
            try{
               f.createNewFile();
            }catch(Exception e){}
         }
         //get file name
         System.out.println("File name:"+f.getName());
         //get file path
         System.out.println("File path:"+f.getPath());
         //get absolute path
         System.out.println("File absolute path:"+f.getAbsolutePath());
         //get parent folder name
         System.out.println("Parent folder name:"+f.getParent());
         //file is exists
         System.out.println("File exists:"+f.exists());
         //file can write
         System.out.println("File writble:"+f.canWrite());
         //file can read
         System.out.println("File readable:"+f.canRead());
         //file is a directory
         System.out.println("File is a directory:"+f.isDirectory());
         //file is a file
         System.out.println("File is a file:"+f.isFile());        
        //file is absolute path name
         System.out.println("File is absolute path:"+f.isAbsolute());
         //last modifed time
         System.out.println("File last modified time:"+f.lastModified());
         //file length
         System.out.println("File size:"+f.length()+" Bytes");
    }
}
運行結果:
File name:test.txt
File path:/home/dingfeng/java_study/test.txt
File absolute path:/home/dingfeng/java_study/test.txt
Parent folder name:/home/dingfeng/java_study
File exists:false
File writble:false
File readable:false
File is a directory:false
File is a file:false
File is absolute path:true
File last modified time:0
File size:0 Bytes
   還有其他的一些方法,可查閱Java的API手冊就可以了。

二、RandomAccessFile類
     RandomAccessFile類可以說是Java語言中功能最爲豐富的文件訪問類,提供了衆多的訪問方法。支持“隨機訪問”方式,可以跳轉到文件的任意位置處讀寫數據。
     RandomAccessFile有個位置指示器,指向當前讀寫處的位置,當讀寫n個字節後,文件指示器指向n個字節後面的下一個字節處。剛打開文件時,文件指示器指向文件的開頭處,可以移動文件指示器到新的位置,隨後的讀寫操作將從新的位置開始。RandomAccessFile在數據等長記錄格式文件的隨機(相對順序而言)讀取時有很大的優勢,但該類僅限於操作文件,不能訪問其他的IO設備、如網絡、內存映像等。
範例2(查看網上的):
import java.io.*;
public class Test {
 public static void main(String[] args) throws Exception {
  RandomAccessFile file = new RandomAccessFile("file", "rw");
  // 以下向file文件中寫數據
  file.writeInt(20);// 佔4個字節
  file.writeDouble(8.236598);// 佔8個字節
  file.writeUTF("這是一個UTF字符串");// 這個長度寫在當前文件指針的前兩個字節處,可用readShort()讀取
  file.writeBoolean(true);// 佔1個字節
  file.writeShort(395);// 佔2個字節
  file.writeLong(2325451l);// 佔8個字節
  file.writeUTF("又是一個UTF字符串");
  file.writeFloat(35.5f);// 佔4個字節
  file.writeChar('a');// 佔2個字節
  file.seek(0);// 把文件指針位置設置到文件起始處
  // 以下從file文件中讀數據,要注意文件指針的位置
  System.out.println("——————從file文件指定位置讀數據——————");
  System.out.println(file.readInt());
  System.out.println(file.readDouble());
  System.out.println(file.readUTF());
  file.skipBytes(3);// 將文件指針跳過3個字節,本例中即跳過了一個boolean值和short值。
  System.out.println(file.readLong());
  file.skipBytes(file.readShort()); // 跳過文件中“又是一個UTF字符串”所佔字節,注意readShort()方法會移動文件指針,所以不用加2。
  System.out.println(file.readFloat());
  //以下演示文件複製操作
  System.out.println("——————文件複製(從file到fileCopy)——————");
  file.seek(0);
  RandomAccessFile fileCopy=new RandomAccessFile("fileCopy","rw");
  int len=(int)file.length();//取得文件長度(字節數)
  byte[] b=new byte[len];
  file.readFully(b);
  fileCopy.write(b);
  System.out.println("複製完成!");
 }
}
運行結果:
——————從file文件指定位置讀數據——————
20
8.236598
這是一個UTF字符串
2325451
35.5
——————文件複製(從file到fileCopy)——————
複製完成!

三、流類
   Java的流式輸入/輸出建立在4個抽象類的基礎上:InputStream、OutputStream、Reader和Writer。它們用來創建具體流式子類。儘管程序通過具體子類進行輸入/輸出操作,但頂層的類定義了所有流類的基本通用功能。
   InputStream和OutputStream被設計成字節流類,Reader和Writer被設計成字符流類。字節流類和字符流類形成分離的層次結構。一般來說,處理字符後字符串時應使用字符流類,處理字節或二進制對象時應使用字節流類。
   一般在操作文件流時,不管是字節流還是字符流,都可以按照以下方式進行。
   a、使用File類找到一個文件
   b、通過File類的對象去實例化字節流或字符流的子類
   c、進行字節(字符)的讀、寫操作
   d、關閉文件流

1、字節流
   字節流類爲處理字節式輸入/輸出提供了豐富的環境。一個字節流可以和其他任何類型的對象並用,包括二進制數據。這樣的多功能性使得字節流對很多類型的程序都很重要。因爲字節流類以InputStream和OutputStream爲頂層,從討論着兩個類開始。
   (1)InputStream
       InputStream是一個定義了Java流式字節輸入模式的抽象類,該類的所有方法在出錯時都會引發一個IOException異常(下面OutputStream一樣),下表顯示了該類的方法.

int available() 
          返回此輸入流下一個方法調用可以不受阻塞地從此輸入流讀取(或跳過)的估計字節數。
void close() 
          關閉此輸入流並釋放與該流關聯的所有系統資源。
void mark(int readlimit) 
          在此輸入流中標記當前的位置。
boolean markSupported() 
          測試此輸入流是否支持 mark 和 reset 方法。
abstract  int read() 
          從輸入流中讀取數據的下一個字節。
int read(byte[] b) 
          從輸入流中讀取一定數量的字節,並將其存儲在緩衝區數組 b 中。
int read(byte[] b, int off, int len) 
          將輸入流中最多 len 個數據字節讀入 byte 數組。
void reset() 
          將此流重新定位到最後一次對此輸入流調用 mark 方法時的位置。
long skip(long n) 
          跳過和丟棄此輸入流中數據的 n 個字節。
    (2)OutputStream
        OutputStream定義了流式字節輸出模式的抽象類,其方法如下表。

 void close() 
          關閉此輸出流並釋放與此流有關的所有系統資源。
 void flush() 
          刷新此輸出流並強制寫出所有緩衝的輸出字節。
 void write(byte[] b) 
          將 b.length 個字節從指定的 byte 數組寫入此輸出流。
 void write(byte[] b, int off, int len) 
          將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此輸出流。
abstract  void write(int b) 
          將指定的字節寫入此輸出流。

    (3)FileInputStream
        該類創建一個能從文件讀取字節的InputStream類,常用構造方法

FileInputStream(File file) 
          通過打開一個到實際文件的連接來創建一個 FileInputStream,該文件通過文件系統中的 File 對象 file 指定。
FileInputStream(FileDescriptor fdObj) 
          通過使用文件描述符 fdObj 創建一個 FileInputStream,該文件描述符表示到文件系統中某個實際文件的現有連接。
FileInputStream(String name) 
          通過打開一個到實際文件的連接來創建一個 FileInputStream,該文件通過文件系統中的路徑名 name 指定。
       比如:
            InputStream f = new FileInputStream("/home/dingfeng/java_study/test.txt" );
            File ff = new File("/home/dingfeng/java_study/test.txt");
            InputStream f1 = new FileInputStream(ff);
       儘管第一個構造方法可能更常用到,而第二個構造方法則允許把文件賦給輸入流之前用File方法更進一步檢查文件。

    (4)FileOutputStream
        創建一個可以向文件寫入字節的類,常用構造方法

FileOutputStream(File file) 
          創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。
FileOutputStream(File file, boolean append) 
          創建一個向指定 File 對象表示的文件中寫入數據的文件輸出流。
FileOutputStream(FileDescriptor fdObj) 
          創建一個向指定文件描述符處寫入數據的輸出文件流,該文件描述符表示一個到文件系統中的某個實際文件的現有連接。
FileOutputStream(String name) 
          創建一個向具有指定名稱的文件中寫入數據的輸出文件流。
FileOutputStream(String name, boolean append) 
          創建一個向具有指定 name 的文件中寫入數據的輸出文件流。
    下例中,用FileOutputStream向文件中寫入一個字符串,並用FileInputStream讀出寫入的內容。
範例3:
import java.io.*;
public class Test {
     public static void main(String[] args) {
            //1.write conten to file
            File f = new File("/home/dingfeng/java_study/test.txt");
            OutputStream out = null;
            try{
               out = new FileOutputStream(f);
            }catch(FileNotFoundException e){}
            String test = "Hello World!";
            //string change to bytes
            byte b[] = test.getBytes();
            try{
               out.write(b);
            }catch(IOException e){}
            try{
               out.close();
            }catch(IOException e){}
            //2.next read file
            InputStream in = null;
            try{
               in = new FileInputStream(f);
            }catch(FileNotFoundException e){}
            byte b1[] = new byte[512];
            int i = 0;
            try{
               i = in.read(b1);
            }catch(IOException e){}
            try{
               in.close();
            }catch(IOException e){}
            System.out.println(new String(b1,0,i));
     }
}
運行結果:
Hello World!
        
2、字符流
   儘管字節流提供了除了任何類型輸入/輸出操作的足夠的功能,但他們不能直接操作Unicode字符。Java的一個主要目標是支持“一次編寫,出處運行”,那麼包含直接的字符輸入/輸出的支持就是必要的。如前所述,字符流層次結構的頂層是Reader和Writer抽象類,從他們開始介紹。
 
   (1)Reader
    該類是定義Java的流式字符輸入模式的抽象類,出錯會報IOException異常(Writer一樣)。

abstract  void close() 
          關閉該流並釋放與之關聯的所有資源。
 void mark(int readAheadLimit) 
          標記流中的當前位置。
 boolean markSupported() 
          判斷此流是否支持 mark() 操作。
 int read() 
          讀取單個字符。
 int read(char[] cbuf) 
          將字符讀入數組。
abstract  int read(char[] cbuf, int off, int len) 
          將字符讀入數組的某一部分。
 int read(CharBuffer target) 
          試圖將字符讀入指定的字符緩衝區。
 boolean ready() 
          判斷是否準備讀取此流。
 void reset() 
          重置該流。
 long skip(long n) 
          跳過字符。
 
    (2)Writer
     定義流式字符輸出的抽象類。

Writer append(char c) 
          將指定字符添加到此 writer。
 Writer append(CharSequence csq) 
          將指定字符序列添加到此 writer。
 Writer append(CharSequence csq, int start, int end) 
          將指定字符序列的子序列添加到此 writer.Appendable
abstract  void close() 
          關閉此流,但要先刷新它。
abstract  void flush() 
          刷新該流的緩衝。
 void write(char[] cbuf) 
          寫入字符數組。
abstract  void write(char[] cbuf, int off, int len) 
          寫入字符數組的某一部分。
 void write(int c) 
          寫入單個字符。
 void write(String str) 
          寫入字符串。
 void write(String str, int off, int len) 
          寫入字符串的某一部分。

    (3)FileReader
     創建了一個可以讀取文件內容的Reader類,構造方法

FileReader(File file) 
          在給定從中讀取數據的 File 的情況下創建一個新 FileReader
FileReader(FileDescriptor fd) 
          在給定從中讀取數據的 FileDescriptor 的情況下創建一個新 FileReader
FileReader(String fileName) 
          在給定從中讀取數據的文件名的情況下創建一個新 FileReader

    (4)FileWriter
     創建了一個可以寫文件的Writer類,構造方法

FileWriter(File file) 
          根據給定的 File 對象構造一個 FileWriter 對象。
FileWriter(File file, boolean append) 
          根據給定的 File 對象構造一個 FileWriter 對象。
FileWriter(FileDescriptor fd) 
          構造與某個文件描述符相關聯的 FileWriter 對象。
FileWriter(String fileName) 
          根據給定的文件名構造一個 FileWriter 對象。
FileWriter(String fileName, boolean append) 
          根據給定的文件名以及指示是否附加寫入數據的 boolean 值來構造 FileWriter 對象。

下例對範例3進行修改,如下
範例4:
import java.io.*;
public class Test {
     public static void main(String[] args) {
            //1.write conten to file
            File f = new File("/home/dingfeng/java_study/test.txt");
            Writer out = null;
            try{
               out = new FileWriter(f);
            }catch(IOException e){}
            String test = "Hello World!";
            //string change to bytes
            //byte b[] = test.getBytes();
            try{
               out.write(test);
            }catch(IOException e){}
            try{
               out.close();
            }catch(IOException e){}
            //2.next read file
            Reader in = null;
            try{
               in = new FileReader(f);
            }catch(FileNotFoundException e){}
            //byte b1[] = new byte[512];
            char c[] = new char[512];
            int i = 0;
            try{
               i = in.read(c);
            }catch(IOException e){}
            try{
               in.close();
            }catch(IOException e){}
            System.out.println(new String(c,0,i));
     }
}
運行結果:
Hello World!

注意:如果我們把上面一個out.close();代碼註釋掉,也就是說在向文件中寫入內容後不關閉文件,然後打開文件,可以發現文件中沒有任何內容,why?
從繼承類圖可以看到,FileWriter不是直接繼承自Writer類,而是繼承了Writer的子類,此類爲字節流和字符流的轉換類。也就是說正在從文件中讀取進來的數據還是字節,只是在內存中將字節轉換成了字符。所有可以得出結論:字符流用到了緩衝區,而字節流沒有用到緩衝區。

3、管道流
   管道流主要用於連接兩個線程間的通信,也分爲字節流(PipedInputStream和PipedOutputStream)和字符流(PipedReader和PipedWriter)兩種類型,這邊以字節型爲例介紹。
   一個PipedInputStream對象必須和一個PipedOutputStream對象進行連接而產生一個通信管道,PipedOutputStream可以向管道中寫入數據,PipedInputStream可以從管道中讀取PipedOutputStream寫入的數據。
範例5:
import java.io.*;
public class Test {
     public static void main(String[] args) {
          try{
             Sender sender = new Sender();
             Receiver receiver = new Receiver();
             PipedOutputStream out = sender.getOutputStream();
             PipedInputStream in = receiver.getInputStream();
             out.connect(in);
             sender.start();
             receiver.start();
          }catch(IOException e){}
     }
}
class Sender extends Thread{
     private PipedOutputStream out = new PipedOutputStream();
     public PipedOutputStream getOutputStream(){
          return out;
     }
     public void run(){
          String s = new String("Hello,Receiver!");
          try{
              out.write(s.getBytes());
              out.close();
          }catch(IOException e){}
     }
}
class Receiver extends Thread{
     private PipedInputStream in = new PipedInputStream();
     public PipedInputStream getInputStream(){
          return in;
     }
     public void run(){
          String s = null;
          byte []buf = new byte[512];
          try{
             int len = in.read(buf);
             s = new String(buf,0,len);
             System.out.println("Receive info:"+s);
             in.close();
          }catch(IOException e){}
     }
}
運行結果:
Receive info:Hello,Receiver!

    管道流用起來比較方便,但也有一些缺點:
    1)管道流只能在兩個線程間傳遞數據
    2)管道流只能實現單向發送,如果要兩個線程間互通訊,則需要兩個管道流

4、ByteArrayInputStream與ByteArrayOutputStream
   ByteArrayInputStream是輸入流的一種實現,他有兩個構造方法,每個構造方法都需要一個字節數組來作爲其數據源。

ByteArrayInputStream(byte[] buf) 
          創建一個 ByteArrayInputStream,使用 buf 作爲其緩衝區數組。
ByteArrayInputStream(byte[] buf, int offset, int length) 
          創建 ByteArrayInputStream,使用 buf 作爲其緩衝區數組。

ByteArrayOutputStream() 
          創建一個新的 byte 數組輸出流。
ByteArrayOutputStream(int size) 
          創建一個新的 byte 數組輸出流,它具有指定大小的緩衝區容量(以字節爲單位)。
    如程序在運行過程中要產生一些臨時文件,可以採用虛擬文件方式實現,這兩個類可以實現類似於內存虛擬文件的功能。
範例6:
import java.io.*;
public class Test {
     public static void main(String[] args) throws Exception{
         String tmp = "abcdefghijklmnopqrstubwxyz";
         byte[] src = tmp.getBytes();
         ByteArrayInputStream input = new ByteArrayInputStream(src);
         ByteArrayOutputStream output = new ByteArrayOutputStream();
         new Test().transform(input,output);
         byte[] result = output.toByteArray();
         System.out.println(new String(result));
     }
     public void transform(InputStream in,OutputStream out){
         int c = 0;
         try{
            while((c=in.read())!=-1){
               int C = (int)Character.toUpperCase((char)c);
               out.write(C);
            }
         }catch(Exception e){}
     }
}
運行結果:
ABCDEFGHIJKLMNOPQRSTUBWXYZ

5、System.in和System.out  
   爲了至此標準輸入輸出設備,Java定義了兩個特殊的流對象:System.in和System.out。System.in對應鍵盤,屬於InputStream類型,程序使用System.in可以讀取從鍵盤上輸入的數據。System.out對於顯示器,屬於PrintStream類型,PrintStream是OutputStream的一個子類,程序使用System.out可以將數據輸出到顯示器上。鍵盤可以被當做一個特殊的輸入流,顯示器可以被當作一個特殊的輸出流。

6、打印流
   1)字節打印流
   PrintStream類提供了一系列的print和println方法,可以實現將基本數據類型的格式轉換成字符串輸出。在前面的範例中大量用到的“System.out.println”語句中的System.out,就是PrintStream類的一個實例對象。
   構造函數

PrintStream(File file) 
          創建具有指定文件且不帶自動行刷新的新打印流。
PrintStream(File file, String csn) 
          創建具有指定文件名稱和字符集且不帶自動行刷新的新打印流。
PrintStream(OutputStream out) 
          創建新的打印流。
PrintStream(OutputStream out, boolean autoFlush) 
          創建新的打印流。
PrintStream(OutputStream out, boolean autoFlush, String encoding) 
          創建新的打印流。
PrintStream(String fileName) 
          創建具有指定文件名稱且不帶自動行刷新的新打印流。
PrintStream(String fileName, String csn) 
          創建具有指定文件名稱和字符集且不帶自動行刷新的新打印流。
   其中,autoFlush控制在Java中遇到換行符(\n)時是否自動清空緩衝區,encoding是指定編碼方式。
   println方法與print方法的區別是:前者會在打印完的內容後面再多打印一個換行符(\n),所以println等於print("\n")。
   該對象有多個重載的print和println方法,可輸出各種類型的數據,包括Object對象。對於基本的數據類型,先轉換成字符串的形式,然後再輸出,而不是輸出原始的字節內容,如整數221的打印結果是字符"2","2","1"所組成的一個字符串,而不是整數221在內存中的原始數據。對於非基本數據類型的對象,先調用對象的toString方法,然後輸出toString方法所返回的字符串。
   2)字符打印流
    與PrintStream對應的字符打印流是PrintWriter,構造方法

PrintWriter(File file) 
          使用指定文件創建不具有自動行刷新的新 PrintWriter。
PrintWriter(File file, String csn) 
          創建具有指定文件和字符集且不帶自動刷行新的新 PrintWriter。
PrintWriter(OutputStream out) 
          根據現有的 OutputStream 創建不帶自動行刷新的新 PrintWriter。
PrintWriter(OutputStream out, boolean autoFlush) 
          通過現有的 OutputStream 創建新的 PrintWriter。
PrintWriter(String fileName) 
          創建具有指定文件名稱且不帶自動行刷新的新 PrintWriter。
PrintWriter(String fileName, String csn) 
          創建具有指定文件名稱和字符集且不帶自動行刷新的新 PrintWriter。
PrintWriter(Writer out) 
          創建不帶自動行刷新的新 PrintWriter。
PrintWriter(Writer out, boolean autoFlush) 
          創建新 PrintWriter。
   PrintWriter即使遇到換行符也不會自動清空緩衝區,只在設置了autoflush模式下使用了println方法纔會自動清空緩衝區。
範例7:通過PrintWriter向屏幕打印信息
import java.io.*;
public class Test {
     public static void main(String[] args) throws Exception{
          PrintWriter out = null;
          out = new PrintWriter(System.out);
          out.println("Hello,World!");
          out.close();
     }
}
運行結果:
Hello,World!

範例8:通過PrintWriter向文件中打印信息
import java.io.*;
public class Test {
     public static void main(String[] args) throws Exception{
          PrintWriter out = null;
          File f = new File("/home/dingfeng/java_study/test.txt");
          try{
             out = new PrintWriter(new FileWriter(f));
          }catch(IOException e){}
          out.println("Hello,World!");
          out.close();
     }
}
運行結果:
打開創建的txt文件,文件內容爲Hello,World!

7、DataInputStream和DataOutputStream
   這兩個類提供了與平臺無關的數據操作,通常會先通過DataOutputStream按照一定的格式輸出,再通過DataInputStream按照一定格式讀入。由於可以得到java的各種類型甚至字符串,這樣對得到的數據便可以方便的進行處理這在通過協議傳輸的信息的網絡上是非常適用的。
範例9:
import java.io.*;
public class Test{
     public static void main(String args[]) throws IOException{
           // write data to txt file
           DataOutputStream out = new DataOutputStream(new FileOutputStream("/home/dingfeng/java_study/test.txt"));
           double []prices = {1.0, 2.0, 3.0, 4.0, 5.0};
           int []units = {10, 20, 30, 40, 50};
           String []descs = {"Apple", "Banana", "Orange", "Watermelon", "Grapes"};
     
           for(int i=0;i<prices.length;i++){
              out.writeDouble(prices[i]);
              out.writeChar('\t');
              out.writeInt(units[i]);
              out.writeChar('\t');
              out.writeChars(descs[i]);
              out.writeChar('\n');
           }
           out.close();
           // read data
           DataInputStream in = new DataInputStream(new FileInputStream("/home/dingfeng/java_study/test.txt"));
           double price;
           int unit;
           StringBuffer desc;
           double total = 0;
           try{
              while(true){
                 price = in.readDouble();
                 in.readChar();
                 unit = in.readInt();
                 in.readChar();
                 char chr;
                 desc = new StringBuffer();
                 while((chr = in.readChar())!='\n'){
                      desc.append(chr);
                 }
                 System.out.println("Info: "+"fruit name: "+desc+"\tcounts:"+unit+"\tprice:"+price);
                 total = total + unit*price;
              }
           }catch(EOFException e){
              System.out.println("\ntotal money: "+total+" RMB");
           }
           in.close();
     }
}
運行結果:
Info: fruit name: Apple counts:10 price:1.0
Info: fruit name: Banana counts:20 price:2.0
Info: fruit name: Orange counts:30 price:3.0
Info: fruit name: Watermelon counts:40 price:4.0
Info: fruit name: Grapes counts:50 price:5.0


8、合併流
   採用SeqpuenceInputStream類,可以實現兩個文件的合併操作。如下圖:
    
範例9:
import java.io.*;
public class Test{
     public static void main(String args[]) throws IOException{
          FileInputStream in1 = null;
          FileInputStream in2 = null;
          SequenceInputStream s = null;
          FileOutputStream out = null;
   
          try{
             File inputFile1 = new File("/home/dingfeng/java_study/1.txt");
             File inputFile2 = new File("/home/dingfeng/java_study/2.txt");
             File outputFile = new File("/home/dingfeng/java_study/12.txt");
             in1 = new FileInputStream(inputFile1);
             in2 = new FileInputStream(inputFile2);
             s = new SequenceInputStream(in1,in2);
             out = new FileOutputStream(outputFile);
             int c;
             while((c = s.read())!= -1) out.write(c);
             in1.close();
             in2.close();
             s.close();
             out.close();
             System.out.println("OK............");
          }catch(IOException e){
             e.printStackTrace();
          }finally{
             if(in1 != null)
               try{
                  in1.close();
               }catch(IOException e){}
             if(in2 != null)
               try{
                  in2.close();
               }catch(IOException e){}
             if(s != null)
               try{
                  s.close();
               }catch(IOException e){}
             if(out != null)
               try{
                  out.close();
               }catch(IOException e){}
          }
    }
}
運行結果:
 

9、字節流與字符流的轉換
   Java支持字節流和字符流,但有時需要在字節流和字符流之間轉換。InputStreamReader和OutputStreamWriter,這兩個類是字節流和字符流之間相互轉換的類,前者用於將一個字節流中的字節解碼成字符,後者用於將寫入的字符編碼成字節後寫入一個字節流。
   InputStreamReader 構造方法

InputStreamReader(InputStream in) 
          創建一個使用默認字符集的 InputStreamReader。
InputStreamReader(InputStream in, Charset cs) 
          創建使用給定字符集的 InputStreamReader。
InputStreamReader(InputStream in, CharsetDecoder dec) 
          創建使用給定字符集解碼器的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName) 
          創建使用指定字符集的 InputStreamReader。
      OutputStreamWriter 構造方法

OutputStreamWriter(OutputStream out) 
          創建使用默認字符編碼的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, Charset cs) 
          創建使用給定字符集的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, CharsetEncoder enc) 
          創建使用給定字符集編碼器的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName) 
          創建使用指定字符集的 OutputStreamWriter。
   爲了達到最高的效率,避免頻繁的進行字符與字節間的相互轉換,最好不要直接使用這兩個類來進行讀寫,可要考慮在 BufferedReader內包裝 InputStreamReader,將 OutputStreamWriter 包裝到 BufferedWriter 中。

BufferedReader in = new BufferedReader(new InputStreamReader(System.in))
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out))

範例10:

import java.io.*; 
public class Test{ 
     public static void main(String args[]) { 
           BufferedReader buf = null; 
           buf = new BufferedReader(new InputStreamReader(System.in)); 
           String str = null; 
           while(true){ 
               System.out.print("Please input number:"); 
               try{ 
                 str = buf.readLine(); 
               }catch(IOException e){} 
               int i = -1; 
               try{ 
                  i = Integer.parseInt(str); 
                  i++; 
                  System.out.println("Afrer modify:"+i); 
                  break; 
               }catch(Exception e){} 
           } 
     } 

運行結果:

Please input number:2 
Afrer modify:3 
10、IO類層次關係圖
下面列出IO保重的各類層次關係圖。
字節輸入流InputStream類

字節輸出流OutputStream
字符輸入流Reader
字符輸出流

四、對象序列化
   所謂的對象序列化,是指將對象轉換成二進制數據流的一種實現手段。通過將對象序列化,可以方便的實現對象的傳輸及保存。
   在Java中,ObjectOutputStream和ObjectInputStream這兩個類用於序列化對象的操作,用於存儲和讀取對象的輸入輸出流類,不難想象,只要把對象中的所有成員變量都存儲起來,就等於保存了這個對象,之後從保存的對象中再將對象讀取出來就可以繼續使用此對象。對象必須實現Serializable接口,才能進行序列化。
範例11:
import java.io.*;
public class Test{
     public static void main(String args[]) throws Exception{
              File f = new File("/home/dingfeng/java_study/test.txt");
              serialize(f);
              deserialize(f);
     }
     public static void serialize(File f) throws Exception{
              OutputStream outputFile = new FileOutputStream(f);
              ObjectOutputStream cout = new ObjectOutputStream(outputFile);
              cout.writeObject(new Person("zhangsan",20));
              cout.close();
     }
     public static void deserialize(File f) throws Exception{
              InputStream inputFile = new FileInputStream(f);
              ObjectInputStream cin = new ObjectInputStream(inputFile);
              Person p = (Person)cin.readObject();
              System.out.println(p);
     }
}
class Person implements Serializable{
    private String name;
    private int age;
    public Person(String name,int age){
          this.name = name;
          this.age = age;
    }
    public String toString(){
       return "name:"+this.name+" age:"+this.age;
    }
}
運行結果:
name:zhangsan age:20

如果不希望類中的某個屬性被序列化,可以在聲明屬性之前加上transient關鍵字,例如:
    private transient String name;
    private transient int age;
結果:name:null age:0

另外:
   a)當一個父類實現序列化,子類自動實現序列化,不需要顯式實現Serializable接口;
   b)當一個對象的實例變量引用其他對象,序列化該對象時也把引用對象進行序列化;
   c)並非所有的對象都可以序列化。

發佈了35 篇原創文章 · 獲贊 10 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章