Java 数据流

接口 DataInput 和 DataOutput,设计了一种较为高级的数据输入输出方式:除了可处理字节和字节数组外,还可以处理 int、float、boolean 等基本数据类型,这些数据在文件中的表示方式和它们在内存中的一样,无须转换,如 read(), readInt(), readByte()…; write(), writeChar(), writeBoolean()… 此外,还可以用 readLine() 方法读取一行信息。

常用方法

方法 返回值 说明
readBoolean() boolean
readByte() byte
readShort() short
readChar() char
readInt() int
readLong() long
readDouble() double
readFloat() float
readUnsignedByte() int
readUnsignedShort() int
readFully(byte[] b) void 从输入流中读取一些字节,并将它们存储在缓冲区数组 b 中
reaFully(byte[] b, int off,int len) void 从输入流中读取 len 个字节
skipBytes(int n) int 与 InputStream.skip 等价
readUTF() String 按照 UTF-8 形式从输入中读取字符串
readLine() String 按回车 (\r) 换行 (\n) 为分割符读取一行字符串,不完全支持 UNICODE
writeBoolean(boolean v) void
writeByte(int v) void
writeShort(int v) void
writeChar(int v) void
writeInt(int v) void
writeLong(long v) void
writeFloat(float v) void
writeDouble(double v) void
write(byte[] b) void 与 OutputStream.write 同义
write(byte[] b, int off, int len) void 与 OutputStream.write 同义
write(int b) void 与 OutputStream.write 同义
writeBytes(String s) void 只输出每个字符的低 8 位;不完全支持 UNICODE
writeChars(String s) void 每个字符在输出中都占两个字节

数据流类 DataInputStream 和 DataOutputStream 的处理对象除了是字节或字节数组外,还可以实现对文件的不同数据类型的读写:

  1. 分别实现了 DataInput 和 DataOutput 接口。
  2. 在提供字节流的读写手段同时,以统一的形式向输入流中写入 boolean,int,long,double 等基本数据类型,并可以再次把基本数据类型的值读取回来。
  3. 提供了字符串读写的手段。

数据流可以连接一个已经建立好的数据对象,例如网络连接、文件等。数据流可以通过如下方式建立:

1
2
3
4
FileInputStream fis = new FileInputStream("file1.txt");
FileOutputStream fos = new FileOutputStream("file2.txt");
DataInputStream dis = new DataInputStream(fis);
DataOutputStream dos = new DataOutputStream(fos);

新建源代码文件 DataStream.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataStream {

public static void main(String[] args) throws IOException{
//向文件 a.txt 写入
FileOutputStream fos = new FileOutputStream("a.txt");
DataOutputStream dos = new DataOutputStream(fos);
try {
dos.writeBoolean(true);
dos.writeByte((byte)123);
dos.writeChar('J');
dos.writeDouble(3.1415926);
dos.writeFloat(2.122f);
dos.writeInt(123);
}
finally {
dos.close();
}
//从文件 a.txt 读出
FileInputStream fis = new FileInputStream("a.txt");
DataInputStream dis = new DataInputStream(fis);
try {
System.out.println("\t" + dis.readBoolean());
System.out.println("\t" + dis.readByte());
System.out.println("\t" + dis.readChar());
System.out.println("\t" + dis.readDouble());
System.out.println("\t" + dis.readFloat());
System.out.println("\t" + dis.readInt());
}
finally {
dis.close();
}
}

}

编译运行:

1
2
3
4
5
6
7
8
$ javac DataStream.java
$ java DataStream
true
123
J
3.1415926
2.122
123

读写对象

我们知道实例化的对象存在于内存中,如果我们想传输实例化的对象怎么办呢?可以通过 ObjectOutputStream 和 ObjectInputStream 将对象输入输出。 将对象的状态信息转换为可以存储或者传输的形式的过程又叫序列化。

新建一个源代码文件 ReadWriteObject.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.*;

