1. 程式人生 > >Java筆記:流I/O

Java筆記:流I/O

對象 輸入流 isa not 指定 printf compare turn 文件

一、基礎知識

Java通過流實現I/O,流是一種可以產生或使用信息的抽象。

Java定義了兩種類型的流:

  • 字節流:處理字節的輸入和輸出,例如讀寫二進制數據。
  • 字符流:處理字符的輸入和輸出。

在底層所有I/O仍然是面向字節的,字符流知識為處理字符提供更高效的方法。

二、字節流

FileInputStream和FileOutputStream提供了文件字節的讀寫能力。

技術分享圖片技術分享圖片
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

class Solution {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("file.txt")) {
            int available = fis.available();
            for (int i = 0; i < available; i++)
                System.out.print((char) fis.read());
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }

        System.out.println();
        try (FileInputStream fis = new FileInputStream("file.txt")) {
            int available = fis.available();
            byte[] arr = new byte[available];
            if (fis.read(arr) == available)
                for (byte b : arr)
                    System.out.print(b + " ");
            else System.out.println("Cannot read file");
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }

        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            String str = "Hello World";
            byte[] buf = str.getBytes();
            fos.write(buf);
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }
    }
}
View Code

ByteArrayInputStream和ByteArrayOutputStream提供了字節數組的讀寫能力。

技術分享圖片技術分享圖片
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

class Solution {
    public static void main(String[] args) {
        String str = "Hello World";
        byte[] arr = str.getBytes();
        ByteArrayInputStream in = new ByteArrayInputStream(arr);
        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            int available = in.available();
            for (int i = 0; i < available; i++)
                fos.write(in.read());
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }
        in.reset();//重置流指針位置

        
        
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            out.write(arr);//寫入緩沖區
        } catch (IOException exc) {
            System.out.println("Error writing to arr");
        }
        byte[] cpy = out.toByteArray();
        for (byte i : cpy)
            System.out.println(i);
        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            out.writeTo(fos);
        } catch (IOException exc) {
            System.out.println("Error writing to file");
        }
        out.reset();
    }
}
View Code

BufferedInputStream和BufferedOutputStream通過緩沖減少實際讀寫次數來提高性能。

技術分享圖片技術分享圖片
import java.io.*;

class Solution {
    public static void main(String[] args) {
        String str = "Hello World";
        byte[] arr = str.getBytes();
        ByteArrayInputStream in = new ByteArrayInputStream(arr);

        try (BufferedInputStream bis = new BufferedInputStream(in)) {
            for (int i = 0; i < 5; i++)
                System.out.print(bis.read() + " ");
            bis.mark(32);//向後讀取32個以內字符標記有效
            while (bis.available() != 0)
                System.out.print(bis.read() + " ");

            System.out.println();
            bis.reset();//回到標記位置
            while (bis.available() != 0)
                System.out.print(bis.read() + " ");
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }


        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            BufferedOutputStream out = new BufferedOutputStream(fos);
            out.write(arr);//寫入緩沖
            out.flush();//刷新緩沖
            out.close();
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }
    }
}
View Code

PushbackInputStream利用緩沖實現了回推。回推用於輸入流,以允許讀取字節然後將它們返回到流中。

技術分享圖片技術分享圖片
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

class Solution {
    public static void main(String[] args) {
        String str = "Hello World";
        byte[] buf = str.getBytes();
        ByteArrayInputStream in = new ByteArrayInputStream(buf);
        try (PushbackInputStream pis = new PushbackInputStream(in)) {
            int c;
            while ((c = pis.read()) != -1) {//Hello.World
                if (c == ‘ ‘)//遇到空格則回推小數點
                    pis.unread(‘.‘);
                else System.out.print((char) c);
            }
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
    }
}
View Code

SequenceInputStream允許連接多個InputStream對象。

技術分享圖片技術分享圖片
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;

class InputStreamEnumerator implements Enumeration<FileInputStream> {
    private Enumeration<String> files;//Enumeration已被叠代器取代

    public InputStreamEnumerator(Vector<String> files) {
        this.files = files.elements();
    }

    @Override
    public boolean hasMoreElements() {
        return files.hasMoreElements();
    }

