0九、IO流—File類與IO流


1、File類

基本認識

File類:java.io.File,也是io流中的一部分,File類可以新建、刪除、重命名文件與目錄,可是不能訪問文件自己,如果須要訪問文件內容須要使用到輸入輸出流。html

建立對象:經過構造器獲取,介紹三個java

  • File(String pathname):可填入絕對路徑與至關路徑,相對路徑就是在本項目目錄下。
  • File(String parent, String child):parent是父路徑、child是子文件路徑。
  • File(File parent, String child):父File對象與子文件路徑。
@Test
public void test01(){
    //第一個構造器:File(String pathname)
    File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner");
    //第二個構造器:File(String parent, String child)
    File file1 = new File("C:\\Users\\93997\\Desktop\\fileexer","javahoner");
    //第三個構造器:File(File parent, String child)
    File file2 = new File(new File("C:\\Users\\93997\\Desktop\\fileexer"),"javahoner");
    System.out.println(file);
    System.out.println(file1);
    System.out.println(file2);
}
  • 第三個構造器第一個參數File parent:當其不爲空時,實際上也是取該對象屬性string path
  • println()輸出路徑:File類其toString()方法就是打印了String path參數。

image-20210131090330410


路徑分隔符:就是上面構造器中的那個\\,不一樣操做系統使用的也不一樣c++

  • windows與Dos系統默認:\數據庫

  • Unix與URL使用:/windows

  • 由於Java跨平臺,因此對於路徑分隔符需慎用,Java也給出通用的:File.separator,咱們在建立實例時可使用這個參數來代替咱們手寫/或\。(這個屬性是經過調用方法來獲取到本地文件系統的默認分隔符)數組

    • //至關於C:\Users\93997\Desktop\fileexer\javahoner 
      File file = new File("C:\\Users\\93997\\Desktop\\fileexer\"+File.separator+\"javahoner");


實用方法

獲取功能

獲取文件對象的相關信息緩存

String getAbsolutePath():獲取文件的絕對路徑安全

String getPath:獲取路徑(也是絕對路徑)網絡

String getName:獲取路徑下的最後一個文件名app

String getParent():獲取上層文件目錄路徑,若無,返回null

Long length():獲取文件長度(字節數)

Long lastModified():獲取最後一次修改文件的時間戳(毫秒值)

String[] list():獲取指定目錄下全部文件的名稱

File[] listFiles():獲取指定目錄下全部文件,以對象形式返回

  • 若調用方法的抽象路徑名不表示目錄,返回值爲null

.....

實際使用:

@Test
public void test01(){
    //第一個構造器:File(String pathname)
    File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner");
    System.out.println(file.getAbsolutePath());//C:\Users\93997\Desktop\fileexer\javahoner
    System.out.println(file.getPath());//C:\Users\93997\Desktop\fileexer\javahoner
    System.out.println(file.getName());//javahoner
    System.out.println(file.getParent());//C:\Users\93997\Desktop\fileexer
    System.out.println(file.length());//0
    System.out.println(file.lastModified());//1612056310161
    for (String childFile : file.list()) {
    System.out.print(childFile+" ");
    }//文件1 文件2
    System.out.println();
    for (File listFile : file.listFiles()) {
    System.out.println(listFile);
    }
    //C:\Users\93997\Desktop\fileexer\javahoner\文件1
    //C:\Users\93997\Desktop\fileexer\javahoner\文件2
}


重命名功能(包含剪切)

方法介紹

public boolean renameTo(File dest) :剪切文件更名到指定路徑(也能夠實現重命名效果)

  • 調用該方法的File,其指定路徑下是否有文件都沒事;注意dest的文件路徑應該是不存在的
  • 返回值:dest的路徑下應該不存在文件,若存在返回false。

實際應用:重命名與剪切功能

場景1:想將javahoner目錄下的文件名爲文件修改成長路鍋鍋

