天天看點

JAVA基礎知識之FileOutputStream流一、FileOutputStream流二、構造方法三、FileOutputStream流構造方法的特殊之處四、FileOutputStream流的的常用API五、三種write方法對比

一、FileOutputStream流

         FileOutputStream流是指檔案位元組輸出流,專用于輸出原始位元組流如圖像資料等,其繼承OutputStream類,擁有輸出流的基本特性

public class FileOutputStream extends OutputStream{}
           

二、構造方法

1)建立FileOutputStream流以寫入資料到File對象所代表的檔案,同時建立一個新的FileDescriptor對象來表示與該檔案的關聯(源碼中會new一個該對象)

public FileOutputStream(File file) throws FileNotFoundException{}
           

   檢視底層源碼發現該構造方法實際是調用了另一個構造方法

public FileOutputStream(File file) throws FileNotFoundException {
        this(file, false);
    }
           

若檔案存在,但是是目錄而不是檔案,則會抛出FileNotFoundException異常

public class FileStream
{

    
    /**
     * File對象所代表的檔案是目錄,是以會抛異常
     * @param args
     */
    public static void main(String[] args)
    {
        //建立檔案對象
        File file=new File("C:\\Users\\Administrator\\Desktop"); 
   
        try
        {
            FileOutputStream out=new FileOutputStream(file);

        }

        catch (FileNotFoundException e)
        {
          
           System.out.println("檔案不存在或者檔案不可讀或者檔案是目錄");
        }
        catch (IOException e)
        {
           System.out.println("讀取過程存在異常");
        } 
    }

}
           

    若不存在且無法建立,則會抛出FileNotFoundException異常----此處存疑,不存在可了解,無法建立無法了解,什麼情況會導緻無法建立?

   若因為其他原因無法打開(如設定了不可寫),則會抛出FileNotFoundException異常

public class FileStream
{

    
    /**
     * 檔案不可寫,導緻報錯
     * @param args
     */
    public static void main(String[] args)
    {
        //建立檔案對象
        File file=new File("C:\\Users\\Administrator\\Desktop\\2.txt"); 
        
        file.setWritable(false); //設定不可寫
   
        try
        {
            FileOutputStream out=new FileOutputStream(file);
            
        }

        catch (FileNotFoundException e)
        {
          
           System.out.println("檔案不存在或者檔案不可讀或者檔案是目錄");
        }
        catch (IOException e)
        {
           System.out.println("讀取過程存在異常");
        } 
    }

}
           

2)建立FileOutputStream流以寫入資料到File對象表示的檔案。 如果第二個參數為true,則位元組将寫入檔案的末尾而不是開頭。 建立一個新的FileDescriptor對象來表示此檔案連接配接。其抛異常的規則與第一個構造函數一緻

public FileOutputStream(File file,boolean append) throws FileNotFoundException{}
           

        若第二個參數為真,則意味着會寫入位元組到檔案的末尾,意味着追加内容,若為假,則是寫入位元組到檔案的開頭,意味着是覆寫,如現在2.txt中的内容是123456

        當參數為真時,程式運作後文本内容是123456789,是以是追加内容

public class FileStream
{

    
    /**
     *構造函數第二個參數為真,意味着追加内容到末尾
     * @param args
     */
    public static void main(String[] args)
    {
        //建立檔案對象
        File file=new File("C:\\Users\\Administrator\\Desktop\\2.txt"); 
        
   
        try
        {
            String content="789";
            FileOutputStream out=new FileOutputStream(file,true);
            out.write(content.getBytes());
            
        }

        catch (FileNotFoundException e)
        {
          
           System.out.println("檔案不存在或者檔案不可讀或者檔案是目錄");
        }
        catch (IOException e)
        {
           System.out.println("讀取過程存在異常");
        } 
    }

}
           

     當第二個參數為false時,程式運作後内容是789,是以是覆寫内容

public class FileStream
{

    
    /**
     *構造函數第二個參數為假,意味着覆寫檔案的原本内容
     * @param args
     */
    public static void main(String[] args)
    {
        //建立檔案對象
        File file=new File("C:\\Users\\Administrator\\Desktop\\2.txt"); 
        
   
        try
        {
            String content="789";
            FileOutputStream out=new FileOutputStream(file,false);
            out.write(content.getBytes());
            
        }

        catch (FileNotFoundException e)
        {
          
           System.out.println("檔案不存在或者檔案不可讀或者檔案是目錄");
        }
        catch (IOException e)
        {
           System.out.println("讀取過程存在異常");
        } 
    }

}
           