    @Override
    public FileInputStream nextElement() {
        try {
            return new FileInputStream(files.nextElement().toString());
        } catch (IOException exc) {
            return null;
        }
    }
}

class Solution {
    public static void main(String[] args) {
        Vector<String> files = new Vector<>();
        files.addElement("fileA.txt");
        files.addElement("fileB.txt");
        files.addElement("fileC.txt");

        InputStreamEnumerator ise = new InputStreamEnumerator(files);
        InputStream in = new SequenceInputStream(ise);

        try {
            int c;
            while ((c = in.read()) != -1)
                System.out.print((char) c);
        } catch (IOException exc) {
            System.out.println("Error reading file");
        } finally {
            try {
                in.close();
            } catch (IOException exc) {
                System.out.println("Error closing file");
            }
        }
    }
}
View Code

PrintStream使其它流能夠方便地打印各種數據表示形式。

技術分享圖片技術分享圖片
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;

class Solution {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            try (PrintStream ps = new PrintStream(fos)) {
                ps.printf("Hello %s", "World");
            }
        } catch (IOException exc) {
            System.out.println("Error opening file");
        }
    }
}
View Code

DataInputStream和DataOutputStream允許向流中寫入或讀取基本類型數據。

技術分享圖片技術分享圖片
import java.io.*;

class Solution {
    public static void main(String[] args) {
        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("file.txt"))) {
            dos.writeBoolean(true);
            dos.writeInt(1);
            dos.writeChar(‘A‘);
            dos.writeDouble(1.0);
        } catch (IOException exc) {
            System.out.println("Error writing file");
        }

        try (DataInputStream dis = new DataInputStream(new FileInputStream("file.txt"))) {
            //讀取順序必須和寫入順序相同
            System.out.println(dis.readBoolean());
            System.out.println(dis.readInt());
            System.out.println(dis.readChar());
            System.out.println(dis.readDouble());
        } catch (IOException exc) {
            System.out.println("Error reading file");
        }
    }
}
View Code

三、字符流類

字符流提供了直接對Unicode字符進行操作的功能。

FileReader和FileWriter提供了文件字符的讀寫能力。

技術分享圖片技術分享圖片
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

class Solution {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("file.txt")) {
            int c;
            while ((c = fr.read()) != -1)
                System.out.print((char) c);
        } catch (IOException exc) {
            System.out.println("Error reading file");
        }
        
        try (FileWriter fw = new FileWriter("file.txt")) {
            String str = "Hello World";
            fw.write(str);
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
    }
}
View Code

CharArrayReader和charArrayWriter提供了字符數組的讀寫能力。

技術分享圖片技術分享圖片
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.FileWriter;
import java.io.IOException;

class Solution {
    public static void main(String[] args) {
        String str = "Hello World";
        char[] arr = str.toCharArray();
        try (CharArrayReader cr = new CharArrayReader(arr)) {
            int c;
            while ((c = cr.read()) != -1)
                System.out.print((char) c);
        } catch (IOException exc) {
            System.out.println("Error reading");
        }

        try (CharArrayWriter cw = new CharArrayWriter()) {
            cw.write(arr);
            char[] cpy = cw.toCharArray();
            try (FileWriter fw = new FileWriter("file.txt")) {
                cw.writeTo(fw);
            }
            cw.reset();
        } catch (IOException exc) {
            System.out.println("Error writing");
        }
    }
}
View Code

BufferedReader和BufferedWriter通過緩沖減少實際讀寫次數提高性能。

技術分享圖片技術分享圖片
import java.io.*;

class Solution {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
            int c;
            for (int i = 0; (c = br.read()) != -1; i++) {
                if (i == 5) br.mark(32);
                System.out.print((char) c);
            }
            br.reset();
            while ((c = br.read()) != -1)
                System.out.print((char) c);
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
        
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"))) {
            bw.write("Hello World");
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
    }
}
View Code

PushbackReader允許將字符返回到輸入流。

技術分享圖片技術分享圖片
import java.io.FileReader;
import java.io.IOException;
import java.io.PushbackReader;

