这些Java教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并且可能使用不再可用的技术。
请参阅Java语言变更,了解Java SE 9及其后续版本中更新的语言特性的摘要。
请参阅JDK版本说明,了解所有JDK版本的新功能、增强功能以及已删除或不推荐使用的选项的信息。
本页面讨论了读取、写入、创建和打开文件的细节。有各种各样的文件I/O方法可供选择。为了帮助理解API,以下图表按照复杂性排列了文件I/O方法。
图表最左侧是实用方法readAllBytes
、readAllLines
和write
,设计用于简单的常见情况。这些方法右侧是用于迭代流或文本行的方法,如newBufferedReader
、newBufferedWriter
,然后是newInputStream
和newOutputStream
。这些方法与java.io
包是可互操作的。这些方法右侧是处理ByteChannels
、SeekableByteChannels
和ByteBuffers
的方法,如newByteChannel
方法。最后,最右侧的方法使用FileChannel
进行文件锁定或内存映射I/O的高级应用。
本页面包含以下主题:
本节中的几个方法都有一个可选的OpenOptions参数。该参数是可选的,API会告诉您当未指定时该方法的默认行为是什么。
支持以下StandardOpenOptions枚举:
WRITE
– 打开文件以进行写入访问。APPEND
– 将新数据追加到文件末尾。此选项与WRITE
或CREATE
选项一起使用。TRUNCATE_EXISTING
– 将文件截断为零字节。此选项与WRITE
选项一起使用。CREATE_NEW
– 创建新文件,如果文件已存在则抛出异常。CREATE
– 如果文件存在则打开文件,如果不存在则创建新文件。DELETE_ON_CLOSE
– 在流关闭时删除文件。此选项对于临时文件很有用。SPARSE
– 提示新创建的文件将是稀疏的。此高级选项适用于某些文件系统,如NTFS,其中具有数据“间隙”的大文件可以以更高效的方式存储,而这些空的间隙不会占用磁盘空间。SYNC
– 使文件(内容和元数据)与底层存储设备同步。DSYNC
– 使文件内容与底层存储设备同步。如果您有一个较小的文件,并希望一次读取其全部内容,您可以使用readAllBytes(Path)
或readAllLines(Path, Charset)
方法。这些方法会为您处理大部分工作,例如打开和关闭流,但不适用于处理大文件。以下代码显示了如何使用readAllBytes
方法:
Path file = ...; byte[] fileArray; fileArray = Files.readAllBytes(file);
您可以使用写入方法之一将字节或行写入文件。
write(Path, byte[], OpenOption...)
write(Path, Iterable< extends CharSequence>, Charset, OpenOption...)
以下代码片段显示了如何使用write
方法。
Path file = ...; byte[] buf = ...; Files.write(file, buf);
java.nio.file
包支持通道I/O,该方法使用缓冲区移动数据,绕过了可能导致流I/O瓶颈的一些层次。
newBufferedReader(Path, Charset)
方法打开一个文件进行读取,返回一个BufferedReader
,可以高效地从文件中读取文本。
以下代码片段展示了如何使用newBufferedReader
方法从文件中读取数据。文件使用"US-ASCII"编码。
Charset charset = Charset.forName("US-ASCII"); try (BufferedReader reader = Files.newBufferedReader(file, charset)) { String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException x) { System.err.format("IOException: %s%n", x); }
可以使用newBufferedWriter(Path, Charset, OpenOption...)
方法使用BufferedWriter
来写入文件。
以下代码片段展示了如何使用这种方法创建一个使用"US-ASCII"编码的文件:
Charset charset = Charset.forName("US-ASCII"); String s = ...; try (BufferedWriter writer = Files.newBufferedWriter(file, charset)) { writer.write(s, 0, s.length()); } catch (IOException x) { System.err.format("IOException: %s%n", x); }
java.io
API互操作的方法要打开一个文件进行读取,可以使用newInputStream(Path, OpenOption...)
方法。此方法返回一个用于读取文件字节的非缓冲输入流。
Path file = ...; try (InputStream in = Files.newInputStream(file); BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException x) { System.err.println(x); }
您可以使用newOutputStream(Path, OpenOption...)
方法创建文件、追加文件或写入文件。该方法打开或创建一个文件用于写入字节,并返回一个未缓冲的输出流。
该方法接受一个可选的OpenOption
参数。如果没有指定打开选项,并且文件不存在,则创建一个新文件。如果文件存在,则截断文件。此选项相当于使用CREATE
和TRUNCATE_EXISTING
选项调用该方法。
以下示例打开一个日志文件。如果文件不存在,则创建该文件。如果文件存在,则以追加方式打开。
import static java.nio.file.StandardOpenOption.*; import java.nio.file.*; import java.io.*; public class LogFileTest { public static void main(String[] args) { // 将字符串转换为字节数组。 String s = "Hello World! "; byte data[] = s.getBytes(); Path p = Paths.get("./logfile.txt"); try (OutputStream out = new BufferedOutputStream( Files.newOutputStream(p, CREATE, APPEND))) { out.write(data, 0, data.length); } catch (IOException x) { System.err.println(x); } } }
ByteBuffers
的方法流I/O每次读取一个字符,而通道I/O每次读取一个缓冲区。接口ByteChannel
提供了基本的read
和write
功能。一个SeekableByteChannel
是一个具有在通道中保持位置并更改该位置的能力的ByteChannel
。SeekableByteChannel
还支持截断与通道关联的文件和查询文件的大小。
可以移动到文件中的不同位置,然后从该位置读取或写入,从而实现对文件的随机访问。有关更多信息,请参阅随机访问文件。
有两种方法用于读写通道I/O。
newByteChannel(Path, OpenOption...)
newByteChannel(Path, Set<? extends OpenOption>, FileAttribute<?>...)
newByteChannel
方法返回一个SeekableByteChannel
实例。在默认文件系统中,您可以将此可寻址字节通道强制转换为FileChannel
,以获得更高级的功能,例如将文件的某个区域直接映射到内存中以加快访问速度、锁定文件的某个区域以防止其他进程访问,或者在不影响通道当前位置的情况下读取和写入字节。
newByteChannel
方法都支持指定一个OpenOption
选项列表。除了支持newOutputStream
方法使用的相同的打开选项外,还支持一个额外的选项:READ
是必需的,因为SeekableByteChannel
同时支持读取和写入。
指定READ
打开通道以进行读取。指定WRITE
或APPEND
打开通道以进行写入。如果没有指定这些选项中的任何一个,那么通道将以读取模式打开。
以下代码片段读取一个文件并将其打印到标准输出:
public static void readFile(Path path) throws IOException { // Files.newByteChannel() 默认使用 StandardOpenOption.READ try (SeekableByteChannel sbc = Files.newByteChannel(path)) { final int BUFFER_CAPACITY = 10; ByteBuffer buf = ByteBuffer.allocate(BUFFER_CAPACITY); // 使用此平台的适当编码读取字节。如果 // 跳过这一步,您可能会看到外来的或难以辨认的 // 字符。 String encoding = System.getProperty("file.encoding"); while (sbc.read(buf) > 0) { buf.flip(); System.out.print(Charset.forName(encoding).decode(buf)); buf.clear(); } } }
以下示例适用于UNIX和其他POSIX文件系统,创建具有特定文件权限集的日志文件。此代码将创建一个日志文件,如果已经存在则追加到日志文件中。该日志文件以所有者的读写权限和组的只读权限创建。
import static java.nio.file.StandardOpenOption.*; import java.nio.*; import java.nio.channels.*; import java.nio.file.*; import java.nio.file.attribute.*; import java.io.*; import java.util.*; public class LogFilePermissionsTest { public static void main(String[] args) { // 创建追加到文件的选项集合。 Set<OpenOption> options = new HashSet<OpenOption>(); options.add(APPEND); options.add(CREATE); // 创建自定义权限属性。 Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r-----"); FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms); // 将字符串转换为ByteBuffer。 String s = "Hello World! "; byte data[] = s.getBytes(); ByteBuffer bb = ByteBuffer.wrap(data); Path file = Paths.get("./permissions.log"); try (SeekableByteChannel sbc = Files.newByteChannel(file, options, attr)) { sbc.write(bb); } catch (IOException x) { System.out.println("Exception thrown: " + x); } } }
您可以使用createFile(Path, FileAttribute<?>)
方法创建一个带有初始属性集的空文件。例如,在创建时,如果您想要文件具有特定的文件权限集,请使用createFile
方法来实现。如果您没有指定任何属性,文件将使用默认属性创建。如果文件已经存在,则createFile
会抛出一个异常。
在单个原子操作中,createFile
方法检查文件是否存在,并使用指定的属性创建该文件,这使得该过程对恶意代码更加安全。
以下代码片段创建一个具有默认属性的文件:
Path file = ...; try { // 使用默认权限创建空文件 Files.createFile(file); } catch (FileAlreadyExistsException x) { System.err.format("名为%s的文件已存在%n", file); } catch (IOException x) { // 其他类型的失败,如权限等。 System.err.format("createFile错误:%s%n", x); }
POSIX文件权限中有一个使用createFile(Path, FileAttribute<?>)
创建带有预设权限的文件的示例。
您也可以使用newOutputStream
方法创建一个新文件,详见使用流I/O创建和写入文件。如果打开一个新的输出流并立即关闭它,将创建一个空文件。
您可以使用以下createTempFile
方法之一创建临时文件:
createTempFile(Path, String, String, FileAttribute<?>)
createTempFile(String, String, FileAttribute<?>)
第一个方法允许代码为临时文件指定一个目录,而第二个方法在默认临时文件目录中创建一个新文件。两种方法都允许您为文件名指定后缀,而第一个方法还允许您指定前缀。以下代码片段给出了第二种方法的示例:
try { Path tempFile = Files.createTempFile(null, ".myapp"); System.out.format("临时文件已创建:%s%n", tempFile) ; } catch (IOException x) { System.err.format("IOException:%s%n", x); }
运行该文件的结果可能类似于以下内容:
临时文件已创建:/tmp/509668702974537184.myapp
临时文件名的具体格式取决于操作系统。