java的IO操作:位元組流與字元流操作

weixin_34088583發表於2017-04-26

流的概念

  

  程式中的輸入輸出都是以流形式,流中儲存的實際上都是位元組檔案。

位元組流與字元流

  位元組流的操作:

    1)輸入:inputStream,

    2)輸出:outPutStream;

  字元流的操作:

    1)輸入主要使用:write類。

    2)輸出主要使用:reader類。

  內容操作就四個類。

操作流程:

  

  使用File類操作一定有路徑問題,注意分隔符:

  實際上四個操作類都是抽象類(區別介面,抽象類的成員都是抽象,並且只能單繼承,介面可以有全域性變數,但是介面可以多繼承

  IO操作屬於資源操作,對於資源操作,操作最後必須關閉,否則有可能出現未知錯誤

位元組流

  位元組流主要操作byte型別資料,以byte陣列為準,注意操作類為位元組輸出流:outputStream,位元組輸入流:inputStream,類。

  Byte是位元組,肯定使用位元組流操作,所有的資料基本都可以使用byte陣列表示出來。

OutputStream類

  位元組輸出流的最大父類:定義如下: 

public abstract class OutputStream extends Object implements  Closeable ,Flushable;

  Closeable表示可以關閉的操作,因為程式最後要關閉。

  Flushable表示重新整理,清空記憶體中資料。

  此類是抽象類,所以要想使用此類,必須要通過子類例項化父類物件

  那麼要操作的是一個檔案,則可以使用FileOutputStream類,通過向上轉型之後,可以為OutputStream例項化。

  

  FileOutputStream構造方法:

public FileOutputStream( File file) throws FielNotFoundException

  可見FileOutputStream需要操作File類的物件,並且有異常處理。

  

import java.io.File ;
import java.io.OutputStream ;
import java.io.FileOutputStream ;
public class OutputStreamDemo01{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "test.txt") ;    // 宣告File物件
        // 第2步通過子類例項化父類物件,注意下面是通過向上轉型,例項化父類物件。
        OutputStream out = null ;    // 準備好一個輸出的物件
        out = new FileOutputStream(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行寫操作
        String str = "Hello World!!!" ;        // 準備一個字串
        byte b[] = str.getBytes() ;            // 只能輸出byte陣列,所以將字串變為byte陣列
        out.write(b) ;                        // 將內容輸出,儲存檔案
        // 第4步、關閉輸出流
        out.close() ;                        // 關閉輸出流
    }
};

  1)因為FielOutputStream有異常,wire()方法也有異常,為了節省操作,可以在主方法丟擲異常,不處理

  2)輸出字串:可以先用方法 getBytes()一次性讀取字串位元組陣列中,然後再把位元組陣列輸出。

  3)OutputStream的例項物件呼叫write(byte []b)方法,把陣列輸出。

  4)在操作時候,如果檔案本身不存在,就會為使用者自動建立新檔案。

  在操作輸出流的時候也可以使用write(int i)的方式寫出資料。

import java.io.File ;
import java.io.OutputStream ;
import java.io.FileOutputStream ;
public class OutputStreamDemo02{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "test.txt") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        OutputStream out = null ;    // 準備好一個輸出的物件
        out = new FileOutputStream(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行寫操作
        String str = "Hello World!!!" ;        // 準備一個字串
        byte b[] = str.getBytes() ;            // 只能輸出byte陣列,所以將字串變為byte陣列
        for(int i=0;i<b.length;i++){        // 採用迴圈方式寫入
            out.write(b[i]) ;    // 每次只寫入一個內容
        }
        // 第4步、關閉輸出流
        out.close() ;                        // 關閉輸出流
    }
};

  以上操作在輸入資料後,以前的資料不存在。因為在IO操作中預設是將其覆蓋。如果要想執行追加功能,必須設定追加操作

  找到FileOutputStream類:

public FileOutputStream(File file,bool append)throws FileNotFoundException

  在構造方法中,如果append的值設為true,表示在檔案的末尾追加

import java.io.File ;
import java.io.OutputStream ;
import java.io.FileOutputStream ;
public class OutputStreamDemo03{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "test.txt") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        OutputStream out = null ;    // 準備好一個輸出的物件
        out = new FileOutputStream(f,true)  ;    // 此處表示在檔案末尾追加內容
        // 第3步、進行寫操作
        String str = "Hello World!!!" ;        // 準備一個字串
        byte b[] = str.getBytes() ;            // 只能輸出byte陣列,所以將字串變為byte陣列
        for(int i=0;i<b.length;i++){        // 採用迴圈方式寫入
            out.write(b[i]) ;    // 每次只寫入一個內容
        }
        // 第4步、關閉輸出流
        out.close() ;                        // 關閉輸出流
    }
};

  上面是在末尾追加,沒換行,如果要換行,在要新增的文子前面,使用“\r\n”完成

