JavaSE-IO流

Zh1z3ven發表於2021-07-19

JavaSE-IO流

I/O流,輸入/輸出流。資料在裝置間的傳輸稱為流,IO流就是用來處理裝置間資料傳輸問題的。

常見應用場景(檔案複製,檔案上傳,檔案下載)

Java中流分為位元組流字元流,又細分為位元組輸入流(Reader),位元組輸出流(Writer),字元輸入流(InputStream),字元輸出流(OutputStream)

位元組流

位元組流抽象基類:

InputStream:這個抽象類是表示位元組輸入流所有類的超類(讀資料)

OutputStream:這個抽象類是表示位元組輸出流所有類的超類(寫資料)

FileOutputStream

構造方法

FileOutputStream(String name) 建立檔案輸出流以指定的名稱寫入檔案, 用於寫入諸如影像資料之類的原始位元組的流
FileOutputStream(File file) 建立檔案輸出流以指定的檔案物件寫入檔案

File file = new File("src");
FileOutputStream fos = new FileOutputStream(file);
等價於
FileOutputStream fos = new FileOutputStream(new File("src"));

public static void main(String[] args) throws IOException {

        //建立位元組輸出流物件: 1、呼叫系統api建立了檔案 2、建立位元組輸出流物件 3、讓位元組輸出流物件指向建立好的檔案
        FileOutputStream fileOutputStream = new FileOutputStream("/Users/b/Desktop/java1.txt");

        // void	write(int b)
        //          將指定位元組寫入此檔案輸出流。
        fileOutputStream.write(97); //根據ascii表對應字元
        fileOutputStream.close();   //1、釋放相關資源,2、關閉此輸出流
        
    }

位元組輸出流寫資料的三種方式

void write(byte[] b) 將 b.length 個位元組從指定 byte 陣列寫入此檔案輸出流中。
void write(byte[] b, int off, int len) 將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。
void write(int b) 將指定位元組寫入此檔案輸出流。

public class FileOutputStreamDemo02 {
    public static void main(String[] args) throws IOException {

        FileOutputStream fileOutputStream = new FileOutputStream("/Users/b/Desktop/java1.txt");

        // void	write(int b)	將指定位元組寫入此檔案輸出流。
        fileOutputStream.write(97);
        fileOutputStream.write(98);
        fileOutputStream.write(99);
        fileOutputStream.write(100);
        fileOutputStream.write(101);


        //void	write(byte[] b)		將 b.length 個位元組從指定 byte 陣列寫入此檔案輸出流中。
        byte[] b = {102, 103, 104, 105, 106}; // f g h i j
        fileOutputStream.write(b);

        //void	write(byte[] b, int off, int len)   將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此檔案輸出流。
        byte[] c = {107, 108, 109, 110, 111}; //k l m n o
        fileOutputStream.write(c, 1, 3);    //這裡偏移量為1,即跳過k,從l開始寫,len為3,即寫入3個位元組也就是lmn 沒有 o

    }
}

String <=> Byte

tring str = "string";
byte[] bytes = str.getBytes();
for (int b1 : bytes
     ) {
    System.out.println(b1);
}

追加寫入

public FileOutputStream(String name, boolean append) throws FileNotFoundException

建立一個向具有指定 name 的檔案中寫入資料的輸出檔案流。如果第二個引數為 true,則將位元組寫入檔案末尾處,而不是寫入檔案開始處。

FileOutputStream fos1 = new FileOutputStream("/Users/b/Desktop/java1.txt", true);
//append:true ,此時為追加寫入的位元組輸出流

public static void main(String[] args) throws IOException {
        FileOutputStream fileOutputStream = new FileOutputStream("/Users/b/Desktop/java1.txt");
        FileOutputStream fos1 = new FileOutputStream("/Users/b/Desktop/java1.txt", true);

        for (int i = 0; i < 10; i++) {
            fileOutputStream.write("Hello".getBytes());
            fileOutputStream.write("\n".getBytes());
        }

        fileOutputStream.close();

        for (int i = 0; i < 10; i++) {
                fos1.write("World".getBytes());
                fos1.write("\r".getBytes());
        }

        fos1.close();

    }