3)建立FileOutputStream流以寫入資料到指定路徑所代表的檔案,同時建立一個新的FileDescriptor對象來表示與該檔案的關聯(源碼中會new一個該對象)

public FileOutputStream(String name) throws FileNotFoundException{}
           

   檢視源碼,其本質就是調用了FileOutputStream(File file, boolean append){}方法,是以規則都和上述構造方法一緻

public FileOutputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null, false);
    }
           

4)建立FileOutputStream流以寫入資料到指定路徑所代表的檔案,同時建立一個新的FileDescriptor對象來表示與該檔案的關聯(源碼中會new一個該對象), 如果第二個參數為true,則位元組将寫入檔案的末尾而不是開頭

public FileOutputStream(String name,boolean append) throws FileNotFoundException
           

    檢視源碼,其本質是調用了FileOutputStream(File file, boolean append){}方法,是以規則都和上述構造方法一緻

public FileOutputStream(String name, boolean append)
        throws FileNotFoundException
    {
        this(name != null ? new File(name) : null, append);
    }
           

4)是以雖然有4個構造方法,但是究其本質發現都是調用了下面這個構造方法,是以後續使用FileOutputStream流時,直接使用該構造方法即可

public FileOutputStream(File file, boolean append)
        throws FileNotFoundException
    {
    }
           

三、FileOutputStream流構造方法的特殊之處

        之前提到過FileIntputStream流建立時若檔案不存在就會報FileNotFoundException異常,但是我們發現FileOutputStream流中的構造方法說明提到的是不存在且無法建立才會報FileNotFoundException異常。也就是意味着若不存在但可建立的情況下是不會有異常産生的

      是以FileOutputStream流的構造方法可以用于生成系統檔案

  如現在桌面上沒有3.txt這個檔案,但是經過程式運作後,會生成一個3.txt檔案,并且輸入了位元組内容到其中

public class FileStream
{

    
    /**
     *  構造函數可以用于生成檔案
     * @param args
     */
    public static void main(String[] args)
    {
        //建立檔案對象
        File file=new File("C:\\Users\\Administrator\\Desktop\\3.txt"); 
        
   
        try
        {
            String content="abcdefg";
            
            FileOutputStream out=new FileOutputStream(file,false);
            
            out.write(content.getBytes());
            
        }

        catch (FileNotFoundException e)
        {
          
           System.out.println("檔案不存在或者檔案不可讀或者檔案是目錄");
        }
        catch (IOException e)
        {
           System.out.println("讀取過程存在異常");
        } 
    }

}
           

四、FileOutputStream流的的常用API

1)将指定的一個位元組寫入檔案的輸出流中,是以是一次寫入一個位元組

public void write(int b) throws IOException
           

     此處存疑--注意參數是int型不是byte型的.這個跟輸入流讀取資料時讀取的是位元組但是傳回的是int型一樣存疑--涉及到位元組的位數問題了

write(int n)方法執行個體:需要先将要寫入的内容轉成位元組數組然後再進行循環多次寫入才可以

public class FileStream
{

    
    /**
     *  write方法
     * @param args
     */
    public static void main(String[] args)
    {
        //建立檔案對象
        File file=new File("C:\\Users\\Administrator\\Desktop\\4.txt"); 
        
   
        try
        {
            String content="abcdefg";
            
            FileOutputStream out=new FileOutputStream(file,false);
            
            byte[] b=content.getBytes();
            
            System.out.println("要寫入的位元組資料長度:"+b.length);
                    
            for (int i = 0; i < b.length; i++)
            {

                out.write(b[i]); //此處等于是進行了向上轉換,即byte類型的轉換成了int類型然後調用方法,向上裝換不需要強轉辨別
                
                System.out.println("寫入次數:"+(i+1)); 
            }
 
        }

        catch (FileNotFoundException e)
        {
          
           System.out.println("檔案不存在或者檔案不可讀或者檔案是目錄");
        }
        catch (IOException e)
        {
           System.out.println("讀取過程存在異常");
        } 
    }

}
           

2)将指定位元組數組中的b.length個位元組寫入到輸出流中

public void write(byte[] b) throws IOException {}
           

         指定位元組數組是指含有要寫入資料的位元組數組作為參數傳入,檢視源碼發現其本質是調用了另一個API方法

public void write(byte b[]) throws IOException {
        writeBytes(b, 0, b.length, append);
    }
           

