JavaNIO学习一
文章来源:http://cucaracha.iteye.com/blog/2041847
对文件来说,可能最常用的操作就是创建、读取和写出。NIO.2 提供了丰富的方法来完成这些任务。本文从简单的小文件操作开始,最后以缓冲和非缓冲流的操作结束。
流分为输入流和输出流(可以输出到任何地方,比如硬盘或内存)。流支持不同类型的数据,比如字符串、字节、原始数据类型、本地化字符、对象等。使用非缓冲流,读和写的操作直接依赖底层文件系统,使用缓冲流,数据从内存的缓冲区读取,只有缓冲区空了之后才会调用本地方法进行读取。同样,缓冲输出流也是先将数据写出缓冲区,当缓冲区满了之后才会使用本地方法写出。如果没有等到缓冲区满就需要写出数据,那么可以使用 flush 方法。
使用标准参数 OpenOption
在 NIO.2 中,创建、读取和写出都支持一个可选的 OpenOption 参数,使用它来设置怎样打开和读取文件。实际上,OpenOption 是 java.nio.file 包中的一个接口,它有两个实现:LinkOption (还记得著名的 NOFOLLOW_LINKS 枚举常量吗?)和 StandardOpenOption 类。StandardOpenOption 提供了以下的枚举常量:
- READ - 打开文件进行读取访问
- WRITE - 打开文件进行写出访问
- CREATE - 如果文件不存在则创建新文件
- CREATE_NEW - 创建新文件,如果文件已存在,则抛出异常
- APPPEND - 在文件末尾添加数据(与 WRITE 和 CREATE 结合使用)
- DELETE_ON_CLOSE - 当流关闭的时候删除文件(用于删除临时文件)
- TRUNCATE_EXISTING - 将文件截断为 0 个字节(与 WRITE 结合使用)
- SPARSE - 新创建的文件是 Sparse 文件
- SYNC - 保持文件内容和元数据同步
- DSYNC - 保持文件内容异步
在本文中,将会演示一些上面的枚举常量的用法。
创建新文件
可以使用 Files.createFile() 方法来创建新文件,这个方法接受一个 Path 类型的参数,并且接受一个可选的 FileAttribute<?> 参数用于在创建文件的时候设置文件属性。调用后返回新创建的文件。下面的代码段将演示在 C:\rafaelnadal\tournaments\2010 目录(此目录必须存在)下创建 SonyEricssonOpen.txt 文件(文件必须不存在,如果已经存在,则会抛出 FileAlreadyExistsException 异常),并使用默认属性。
- Path newfile = FileSystems.getDefault().
- getPath("C:/rafaelnadal/tournaments/2010/SonyEricssonOpen.txt");
- …
- try {
- Files.createFile(newfile);
- } catch (IOException e) {
- System.err.println(e);
- }
也可以在创建文件的时候设置属性,下面的代码演示了如何在 POSIX 文件系统上创建文件,并设置访问权限:
- Path newfile = FileSystems.getDefault().
- getPath("/home/rafaelnadal/tournaments/2010/SonyEricssonOpen.txt");
- Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------");
- FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
- try {
- Files.createFile(newfile, attr);
- } catch (IOException e) {
- System.err.println(e);
- }
随后你将会看到,这并不是创建文件的唯一方式。
写出小文件
NIO.2 提供了优雅的方式来写出小的二进制或文本文件。使用 Files.write() 方法写出文件,这个方法会打开文件用于写出(如果文件不存在会先创建文件)或者将常规文件截取为 0 字节后写出。当所有字节和行写完后,这个方法会关闭文件(即使出现 I/O 错误或异常也会关闭)。简单说来,这个方法相当于使用了 CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 参数。
调用 write() 写出字节
可以使用 Files.write() 来写出字节,这个方法接受一个 Path 类型的参数,一个用于写出的字节数组,和一个可选的文件打开参数(OpenOption)。这个方法会返回写出文件的 Path 对象。
下面的代码段将演示如何写出字节数组,并且使用了默认的打开参数。(文件名为 ball.png,将会被写出到 C:\rafaelnadal\photos 目录):
- Path ball_path = Paths.get("C:/rafaelnadal/photos", "ball.png");
- …
- byte[] ball_bytes = new byte[]{
- (byte)0x89,(byte)0x50,(byte)0x4e,(byte)0x47,(byte)0x0d,(byte)0x0a,(byte)0x1a,(byte)0x0a,
- (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x0d,(byte)0x49,(byte)0x48,(byte)0x44,(byte)0x52,
- (byte)0x00,(byte)0x00,(byte)0x00,(byte)0x10,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x10,
- (byte)0x08,(byte)0x02,(byte)0x00,
- …
- (byte)0x49,(byte)0x45,(byte)0x4e,(byte)0x44,(byte)0xae,(byte)0x42,(byte)0x60,(byte)0x82
- };
- try {
- Files.write(ball_path, ball_bytes);
- } catch (IOException e) {
- System.err.println(e);
- }
现在,你检查目录,就可以看到创建的 ball.png 文件。
如果你想使用字节的方式来写出字符串(String),那么先要将字符串转换为字节数组,下面的代码将在 C:\rafaelnadal\wiki 目录下写出 wiki.txt 文件:
- Path rf_wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
- …
- String rf_wiki = "Rafael \"Rafa\" Nadal Parera (born 3 June 1986) is a Spanish professional
- tennis " + "player and a former World No. 1. As of 29 August 2011 (2011 -08-29)[update], he is
- ranked No. 2 " + "by the Association of Tennis Professionals (ATP). He is widely regarded as
- one of the greatest players " + "of all time; his success on clay has earned him the nickname
- \"The King of Clay\", and has prompted " + "many experts to regard him as the greatest clay
- court player of all time. Some of his best wins are:";
- try {
- byte[] rf_wiki_byte = rf_wiki.getBytes("UTF-8");
- Files.write(rf_wiki_path, rf_wiki_byte);
- } catch (IOException e) {
- System.err.println(e);
- }
调用 write() 按行写出
可以使用 Files.write() 方法按行写出文件,方法会在每行的结尾根据系统添加一个行结束符(line.separator 系统属性)。这个方法接受一个 Path 类型的参数,一个可迭代的字符序列集合,一个用于编码的参数和一个可选的文件打开参数。这个方法会返回写出的文件 Path 对象。
下面的代码将演示如何按行写出文件(实际上,会在上节创建的 wiki.txt 文件后面添加内容)。
- Path rf_wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
- …
- Charset charset = Charset.forName("UTF-8");
- ArrayList<String> lines = new ArrayList<>();
- lines.add("\n");
- lines.add("Rome Masters - 5 titles in 6 years");
- lines.add("Monte Carlo Masters - 7 consecutive titles (2005-2011)");
- lines.add("Australian Open - Winner 2009");
- lines.add("Roland Garros - Winner 2005-2008, 2010, 2011");
- lines.add("Wimbledon - Winner 2008, 2010");
- lines.add("US Open - Winner 2010");
- try {
- Files.write(rf_wiki_path, lines, charset, StandardOpenOption.APPEND);
- } catch (IOException e) {
- System.err.println(e);
- }
读取小文件
NIO.2 提供了快速读取小文件的方法。使用 Files.readAllBytes() 和Files.readAllLines() 方法读取文件。这个方法在读取完成后会自动关闭流(即使出现 I/O 异常或错误也会关闭)。
调用 readAllBytes() 方法读取数据
Files.readAllBytes() 方法将整个文件读入字节数组中。下面的代码将演示读取前面创建的 ball.png 文件(文件必须存在)到字节数组:
- Path ball_path = Paths.get("C:/rafaelnadal/photos", "ball.png");
- …
- try {
- byte[] ballArray = Files.readAllBytes(ball_path);
- } catch (IOException e) {
- System.out.println(e);
- }
如果你想验证返回的字节是否正确,你可以将返回的字节写出到相同目录的 bytes_to_ball.png 文件中:
- …
- Files.write(ball_path.resolveSibling("bytes_to_ball.png"), ballArray);
- …
或者可以通过下面的 ImageIO 方式写出 PNG 图片文件:
- BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(ballArray));
- ImageIO.write(bufferedImage, "png", (ball_path.resolveSibling("bytes_to_ball.png")).toFile());
readAllBytes() 方法也可以用于读取文本文件,这时,读取的字节需要转换成字符串,看看下面的例子(你可以使用任何其它编码):
- …
- try {
- byte[] wikiArray = Files.readAllBytes(wiki_path);
- String wikiString = new String(wikiArray, "ISO-8859-1");
- System.out.println(wikiString);
- } catch (IOException e) {
- System.out.println(e);
- }
注意:如果文件太大(超过 2GB),那么字节数组的长度将会超出上限,会抛出 OutOfMemory 异常。这依赖于 JVM 的 Xmx 参数:在 32 位 JVM 上,不能超过 2GB(通常使用默认,不超过 256 MB)。在 64 位 JVM 上,可以大一些——十几 GB。
调用 readAllLines() 方法读取文件
使用 readAllLines() 方法会按行返回 String 类型的 List,方便进行循环取值(传递给这个方法的 Path 对象用于指定被读取文件,CharSet 用于设置解码的编码格式):
- Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
- …
- Charset charset = Charset.forName("ISO-8859-1");
- try {
- List<String> lines = Files.readAllLines(wiki_path, charset);
- for (String line : lines) {
- System.out.println(line);
- }
- } catch (IOException e) {
- System.out.println(e);
- }
按照官方文档,这个方法会识别以下的行结束符:
- \u000A\u000D - 换行回车
- \u000A - 换行
- \u000D - 回车
使用缓冲流
对于大多数操作系统来说,进行读写操作都是比较消耗资源的操作。NIO.2 提供了两个方法来进行缓冲区读写:Files.newBufferedReader() 和 Files.newBufferedWriter(),这两个方法都接受 Path 类型的对象,并返回老的 JDK 1.1 中的 BufferedReader 或 BufferedWriter 对象。
使用 newBufferedWriter() 方法
newBufferedWriter() 参数为一个 Path 类型的对象,一个 Charset 对象用于编码,一个可选的文件打开方式选项,返回一个新的 BufferedWriter 对象。这个方法将打开文件用于写出(如果文件不存在将会创建文件)或者将已存在的文件截取为 0 字节。简短说来,这个方法默认情况下相当于使用了 CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 属性。
下面的代码将在前面创建的 wiki.txt 文件后面添加内容:
- Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
- …
- Charset charset = Charset.forName("UTF-8");
- String text = "\nVamos Rafa!";
- try (BufferedWriter writer = Files.newBufferedWriter(wiki_path, charset,
- StandardOpenOption.APPEND)) {
- writer.write(text);
- } catch (IOException e) {
- System.err.println(e);
- }
使用 newBufferedReader() 方法
newBufferedReader() 方法可用于通过缓冲区读取文件。它的参数为一个 Path 类型的对象,一个 Charset 用于解码。它将返回一个 BufferedReader 类型的对象。
下面的代码将演示使用 UTF-8 的方式读取 wiki.txt:
- Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
- …
- Charset charset = Charset.forName("UTF-8");
- try (BufferedReader reader = Files.newBufferedReader(wiki_path, charset)) {
- String line = null;
- while ((line = reader.readLine()) != null) {
- System.out.println(line);
- }
- } catch (IOException e) {
- System.err.println(e);
- }
如果按照前文的例子一步步运行下来,那么运行结果将会是:
- Rafael "Rafa" Nadal Parera (born 3 June 1986) is a Spanish professional tennis player and a
- former World No. 1. As of 29 August 2011 (2011 -08-29)[update], he is ranked No. 2 by the
- Association of Tennis Professionals (ATP). He is widely regarded as one of the greatest
- players of all time; his success on clay has earned him the nickname "The King of Clay", and
- has prompted many experts to regard him as the greatest clay court player of all time. Some
- of his best wins are:
- Rome Masters - 5 titles in 6 years
- Monte Carlo Masters - 7 consecutive titles (2005-2011)
- Australian Open - Winner 2009
- Roland Garros - Winnner 2005-2008, 2010, 2011
- Wimbledon - Winner 2008, 2010
- US Open - Winner 2010
- Vamos Rafa!
使用非缓冲流
NIO.2 提供了使用非缓冲流的方法,可以用于直接读取或通过 java.io 的 API 封装成缓冲流使用。使用非缓冲流的方法是 Files.newInputStream() 和 Files.newOutputStream()。
使用 newOutputStream() 方法
newOutputStream() 参数为一个 Path 对象和一个可选的用于配置文件打开方式的 OpenOption 配置选项。它返回一个新的线程安全的非缓冲输出流 OutputStream。这个方法将打开文件用于写出(如果文件不存在将会创建文件)或者将已存在的文件截取为 0 字节。简短说来,这个方法默认情况下相当于使用了 CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 属性。
下面的代码将会写出 "Racquet: Babolat AeroPro Drive GT" 文本内容到文件 C:\rafaelnadal\equipment\racquet.txt,因为没有指定 OpenOption,所以文件不存的话会自动创建:
- Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
- String racquet = "Racquet: Babolat AeroPro Drive GT";
- byte data[] = racquet.getBytes();
- try (OutputStream outputStream = Files.newOutputStream(rn_racquet)) {
- outputStream.write(data);
- } catch (IOException e) {
- System.err.println(e);
- }
另外,如果你决定要使用缓冲流来取代非缓冲流,可以使用 java.io API 提供的方法。看看下面的代码,将会在 racquet.txt 文件(文件必须存在)后面添加“String: Babolat RPM Blast 16” 文本。
- Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
- String string = "\nString: Babolat RPM Blast 16";
- try (OutputStream outputStream = Files.newOutputStream(rn_racquet, StandardOpenOption.APPEND);
- BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) {
- writer.write(string);
- } catch (IOException e) {
- System.err.println(e);
- }
使用 newInputStream() 方法
newInputStream() 的参数为一个 Path 对象和一个可选的用于配置文件打开方式的 OpenOption 配置选项。它返回一个新的线程安全的 InputStream 对象。默认情况下,这个方法是用的是 OpenOption.READ 选项。
下面的代码将读取 racquet.txt 文件(文件必须存在)的内容:
- Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
- …
- int n;
- try (InputStream in = Files.newInputStream(rn_racquet)) {
- while ((n = in.read()) != -1) {
- System.out.print((char)n);
- }
- } catch (IOException e) {
- System.err.println(e);
- }
你也可以使用 InputStream 的 read() 方法将数据读取到用于缓冲的字节数组中。你可以将上面的代码改写为(记住,你的处理对象依然是非缓冲输入流):
- Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
- …
- int n;
- byte[] in_buffer = new byte[1024];
- try (InputStream in = Files.newInputStream(rn_racquet)) {
- while ((n = in.read(in_buffer)) != -1) {
- System.out.println(new String(in_buffer));
- }
- } catch (IOException e) {
- System.err.println(e);
- }
注:调用 read(in_buffer) 方法和调用 read(in_buffer,0,in_buffer.length) 方法是同样的效果。
你也可以通过 java.io API 将非缓冲输入流转换为缓冲输入流,下面的代码和上面的代码运行结果相同,但是更有效率:
- Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt");
- …
- try (InputStream in = Files.newInputStream(rn_racquet);
- BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
- String line = null;
- while ((line = reader.readLine()) != null) {
- System.out.println(line);
- }
- } catch (IOException e) {
- System.err.println(e);
- }
上面三段代码输出的结果都是相同的:
- Racquet: Babolat AeroPro Drive GT
- String: Babolat RPM Blast 16
JavaNIO学习一的更多相关文章
- javaNIO学习
Buffer其实就是是一个容器对象,它包含一些要写入或者刚读出的数据.在NIO中加入Buffer对象,体现了新库与原I/O的一个重要区别.在面向流的I/O中,您将数据直接写入或者将数据直接读到Stre ...
- JavaNIO深入学习
NIO是Jdk中非常重要的一个组成部分,基于它的Netty开源框架可以很方便的开发高性能.高可靠性的网络服务器和客户端程序.本文将就其核心基础类型Channel, Buffer, Selector进行 ...
- Java I/O NIO学习
给出一个学习的链接讲的很全.. http://ifeve.com/java-nio-all/ 上边的是中文翻译的这里是原地址:http://tutorials.jenkov.com/java-nio/ ...
- 【公司要求】- RPC学习(一)
HADOOP-IPC(这里说的是1.0.4版本) 是轻量级RPC,在hadoop中主要用于2方面 1.TaskTracker和JobTracker 通讯. 2.NameNode和DataNode通讯. ...
- javaNIO(转载)
(一) Java NIO 概述 Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Chan ...
- Java NIO 学习总结 学习手册
原文 并发编程网(翻译):http://ifeve.com/java-nio-all/ 源自 http://tutorials.jenkov.com/java-nio/index.html Java ...
- Java NIO学习笔记---Channel
Java NIO 的核心组成部分: 1.Channels 2.Buffers 3.Selectors 我们首先来学习Channels(java.nio.channels): 通道 1)通道基础 通道( ...
- 20155204 2016-2017-2 《Java程序设计》第8周学习总结
学号 2016-2017-2 <Java程序设计>第X周学习总结 教材学习内容总结 想要取得channel的操作对象,可以使用channels类,它定义了静态方法newChannel(). ...
- Java NIO学习笔记
Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...
随机推荐
- body{font-size: 62.5%;} 解释
为什么body{font-size: 62.5%;} 2012-10-25 16:15 16778人阅读 评论(0) 收藏 举报 分类: css问题(17) 在网页设计中我们经常看见body{fo ...
- 遍历python字典几种方法
遍历python字典几种方法 from: http://ghostfromheaven.iteye.com/blog/1549441 aDict = {'key1':'value1', 'key2': ...
- Javaweb连接数据库
在JSP中使用JDBC驱动连接mysql数据库. 1: 下载mysql的Java连接程序 2: 解压目录下的mysql-connector-java-5.0.24-bin.jar文件就是连接MySql ...
- 新手C#SQLServer在程序里实现语句的学习2018.08.12
从C#中连接到SQL Server数据库,再通过C#编程实现SQL数据库的增删改查. ado.net提供了丰富的数据库操作,这些操作可以分为三个步骤: 第一,使用SqlConnection对象连接数据 ...
- MySql的基本架构演变
[MySql的基本架构演变] 没有并发的增长,也就没有必要做高可扩展性的架构. Scale-up : 纵向扩展,通过替换为更好的机器和资源来实现伸缩,提升服务能力 Scale-out : 横向扩展, ...
- goim源码分析与二次开发-comet分析二
这篇就是完全原版了,作为一个开始,先介绍comet入口文件main.go 第一步是初始化配置,还有白名单.还有性能监口,整体来说入口代码简洁可读性很强 然后开始初始化监控,还有bukcet这里buck ...
- SSH(Struts,Spring,Hibernate)和SSM(SpringMVC,Spring,MyBatis)的区别
SSH 通常指的是 Struts2 做前端控制器,Spring 管理各层的组件,Hibernate 负责持久化层. SSM 则指的是 SpringMVC 做前端控制器,Spring 管理各层的组件,M ...
- unity3d-ngui UIScrollView 滚动方向与滚轮相反
生成一个滚动面板之后发现滚轮向上滚,界面向下:滚轮向下界面向上.在编辑窗口里发现这个选项 本来是-2,修改成正数就可以了. http://ju.outofmemory.cn/entry/146754
- Unity即将内置骨骼动画插件Anima2D
Unity一直在寻找新的方法来帮助开发者,并为他们提供最好的工具.在此我们向大家宣布,Unity将内置流行的骨骼动画插件Anima2D,从2017年1月开始免费供所有Unity开发者使用! 同时也欢迎 ...
- (hash map)Two Sum, sorted(排序+双指针)closest,小于或大于的对数,组成不同的对数
原版 sorted [抄题]: [思维问题]: 存sum - nums[i](补集),若出现第二次则调出 [一句话思路]: hashmap中,重要的数值当做key,角标当做value. [画图]: [ ...