如:String str="\r\n在這裡追加";

package Thread1;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;

public class demo1 {
    // 所有的異常直接丟擲,程式中不再進行處理
    public static void main(String args[]) throws Exception{
        File f=new File("D:\\outputStream.txt");
        OutputStream out=new FileOutputStream(f,true);
        String str="\r\n在這裡追加";
        byte[]b=str.getBytes();
        out.write(b);
        out.close();
    }
};

  執行結果:

位元組輸入流InputStream

  定義:public abstract class inputStream extends Object implements Clossablle;

與OutputStream類似,也是抽象類,必須依靠子類,如果從檔案讀取,肯定是FileInputStream;

  構造方法:

public FileInputStream(File file) throws FileNotFoundException

  要處理異常,引數需要File物件,向上轉型。

  

  以read(byte[] b)形式讀取到陣列中。

package Thread1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class demo1 {
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
    // 第1步、使用File類找到一個檔案
            File f= new File("d:" + File.separator + "outputStream.txt") ;    // 宣告File物件
            // 第2步、通過子類例項化父類物件
            InputStream input=null;     // 準備好一個輸入的物件
            input = new FileInputStream(f)  ;    // 通過物件多型性,進行例項化
            // 第3步、進行讀操作
            byte b[] = new byte[1024] ;        // 所有的內容都讀到此陣列之中
            input.read(b) ;        // 讀取內容
            // 第4步、關閉輸出流
            input.close() ;                        // 關閉輸出流
            System.out.println("內容為:" + new String(b)) ;    // 把byte陣列變為字串輸出
}
};

  執行結果:

內容為:hello moto在這裡追加
在這裡追加

  1)定義一個byte型別的陣列物件,用於讀取從File類的指定檔案的內容。

  2)InputStream通過子類,利用多型性,向上轉型,例項化物件。

  3)InputStream物件呼叫read(byte b[])方法,把File中的內容讀取給b[]陣列。

  此刻雖然讀取出來了,但是存在問題,檔案沒有這麼大,但是開闢了這麼大空間,浪費記憶體。

能不能根據檔案大小開闢陣列空間?

  如果要想知道檔案大小,直接使用File類即可。f.length()方法獲得檔案的大小,注意同時要型別轉換

package Thread1;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class demo1 {
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "outputStream.txt") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        InputStream input = null ;    // 準備好一個輸入的物件
        input = new FileInputStream(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行讀操作
        byte b[] = new byte[(int)f.length()] ;        // 陣列大小由檔案決定
        int len = input.read(b) ;        // 讀取內容
        // 第4步、關閉輸出流
        input.close() ;                        // 關閉輸出流\
        System.out.println("讀入資料的長度:" + len) ;
        System.out.println("內容為:" + new String(b)) ;    // 把byte陣列變為字串輸出
    }
};

  執行結果:

讀入資料的長度:32
內容為:hello moto在這裡追加
在這裡追加

new String(b),因為讀取的時候是從字元型別先轉化成byte型別,所以輸出要轉換成String型別。

這裡其實是String類的構造方法,相當於例項化了一個String類的物件,生成了一個String 變數。

 

以上是利用byte陣列形式讀取檔案。read(byte [] b)

另外一個常用的讀取方法read();位元組一個個的讀

abstract  int read() 
          從輸入流中讀取資料的下一個位元組 

注意:read()方法的返回結果是int型別,所以對於位元組需要轉化型別

 

package 類集;
import java.io.File ;
import java.io.InputStream ;
import java.io.FileInputStream ;
public class InputStreamDemo04{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "test.txt") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        InputStream input = null ;    // 準備好一個輸入的物件
        input = new FileInputStream(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行讀操作
        byte b[] = new byte[(int)f.length()] ;        // 陣列大小由檔案決定
        for(int i=0;i<b.length;i++){
            b[i] = (byte)input.read() ;        // 讀取內容
        }
        // 第4步、關閉輸出流
        input.close() ;                        // 關閉輸出流\
        System.out.println("內容為:" + new String(b)) ;    // 把byte陣列變為字串輸出
    }
};

 

以上操作只適合知道輸入流大小的時候,如果現在不知道大小呢?

package 類集;
import java.io.File ;
import java.io.InputStream ;
import java.io.FileInputStream ;
public class InputStreamDemo05{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "te.text") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        InputStream input = null ;    // 準備好一個輸入的物件
        input = new FileInputStream(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行讀操作
        byte b[] = new byte[1024] ;        // 陣列大小由檔案決定
        int len = 0 ; 
        int temp = 0 ;            // 接收每一個讀取進來的資料
        while((temp=input.read())!=-1){  //這裡先把位元組讀取出來,賦值給temp,如果temp不等於-1,表示還有內容,檔案沒有讀完
            b[len] = (byte)temp ;
            len++ ;
        }
        // 第4步、關閉輸出流
        input.close() ;                        // 關閉輸出流\
        System.out.println("內容為:" + new String(b,0,len)) ;    // 把byte陣列變為字串輸出
    }
};

