JAVA I/O(三)

文件創建
調用方法:createNewFile()
這個方法會拋出一個IOException的異常
    File file = new File("D:/1.txt");
    file.createNewFile();
對於createNewFile方法它只能去創建文件,不可以創建目錄
創建目錄
調用方法mkdir()
    File file = new File("D:/newDir");
    file.mkdir();
創建目錄時注意還有一個方法mkdirs()
mkdir與mkdirs方法的區別
mkdir:它只能創建一級的目錄
mkdir:它可以創建多級的目錄
    File file = new File("D:/newDir/xiaoxie/proj");
    file.mkdirs();
刪除文件
調用方法delete()    
    File file = new File("D:/1.txt");
    file.delete();
對於delete方法即可以刪除文件也可以刪除目錄,但是在刪除目錄時有一個限制條件:必須保證所刪除的目錄爲空目錄
如果要刪除非空目錄,則需要進行遍歷刪除
遍歷刪除目錄的方法:
public static void delete(File dir){
    //如果是目錄需要刪除它下面的所有文件
    if(dir.isDirectory()){
        //遍歷它的下一級進行刪除,使它成爲一個空目錄
        File[] files = dir.listFiles();
        for (File file : files) {
            delete(file);
        }
    }
    //如果是文件則直接刪除,如果是目錄則在上一個if塊中處理完後最後做一次刪除
    dir.delete();
}
注意:上述的刪除操作是不會到回收站當中去的,它會進行徹底的刪除操作
文件重命名
調用方法renameTo(File 新文件/目錄)
這個方法可以實現把一個文件或目錄移動至什麼位置同時可以給定它新的名稱,從而實現了重命名的目的
    File file = new File("D:/newDir");
    file.renameTo(new File("D:/資料/haha"));
上面這個則會把D盤下的newDir這個目錄移到D:\資料目錄下並且由原來的newDir這個目錄名稱變更爲haha
 
 
I/O流的基本介紹
    關於IO流的四個基本抽象類
        InputStream    字節的輸入流
        OutputStream    字節的輸出流
        Reader        字符的輸入流
        Writer        字符的輸出流
    
    IO流的分類
        按方向分(是輸入還是輸出),這個是以程序爲中心來討論的
            輸入流:進行讀的操作
            輸出流:進行寫的操作
        按數據處理的單位不同分(字符、字節)
            字節流:以字節爲單位,它適用於所有類型的數據
            字符流:以字符爲單位,它僅僅能處理字符數據(.txt、.java、.js、.html、.css、……)
        按IO流的角色來分
            節點流:如:文件流
            處理流:如:緩衝流、編碼解碼IO流、序列化與反序列化流(它是在其它流【節點流和處理流】的基礎上增加功能用的,它依賴於節點流,節點流纔是最終要達成的目標,而處理流只是在這個達成目標過程中做的一些功能增強)
            IO流的設計用到了裝飾者設計模式
                如讀取一個文件
                    文件輸入流(節點流) + 緩衝流(處理流) + 反序列化流(處理流)
    
    常見的IO流
分類
類型
說明
備註
文件相關IO流
FileInputStream
文件字節的輸入流
讀取任意類型的文件
FileOutputStream
文件字節的輸出流
可以把數據寫到任意類型的文件
FileReader
文件字符的輸入流
只能讀取純文本文件
FileWriter
文件字符的輸出流
只能把數據寫到純文本的文件
緩衝流
BufferedInputStream
字節緩衝的輸入流
給InputStream系列的IO流增加緩衝功能
BufferedOutputStream
字節緩衝的輸出流
給OutputStream系列的IO流增加緩衝功能
BufferedReader
字符緩衝的輸入流
給Reader系列的IO流增加緩衝功能
BufferedWriter
字符緩衝的輸出流
給Writer系列的IO流增加緩衝功能
數據IO流
DataInputStream
數據字節輸入流
 
