NIO
1.缓冲区
2.通道
Channel是通道,是程序与文件连接的桥梁,不能直接进行数据的交互需要借助buffer来进行数据的传输。要先使用的话需要建立通道,要想进行交互通道必须是打开状态.
2.1Channel常用方法
- void
close() 关闭此通道。 - boolean
isOpen() 判断此通道是否处于打开状态。
2.2计算机底层工作原理
-
最原始的思路
-
现在传统的io文件操作
如果有大量的请求操作会出现DMA总线冲突问题 -
独立的channel操作
2.3FileChannel 本地文件的操作
- 使用channel+buffer实现本地文件数据的读写操作
- 通道的获取方式 :
A. Java 针对支持通道的类提供了 getChannel() 方法
本地 IO:
-
FileInputStream/FileOutputStream
-
RandomAccessFile
网络IO: -
Socket
-
ServerSocket
-
DatagramSocket
B. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
C. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
2.4 FileChannel的常用方法
- long
transferFrom(ReadableByteChannel src, long position, long count) 将字节从给定的可读取字节通道传输到此通道的文件中。
- src - 源通道
- position - 文件中的位置,从此位置开始传输;必须为非负数
- count - 要传输的最大字节数;必须为非负数
- abstract long
transferTo(long position, long count, WritableByteChannel target) 将字节从此通道的文件传输到给定的可写入字节通道。
- position - 文件中的位置,从此位置开始传输;必须为非负数
- count - 要传输的最大字节数;必须为非负数
- target - 目标通道
@Test
void channel() {
// 创建io流,指定目标文件
try {
FileInputStream fis = new FileInputStream(new File("d:\\hello.txt"));
FileOutputStream fos = new FileOutputStream(new File("d:\\newhello.txt"));
// 创建通道,输入通道和输出通道(FileChannel)
FileChannel inchannel = fis.getChannel();
FileChannel outchannel = fos.getChannel();
// 创建一个缓冲区,用来存放数据
ByteBuffer buffer = ByteBuffer.allocate(5);// 该缓冲区的大小=文件大小
// 将源文件中的数据写入到缓冲区中
inchannel.read(buffer);
//测试
System.out.println(" ================== 写入模式 ========================");
System.out.println("总容量:" + buffer.capacity());
System.out.println("写入的起始标记:" + buffer.position());
System.out.println("写入时的limit: " + buffer.limit());
// 切换模式
buffer.flip();
// 读取通道中的缓冲区,将内容写出到新文件中
outchannel.write(buffer);
// 关闭资源
outchannel.close();
inchannel.close();
fos.flush();
fos.close();
fis.close();
System.out.println("文件写出成功");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
void channel01() {
try {
// 创建io流
FileInputStream fis = new FileInputStream(new File("d:\\hello.txt"));
FileOutputStream fos = new FileOutputStream(new File("d:\\newhello.txt"));
// 创建通道
FileChannel inchannel = fis.getChannel();
FileChannel outchannel = fos.getChannel();
// 创建一个缓冲区,用来存放数据
ByteBuffer buffer = ByteBuffer.allocate(5);// 该缓冲区的大小=文件大小
// int read = inchannel.read(buffer);
// System.out.println(read);
// buffer.clear();
// read = inchannel.read(buffer);
// System.out.println(read);
// buffer.clear();
// read = inchannel.read(buffer);
// System.out.println(read);
// buffer.clear();
// read = inchannel.read(buffer);
// System.out.println(read);
while (inchannel.read(buffer) != -1) {
// 转模式
buffer.flip();
// 写出操作
outchannel.read(buffer);
// 清空缓冲区
buffer.clear();
}
// 关闭资源
outchannel.close();
inchannel.close();
fos.flush();
fos.close();
fis.close();
System.out.println("文件写出成功");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//使用直接缓冲区
@Test
void channel03() {
try {
// 创建io流
FileInputStream fis = new FileInputStream(new File("d:\\hello.txt"));
FileOutputStream fos = new FileOutputStream(new File("d:\\newhello.txt"),true);
// 创建通道
FileChannel inchannel = fis.getChannel();
FileChannel outchannel = fos.getChannel();
// 创建一个缓冲区,用来存放数据
ByteBuffer buffer = ByteBuffer.allocate(5);// 该缓冲区的大小=文件大小
while (inchannel.read(buffer) != -1) {
// 转模式
buffer.flip();
// 写出操作
outchannel.read(buffer);
// 清空缓冲区
buffer.clear();
}
// 关闭资源
outchannel.close();
inchannel.close();
fos.flush();
fos.close();
fis.close();
System.out.println("文件写出成功");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//通道io文件拷贝
@Test
void channel04() {
//缓冲区
ByteBuffer buffer = ByteBuffer.allocate(100);
//读取数据到io流,保存到buffer中
try {
FileInputStream fis = new FileInputStream(new File("d:\\hello.txt"));
FileChannel inchannel = fis.getChannel();
inchannel.read(buffer);
buffer.flip();
//从buffer中读取数据,写出到目标文件中
FileOutputStream fos = new FileOutputStream(new File("hello.txt"));
FileChannel outchannel = fos.getChannel();
outchannel.write(buffer);
//关闭资源
inchannel.close();
outchannel.close();
System.out.println("文件写出成功");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
JDK1.7以后增加的方法
-
abstract MappedByteBuffer
map(FileChannel.MapMode mode, long position, long size) 将此通道的文件的区域映射到内存中。
-
static FileChannel
open(Path path, OpenOption… options) 打开或创建一个文件,返回一个文件通道来访问该文件。
- path -打开或创建的文件的路径
- options选项指定如何打开文件
- static FileChannel
open(Path path, Set<? extends OpenOption> options, FileAttribute<?>… attrs) 打开或创建一个文件,返回一个文件通道来访问该文件。
void channel02() {
//源文件,读源文件,设置读取权限
try {
FileChannel inChannel = FileChannel.open(Paths.get("d:\\hello.txt"), StandardOpenOption.READ);
//创建文件,先读取该文件在写出去
FileChannel outChannel = FileChannel.open(Paths.get("d:\\newhello.txt"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//创建内存映射文件MappedByteBuffer
MappedByteBuffer inmap = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outmap = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());
//直接缓冲区
ByteBuffer buffer = ByteBuffer.allocateDirect(inmap.limit());
byte[] b=new byte[inmap.limit()];
//通过直接缓冲区进行文件操作
//将源文件通过物理内存映射文件写入到缓冲区中
//将缓冲区中的数据要通过物理内存映射文件,将数据写出到目标文件
inmap.get(b);
outmap.get(b);
//关闭通道
inChannel.close();
outChannel.close();
System.out.println("文件拷贝成功");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
3.通道的操作
创建channel的方式三种:
- Io的getchannel方法,非直接缓冲区,需要缓冲区Buffer.
- Filechannel的open,直接缓冲区,不需要buffer.
- Files 工具类的 newByteChannel()
(1)通道与通道之间的数据传输
- 要使用FileChannel,只有1.7以后才能直接通过 channel来进行文件的拷贝操作.
@Test
void ChannerDemo() {
// 创建一个通道
try {
FileInputStream fis = new FileInputStream(new File("channel.txt"));
System.out.println(fis.available());
// 创建一个通道
FileChannel inchannel = fis.getChannel();
System.out.println(inchannel.size());
FileOutputStream fos = new FileOutputStream(new File("D:\\java笔记\\图片\\slxy2.jpg"));
// 创建一个写通道
FileChannel outchannel = fos.getChannel();
System.out.println(outchannel.size());
// 其他属性
System.out.println(inchannel.position());
// 将inchannel中的数据转到outchannel中
inchannel.transferTo(0, inchannel.size(), outchannel);
System.out.println(outchannel.size());
System.out.println(inchannel.size());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//可以通过channel将数据快速复制到其他channel中
@Test
void ChannerDemo01() {
try {
//通道一
FileChannel inChannel=FileChannel.open(Paths.get("D:\\图片\\德云社06.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//通道二
FileChannel outChannel=FileChannel.open(Paths.get("D:\\图片\\德云社06.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
System.out.println("交互之前"+inChannel.size());
//数据交互
inChannel.transferFrom(outChannel, 0, outChannel.size());//out-->in
outChannel.transferTo(0, outChannel.size(), inChannel);//in-->out
System.out.println("交互之后"+inChannel.size());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
void ChannerDemo02() {
try {
//通道一
FileChannel inChannel=FileChannel.open(Paths.get("D:\\图片\\德云社05.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
//通道二
FileChannel outChannel=FileChannel.open(Paths.get("D:\\图片\\德云社08.jpg"),StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
outChannel.transferTo(0, inChannel.size(),outChannel);
System.out.println("拷贝成功");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
(2)分散和聚合数据操作
- 多个缓冲区与一个通道的操作
//将通道中的数据保存到多个缓冲区中
@Test
void channelDemo() {
//创建通道
try {
FileInputStream fis=new FileInputStream(new File("D:\\hello.txt"));
FileChannel channel=fis.getChannel();
//创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(7);
ByteBuffer buffer01 = ByteBuffer.allocate(6);
ByteBuffer[] bbf=new ByteBuffer[2];
bbf[0]=buffer;
bbf[1]=buffer01;
//将数据写入到缓冲区中
channel.read(bbf);
System.out.println(buffer.position());
System.out.println(buffer01.position());
//获取所有缓冲区中的数据
for (ByteBuffer byteBuffer : bbf) {
byteBuffer.flip();
System.out.println(byteBuffer.array());
}
//获取Buffer中的数据打印到控制台
buffer01.flip();
byte[] b=new byte[buffer01.limit()];
buffer01.get(b);
System.out.println(new String(b));
byte[] b1=buffer01.array();
System.out.println(new String(b1));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
void channelDemo01() {
//创建缓冲区
ByteBuffer allocate01 = ByteBuffer.allocate(5);
ByteBuffer allocate02 = ByteBuffer.allocate(5);
ByteBuffer allocate03 = ByteBuffer.allocate(5);
//添加数据
allocate01.put("12345".getBytes());
allocate02.put("lucky".getBytes());
allocate03.put("64486".getBytes());
ByteBuffer[] bbf=new ByteBuffer[3];
bbf[0]=allocate01;
bbf[1]=allocate02;
bbf[2]=allocate03;
//转为写入模式
for (ByteBuffer byteBuffer : bbf) {
byteBuffer.flip();
}
//创建写入通道
try {
FileOutputStream fos=new FileOutputStream(new File("D:\\hello.txt"));
FileChannel channel = fos.getChannel();
channel.write(bbf);
//在关闭资源之前进行非null判断
if(channel!=null) {
channel.close();
}
fos.flush();
fos.close();
System.out.println("写出成功");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
4.随机读写流RandomAccessFile
- 支持对随机存取文件的读取和写入
对文件来说
值 | 含义 |
---|---|
“r” | 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 |
“rw” | 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。 |
“rws” | 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到基础存储设备。 |
“rwd” | 打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到基础存储设备。 |
- 读取文件内容
@Test
void randomRead() {
try {
//创建随机读写流
RandomAccessFile raf=new RandomAccessFile(new File("D:\\java笔记\\newhello.txt"),"rw");
//文件长度
System.out.println(raf.length());
//获取指针位置
System.out.println("初始指针位置"+raf.getFilePointer());
//设置指针位置
raf.seek(5);
System.out.println("修改后指针位置"+raf.getFilePointer());
//读取数据
byte[] bytes=new byte[(int)raf.length()];
raf.read(bytes);
System.out.println(new String(bytes));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}@Test
void randomWrite() {
//创建随机读写流
try {
RandomAccessFile raf=new RandomAccessFile(new File("hello.txt"),"rw");
String mess="1234567890";
raf.seek(4);
raf.write(mess.getBytes());
raf.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
5.字符编码设置
//java中,使用Charset类表示表示编码对象
@Test
void character() {
//获取所有编码
Map<String,Charset> charsets = Charset.availableCharsets();
Set<Entry<String, Charset>> entrySet = charsets.entrySet();
for (Entry<String, Charset> entry : entrySet) {
System.out.println(entry.getKey()+"-----"+entry.getValue().name());
}
}
//java进行编码转换
@Test
void character01() {
String mess="未来,你好!!!";
try {
byte[] bytes=mess.getBytes("GBK");//将内容使用其他编码进行转换(编码)
System.out.println(new String(bytes,"GBK"));//解码
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//
}
NIO处理编码的
- 使用编码器和解码器的方式
- 创建charset对象,指定需要使用的编码对象
- 创建编码器和解码器,通过编码对象
- 通过编码器或者缓冲区,解码是使用缓冲区来进行读取数据
@Test
void charset02() {
// 编码器和解码器 使用的是同一charset得到的
System.out.println("================正常编码和解码 ===================");
Charset cs = Charset.forName("GBK");//编码对象
System.out.println(cs.name());
//创建编码器
CharsetEncoder encoder = cs.newEncoder();
//创建解码器
CharsetDecoder decoder = cs.newDecoder();
//创建字符缓冲区 用来存放 内容
CharBuffer cbuff = CharBuffer.allocate(10);
cbuff.put("世界美好与你环环相扣");
cbuff.flip();
try {
// 编码,获取字符缓冲区中的数据进行重新编码,得到一个字节缓冲区
ByteBuffer buff = encoder.encode(cbuff);//buff编码是GBK
// System.out.println(new String(buff.array(),"GBK"));
// 依次获取 buff中的元素
for (int i = 0; i < 12; i++) {
System.out.println(buff.get());
}
// 解码
buff.flip();
buff.limit(12);// 设置limit
System.out.println(buff.position());
System.out.println(buff.limit());
CharBuffer bf = decoder.decode(buff);
System.out.println(bf.position());
System.out.println(bf.limit());
System.out.println("===="+bf.toString());
System.out.println("--------------------非正常解码 -----------------");
//解码器和编码器使用的不是同一个charset
Charset ucs = Charset.forName("utf-8");// 12
buff.flip();
// buff.limit(12);// 设置limit
System.out.println(buff.position());
System.out.println(buff.limit());
CharBuffer decode = ucs.decode(buff);
System.out.println(decode.position());
System.out.println(decode.limit());
System.out.println("不同解码器进行解码:"+decode.toString());
} catch (CharacterCodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}