class Solution {
    public static void main(String[] args) {
        try (PushbackReader pr = new PushbackReader(new FileReader("file.txt"))) {
            int c;
            for (int i = 0; (c = pr.read()) != -1; i++) {
                if (i % 2 == 0)
                    pr.unread(‘ ‘);
                System.out.print((char) c);
            }
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
    }
}
View Code

PrintWriter本質上是PrintStream的面向字符版本,可輸出內容到控制臺。

技術分享圖片技術分享圖片
import java.io.PrintWriter;

class Solution {
    public static void main(String[] args) {
        try (PrintWriter pw = new PrintWriter(System.out)) {
            pw.write("Hello World");
            pw.flush();
        }
        try (PrintWriter pw = new PrintWriter(System.out, true)) {//自動刷新
            pw.println("Hello World");
        }
    }
}
View Code

Console的主要目的是簡化與控制臺的交互。

技術分享圖片技術分享圖片
import java.io.Console;

class Solution {
    public static void main(String[] args) {
        Console console = System.console();
        if (console != null) {
            String str = console.readLine();
            console.printf(str);
        }
    }
}
View Code


四、文件系統

File類用於描述文件本身的屬性,可獲取和操作文件關聯的信息。例如權限、時間、日期及目錄路徑,還可以瀏覽子目錄。

技術分享圖片技術分享圖片
import java.io.File;

class Solution {
    public static void main(String[] args) {
        File fileA = new File("/");//目錄路徑
        File fileB = new File("/", "file.txt");//目錄路徑中的文件

        System.out.println(fileB.getName());
        System.out.println(fileB.getPath());
        System.out.println(fileB.getAbsolutePath());
        System.out.println(fileB.getParent());
        System.out.println(fileB.exists());
        System.out.println(fileB.canRead());
        System.out.println(fileB.canWrite());
        System.out.println(fileB.isDirectory());
        System.out.println(fileB.isFile());
        System.out.println(fileB.isAbsolute());
        System.out.println(fileB.lastModified());
        System.out.println(fileB.length());
    }
}
View Code

獲取目錄文件字符串列表。

技術分享圖片技術分享圖片
import java.io.File;

class Solution {
    public static void main(String[] args) {
        String dir = "/";
        File file = new File(dir);
        System.out.println(file.getName());
        if (file.isDirectory()) {
            String[] str = file.list();
            for (String s : str) {
                File sub = new File(dir + "/" + s);
                System.out.println(sub.getName());
            }
        }
    }
}
View Code

使用FilenameFilter接口過濾文件。

技術分享圖片技術分享圖片
import java.io.File;
import java.io.FilenameFilter;

class OnlyExt implements FilenameFilter {
    private String ext;

    OnlyExt(String ext) {
        this.ext = ext;
    }

    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(ext);
    }
}

class Solution {
    public static void main(String[] args) {
        File file = new File("/");
        FilenameFilter onlyExt = new OnlyExt(".dat");
        String[] str = file.list(onlyExt);//指定過濾器
        for (String s : str)
            System.out.println(s);
    }
}
View Code

獲取目錄文件列表。

技術分享圖片技術分享圖片
import java.io.File;
import java.io.FilenameFilter;

class OnlyExt implements FilenameFilter {
    private String ext;

    OnlyExt(String ext) {
        this.ext = ext;
    }

    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(ext);
    }
}

class Solution {
    public static void main(String[] args) {
        File file = new File("/");
        FilenameFilter onlyExt = new OnlyExt(".dat");
        File[] list = file.listFiles(onlyExt);
        for (File dat : list)
            System.out.println(dat.getName());
    }
}
View Code

創建目錄。

技術分享圖片技術分享圖片
import java.io.File;

class Solution {
    public static void main(String[] args) {
        File dir = new File("/Hello/World");
        if (dir.mkdir())//無法自動創建父目錄 創建失敗
            System.out.println(dir.getAbsolutePath());
        if (dir.mkdirs())//自動創建父目錄
            System.out.println(dir.getAbsolutePath());//C:\Hello\World

    }
}
View Code

RandomAccessFile封裝了隨機訪問文件的功能

技術分享圖片技術分享圖片
import java.io.*;

class Solution {
    public static void main(String[] args) {
        File file = new File("file.txt");
        try {
            RandomAccessFile rafA = new RandomAccessFile(file, "r");//只讀模式
            RandomAccessFile rafB = new RandomAccessFile(file, "rw");//讀寫模式
            rafA.seek(5);//文件指針置為距離開頭5字節的位置
            int c;
            while ((c = rafA.read()) != -1)
                System.out.print((char) c);

        } catch (FileNotFoundException exc) {
            System.out.println("File not found");
        } catch (IOException exc) {
            System.out.println("Error setting pointer");
        }
    }
}
View Code

五、串行化

串行化是將對象的狀態寫入字節流的過程,將程序的狀態保存到永久性存儲區域中。

只有實現了Serializable接口的類才能通過串行化功能進行保存和恢復。Serializable接口沒有定義成員,僅用於指示類可以被串行化。如果一個類可以被串行化,那麽其所有子類都是可以串行化的。聲明為transient的變量不會被串行化。

ObjectOutputStream實現了ObjectOutput接口,可將對象寫入流中。