DataOutputStream
數據字節輸出流
 
字符串IO流
StringReader
字符串輸入流
 
StringWriter
字符串輸出流
 
數組IO流
ByteArrayInputStream
從byte[]數組中讀取
 
ByteArrayOutputStram
寫到byte[]數組
 
CharArrayReader
從char[]數組中讀取
 
CharArrayWriter
寫入char[]數組
 
對象IO流
ObjectInputStream
對象字節輸入流
反序列化(反序列化就是把字節序列解析爲java對象)
ObjectOutputStram
對象字節輸出流
序列化(序列化的意思就是把java對象轉爲字節序列)
打印流
PrintStream
字節輸出
 
PrintWriter
字符輸出
 
 
 
IO流的基本使用
 
    IO流相關的程序的編碼步驟
       1. 選擇一個合適的IO流類型
            比如:我們要讀取一個文件,那麼第一,想到我們需要使用文件相關的流;第二,讀相關流
                FileInputStream、FileReader  框定了這兩個後再細看是否爲純文本的,如果是則可以使用FileReader,從而確定具體要使用的那個類型
       2.調用合適的方法,讀:read系列;寫:write系列
  1. 關閉IO流,在流使用完成後要關閉流
 
    讀取文件的操作(編碼步驟的框架測試)
    
