Java筆記:流I/O
一、基礎知識
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"); } } }
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(); } }
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"); } } }
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