File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件");
boolean b = file.renameTo(new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\重命名的文件"));
System.out.println(b);
  • 實際上就是將C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件先剪切到C:\\Users\\93997\\Desktop\\fileexer\\javahoner路徑下再更名爲不存在的文件名爲重命名文件。實現了修更名稱效果

場景2:想將javahoner目錄下的文件長路鍋鍋移動到1(2)文件中

File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\長路鍋鍋");
boolean b = file.renameTo(new File("C:\\Users\\93997\\Desktop\\1(2)\\長路鍋鍋"));
System.out.println(b);
  • 其實原理操做與上面同樣的,須要注意1(2)要不存在長路鍋鍋文件。

總結:秒呀這個方法,看了下源碼最終定位到一個WinNTFileSystemprivate native boolean rename0(File f1, File f2);根據native關鍵字,說明這個方法是一個原生函數,是使用c/c++來實現並編譯成DLL文件由java去調用。



判斷功能

這裏僅僅介紹下方法使用

public boolean isDirectory:判斷是不是文件目錄

public boolean isFile():判斷是不是文件

public boolean exists():判斷是否存在

public boolean canRead():判斷是否可讀

public boolean canWrite():判斷是否可寫

public boolean is Hidden():判斷是否隱藏



建立、刪除文件

建立功能

public boolean createNewFile():文件存在不建立返回false。

public boolean mkdir():文件目錄存在不建立,若此文件目錄的上級目錄不存在也不建立。

public boolean mkdirs():不只僅是此目錄文件,如果此目錄的上層文件目錄不存在一併建立。


刪除功能

public boolean delete():刪除此抽象路徑名錶示的文件或目錄,如果此路徑名錶示目錄,則目錄必須爲空才能刪除。



實際小案例

案例:刪除指定路徑下全部文件

public class Main {

    //生成num個空白字符串,如果num非0最後添加-
    public static String blankStr(int num){
        StringBuilder str = new StringBuilder();
        for (int i=0;i<num;i++){
            str.append(" ");
        }
        if(num != 0){
            str.append("-");
        }
        return str.toString();
    }



    //刪除路徑下內容(包含層級關係)
    public static void deleteAllFile(File file,int tier) throws RuntimeException{
        //若是該路徑是一個文件直接刪除
        if(file.isFile()){
            System.out.println(file.delete()?blankStr(tier)+file.getName()+"已刪除":blankStr(tier)+file.getName()+"刪除失敗");
            return;
        }
        //該路徑是目錄
        File[] files = file.listFiles();
        //空目錄直接刪除
        if(files == null || files.length == 0){
            System.out.println(file.delete()?blankStr(tier)+"成功刪除"+file.getName():blankStr(tier)+file.getName()+"空目錄刪除失敗");
            return;
        }
        //目錄不爲空,遍歷當前文件
        for (File dict : files) {
            //目錄:刪除目錄中內容+刪除本身自己
            if(dict.isDirectory()){
                //獲取目錄下的全部文件
                File[] files2 = dict.listFiles();
                //若是目錄爲空直接刪除
                if(files2 == null || files2.length == 0){
                    System.out.println(dict.delete()?blankStr(tier)+"成功刪除"+dict.getName():blankStr(tier)+dict.getName()+"空目錄刪除失敗");
                }else{
                    //非空目錄狀況
                    deleteAllFile(dict,tier+1);
                    System.out.println(dict.delete()?blankStr(tier)+"成功刪除"+dict.getName():blankStr(tier)+dict.getName()+"空目錄刪除失敗");
                }
            }else{
                //非目錄:無提示直接調用
                deleteAllFile(dict,tier+1);
            }
        }
    }

    //測試
    @Test
    public void test01(){
        File file = new File("C:\\Users\\93997\\Desktop\\fileexer\\javahoner\\文件1");
        try {
            deleteAllFile(file,0);
        } catch (RuntimeException e) {
            System.out.println("刪除非空文件時出現異常!!!");
        }
        System.out.println("該文件路徑下內容已被刪除");
    }
}
  • 調用deleteAllFile(File file,int tier)便可:參數1就是刪除指定文件路徑,參數2表示層級關係(設置爲0最高級)


2、IO流

一、認識IO流

Google將I/O譽爲"開放中創新",input/output:二進制1,0

IO原理:是Input/Output的縮寫,I/O技術是很實用的技術,用來處理設備之間的數據傳輸。例如讀/寫文件,網絡通信等。

  • input:讀取外部數據(例如磁盤、光盤等存儲設備的數據)到程序(內存)中。
  • output:將程序(內存)中的數據庫輸出到磁盤、光盤等存儲設備中。

在Java程序中,對於數據的輸入/輸出操做以"流(stream)"的方式進行。java.io包下提供了各類"流"類和接口,用以獲取不一樣種類的數據,並經過標準的方法輸入或輸出數據。


流的分類

根據數據單位不一樣分爲兩類:字節流與字符流

  • 字節流:最基本單位是字節byte的流,一般用來傳輸視頻、圖片(非文本的數據)
  • 字符流:最基本單位是字符char的流,一般用來文本輸入輸出。
    • 說明:中文字體在不一樣編碼下佔不一樣字節數,在UTF-8中文所佔字節不肯定多是2,3,4個字節,因此建議進行文本輸入輸出選擇字符流。
  • 區別(重要):字節流按字節讀數據,不須要編碼解碼;字符流按字符讀數據,一次讀兩個字節,並返回這兩個字節所對應的int型數值)(編碼)。

