1. 流
概念:内存与存储设备之间传输数据的通道
1.1 流的分类
按方向
输入流:将存储设备中的数据读取到内存
输出流:将内存中的数据写入存储设备
文件 ---输入流
--> 程序 ---输出流
--> 文件
按单位
字节流:以字节为单位,可以读写所有数据
字符流:以字符为单位,只能读写文本数据
按功能
节点流:具有实际传输数据的读写功能
过滤流:在节点流的基础之上增强功能
1.2 字节 VS 字符
- 字节(英语:Byte),通常用作计算机信息计量单位,不分数据类型。
- 字符,任何一个文字或符号都是一个字符,但所占字节不一定,不同的编码导致一个字符所占的内存不同
- 字符的集合就叫字符集
1 Byte = 8 bits
1 KB = 1024 Bytes
1 MB = 1024 KB
1 GB = 1024 MB
2. 字节流
基本的流按字节读写,没有缓冲区
抽象父类 InputStream / OutputStream
public abstract class InputStream implements Closeable {
/**
* Reads the next byte of data from the input stream
* If no byte is available, the value -1 is returned
*/
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {}
// len – the maximum number of bytes to read.
public int read(byte b[], int off, int len) throws IOException {}
}
public abstract class OutputStream implements Closeable, Flushable {
// The byte to be written is the eight low-order bits of the argument b. The 24 high-order bits of b are ignored
public abstract void write(int b) throws IOException;
}
2.1 FileInputStream
如果文件不存在,会抛出异常FileNotFoundException,如果当前用户没有读的权限,会抛出异常SecurityException
// demo.txt 内容:abcd
InputStream fis = new FileInputStream("src/main/resources/demo.txt");
int length = 0;
while ((length = fis.read()) != -1) {
System.out.println(length);
}
fis.close();
打印的结果是:97 98 99 100
原因 read() 是一个字节一个字节的读取
改为 System.out.println((char) length);
会打印 a b c d
一次读取多个字节
InputStream fis = new FileInputStream("src/main/resources/demo.txt");
byte[] buf = new byte[3];
int bytes = fis.read(buf);
System.out.println(new String(buf)); // abc
// new String(buf, 0, bytes)
因为最后一次读取的数据可能不够 3 个字节,所以要指定长度
循环读取多次
InputStream fis = new FileInputStream("src/main/resources/input.txt");
byte[] buf = new byte[3];
int bytes = 0;
while ((bytes = fis.read(buf)) != -1) {
System.out.println(new String(buf, 0, bytes));
}
fis.close();
// String(byte bytes[], int offset, int length, String charsetName)
new String(buf, 0, bytes, "UTF-8");
2.2 FileOutputStream
OutputStream out = new FileOutputStream("src/main/resources/out.txt");
out.write(97);
out.write('b');
out.close();
// OutputStream out = new FileOutputStream("src/main/resources/out.txt");
// append:true 追加的方式写入文件,默认时覆盖文件内容
OutputStream out = new FileOutputStream("src/main/resources/out.txt", true);
String str = "hello world";
out.write(str.getBytes());
out.close();
边读边写
public static void main(String[] args) throws Exception {
InputStream input = new FileInputStream("src/main/resources/input.txt");
OutputStream out = new FileOutputStream("src/main/resources/out.txt");
// 1024 Byte = 1K
byte[] buf = new byte[1024];
int bytes = 0;
while ((bytes = input.read(buf)) != -1) {
out.write(buf, 0, bytes);
}
input.close();
out.close();
}
读取全部后再打印
byte[] buf = new byte[1024];
int off = 0;
int len = 0;
// 每次读取 buf.length 到 buf 数组
while ((len = fis.read(buf)) != -1) {
off += len;
}
String data = new String(buf, 0, off, "UTF-8");
2.3 ByteArrayInputStream / ByteArrayOutputStream
- ByteArrayOutputStream的输出目标是一个byte数组,这个数组的长度是根据数据内容动态扩展的
3. 字节缓冲流
缓冲流:BufferedInputStream / BufferedOutputStream
数据存储在缓冲区,提高 I/O 效率,减少访问磁盘的次数
public class BufferedInputStream extends FilterInputStream {
// 默认缓存区:8K
private static int DEFAULT_BUFFER_SIZE = 8192;
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
}
public class BufferedOutputStream extends FilterOutputStream {
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
}
3.1 BufferedInputStream
InputStream input = new BufferedInputStream(new FileInputStream("src/main/resources/input.txt"));
int bytes = 0;
while ((bytes = input.read()) != -1) {
System.out.println((char) bytes);
}
input.close();
也可以自己设置缓存区大小
InputStream input = new BufferedInputStream(new FileInputStream("src/main/resources/input.txt"));
byte[] buf = new byte[1024];
int bytes = 0;
while ((bytes = input.read(buf)) != -1) {
System.out.println(new String(buf, 0, bytes));
}
input.close();
3.2 BufferedOutputStream
OutputStream out = new BufferedOutputStream(new FileOutputStream("src/main/resources/out.txt", true));
for (int i = 0; i < 10; i++) {
// 写入缓冲区,此时还未写入文件,需要再调用 flush()
out.write("hello\r\n".getBytes());
out.flush();
}
// 关闭,内部也会调用flush()
out.close();
4. 对象流
- 增强了缓存区功能
- 可以读写 8 种基本数据类型和字符串
- 可以读写对象
- readObject() 从流中读取一个对象
- writeObject(Object obj) 向流中写入一个对象
4.1 ObjectInputStream / ObjectOutputStream
public class Server {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ServerSocket serverSocket = null;
serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept();
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
User user = (User) input.readObject();
System.out.println(user);
}
}
}
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = null;
socket = new Socket("localhost", 8080);
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
User user = new User("Tinyspot", 20);
out.writeObject(user);
socket.close();
}
}
4.2 序列化多个对象
- 可以借助集合实现
public static void main(String[] args) throws Exception {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("src/main/resources/student.bin"));
List<Student> list = Arrays.asList(new Student("tinyspot", 20), new Student("echo", 25));
out.writeObject(list);
out.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/main/resources/student.bin"));
List<Student> students = (List<Student>) ois.readObject();
ois.close();
System.out.println(JSON.toJSONString(students));
}
public class Student implements Serializable {
// 序列化版本号,保证序列化和反序列化的是同一个类
private static final long serialVersionUID = -3881208742057626511L;
private String name;
private transient Integer age;
}
网友评论