public void testReadFile(){
    try {
        //選擇合適的IO流類型,並創建對象
        FileInputStream fis = new FileInputStream("D:/a.txt");
        //讀取文件
        int read = fis.read();
        System.out.println(read);
        //關閉流
        fis.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
 
    讀文件分析
FileInputStream 對象的創建:FileInputStream fis = new FileInputStream("D:/a.txt");
關於FileInputStream對象的創建源碼如下:(構造函數),它有三個重載的構造函數
public FileInputStream(String name) throws FileNotFoundException {
    this(name != null ? new File(name) : null);
}

public FileInputStream(File file) throws FileNotFoundException {
    String name = (file != null ? file.getPath() : null);
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkRead(name);
    }
    if (name == null) {
        throw new NullPointerException();
    }
    if (file.isInvalid()) {
        throw new FileNotFoundException("Invalid file path");
    }
    fd = new FileDescriptor();
    fd.attach(this);
    path = name;
    open(name);
}

public FileInputStream(FileDescriptor fdObj) {
    SecurityManager security = System.getSecurityManager();
    if (fdObj == null) {
        throw new NullPointerException();
    }
    if (security != null) {
        security.checkRead(fdObj);
    }
    fd = fdObj;
    path = null;


    /*
     * FileDescriptor is being shared by streams.
     * Register this stream with FileDescriptor tracker.
     */
    fd.attach(this);
}

 

 
讀取文件的read方法源碼:
它讀取一個byte字節返回,如果已經讀到了文件的未尾則返回-1
public int read() throws IOException {
    return read0();
}

private native int read0() throws IOException;

 

如果需要一次讀取多個節字則可以如下進行操作
    byte[] data = new byte[100];
    int read = fis.read(data); //這個方法也有一個返回值int,它表示讀取到的字節個數,如果讀取時已經到了文件的未尾則返回-1
    System.out.println(read);
    System.out.println(new String(data,0,read));
 
這個方法的源碼如下:
public int read(byte b[]) throws IOException {
    return readBytes(b, 0, b.length);
}

private native int readBytes(byte b[], int off, int len) throws IOException;

 

 
還有一種方式進行讀取【這種方式常用
    byte[] data1 = new byte[100];
    int read1 = fis.read(data1, 10, 22);    //這裏返回讀取到了多少個字節,但是從字節數組下標爲10的開始進行存儲也就是前面的下標0-9中不存儲讀到的數據
    System.out.println(read1);
    System.out.println(new String(data1,0,read1));
這個方法的源碼如下:
public int read(byte b[], int off, int len) throws IOException {
    return readBytes(b, off, len);
}

private native int readBytes(byte b[], int off, int len) throws IOException;

 

 
 如果我們的文件內容比較長,如何進行處理呢?
這個時候我們一般使用循環進行反覆讀取
如下代碼
    byte[] data = new byte[3];
    int len = 0;
    while((len = fis.read(data)) != -1){
    System.out.print(new String(data,0,len));
    }
注意:這裏的byte字節的大小設置爲3是考慮到可能中文亂碼的問題,我們按一次3個字節的方式來讀取可以暫時規避中文亂碼問題,但是一般來說對於純文本的文件來說讀取一般考慮使用字符流的方式來進行讀取(FileReader)。
使用FileReader進行文件的讀取的步驟是不變的
1.創建一個FileReader
      FileReader fr = new FileReader("D:/a.txt");
2.讀取文件內容
    讀取年的方式也有三種
    int read();
    int read(char[] data);
    int read(char[] data,int offset,int len);
    讀取的代碼如下:
       char[] data = new char[10];
    int read = fr.read(data); //這個時候按字符來進行讀取則不會存在中文亂碼的問題
    System.out.println(new String(data));
3.關閉IO流
       fr.close();
            
    寫文件分析
寫入文件的代碼框架與讀取的一致
       //創建一個IO
    FileOutputStream fos = new FileOutputStream("D:/c.txt");
    //寫操作
    fos.write("你好,中國!".getBytes());
    //關閉IO
    fos.close();
注意:在這裏使用了FileOutputStream字節輸出流,那麼在調用write方時需要傳入一個字節或者字節數組,上面方法調用時把要寫入的字符串轉爲了一個字節數組
如果我們寫入的是文本信息則可以選擇字符流FileWriter來方便我們操作字符,相關代碼如下:
    FileWriter fw = new FileWriter("D:/c.txt");
    fw.write("你好,祖國!");
    fw.close();
注意:按上面的方式進行寫入文本內容時會把原來D:\c.txt中的內容覆蓋掉,這個時會把原來的內容全部清除掉然後把新的文本內容寫入
如果我們需要使用追加的方式寫入內容則可以如下
    //下面這種創建方式表示使用追加的方式
    FileWriter fw = new FileWriter("D:/c.txt",true);
    fw.write("我是在後面再次寫入的內容");
    fw.close();
也就是在創建輸出流對象是地第二個參數指定爲true,表示追加寫入
 
    文件複製操作
對於文件的複製操作有如下幾個步驟
1.選擇IO流
    要讀取源文件:FileInputStream (如果是文本類型的文件可以使用FileReader)
    要輸出到目標文件:FileOutputStream(如果是文本類型的文件可以使用FileWrite)
2.一邊進行讀取,同時進行寫入
    從源文件中讀取數據後寫入到目標文件中
3.關閉IO流
代碼實現如下:
    /**
    * 複製一個文件
    * @param srcPath 原文件的目錄
    * @param destPath 要複製到的文件目錄
    */
    public static void copyFile(String srcPath,String destPath) throws IOException {
    // 創建IO
    FileInputStream fis = new FileInputStream(srcPath);
    FileOutputStream fos = new FileOutputStream(destPath);
 
    //進行數據的讀、寫操作
    //定義一個緩衝字節數組,存儲每次讀取到的字節數據
    byte[] data = new byte[1024]; //這裏的1024表示一次最多讀取1KB的字節數據,這裏要做合理的設置,太大佔用內存,太小則在大文件的複製過程中效率低下
    int len;
    while((len = fis.read(data))!=-1){
        fos.write(data,0,len); //保證每次只寫入讀取到的字節數
    }
    //關閉IO
    fis.close();
    fis.close();
    }
 
    緩衝IO流
緩衝IO流的目的是爲了用來提高IO讀寫的效率,我們可以使用緩衝IO流來改造上述的文件複製過程
它的操作步驟如下:
1.選擇IO流
    從文件讀,並且寫到文件中的IO流:FileInputStream、FileOutPutStream
    使用緩衝IO流來提高效率:BufferedInputStream(包裝FileInputStream)、BufferedOutputStream(包裝FileOutputStream)
2.一邊讀一邊寫
3.關閉IO流
代碼實現如下:
    /**
    * 複製一個文件
    * @param srcPath 原文件的目錄
    * @param destPath 要複製到的文件目錄
    */
    public static void copyFile(String srcPath,String destPath) throws IOException {
    // 創建IO
    FileInputStream fis = new FileInputStream(srcPath);
    FileOutputStream fos = new FileOutputStream(destPath);
    // IO流進行包裝,增加緩衝效果
    BufferedInputStream bis = new BufferedInputStream(fis);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
 
    //對文件進行讀寫操作
    byte[] data = new byte[1024];
    int len;
    while((len = bis.read(data)) != -1){
        bos.write(data,0,len);
    }
    //關閉IO,這裏需要注意,可以只關閉最外層的IO流,如要關閉全部則需要使用先創建後關閉的原則
    bis.close();
    fis.close();
    bos.close();
    fos.close();
    }
 
 
IO流的異常處理
傳統的異常處理的框架如下:
try{
    //執行的代碼塊
} catch (Exception e){
    //異常捕獲後的處理
} finally{
    //不管是否產生異常都會執行的語句
}
 
首先,我們在使用IO流的過程中可能會產生異常,需要使用try塊進行包裹,並catch對應的異常
其次,我們在finally中關閉我們的IO流(原因是不管我們是否在使用IO流進行操作過程中是否產生了異常,都需要把創建的IO流關閉)
那麼我們的copyFile方法,會如同如下:
/**
* 複製一個文件
* @param srcPath 原文件的目錄
* @param destPath 要複製到的文件目錄
*/
public static void copyFile(String srcPath,String destPath) {
    // 創建IO流
    FileInputStream fis = null;
    FileOutputStream fos = null;
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
        fis = new FileInputStream(srcPath);
        fos = new FileOutputStream(destPath);
        // 對IO流進行包裝,增加緩衝效果
        bis = new BufferedInputStream(fis);
        bos = new BufferedOutputStream(fos);
        //對文件進行讀寫操作
        byte[] data = new byte[1024];
        int len;
        while((len = bis.read(data)) != -1){
            bos.write(data,0,len);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally{
        //關閉IO流,這裏需要注意,可以只關閉最外層的IO流,如要關閉全部則需要使用先創建後關閉的原則
        try {
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

爲什麼在finally中也需要使用try呢?這個是由於我們關閉流的操作也是會有可能拋出異常的。
上面的寫法看上去是非常繁瑣的,在JDK1.7後新增語法如下:
try(資源聲明){
    ...
} catch(Exception e) {
這個時候finally不用寫了,使用完後,上的聲明的資源會自動關閉
修改上面的複製文件的方法,代碼如下:
/**
* 複製一個文件
* @param srcPath 原文件的目錄
* @param destPath 要複製到的文件目錄
*/
public static void copyFile1(String srcPath,String destPath) {


    try(
            //聲明資源,在此處聲明後會自動釋放這些資源
            FileInputStream fis = new FileInputStream(srcPath);
            FileOutputStream fos = new FileOutputStream(destPath);
            BufferedInputStream bis = new BufferedInputStream(fis);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            ){
        //對文件進行讀寫操作
        byte[] data = new byte[1024];
        int len;
        while((len = bis.read(data)) != -1){
            bos.write(data,0,len);
        }
    }catch (FileNotFoundException e){
        e.printStackTrace();
    } catch (IOException e){
        e.printStackTrace();
    }
}

 

上面的代碼看上去則沒有那麼的繁瑣,並且對於資源來說也一定會做到釋放
注意:上面的代碼需要在JDK 1.7之後的版本才能執行
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章