數據的流向不一樣分爲:輸入流、輸出流

角色不一樣分爲:節點流、處理流

Java的IO流所有都是由下面這4個抽象基類派生:

(抽象基類) 字節流 字符流
輸入流 InputStream Reader
輸出流 OutputStream Writer

I/O流體系圖

image-20210131142003105


關係圖

image-20210131142034039



二、IO流基類介紹

字節流基類介紹

對於字節流的輸入輸出流的基類,首先介紹一下InputSream以及OutputSream

  • InputSream爲字節輸入流的抽象基類,其基類定義瞭如下幾個通用方法,這裏列舉幾個
    • int read():從流中讀取1個字節並返回,若是達到文件末尾返回-1;
    • read(byte b[]):從流中讀取b的長度個字節的數據存儲到b中,返回的是讀取的字節個數,如果返回-1表示到告終尾,沒有數據。
    • int read(byte b[], int off, int len):從流中的off位置開始讀取len個字節的數據到b中,返回結果是實際讀取的字節個數,如果返回-1表示沒有數據。
    • void close():關閉輸入流
  • OutputSream爲字節輸出流的抽象基類,這裏列舉幾個該基類經常使用方法
    • void write(int b):將1個字節寫入到輸出流中
    • void write(byte b[]):從b的off位置開始,獲取len個字節數據,寫到輸出流中
    • void flush():刷新此輸出流並強制任何緩衝的輸出字節被寫出。(爲以後緩衝流提供抽象方法)
    • void close():關閉輸出流

輸出入流關字節流的含節點流以及各類處理流它們的基類就是這兩個。



字符流基類介紹

對於字符流的輸入輸出流的基類,介紹一下ReaderWriter

  • Reader:是字符輸入流的抽象基類 ,定義了幾個函數以下
    • int read():讀取單個字符,返回結果是一個int,若想要字符顯示須要轉爲char;如果到達流的末尾,返回-1
    • int read(char cbuf[]):從流中讀取b的長度個字符的數據存儲到b中,返回的是讀取的字節個數,如果返回-1表示到告終尾,沒有數據。
    • void close() :關閉字符輸入流
  • writer:是字符輸入流的抽象基類 ,定義了幾個函數以下
    • void write(int b):將1個字節寫入到輸出流中
    • void write(byte b[]):從b的off位置開始,獲取len個字節數據,寫到輸出流中
    • void flush():刷新此輸出流並強制任何緩衝的輸出字節被寫出。(爲以後緩衝流提供抽象方法)
    • void close():關閉輸出流

與以前介紹的字節流大體相同,他們兩個區別之一就是它們傳遞的數據單位不一樣一個是字節、一個是流。



3、節點流與處理流

一、節點流

介紹說明

節點流也稱文件流,對應節點流包含了字節流與字符流

  • 字節流:FileInputSreamFileOutputSream
  • 字符流:FileReaderFileWriter
  • 注意點
    • ①字符流不能用來讀取圖片、視頻等由於字符涉及到編碼轉換問題,在讀取使用字節存儲的圖片時會對於某些字節轉換不到形成問題;
    • ②字節流能夠用來讀文本文件之類,不過在讀取過程當中如果打印顯示可能會有亂碼存在,中文通常佔2-4個字節,有時候讀取了一半就顯示就會出現問題。