ObjectInputStream實現了ObjectInput接口,可從流中讀取對象。

技術分享圖片技術分享圖片
import java.io.*;

class Solution {
    static class MyClass implements Serializable {
        String str;
        transient Integer i;

        MyClass(String str, int i) {
            this.str = str;
            this.i = i;
        }
    }

    public static void main(String[] args) {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file.txt"))) {
            for (int i = 0; i < 10; i++)
                out.writeObject(new MyClass(Integer.toString(i), i));
        } catch (IOException exc) {
            System.out.println("Error writing file");
        }

        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file.txt"))) {
            MyClass[] arr = new MyClass[10];
            for (int i = 0; i < 10; i++)
                arr[i] = (MyClass) in.readObject();
            for (MyClass c : arr)
                System.out.println(c.str + " " + c.i);//i為null
        } catch (IOException | ClassNotFoundException exc) {
            System.out.println("Error reading file");
        }
    }
}
View Code

六、流接口

從集合獲取流的方法及簡單操作。

技術分享圖片技術分享圖片
import java.util.ArrayList;
import java.util.Optional;
import java.util.stream.Stream;

class Solution {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 1000; i >= 0; i--)
            arrayList.add(i);

        Stream<Integer> stream = arrayList.stream();
        Optional<Integer> minVal = stream.min(Integer::compare);
        System.out.println(minVal.orElse(-1));

        Stream<Integer> sortedStream = arrayList.stream().sorted();
        Stream<Integer> oddStream = sortedStream.filter((i) -> i % 2 == 1);
        oddStream.forEach((i) -> System.out.println(i + " "));
    }
}
View Code

將流轉換為值的操作稱為縮減操作。流接口泛化了這種概念,提供了reduce方法,通過該方法可基於任意條件從流中獲取值。

調用集合對象的parallelStream方法可獲得並行流,並行流的運算可運算在多線程上。

技術分享圖片技術分享圖片
import java.util.LinkedList;
import java.util.stream.Stream;

class Solution {
    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        for (int i = 1; i <= 10; i++)
            linkedList.add(i);

        Stream<Integer> stream = linkedList.parallelStream();
        int factorial = stream.reduce(1, (i, j) -> i * j);//指定運算單位元為1
        System.out.println(factorial);
    }
}
View Code

流的映射操作。

技術分享圖片技術分享圖片
import java.util.ArrayList;
import java.util.stream.Stream;

class Email {
    String name;
    String addr;

    Email(String name, String addr) {
        this.name = name;
        this.addr = addr;
    }
}

class Solution {
    public static void main(String[] args) {
        ArrayList<Email> arrayList = new ArrayList<>();

        arrayList.add(new Email("Durant", "[email protected]"));
        arrayList.add(new Email("Tompson", "[email protected]"));
        arrayList.add(new Email("Curry", "[email protected]"));

        Stream<Email> stream = arrayList.parallelStream();
        Stream<String> addrStream = stream.map((email) -> email.addr);
        addrStream.forEach((addr) -> System.out.println(addr));
    }
}
View Code

基本類型流。

技術分享圖片技術分享圖片
import java.util.ArrayList;
import java.util.stream.DoubleStream;

class Solution {
    public static void main(String[] args) {
        ArrayList<Double> arrayList = new ArrayList<>();
        for (int i = 0; i < 100; i++)
            arrayList.add(Math.random());

        DoubleStream stream = arrayList.stream().mapToDouble((i) -> i);
        stream.forEach((i) -> System.out.println(i));
    }
}
View Code

從流中獲取集合。

技術分享圖片技術分享圖片
import java.util.ArrayList;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Solution {
    public static void main(String[] args) {
        ArrayList<Double> arrayList = new ArrayList<>();
        for (int i = 0; i < 100; i++)
            arrayList.add(Math.random());

        Stream<Double> stream = arrayList.stream();
        Set<Double> set = stream.collect(Collectors.toSet());
        System.out.println(set);
    }
}
View Code

使用流叠代器。

技術分享圖片技術分享圖片
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Spliterator;

class Solution {
    public static void main(String[] args) {
        ArrayList<Double> arrayList = new ArrayList<>();
        for (int i = 0; i < 10; i++)
            arrayList.add(Math.random());

        Iterator<Double> itr = arrayList.stream().iterator();
        while (itr.hasNext())
            System.out.println(itr.next());

        Spliterator splitr = arrayList.stream().spliterator();
        while (splitr.tryAdvance((i) -> System.out.println(i))) ;
    }
}
View Code

Java筆記:流I/O