當不知道讀取的內容有多大的時候,就只能通過以讀取的內容是否為-1為讀完的標誌

這個是常用的位元組流操作方法:

     InputStream input = null ;    // 準備好一個輸入的物件
        input = new FileInputStream(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行讀操作
        byte b[] = new byte[1024] ;        // 陣列大小由檔案決定
        int len = 0 ; 
        int temp = 0 ;            // 接收每一個讀取進來的資料
        while((temp=input.read())!=-1){  //這裡先把位元組讀取出來,賦值給temp,如果temp不等於-1,表示還有內容,檔案沒有讀完
            b[len] = (byte)temp ;
            len++ ;
        }

 

 

字元流操作

字元輸入流:Writer類

字元輸出流:Reader類。

Writer類

常用方法:

字元流比位元組流操作好在一點,就是可以直接輸出字串了。不用跟之前一樣進行轉換操作

程式碼例項:

import java.io.File ;
import java.io.Writer ;
import java.io.FileWriter ;
public class WriterDemo01{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "test.txt") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        Writer out = null ;    // 準備好一個輸出的物件
        out = new FileWriter(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行寫操作
        String str = "Hello World!!!" ;        // 準備一個字串,注意,這裡不用跟位元組流操作一樣轉換成Byte陣列型別。
        out.write(str) ;                        // 將內容輸出,儲存檔案
        // 第4步、關閉輸出流
        out.close() ;                        // 關閉輸出流
    }
};

使用字元流預設情況下依然覆蓋已有檔案,要想追加,則在FileWriter上加一個追加標記即可。

package 類集;
import java.io.File ;
import java.io.Writer ;
import java.io.FileWriter ;
public class WriterDemo02{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "te.text") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        Writer out = null ;    // 準備好一個輸出的物件
        out = new FileWriter(f,true)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行寫操作
        String str = "\r\nLIXINGHUA\r\nHello World!!!" ;        // 準備一個字串
        out.write(str) ;                        // 將內容輸出,儲存檔案
        // 第4步、關閉輸出流
        out.close() ;                        // 關閉輸出流
    }
};

字元輸入流:Reader()

常用方法

以字元陣列形式讀取出陣列。

package 類集;
import java.io.File ;
import java.io.Reader ;
import java.io.FileReader ;
public class ReaderDemo01{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "te.text") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        Reader input = null ;    // 準備好一個輸入的物件
        input = new FileReader(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行讀操作
        char c[] = new char[1024] ;        // 所有的內容都讀到此陣列之中
        int len = input.read(c) ;        // 讀取內容
        // 第4步、關閉輸出流
        input.close() ;                        // 關閉輸出流
        System.out.println("內容為:" + new String(c,0,len)) ;    // 把字元陣列變為字串輸出
    }
};

輸出結果:

內容為:hello    fsdfsdfshello    
LIXINGHUA
Hello World!!!

也可以以迴圈方式,通過檔案是否讀到底判斷。

import java.io.File ;
import java.io.Reader ;
import java.io.FileReader ;
public class ReaderDemo02{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "test.txt") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        Reader input = null ;    // 準備好一個輸入的物件
        input = new FileReader(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行讀操作
         char c[] = new char[1024] ;        // 所有的內容都讀到此陣列之中
       int temp = 0 ;    // 接收每一個內容
       int len = 0 ;        // 讀取內容
        while((temp=input.read())!=-1){
            // 如果不是-1就表示還有內容,可以繼續讀取
             c[len] = (char)temp ;
           len++ ;
        }
        // 第4步、關閉輸出流
        input.close() ;                        // 關閉輸出流
        System.out.println("內容為:" + new String(c,0,len)) ;    // 把字元陣列變為字串輸出
    }
};

 

字元流與位元組流的區別

位元組和字元使用非常相似,有哪些不同呢?

1,位元組流不會用到快取,字元流會用到快取。

通過程式碼驗證位元組流使用到了快取。

 位元組流操作:

package 類集;
import java.io.File ;
import java.io.OutputStream ;
import java.io.FileOutputStream ;
public class OutputStreamDemo05{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "te.text") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        OutputStream out = null ;    // 準備好一個輸出的物件
        out = new FileOutputStream(f)  ;    // 例項化
        // 第3步、進行寫操作
        String str = "Hello World!!!" ;        // 準備一個字串
        byte b[] = str.getBytes() ;            // 只能輸出byte陣列,所以將字串變為byte陣列
        out.write(b) ;        // 寫入資料
        // 第4步、關閉輸出流
        // out.close() ;                        // 關閉輸出流
    }
};

