一、基础知识
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"); } } }
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"); } } } }
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"); } } }
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"); } } }
三、字符流类
字符流提供了直接对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"); } } }
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"); } } }
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"); } } }
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"); } } }
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"); } } }
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); } } }
四、文件系统
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()); } }
获取目录文件字符串列表。
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()); } } } }
使用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); } }
获取目录文件列表。
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()); } }
创建目录。
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 } }
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"); } } }
五、串行化
串行化是将对象的状态写入字节流的过程,将程序的状态保存到永久性存储区域中。
只有实现了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"); } } }
六、流接口
从集合获取流的方法及简单操作。
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 + " ")); } }
将流转换为值的操作称为缩减操作。流接口泛化了这种概念,提供了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); } }
流的映射操作。
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)); } }
基本类型流。
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)); } }
从流中获取集合。
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); } }
使用流迭代器。
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))) ; } }