Java输入输出流

read & write方法

read和write方法在执行时都将阻塞,直至字节确实被读入或写出。available方法使我们可以去检查当前可读入的字节数量。当你完成对流的读写时,应该通过调用close方法来关闭它,否则可能写出字节的最后一个包永远也得不到传递,当然也可以用flush方法来人为地冲刷这些输出。

1
2
3
4
5
if(is.available()>0){
...
is.read(buffer);
...
}

注意:

  1. read和write方法一次只读取写入一个字节。
  2. 所有在java.io中的类都相对路径名解释为以用户工作目录开始,可以通过调用System.getProperty(“user.dir”)来获得这个信息
  3. 由于反斜杠字符在Java中是转义字符,因此要确保在windows风格的相对路径名中使用\,也可以使用单斜杆字符/。因为大部分文件处理的系统调用都会将反斜杠解释成文件分割符。但最好使用java.io.File.separator获得系统文件分割符。

    Stream

    与抽象类InputStream和OutputStream一样,这些类只支持在字节级别上的读写。使用DataInputStream可以读入数值类型,如:
    1
    2
    DataInputStream din = ...;
    double s = din.readDouble();

回推流:当读入输入时,经常需要浏览下一个字节,以了解它是否是你想要的值,Java提供了用于此目的的PushbackInputStream:

1
2
3
4
5
PushBackInputStream pbin = ...;
// 预读下一个字节
int b = pbin.read();
// 在它并非你说期望的值时将其推回流中
if(b != '<') pbin.unread(b);

FileOutputStream(File file, boolean append) 重载方法:append决定是否以增加模式写入文件

文本输入输出

整数1234储存成二进制数时,它被写成为由字节码00 00 04 D2构成的序列(16进制表示法)。

  1. 字节: 用某种字符编码方式表示的字符
  2. InputStreamReader将包含字节的输入流转换为可以产生Unicode码元的读入器

对于文本输出,可以使用PrintWriter简便方法:

1
2
3
PrintWriter out = new PrintWriter("d:\\data.txt");
String name = "Hello World!";
out.println(name); // name将被写入到txt文件中

调用println()不会自动刷新
可以使用PrintWriter(Writer out, Boolean autoFlush)来启用自动刷新

BufferReader 没有任何用于读入数值的方法,可以使用Scanner来读入文本输入。
DataOutputStream.writeUTF(String s): 写出由“修订过的UTF-8”格式的字符构成的字符串。

对象流与序列化

为了保存对象,可以使用ObjectOutputStream的writeObject方法写入对象。读回对象需要获得一个ObjectInputStream对象,然后调用readObject方法顺序读回它们,如:

1
2
3
4
5
6
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.dat"));
Manager boss = bew Manager("Carl", 8000, 1987, 12, 15);
out.writeObject(boss);

ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.dat"));
Employee e = (Employee) in.readObject();

但是,对希望在对象流中储存或恢复的所有类都应进行一下修改,这些类必须实现Serializable接口,然而,Serializable接口中没有任何方法,因此不需要对这些类做任何改动。

  1. 对象序列化是以特殊的文件格式储存对象数据的,当然,由于良好的封装性,我们需要了解文件中表示对象的确切字节序列,就可以使用writeObject/readObject方法。
  2. 安全散列算法(SHA)是一种可以为较大的信息块提供指纹的快速算法,不论最初的数据块尺寸有多大,这种指纹总是20个字节的数据包。
  3. 对象流输出中包含所有的类型和数据域。每个对象都被赋予一个序列号。相同对象的重复出现将被储存为对这个对象的序列号的引用。

transient关键字: 不可被序列化的数据域,仅加入关键字不够,还需要调用out.defaultWriteObject()来写入描述信息,再写入其它数据,如:

1
2
3
out.defaultWriteObject();
out.writeDouble(point.getX());
out.writeDouble(point.getY());

枚举的默认序列化是不适用的,在它储存后被读取,会被创建为一个全新的对象!
其它情况下,序列化可作为深度克隆对象。


2016年1月21日续

文件操作

1
Path p = Paths.get("\home", "dir", "test");

静态的Paths.get方法接受一个或多个字符串,并将它们用默认的文件系统的路径分隔符连接起来。

有时需要与遗留系统的API交互,它们使用的是File类而不是Path类。Path类有一个toFile方法,而File类也有一个toPath方法。

文件复制、移动操作:

1
2
Files.copy(fromPath, toPath);   //复制文件
Files.move(fromPath, toPath); //移动文件 可附加StandardCopyOption.REPLACE_EXISITING选项

迭代文件:

1
2
3
4
DirectoryStream<Path> entries = Files.newDirectoryStream(dir);
for(Path entry : entries) {
...
}

Glob模式:用于过滤文件,如”*.java”

文件加锁机制:

1
2
3
4
FileChannel channel = FileChannel.open(path);
FileLock lock = channel.lock(); //阻塞式方法
FileLock lock = channel.tryLock(); //调用立即返回,不可获得锁则返回null
lock.release(); //解锁