package top.liangliangzi.nio;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.WritableByteChannel;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
/**
* NIO
* 基类是Buffer 子类有:ByteBuffer , CharBuffer , DoubleBuffer
* FloatBuffer , IntBuffer , LongBuffer , ShortBuffer
* 所有的NIO操作均为异步操作,类似AJAX
* 所有的IO操作均为同步操作。
* @author 32081
*
*/
public class TestNewIO {
public static void main(String[] args) throws FileNotFoundException, IOException {
//nio的读写数据
//testNewIO();
//从文件中读或者写数据
//testReadWriteToFile();
//更新缓冲区里面的部分值的操作
//缓冲区里面有:0,1,2,3,4,5,6,7,8,9
//更改为:0,1,2,6,8,10,12,14,8,9
//testByteBuffer();
//NIO复制文件三种不同的方法的效率对比
//nioCopyFile();
//文件锁
fileLock();
}
/**
* 文件锁,当当前线程获取到文件锁时,不在允许别的线程访问操作此文件,直到文件锁释放
* (1.获取文件通道
* (2.文件通道获取文件锁,获取时候就锁定文件 fc.lock()
* (3.当文件锁对象调用 lock.release()或lock.close()方法时,锁才释放允许其他线程访问文件
* fc.tryLock();trylock锁与lock锁的区别是,前者在没有获取到锁的时候回返回Null,后者会休眠等待
* 测试,打开两个命令控制台,分别同时运行此文件查看
*/
private static void fileLock() {
try(FileChannel fc = FileChannel.open(
Paths.get("D:\\图片\\background\\background8.jpg"),
StandardOpenOption.READ,StandardOpenOption.WRITE);
){
//获取文件锁
FileLock lock = fc.lock();
for(int i = 0;i < 10;i++) {
Thread.sleep(1000);
System.out.println(i);
}
//释放锁
lock.release();
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* nio的读写数据
* nio的三个核心变量
* (1.position ,下标,角标,要操作数据的位置,从0开始, 类似数组的index
* (2.limit ,界限,最多能够操作的位置。比如说:容量100,但是里面只装了10个数据,最多只能读取到10个数据。10就是界限
* (3.capacity ,容量,指最多能容纳多少数据,单位是byte,类似数组的length
* 1.创建缓冲流
* (1.ByteBuffer bf = ByteBuffer.allocate(缓冲大小);
* (2.byte[] b = new byte[1024]; ByteBuffer bf = ByteBuffer.wrap(b);
* 2.用putXX/put方法写入数据
* 3.获取数据前需要进行 反转 bf.flip();limit = position; position = 0;mark = -1;
* 4.用getXX/get方法获取数据,且获取的数据是要跟插入的数据顺序一样
*/
private static void testNewIO() {
ByteBuffer bf = ByteBuffer.allocate(25);
// byte[] b = new byte[1024];
// ByteBuffer bf1 = ByteBuffer.wrap(b);
System.out.println(bf);
bf.putInt(12);
System.out.println(bf);
bf.putDouble(12.14);
System.out.println(bf);
bf.putLong(1000000l);
System.out.println(bf);
System.out.println("------------------");
//反转,limit = position;position = 0;mark = -1;
//要取值就要反转一下
//且取值要按照插入的顺序取值,不然值会乱
bf.flip();
System.out.println(bf);
System.out.println(bf.getDouble());
System.out.println(bf);
System.out.println(bf.getLong());
System.out.println(bf);
System.out.println(bf.getInt());
System.out.println(bf);
}
/**
* 从文件中读写数据
* 一.写数据
* (1.文件源
* (2.获取写流FileOutputStream()
* (3.通过流打开通道FileCannel is.getFileCannel()
* (4.创建缓冲区
* (5.写数据的时候不能调用 反转 bf.flip(),调用了之后会写入数据失败
* (6.fc.write(ByteBuffer bf);
* 二.读数据
* (1.文件源
* (2.获取读流FileInputStream()
* (3.打开通道 is.getFileCannel()
* (4.创建缓冲区
* (5.读取数据到缓冲区 is.read(FileCannel fc)
* (6.缓冲区进行 反转 fc.flip();
* (7.循环读取数据
* (8.fc.hasRemaining()方式是判断position与limit之间是否还存在数据,存在返回true
* @throws IOException
* @throws FileNotFoundException
*/
private static void testReadWriteToFile() throws FileNotFoundException, IOException {
//把数据写入文件中
File file = new File("C:\\Users\\32081\\Desktop\\test.txt");
try(FileOutputStream os = new FileOutputStream(file);){
//打开通道
FileChannel fc = os.getChannel();
//创建缓冲
byte[] b = new byte[] {97,98,99,100,101,102};
ByteBuffer bf = ByteBuffer.wrap(b);
//写入数据
fc.write(bf);
System.out.println("写入文件完成!");
}catch (Exception e) {
e.printStackTrace();
}
//从文件中读数据
File file2 = new File("C:\\Users\\32081\\Desktop\\test.txt");
try(FileInputStream is = new FileInputStream(file2)){
//打开通道
FileChannel channel = is.getChannel();
//创建缓冲区
ByteBuffer bf = ByteBuffer.allocate(1024);
//读取数据到缓冲区
channel.read(bf);
//反转
bf.flip();
//循环遍历
while(bf.hasRemaining()) {
System.out.println((char)bf.get());
}
}
}
/**
* 缓冲区里面有:0,1,2,3,4,5,6,7,8,9
* 更改为:0,1,2,6,8,10,12,14,8,9
* (1.创建缓冲区并指定容量
* (2.放入数据
* (3.设置position与limit的位置;fc.position(index),fc.limit(index);
* (4.调用slice方法创建一个同步的新缓冲区,
* 新缓冲区的内容将从此缓冲区的当前位置开始。 对这个缓冲区内容的更改将在新的缓冲区中可见
* ,反之亦然; 两个缓冲区的位置,极限和标记值将是独立的。
* (5.在新缓冲区中循环进行相关操作并赋值在新缓冲区,此操作相当于操作原本的缓冲区
* (6.调整position与limit的位置然后遍历输出原本的缓冲区
*/
private static void testByteBuffer() {
//创建40个字节,10个整型的缓冲区
IntBuffer bf = IntBuffer.allocate(10);
//放入数据
for(int i = 0;i < bf.capacity();i++) {
bf.put(i);
}
//设置position与limit的指向
bf.position(3);//指向第四个元素,包括本身
bf.limit(8);//指向第九个元素但不包括本身:排他
//获取新缓冲区
IntBuffer slice = bf.slice();
//循环操作
for(int i = 0;i < slice.capacity();i++) {
slice.put(slice.get(i)*2);//更新操作
}
//从新调整posistion与limit位置
bf.position(0);
bf.limit(bf.capacity());
for(int i = 0;i < bf.capacity();i++) {
System.out.print(bf.get(i)+" ");
}
}
private static void nioCopyFile() throws FileNotFoundException, IOException {
//源文件
File source = new File("D:\\图片\\background\\background8.jpg");
//目标文件
File target = new File("D:\\图片\\copy.jpg");
//baseCopy(source,target);//基础的NIO复制文件 耗时:48
//baseAdvancedCopy(source,target);//基础进阶的NIO复制文件 耗时:10
//SuperAdvancedCopy(source,target);//超进阶的NIO复制文件 耗时:13
}
/**
* 超进阶的NIO复制文件
* (1.调用Files工具类的copy方法copy(Path source, Path target, CopyOption... options)
* (2.source:源的Path对象 File.toPath()即可
* target:目标的Path对象 File.toPath()
* options: 调用枚举类StandardCopyOption的属性
* ATOMIC_MOVE 将文件作为原子文件系统操作移动。
* COPY_ATTRIBUTES 将属性复制到新文件。
* REPLACE_EXISTING 替换现有文件(如果存在)。
* @param source
* @param target
* @throws IOException
*/
private static void SuperAdvancedCopy(File source, File target) throws IOException {
Long start = System.currentTimeMillis();
Files.copy(source.toPath(), target.toPath(),
StandardCopyOption.COPY_ATTRIBUTES,StandardCopyOption.REPLACE_EXISTING);
Long end = System.currentTimeMillis();
System.out.println("SuperAdvancedCopy超进阶的NIO复制文件耗时:" + (end - start));
}
/**
* 基础进阶的NIO复制文件
* (1.创建输入输出流
* (2.获取输出跟输出流的通道
* (3.输入通道里面的transferTo(long position, long count,WritableByteChannel target)
* 方法一行搞定复制,position:开始位置 一般为0
* count:结束位置 一般为输入通道.size()
* target:写出通道
* @param source
* @param target
* @throws FileNotFoundException
* @throws IOException
*/
private static void baseAdvancedCopy(File source, File target) throws FileNotFoundException, IOException {
Long start = System.currentTimeMillis();
try(FileInputStream is = new FileInputStream(source);
FileOutputStream os = new FileOutputStream(target);
){
//打开两个通道
FileChannel fis = is.getChannel();
FileChannel fos = os.getChannel();
//一行搞定复制
fis.transferTo(0, fis.size(), fos);
Long end = System.currentTimeMillis();
System.out.println("baseAdvancedCopy基础进阶新IO的复制文件耗时:" + (end - start));
}
}
/**
* 基础的NIO复制文件
* (1.创建输入输出流
* (2.获取输出跟输出流的通道
* (3.创建缓冲区
* (4.while循环进行复制操作,每次循环都必须重置position与limit的下标
* 调用bf.clear(),因为每次循环都传输了一整个缓冲区大小的数据,
* 此时position与limit均已经指向缓冲区的末尾,即posiotion == capacity == limit
* (5.利用通道读取数据传输到缓冲区
* (6.缓冲区进行反转操作,然后利用写通道把缓冲区的数据写出
* @param source 文件源
* @param target 目标文件
* @throws FileNotFoundException
* @throws IOException
*/
private static void baseCopy(File source, File target) throws FileNotFoundException, IOException {
Long start = System.currentTimeMillis();
try(FileInputStream is = new FileInputStream(source);
FileOutputStream os = new FileOutputStream(target);
){
//打开两个通道
FileChannel fis = is.getChannel();
FileChannel fos = os.getChannel();
//创建缓冲区
ByteBuffer bf = ByteBuffer.allocate(1024);
while(true) {
//重置limit与position,因为每次循环
//position与limit都会移动到缓冲区最后,所以需要重置
bf.clear();
int read = fis.read(bf);
if(read == -1) { //读完没有资源了就退出循环
break;
}
//缓冲区进行反转
bf.flip();
//写出资源
fos.write(bf);
}
Long end = System.currentTimeMillis();
System.out.println("baseCopy基础新IO的复制文件耗时:" + (end - start));
}
}
}