實際小案例

案例1:使用字節流來複製圖片

import org.junit.Test;
import java.io.*;

public class Main {
    
    @Test
    public void test02(){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //目標圖片1234.jpg
            fis = new FileInputStream(new File("1234.jpg"));
            //複製地址
            fos = new FileOutputStream(new File("圖片.jpg"));
            byte[] data = new byte[1024];
            int len;
            //讀取字節
            while((len = fis.read(data)) != -1){
                //寫入data數組中讀取到的len個字節
                fos.write(data,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 輸入輸出流對象建立須要帶有File對象或者路徑,若是File對應的路徑不存在,會自動新建的。
  • 說明:這個案例是將相對路徑下的1234.jpg複製一份爲圖片.jpg。複製視頻只要更改路徑便可,同樣的方法。

案例2:複製文本文件,而且在控制檯顯示

import org.junit.Test;
import java.io.*;


public class Main {

	//這裏主要爲了演示就不像以前那麼規範,直接拋出異常
    @Test
    public void test02() throws IOException {
        FileReader fr = new FileReader("changlu.txt");;
        FileWriter fw = new FileWriter("cl.txt");
        char[] data = new char[1024];
        int len;
        while((len = fr.read(data)) != -1){
            //顯示在控制檯中
            System.out.print(new String(data,0,len));
            fw.write(data,0,len);
        }
        fr.close();
        fw.close();
    }
}
  • 這裏是複製到指定路徑下的文件目錄中(覆蓋操做)。如果想要追加可以使用FileWriter(File file, boolean append)這種構造器方式,第二個參數填true表示數據寫入從文件末尾開始。

image-20210131160131283



總結

  1. 對於文本文件(例如.txt,.java,.c,.cpp)儘可能使用字符流處理,字節流也是能夠的。
  2. 對於非文本文件(例如.jpg,.mp3,.mp4,.avi,.doc,.ppt,.....) 之類使用字節流處理。


二、緩衝流

緩衝流介紹

首先看一下緩衝流,前兩個是用於傳輸字節的緩衝流,後兩個是傳輸字符的緩衝流

image-20210131160820342

看一下繼承關係

傳輸字節的兩個緩衝流都是繼承於FilterInputStream:

image-20210131161006356

傳輸字符的兩個緩衝流都是繼承於Writer

image-20210131161126155

緩衝流的做用:提升文件的讀寫效率

提升讀寫速度的緣由:內部提供了一個緩衝區

  • 對於緩衝流對象其中會建立一個內部緩衝區數組,缺省使用8192個字節(8kb)的緩衝區,之前讀取、寫入數據每使用read()或write()方法就會寫入到文件中,而如今執行會先寫到緩衝區中,直到緩衝區寫滿以後才一次性寫到文件裏。
  • 這就有個問題:有時候明明去讀取或寫入了卻沒有讀到或者文件內沒有存儲,那可能就是存儲到緩衝區沒有到文件中,可使用flush()方法強制將緩衝區內容寫入到輸出流中。

使用了緩衝區與沒有使用的圖示

image-20210131161950695



使用緩衝流

實際上使用緩衝流很簡單,直接在節點流上包一層,緩衝流也是須要進行手動關閉的,關閉的同時會將節點流也關閉。

示例:這裏

//字節流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("changlu.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("test.txt")));

//字符流
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("file2.txt"));

針對於緩衝流本身實現了一個方法

  • void newLine():向文件中寫入換行符

通常寫入完以後咱們還須要使用flush()方法來將緩存區的內容手動寫入到文件中。

  • 通常只有當緩衝區存滿8kb字節時纔會寫入,如果咱們寫入內容不足以8kb時就須要咱們本身手動寫入,養成好的習慣都要寫。

測試複製66MB的視頻速度

  • 字節流:932ms
  • 緩衝流:189ms

介紹圖片加密的方法

//加密data數組中0-len中的字節
public static byte[] encryptChar(byte[] data,int len){
    for (int i = 0; i < len; i++) {
        data[i] ^= 5;//經過異或的方式
    }
    return data;
}

//寫入操做 省略了內容
byte[] data = new byte[1024];
int len;
//讀取字節
while((len = bis.read(data)) != -1){
    //寫入data數組中讀取到的len個字節
    bos.write(encryptChar(data,len));
}
  • 如何解密呢?從新讀取再次進行異或便可實現解密的效果!!!


三、轉換流

認識轉換流

轉換流提供了在字節流和字符流之間的轉換

image-20210131172749165

  • InputStreamReader:將InputStream轉換爲Reader 字節轉字符
  • OutputStreamWriter:將Writer轉換爲OutputStream 字符轉字節

當字節流中的數據都是字符時,轉成字符流更高效。大多經過使用轉換流來處理文件亂碼問題,實現編碼和解碼功能!

  • 編碼:字符串 =》字節數組
  • 解碼:字節數組 =》字符串

簡單舉個例子:將changlu.txt(UTF-8編碼)先經過InputStreamReader轉爲字符,再經過使用OutputStreamWriter指定另外一個編碼轉爲長changlu.txt。(gbk編碼)

image-20210131173647862

轉換流的編碼應用

  • 能夠將字符按指定編碼格式存儲
  • 能夠對文本數據按指定編碼格式來解讀
  • 指定編碼表的動做由構造器完成


實際小案例

使用轉換流將一個UTF-8編碼文件轉爲GBK編碼的文件

public class Main {

    public static void main(String[] args) throws IOException {
        //轉換流 將一個UTF-8編碼的轉爲GBK編碼的
        InputStreamReader isr = new InputStreamReader(new FileInputStream("changlu.txt"), StandardCharsets.UTF_8);
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("changluya.txt"), "GBK");
        //使用緩衝流加速
        BufferedReader br = new BufferedReader(isr);
        BufferedWriter bw = new BufferedWriter(osw);
        char[] data = new char[1024];
        int len;
        while((len = br.read(data)) != -1){
            bw.write(data,0,len);
        }
        br.close();
        bw.close();
    }

}


四、標準輸入、輸出流

系統標準的輸入和輸出設備分別爲:System.in與System.out。默認輸入設備:鍵盤;默認輸出設備:顯示器

  • System.in:實際類型爲InputStream
  • System.out:實際類型爲PrintStream,其次是OutputStream的子類

咱們能夠更改System的輸入輸出設備經過System的的setInsetOut方法。



實際小案例

傳統咱們經過使用Scanner來進行數據的輸入獲取,在這裏不容許使用Scanner,要求從鍵盤輸入字符串,要求將讀取到的整行字符串轉成大寫輸出。而後繼續 進行輸入操做,直至當輸入「e」或者「exit」時,退出程序。

public static void main(String[] args) throws IOException {
    //System.in是InputStream實例(字節流),這裏包一層轉換流轉換爲字符流
    InputStreamReader is = new InputStreamReader(System.in);
    //包裝上一層緩衝流
    BufferedReader br = new BufferedReader(is);
    String str;
    //鍵盤中每讀取一行數據進行循環
    while((str = br.readLine()) != null){
        if("e".equals(str) || "exit".equals(str)){
            System.out.println("安全退出");
            break;
        }
        str = str.toUpperCase();
        //System.out => PrintStream
        System.out.println(str);
        System.out.println("繼續輸入信息");
    }
    br.close();
}

image-20210131190937116



五、打印流(PrintStream與PrintWriter)

基本介紹

實現將基本數據的類型格式轉換爲字符串輸出

image-20210131193339183

  • 提供了一系列重載的print()和println()方法,用於多種數據類型的輸出。
  • 這兩個打印流都不會拋出IOException異常(受檢)。
  • 都有自動flush()功能。
  • PrintStream打印的全部字符都使用平臺的默認字符編碼轉換爲字節,在須要寫入字符而不是寫入字節的狀況下應該使用PrintWriter

單個介紹

PrintStream:在實現OutputStream接口上又實現了打印各類數據的print方法,一般使用系統默認的System.out調用方法輸出

  • image-20210131191424786 屬於字節流
  • 最終輸出的老是byte數據

PrintWriter:擴展了Write接口,也實現了許多print打印輸出方法

  • image-20210131192010729 屬於字符流

  • 最終輸出的是char字符

  • //使用方式:配合StringWriter獲取數據並打印到控制檯
    public static void main(String[] args) throws IOException {
        //內部定義了一個StringBuffer存儲數據
        StringWriter str = new StringWriter();
        try (PrintWriter pw = new PrintWriter(str)){
            //將指定內容寫入到str中,實際上仍是調用了write方法
            pw.println("hello");
            pw.println("changlu");
            pw.println(2020);
        }
        //將StringBuffer對象打印
        System.out.println(str.toString());
    }


小案例

案例描述:將本來輸出到控制檯的內容輸入到文件中

思路:更改System中的輸出設備(顯示屏 =》文件)

public static void main(String[] args) throws IOException {
    //try(聲明1;聲明2;){ ... }  這種方式會自動執行close()方法
    try(//①建立文件字節流
        FileOutputStream fos = new FileOutputStream("changlu.txt");
        //②PrintStream處理流包裹節點流
        PrintStream ps = new PrintStream(fos);){
        //更改System的輸出設備爲文件流
        System.setOut(ps);
        //輸出到文件中
        System.out.println("長路&林兒");
    }
}

image-20210131195730490

執行結果:成功建立changlu.txt,並輸入到文件中。



六、數據流

image-20210202201203423

  • 只有兩個流都是字節流,分別"套接"在InputStreamOutputStream子類的流上。

目的:爲了方便操做Java的基本數據類型與String類型,可使用數據流。

這裏列舉一下DataInputStream的幾個方法以下:byte readByte() char readChar()float readFloat()long readLong()int readInt()String readUTF()

  • 例如:int readInt()一次性讀出四個字節並將其轉爲int值讀出

OutputStream幾個相似read換write便可。

  • 例如:void writeInt(int v)一次寫入四個字節並將其轉爲字節寫出

直接上小案例:輸出流與輸入流配合使用(增添了一個緩衝流來提高速度)

public class Main {

    //使用數據流的輸出流存儲不一樣類屬數據
    @Test
    public void test01(){
        try(BufferedOutputStream bis = new BufferedOutputStream(new FileOutputStream("changlu.data"));
            DataOutputStream dos = new DataOutputStream(bis);){
            dos.writeUTF("長路");
            dos.writeInt(666);
            dos.writeBoolean(false);
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //使用數據流的輸入流來獲取指定順序的數據類型
    @Test
    public void test02(){
        try(BufferedInputStream bos = new BufferedInputStream(new FileInputStream("changlu.data"));
            DataInputStream dis = new DataInputStream(bos);) {
            String name = dis.readUTF();
            int num = dis.readInt();
            boolean bol = dis.readBoolean();
            System.out.println(name+"\n"+num+"\n"+bol);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


七、對象流(序列化)

詳細對象流以及序列化見:IO流—對象序列化



八、隨機存取文件流

認識RandomAccessFile

接下來要介紹的隨機存取文件流只有一個類RandomAccessFile,它實現了DataOutputDataInput接口,直接繼承於Object,說明其實現了讀取與寫入的功能。

RandomAccessFile類功能描述:

  1. 支持隨機訪問的方式,程序能夠直接跳到文件的任意位置來讀、寫文件。
  2. 支持只訪問文件的部份內容。
  3. 能夠向已存在的文件後追加內容。
  4. 包含一個記錄指針,用來標示當前讀寫處的位置。
  5. 能夠自由獲取並移動指針的位置,例如:long getFilePointer()獲取指針的位置,void seek(long pos)將指針定位到pos位置。

構造器介紹RandomAccessFile(String name, String mode)RandomAccessFile(File file, String mode)

  • 第一個參數:File對象,其中name實際上也是建立的File實例;
  • 第二個參數:mode參數指定的是該類的訪問模式,訪問模式以下:
    • r:只讀方式打開。
    • rw:打開以便讀取和寫入。
    • rwd:打開以便讀取和寫入;同步文件內容的更新。
    • rws:打開以便讀取與寫入;同步文件內容和元數據的更新。

注意點:若模式爲r(只讀),不可以建立文件,只能讀取存在的文件,如果不存在就會出現異常;rw模式是如果不存在就會去建立文件。

  • 針對於JDK1.6上面的每次write數據,rw模式,數據不會當即寫入到硬盤中,一旦寫入過程當中有異常數據所有丟失;rwd模式數據會被當即寫入硬盤。一旦寫數據發生異常,rwd模式中會將已被寫入的數據保存到硬盤中。


小案例

案例1:複製圖片

@Test
public void test01(){
    //建立兩個隨機存取流的實例對象,分爲來進行讀或寫的操做
    try(RandomAccessFile rafRead = new RandomAccessFile("1234.jpg", "r");
        RandomAccessFile rafWrite = new RandomAccessFile("changlu.jpg", "rw");){
        byte[] data = new byte[1024];
        int len;
        while((len = rafRead.read(data))!=-1){
            rafWrite.write(data,0,len);
        }
    }catch (IOException e) {
        e.printStackTrace();
    }

}
  • 定義兩個實例來進行讀與寫的操做,該類讀取的也是字節,因此大體與以前使用節點流差很少。

案例2:複製一個文件中的內容到另外一個文件追加內容

image-20210202221134698

@Test
    public void test01(){
        //建立兩個隨機存取流的實例對象,分爲來進行讀或寫的操做
        try(RandomAccessFile rafRead = new RandomAccessFile("changlu.txt", "r");
            RandomAccessFile rafWrite = new RandomAccessFile("changlu222.txt", "rw");){
            //獲取其中的字節
            int fileLength = (int) rafRead.length();
            byte[] data = new byte[fileLength];
            for(int i=0;i<data.length;i++){
                data[i] = rafRead.readByte();
            }
            //複製內容到其餘文件中
            rafWrite.seek(2);//空兩格
            for(int i = 0 ;i<data.length;i++){
                rafWrite.writeByte(data[i]);
            }
            //新增指定內容
//            byte[] bytes = "想對林兒說我想你了".getBytes("utf-8");
//            for(int i=0;i<bytes.length;i++){
//                rafWrite.writeByte(bytes[i]);
//            }
            rafWrite.writeUTF("想對林兒說我想你了");

        }catch (IOException e) {
            e.printStackTrace();
        }

    }

image-20210202221208188 出現亂碼,比較迷糊搞不清



NIO擴展

java.nio這個類帶來了重要的效能提高並能夠充分利用執行程序的機器上的原始容量。

java1.4版新增的輸入輸出java.nio這個類帶來了重要的效能提高並能夠充分利用執行程序的機器上的原始容量。

包含一項關鍵能力是能夠直接控制buffer以及nonblocking的輸入域輸出,它能讓你的輸入/輸出程序代碼在沒有東西可讀取或寫入

時不用等在那裏。

對於nio可能會引起效能損失,非nio的輸入/輸出適合九成以上的應用,依舊可使用FileInputStream並經過getChannel()方法來開始使用nio。

NIO.2中Path、Paths、Files類的使用見語雀-NIO部分



參考資料

[1]. Java中Native關鍵字的做用

[2]. Java一個漢字佔幾個字節(詳解與原理) 特別詳細

[3]. 爲何用字符流複製的圖片打不開,而用字節流複製的卻能夠打開?

[4]. 對比字節流和字符流,回答爲何FileReader不能用來拷貝圖片

[5]. Java I/O流之隨機流詳解,包含隨機流讀寫數據時編碼格式問題!

[6]. 尚硅谷Java視頻-IO流(宋紅康)



我是長路,感謝你的閱讀,若有問題請指出,我會聽取建議並進行修正。 歡迎關注個人公衆號:長路Java,其中會包含軟件安裝等其餘一些資料,包含一些視頻教程以及學習路徑分享。 學習討論qq羣:891507813 咱們能夠一塊兒探討學習 註明:轉載可,須要附帶上文章連接

相關文章
相關標籤/搜索