一、基础知识
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", "kevin35@gmail.com"));
arrayList.add(new Email("Tompson", "klay11@gmail.com"));
arrayList.add(new Email("Curry", "stephen30@gmail.com"));
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))) ;
}
}