write(byte[] b)方法執行個體:本質是調用其他方法執行的 ,注意參數append是構造函數中的第二個參數,預設為false

public class FileStream
{

    
    /**
     *  write(byte【】 b)方法
     * @param args
     */
    public static void main(String[] args)
    {
        //建立檔案對象
        File file=new File("C:\\Users\\Administrator\\Desktop\\4.txt"); 
        
   
        try
        {
            String content="abcdefg";
            
            FileOutputStream out=new FileOutputStream(file,false);
            
            out.write(content.getBytes()); 
         
        }

        catch (FileNotFoundException e)
        {
          
           System.out.println("檔案不存在或者檔案不可讀或者檔案是目錄");
        }
        catch (IOException e)
        {
           System.out.println("讀取過程存在異常");
        } 
    }

}
           

3)将從偏移量off開始的指定位元組數組中的len個位元組寫入輸出流中

public void write(byte[] b,int off,int len) throws IOException{}
           

   參數b代表着含有要寫入資料的位元組數組,參數off代表着從數組下标off開始,參數len表示最終寫入的位元組個數

   如write(bytes,0,5)則意味着從位元組數組bytes中下标0開始讀5個位元組到輸出流中

  檢視源碼發現其調用的是另外一個native方法,前面提過native方法是調用的其它語言的方法,無法檢視實作

public void write(byte b[], int off, int len) throws IOException {
        writeBytes(b, off, len, append);
    }
    
    private native void writeBytes(byte b[], int off, int len, boolean append)
        throws IOException;
           

但是我們根據其父類OutputStream類中參考下其類似的方法來了解

public void write(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if ((off < 0) || (off > b.length) || (len < 0) ||
                   ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }
    }
           

  可以看出最終的本質還是write(byte b[], int off, int len)方法等于多次調用write(int n)方法

  而根據上面的write(byte b[])方法本質是調用writeBytes(b, 0, b.length, append)方法的

write(byte b[], int off, int len)方法執行個體 : 相比write(byte b[])方法等于是靈活控制了要輸入的内容

public class FileStream
{

    
    /**
     *  write(byte b[], int off, int len)方法
     * @param args
     */
    public static void main(String[] args)
    {
        //建立檔案對象
        File file=new File("C:\\Users\\Administrator\\Desktop\\4.txt"); 
        
   
        try
        {
            String content="abcdefg";
            
            FileOutputStream out=new FileOutputStream(file,false);
            
            byte[] bytes=content.getBytes(); //得到裝有内容的位元組數組
            
            out.write(bytes,1,4);  //代表我隻想要從下标1開始的4個位元組,即bcde
         
        }

        catch (FileNotFoundException e)
        {
          
           System.out.println("檔案不存在或者檔案不可讀或者檔案是目錄");
        }
        catch (IOException e)
        {
           System.out.println("讀取過程存在異常");
        } 
    }

}
           

4)關閉輸出流并釋放與此流關聯的所有系統資源。輸出流可能不再用于寫入位元組。

public void close() throws IOException{}
           

     檢視源碼如下:其中有同步鎖關鍵詞,個人了解為若寫完資源後不進行關閉,則不會釋放鎖,那麼其他地方也無法對該檔案資源進行其他的讀寫操作

public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }

        if (channel != null) {
            /*
             * Decrement FD use count associated with the channel
             * The use count is incremented whenever a new channel
             * is obtained from this stream.
             */
            fd.decrementAndGetUseCount();
            channel.close();
        }

        /*
         * Decrement FD use count associated with this stream
         */
        int useCount = fd.decrementAndGetUseCount();

        /*
         * If FileDescriptor is still in use by another stream, the finalizer
         * will not close it.
         */
        if ((useCount <= 0) || !isRunningFinalize()) {
            close0();
        }
    }
           

五、三種write方法對比

         1、通過上述執行個體代碼其實可以發現使用write(int n)是需要傳遞單位元組作為參數,但是一般情況我們都是接收的位元組數組,與其使用位元組數組進行循環調用還不如使用write(byte[] b)方法,直接把内容轉化成位元組數組作為參數調用即可

         2、 三者之間寫資料的效率,根據源代碼可以看出,雖然具體的實作方法是native修飾無法檢視,但是根據父類方法對比發現三者實質都是通過for循環進行的單位元組寫入,是以認定三者寫資料的效率差不多一樣

         3、是以使用FileOutputStream流寫資料時一般使用第二種和第三種write方法即可