異常捕獲

public class FileOutpuStreamExcept {
    public static void main(String[] args) {

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("/Users/b/Desktop/java1.txt");
            fos.write("Exception".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

readFile&writeFile

import java.io.*;

public class ByteStreamTestDemo {
    public static void main(String[] args) {

        ByteStreamTestDemo bstd = new ByteStreamTestDemo();
        String inputstr = bstd.readFile("/Users/b/Desktop/image1.txt");
        System.out.println(inputstr);

        bstd.writeFile("/Users/b/Desktop/image1.txt", "Hello Java");


    }

    public String readFile(String path) {
        FileInputStream fis = null;// 例項化FileInputStream物件;
        String str = null;
        try {
            // 1.根據path路徑例項化一個輸入流的物件
            fis = new FileInputStream(path);

            //2. 返回這個輸入流中可以被讀的剩下的bytes位元組的估計值;
            int size = fis.available();

            //3. 根據輸入流中的位元組數建立byte陣列;
            byte[] array = new byte[size];
            fis.read(array);//4.把資料讀取到陣列中;

            //5.根據獲取到的Byte陣列新建一個字串,然後輸出;
            str = new String(array);
            System.out.println(str);//輸出;
        } catch( FileNotFoundException e) {
            // 解決new FileInputStream(path);可能會發生的異常;
            e.printStackTrace();
        } catch (IOException e) {
            // 解決fis.available()時可能發生的異常;
            e.printStackTrace();
        } finally {
            //6.在最後,關閉我們的IO流
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return str;
    }

    public void writeFile(String path, String content) {
        FileOutputStream fos = null;

        try {
            //1.根據檔案路徑建立輸出流
            fos = new FileOutputStream(path);
            //2.把string轉換為byte陣列;
            byte[] array = content.getBytes();
            //3.把byte陣列輸出;
            fos.write(array);

        } catch (FileNotFoundException e) {
            // 解決new FileOutputStream(path)出現的問題
            e.printStackTrace();
        } catch (IOException e) {
            // 解決fos.write(array)可能會出現的問題;
            e.printStackTrace();
        } finally {
            /*
             * 在方法的最後一定實現這個方法;
             */
            try {//4.關閉IO流
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

FileInputStream

FileInputStream.read()

int read() 從此輸入流中讀取一個資料位元組。
int read(byte[] b)從此輸入流中將最多 b.length 個位元組的資料讀入一個 byte 陣列中。
int read(byte[] b, int off, int len) 從此輸入流中將最多 len 個位元組的資料讀入一個 byte 陣列中。

FileInputStream.read()

public static void main(String[] args) throws IOException {

    //建立讀資料物件
    FileInputStream fis = new FileInputStream("/Users/b/Desktop/java1.txt");

    //讀取物件
    int read = fis.read();
    while(read != -1) {

        System.out.println(read);
        System.out.println((char)read);
        read = fis.read();

    }

    //釋放資源
    fis.close();
}

int by;
while((by = fis.read()) != -1) {
  System.out.print((char)read);
}

複製字元流檔案

public static void main(String[] args) throws IOException {

    FileInputStream fis = new FileInputStream("/Users/b/Desktop/java1.txt");
    FileOutputStream fos = new FileOutputStream("/Users/b/Desktop/java2.txt");

    int by;
    while ((by = fis.read()) != -1) {
            fos.write((char)by);
    }

    fis.close();
    fos.close();
}

複製Byte流檔案

public static void main(String[] args) throws IOException {

        FileInputStream fis = new FileInputStream("/Users/b/Desktop/Images/image-2.png");
        FileOutputStream fos = new FileOutputStream("/Users/b/Desktop/image1.png");

        byte[] b = new byte[1024];
        int len;
        while ((len = fis.read(b)) != -1){
            fos.write(b);
        }

        fis.close();
        fos.close();

    }

讀取內容方法

int read() 從此輸入流中讀取一個資料位元組。 int read(byte[] b) 從此輸入流中將最多 b.length 個位元組的資料讀入一個 byte 陣列中。 int read(byte[] b, int off, int len) 從此輸入流中將最多 len 個位元組的資料讀入一個 byte 陣列中。 引數為內容存入的byte陣列,返回值為該陣列的長度或者說資料元素的個數,如果已到達檔案末尾,則返回 -1。

public static void main(String[] args) throws IOException {

        FileInputStream fis = new FileInputStream("/Users/b/Desktop/java2.txt");

        byte[] bys = new byte[1024];//  這裡通常放1024及其整數倍的資料
        int len;
        while((len = fis.read(bys)) != -1){
            System.out.println(len);
            System.out.print(new String(bys, 0, len));
        }

        fis.close();
    }

String <=> Byte 編碼轉換

String -> Byte byte[] getBytes() 使用預設字符集將String轉換為Byte儲存在陣列內 byte[] getBytes(String charsetName) 使用指定的字符集將該String編碼為Byte儲存在陣列內

Byte -> String String(byte[] bytes) 使用預設字符集將Byte轉換為String String(byte[] bytes, String CharsetName) 使用指定字符集將Byte轉換為String

public static void main(String[] args) {

        //String => Byte,預設UTF-8
        String s = "zh1z3ven";

        byte[] bytes = s.getBytes();
        System.out.println(bytes);
        
        for (int i : bytes
             ) {
            System.out.println(i);
        }

        System.out.println(Arrays.toString(bytes));

        //Byte => String
        byte[] b = {97, 98, 99, 100, 101, 102};
        String str = new String(b);
        System.out.println(str);
    }

字元流

字元流抽象基類

字元流處理的是字元資料,而位元組流處理的是byte資料

Witer 字元輸出流抽象基類

Reader 字元輸入流抽象基類

對應的子類為

OutputStreamWriter 使用指定的Charset將字元編碼為位元組,從字元流到位元組流的橋樑

InputStreamReader 讀取位元組,用指定的Charset將位元組編碼為字元,從位元組流到字元流的橋樑

這兩個類的優點就是可以處理不同字符集的流

如果都是預設字符集或者字符集一樣的話可以使用 FileReaderFileWriter

public static void main(String[] args) throws IOException {

        File f = new File("/Users/b/Desktop/image1.txt");
        f.createNewFile();

        FileOutputStream fos = new FileOutputStream(f);
        OutputStreamWriter osw = new OutputStreamWriter(fos);

        osw.write("zh1z3ven");
        osw.close();

        FileInputStream fis = new FileInputStream(f);
        InputStreamReader isr = new InputStreamReader(fis);

        int len;
        while ((len = isr.read()) != -1) {
            System.out.println(len);
            System.out.println((char)len);
        }
        isr.close();

    }

OutputStreamWriter

void write(int c) 寫入一個字元 void write(char[] cbuf) 寫入一個字元陣列 void write(char[] cbuf, int off, int len) 寫入一個根據cbuf,以off為偏移量,長度為len的字元陣列的一部分 void write(String str) 寫入一個字串 void write(String str, int off, int len) 寫入字串的一部分

void write()

public static void main(String[] args) throws IOException{

    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("/Users/b/Desktop/image1.txt"));

    osw.write(97);

    char[] cbuf = {'c', 'v', 'b', 'n', 'm'};
    osw.write(cbuf);

    osw.write(cbuf, 1, 4);

    String str = "zh1z3ven";
    osw.write(str);

    osw.write(str, 2, 3);

    osw.flush();
    osw.close();
}

InputStreamReader

int read() 一次讀一個字元,返回字元對應的ascii值
int read(char[] buf) 一次讀一個字元陣列

int read()

public static void main(String[] args) throws IOException{

    InputStreamReader isr = new InputStreamReader(new FileInputStream("/Users/b/Desktop/image1.txt"));

    //int read() 讀取檔案
    int ch;
    while ((ch = isr.read()) != -1){
        System.out.print((char)ch);
    }

    isr.close();

}

int read(char[] buf)

public class InputStreamReaderDemo {
    public static void main(String[] args) throws IOException{

        InputStreamReader isr = new InputStreamReader(new FileInputStream("/Users/b/Desktop/image1.txt"));

        //int read(char[] buf) 讀取檔案
        char[] ch = new char[1024];   //以char陣列接受檔案內容

        int len;        //儲存char陣列長度
        while((len = isr.read(ch)) != -1){
            String str = new String(ch, 0, len);  //將char轉為String
            System.out.print(str);
        }
        
        isr.close();

    }
}

字元流複製檔案

最明顯的一點就是不需要再進行Byte<=>String的轉換了

0x01 一次讀寫一個資料

public static void main(String[] args) throws IOException{

    FileInputStream fis = new FileInputStream("/Users/b/Desktop/java2.txt");
    InputStreamReader isr = new InputStreamReader(fis);

    FileOutputStream fos = new FileOutputStream("/Users/b/Desktop/java4.txt");
    OutputStreamWriter osw = new OutputStreamWriter(fos);

    int ch;
    while((ch = isr.read()) != -1){
        osw.write(ch);
    }
}

0x02 一次讀取一個字元陣列

public static void main(String[] args) throws IOException {

        //OutpuStreamWriter.write(String str) + InputStreamReader().read()

        FileInputStream fis = new FileInputStream("/Users/b/Desktop/java2.txt");
        InputStreamReader isr = new InputStreamReader(fis);
        char[] chars = new char[1024];
        isr.read(chars);
        String str = new String(chars);

        FileOutputStream fos = new FileOutputStream("/Users/b/Desktop/java3.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        // String str = "琥珀色黃昏像風在很美的遠方" + "\n" + "你的臉沒有化妝我卻瘋狂愛上";
        osw.write(str);
        System.out.println(str);

  			isr.close();
        osw.close();

    }

FileReader

用於讀取字元檔案的便捷類

繼承自InputStreamReader類,即可一次讀一個字元或一次讀一個字元陣列

FileReader(String fileName)

FileWriter

用於寫入字元檔案的便捷類

繼承自OutputWriter類

FileWriter(String fileName)

FileReader&FileWriter讀寫複製檔案

public static void main(String[] args) throws IOException {

    FileReader fileReader = new FileReader("/Users/b/Desktop/java2.txt");
    FileWriter fileWriter = new FileWriter("/Users/b/Desktop/java6.txt");

    char[] chars = new char[1024];
    int len;
    while((len = fileReader.read(chars)) != -1){
        fileWriter.write(chars, 0 ,len);
    }

    fileReader.close();
    fileWriter.close();
}

可以簡單理解為簡化了InputStreamReader和OutputStreamWriter新建字元流物件的過程。

但是不可以處理不同字符集的資料流。

字元緩衝流

BufferedReader

從字元輸入流中讀取文字,緩衝各個字元,從而實現字元、陣列和行的高效讀取。

可以指定緩衝區的大小,或者可使用預設的大小。大多數情況下,預設值就足夠大了。

主要用到的還是read方法

int read(char c) int read(char[] cbuf, int off, int len) String readLine() 讀取一個文字行

BufferedWriter

將文字寫入字元輸出流,緩衝各個字元,從而提供單個字元、陣列和字串的高效寫入。

可以指定緩衝區的大小,或者接受預設的大小。在大多數情況下,預設值就足夠大了。

主要用到的還是write方法

void newLine() 寫入一個行分隔符。 void write(char[] cbuf, int off, int len) 寫入字元陣列的某一部分。 void write(int c) 寫入單個字元。 void write(String s, int off, int len) 寫入字串的某一部分。

複製檔案操作

BufferedReader br = new BufferedReader(new FileReader("/Users/b/Desktop/java2.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("/Users/b/Desktop/java7.txt"));

        //一次讀取一個字元
        int ch;
        while ((ch = br.read()) != -1){
            bw.write((char) ch);

        }
        
        br.close();
        bw.close();
BufferedReader br = new BufferedReader(new FileReader("/Users/b/Desktop/java2.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("/Users/b/Desktop/java7.txt"));

        //一次讀取一個字元陣列
        int len;
        char[] chars = new char[1024];
        while ((len = br.read(chars)) != -1){
            bw.write(chars, 0 ,len);
        }

        bw.flush();
        br.close();
        bw.close();

readLine()與newLine()

一般使用位元組緩衝流一次讀取一個位元組陣列的方式讀取檔案

void newLine() 寫入一個行分隔符。自動根據系統而定

String readLine() 讀取一個文字行(不包含換行符)。當讀到null時,證明已讀完此檔案

write()
newLine()
flush()

public static void main(String[] args) throws IOException{

    BufferedReader br = new BufferedReader(new FileReader("/Users/b/Desktop/java2.txt"));
    BufferedWriter bw = new BufferedWriter(new FileWriter("/Users/b/Desktop/java8.txt"));

    String line;
    while ((line = br.readLine()) != null){
        System.out.println(line);
        bw.write(line);
        bw.newLine();
        bw.flush();
    }

    br.close();
    bw.close();
}

標準輸入流

InputStream

InputStream -> InputStreamReader -> BufferedReader

將標準輸入流轉換為字元緩衝輸入流處理

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

獲取一行鍵盤輸入,是Scanner類的底層

public static void main(String[] args) throws IOException {
    
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    System.out.print("請輸入字串:");
    String line = br.readLine();
    System.out.println("你輸入的字串是:" + line);
    
    System.out.print("請輸入一個整數:");
    int len = Integer.parseInt(br.readLine());  //  將字串=>整型
    System.out.println("你輸入的數是:" + len);

}

PrintStream

PrintStream ps = Systm.out

常見的方法有 println,print

位元組列印流

序列化與反序列化

物件序列化,就是將物件變成一個位元組序列儲存在磁碟或者在網路中傳輸物件。

物件反序列化,就是將序列化物件的位元組序列進行反序列化重構物件。

序列化流:ObjectOutputStream

反序列化流:ObjectInputStream

ObjectOutputStream

void writeObject(OutputStream out)

public static void main(String[] args) throws IOException {

    //構造方法: ObjectOutputStream(OutputStream out)
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/b/Desktop/java10.txt"));

    Student zh1z3ven = new Student("zh1z3ven", 21);

    //void wirteObject(Object obj);將指定物件(該物件需要implements Serializable)寫入流中,最後儲存在檔案內
    oos.writeObject(zh1z3ven);

    //釋放資源
    oos.close();
    
}

ObejctInputStream

public static void main(String[] args) throws IOException, ClassNotFoundException {
    //建立ObjectinputStream物件
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/b/Desktop/java10.txt"));

    //readObject()反序列化
    Object obj = ois.readObject();

    //向下轉型
    Student s = (Student) obj;
    System.out.println("Name: " + s.getName() + " Age: " + s.getAge());

    //釋放資源
    ois.close();
}

反序列化時可能存在的問題

1、假如我們在一個類序列化後去修改了它的類檔案,在進行反序列化讀取物件時可能會出現問題會丟擲如下異常java.io.InvalidClassException
因為類的序列版本與從流中讀取出來的不一致而導致的
2、java強烈建議需要序列化的類 定義一個serialVersionUID

private static final long serialVersionUID = 42L
​ 顯示定義序列版本的UID值
3、有transient修飾符修飾的變數並不會被序列化

相關文章