public class ReadWriteObject {
public static void main(String[] args) {
File file = new File("/home/project/user.file");
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file))) {
//将匿名对象 写入到file中,注意:被写入的对象必须实现了Serializable接口
objectOutputStream.writeObject(new User("admin", "adminpwd"));
objectOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
//读取文件 打开输入流
try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file))) {
// 将信息还原为user实例
User user = (User) objectInputStream.readObject();
//打印user信息 和上面创建的匿名对象的信息一致
System.out.println(user.toString());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}

}

//静态内部类 必须实现Serializable
static class User implements Serializable {
private String username;
private String password;

public User(String username, String password) {
this.username = username;
this.password = password;
}

@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
}

编译运行:

1
2
3
$ javac ReadWriteObject.java
$ java ReadWriteObject
User{username='admin', password='adminpwd'}

NIO

Java NIO(New IO) 发布于 JDK1.4,用于代替 Java 标准 IO 。Java NIO 是面向缓存的、非阻塞的 IO,而标准 IO 是面向流的,阻塞的 IO。

首先理解 NIO 的重要概念:Buffer(缓冲区)

  • NIO 读取或者写入数据都要通过 Buffer

  • 通过 allocate() 方法分配 Buffer,Buffer 不可实例化,Buffer 是抽象类,需要使用具体的子类,比如 ByteBuffer。

  • Buffer 的参数

    • capacity :缓冲区的容量
    • position :当前指针位置,没读取一次缓冲区数据或者写入缓冲区一个数据那么指针将会后移一位
    • limit :限制指针的移动,指针不能读取 limit 之后的位置
    • mark :如果设置该值,那么指针将移动到 0 - position 的位置
    • 最后可以这几个参数的关系如下:mark <= position <= limit <= capacity

    新建源代码文件 NioDemo.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Scanner;

public class NioDemo {
public static void main(String[] args) {
try {
File file = new File("/home/project/nio.txt");
if (!file.exists()) {
file.createNewFile();
}
//创建channel nio通过channel来连接文件 相当于桥梁
FileChannel writeChannel = new RandomAccessFile(file, "rw").getChannel();
//创建一个ByteBuffer 容量为100
ByteBuffer byteBuffer = ByteBuffer.allocate(100);
System.out.println("请输入字符串");
Scanner in = new Scanner(System.in);
String s = in.nextLine();
//将字符串写入到缓冲区
byteBuffer.put(s.getBytes());
System.out.println("写入数据后指针变化-position:" + byteBuffer.position() + " limit:" + byteBuffer.limit() + " capacity :" + byteBuffer.capacity());
//为输出数据做准备 将limit移动到position位置,position置0
byteBuffer.flip();
System.out.println("flip后指针变化-position:" + byteBuffer.position() + " limit:" + byteBuffer.limit() + " capacity :" + byteBuffer.capacity());
//将缓冲区写入channel
writeChannel.write(byteBuffer);
//清除缓冲区 为下次写入或者读取数据做准备 恢复到初始状态 position=0 limit=capacity=100 因为我们这里分配的容量大小为100
byteBuffer.clear();
System.out.println("clear后指针变化-position:" + byteBuffer.position() + " limit:" + byteBuffer.limit() + " capacity :" + byteBuffer.capacity());
//关闭channel
writeChannel.close();
FileChannel readChannel = new RandomAccessFile(file, "r").getChannel();
//从channel中将数据读取到缓冲区
while (readChannel.read(byteBuffer) != -1) {
//为读取数据做准备
byteBuffer.flip();
//输出数据 设置解码器
Charset charset = Charset.forName("UTF-8");
CharsetDecoder decoder = charset.newDecoder();
System.out.println("读取结果:" + decoder.decode(byteBuffer));
//清除缓冲区
byteBuffer.clear();
}
readChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行示例:

1
2
3
4
5
6
7
8
$ javac NioDemo.java
$ java NioDemo
请输入字符串
hellojava
写入数据后指针变化-position:9 limit:100 capacity :100
flip后指针变化-position:0 limit:9 capacity :100
clear后指针变化-position:0 limit:100 capacity :100
shiyanlou