結果在指定目錄成功輸出內容。

在使用位元組流操作中,即使沒有關閉,最終也可以輸出

字元流操作:

package 類集;
import java.io.File ;
import java.io.Writer ;
import java.io.FileWriter ;
public class WriterDemo03{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
        File f= new File("d:" + File.separator + "te.text") ;    // 宣告File物件
        // 第2步、通過子類例項化父類物件
        Writer out = null ;    // 準備好一個輸出的物件
        out = new FileWriter(f)  ;    // 通過物件多型性,進行例項化
        // 第3步、進行寫操作
        String str = "Hello World!!!" ;        // 準備一個字串
        out.write(str) ;                        // 將內容輸出,儲存檔案
        // 第4步、關閉輸出流
        // out.close() ;                        // 此時,沒有關閉
    }
};

執行結果:為空。

以上操作沒有輸出任何內容,所有的內容現在儲存在緩衝區當中,如果執行關閉的時候,會強制性的重新整理緩衝區,所以可以把內容輸出

abstract  void close() 
          關閉此流,但要先重新整理它 

如果現在沒有關閉的話,也可以強制呼叫重新整理方法

abstract  void flush() 
          重新整理該流的緩衝。 
package 類集;
import java.io.File ;
import java.io.Writer ;
import java.io.FileWriter ;
public class WriterDemo03{
    public static void main(String args[]) throws Exception{    // 異常丟擲,不處理
        // 第1步、使用File類找到一個檔案
            File f= new File("d:" + File.separator + "te.text") ;    // 宣告File物件
            // 第2步、通過子類例項化父類物件
            Writer out = null ;    // 準備好一個輸出的物件
            out = new FileWriter(f)  ;    // 通過物件多型性,進行例項化
            // 第3步、進行寫操作
            String str = "Hello World!!!" ;        // 準備一個字串
            out.write(str) ;                        // 將內容輸出,儲存檔案
// 第4步、關閉輸出流
            out.flush() ;    // 強制性清空緩衝區中的內容
// out.close() ;            
    }
}; 

此時成功將內容寫入指定路徑下檔案中。

 

開發中是使用位元組流好還是字元流好。

在所有的硬碟中儲存檔案或進行傳輸的時候,都是以位元組的方式進行的,包括圖片,視訊等多媒體也是以位元組進行

而字元是隻有在記憶體中才會形成的,所以使用位元組的時候是最多的

 

操作範例:檔案拷貝

操作的時候可以按照如下格式進行:

java Copy 原始檔 目標檔案

如果要採用以上的格式,則肯定要使用初始化引數的形式,輸入兩個路徑。所以此時必須對輸入的引數個數進行驗證,判斷其是否為2

是使用位元組流還是字元流呢,肯定使用位元組流,因為萬一拷貝的是圖片呢?

要完成拷貝程式,有兩種方式:

實現一,將原始檔中內容全部讀取進來,之後一次性寫入到目標檔案。

實現二,邊讀邊寫。

很明顯第二種方式更好。

package 類集;
import java.io.* ;
public class Copy{
    public static void main(String args[]){
        if(args.length!=2){        // 判斷是否是兩個引數
            System.out.println("輸入的引數不正確。") ;
            System.out.println("例:java Copy 原始檔路徑 目標檔案路徑") ;
            System.exit(1) ;    // 系統退出
        }
        File f1 = new File(args[0]) ;    // 原始檔的File物件
        File f2 = new File(args[1]) ;    // 目標檔案的File物件
        if(!f1.exists()){
            System.out.println("原始檔不存在!") ;
            System.exit(1) ;
        }
        InputStream input = null ;        // 準備好輸入流物件,讀取原始檔
        OutputStream out = null ;        // 準備好輸出流物件,寫入目標檔案
        try{
            input = new FileInputStream(f1) ;
        }catch(FileNotFoundException e){
            e.printStackTrace() ;
        }
        try{
            out = new FileOutputStream(f2) ;
        }catch(FileNotFoundException e){
            e.printStackTrace() ;
        }
        if(input!=null && out!=null){    // 判斷輸入或輸出是否準備好
            int temp = 0 ;    
            try{
                while((temp=input.read())!=-1){    // 開始拷貝
                    out.write(temp) ;    // 邊讀邊寫
                }
                System.out.println("拷貝完成!") ;
            }catch(IOException e){
                e.printStackTrace() ;
                System.out.println("拷貝失敗!") ;
            }
            try{
                input.close() ;        // 關閉
                out.close() ;        // 關閉
            }catch(IOException e){
                e.printStackTrace() ;
            }
        }
    }    
}

 

相關文章