一、小案例
- 需求:我有一个文本文件,我知道数据是键值对形式的,但是不知道内容是什么。请写一个程序判断是否有“lisi”这样的键存在,如果有就改变其值为”100”
public static void main(String[] args) throws IOException {
//读取文件,把键值读取到集合里
Properties properties = new Properties();
properties.load(new FileReader("User.properties"));
//判断是否这个键存在,有就改
if(properties.containsKey("lisi")){
properties.setProperty("lisi","100");//键相同,值覆盖
}
properties.store(new FileWriter("User.properties"),null);
}
- 将一个music.mp3文件,拆分成多个小文件,再将多个小文件,合并成一个mp3文件
//将一个文件,拆分成多个小文件
FileInputStream in = new FileInputStream("歌曲串烧.mp3");
//我们封装一个文件夹,放我们拆分的文件
File file = new File("E:\\歌曲大合集");
if(!file.exists()){
file.mkdirs();
}
//拆分
int len=0;
byte[] bytes = new byte[1024 * 1024];
int index=1;
while((len=in.read(bytes))!=-1){
FileOutputStream out = new FileOutputStream(new File(file, index + ".mp3"));
out.write(bytes,0,len);
out.close();
index++;
}
in.close();
//将多个小文件,合并成一个大的文件
File file = new File("E:\\歌曲大合集");
File[] files = file.listFiles();
Vector<FileInputStream> vector = new Vector<>();
for (File f : files) {
FileInputStream in = new FileInputStream(f);
vector.add(in);
}
Enumeration<FileInputStream> elements = vector.elements();
SequenceInputStream sequenceInputStream = new SequenceInputStream(elements);
FileOutputStream out = new FileOutputStream(new File(file,"合并.mp3"));
int len=0;
byte[] bytes = new byte[1024 * 1024];
while((len=sequenceInputStream.read(bytes))!=-1){
out.write(bytes,0,len);
out.flush();
}
out.close();
sequenceInputStream.close();
//合并之后,把小文件删掉
for (File ff : files) {
if(ff.length()<=1024*1024){
ff.delete();
}
二、Java NIO
1.Java NIO的简介
Java NIO是从Java1.4版本开始引入的一个新的IO API。可以代替标准的Java IO API。NIO与原来的IO有相同的作用和目的,但是使用方式完全不同,NIO支持面向缓冲区、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
2.Java IO与NIO的区别
IO | NIO |
---|---|
面向流 | 面向缓冲区 |
阻塞IO | 非阻塞IO |
(无) | 选择器 |
3.通道Channel与缓冲区Buffer
- java NIO系统的核心在于:通道Channel与缓冲区Buffer
- 通道负责传输,缓冲区负责存储
三、缓冲区Buffer
缓冲区Buffer:一个用于基本数据类型的容器。由java.nio包定义的,所有缓冲区都是Buffer抽象类的子类
Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通道读入缓冲区的,是从缓冲区写入通道的
Buffer缓冲区底层是数组,它的总用就是用来存储数据的
针对基本数据类型(布尔类型除外),都提供有相应的缓冲区
ByteBuffer 字节缓冲区,最常用
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
CharBuffer
1.缓冲区的基本属性与方法
position位置:文件指针,从position开始可以读数据。缓冲区的位置不能大于其限制
limit界限:从limit往后的数据不可读写。缓冲区的限制不能大于容量
capacity缓冲区的容量,一旦指定容量后,就不能更改。缓冲区容量不能为负
//分配一个缓冲区,指定容量为十个字节
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
System.out.println(byteBuffer.position());//0
System.out.println(byteBuffer.capacity());//10
System.out.println(byteBuffer.limit());//10
//往容器中放数据put()
String str="abcde";
byteBuffer.put(str.getBytes());
System.out.println(byteBuffer.position());//5
System.out.println(byteBuffer.capacity());//10
System.out.println(byteBuffer.limit());//10
//读取缓冲区里的数据,切换为读取模式
byteBuffer.flip();
System.out.println(byteBuffer.position());//0
System.out.println(byteBuffer.capacity());//10
System.out.println(byteBuffer.limit());//5
//读取数据
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.get(bytes);
System.out.println(new String(bytes, 0, byteBuffer.limit()));//abcde
System.out.println(byteBuffer.position());//5
System.out.println(byteBuffer.capacity());//10
System.out.println(byteBuffer.limit());//5
//可重复读取
byteBuffer.rewind();
System.out.println(byteBuffer.position());//0
System.out.println(byteBuffer.capacity());//10
System.out.println(byteBuffer.limit());//5
//清空缓冲区
byteBuffer.clear();//clear()并不是把缓冲区里的字节数据清掉,而是把这些指针,设置到初始状态
System.out.println(byteBuffer.position());//0
System.out.println(byteBuffer.capacity());//10
System.out.println(byteBuffer.limit());//10
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
String str="abcde";
byteBuffer.put(str.getBytes());
byteBuffer.flip();//切换读取模式
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.get(bytes, 0, 2);//读取两个字节
System.out.println(byteBuffer.position());
byteBuffer.mark();//mark()标记,标记当前position的位置
byteBuffer.get(bytes,0,2);//在读取两个字节
System.out.println(byteBuffer.position());
byteBuffer.reset();//使用reset()就可以回到上次标记的位置
System.out.println(byteBuffer.position());
if(byteBuffer.hasRemaining()){
System.out.println(byteBuffer.remaining());//hasRemaining()看一下还有没有可读数据
}//remaining()还有多少可读数据
2.直接缓冲区与非直接缓冲区
缓冲区分为:直接缓冲区与非直接缓冲区
直接缓冲区:将缓冲区建立在物理内存上
非直接缓冲区:将缓冲区建立在JVM的内存上
//非直接缓冲区allocate(1024)
ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024);
//直接缓冲区allocateDirect(1024)
ByteBuffer byteBuffer2= ByteBuffer.allocateDirect(1024);
四、通道Channel
Channel通道,连接源节点与目标节点,不能存储数据
java为Channel接口提供的最主要的实现类如下:
- 本地文件传输通道
FileChannel:用于读取、写入、映射和操作文件的通道 - 网络数据传输通道
DatagramChannel :通过 UDP 读写网络中的数据通道
SocketChannel :通过 TCP 读写网络中的数据。
ServerSocketChannel :可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel
1.获取通道
- 方式一:FileOutputStream、FileInputStream、RandomAccessFile
通过getChannel();获取通道
//非直接缓冲区复制文件
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("曾经的你.mp3");
FileOutputStream out = new FileOutputStream("曾经的你2.mp3");
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();//获取通道
//面向通道与缓冲区来复制文件
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//分配一个非直接缓冲区
//读写文件
while(inChannel.read(byteBuffer)!=-1){
byteBuffer.flip();//切换读写模式
outChannel.write(byteBuffer);//写数据
byteBuffer.clear();//清空缓冲区
}
//释放资源
in.close();
out.close();
inChannel.close();
outChannel.close();
}
- 方式二:通过FileChannel的静态方法open()来打开一个通道
//非直接缓冲区复制文件
FileChannel inChannel = FileChannel.open(Paths.get("曾经的你.mp3"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("曾经的你2.mp3"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//StandardOpenOption.CREATE_NEW 文件不存在就创建,存在就报错
//StandardOpenOption.CREATE 文件不存在就创建,存在就覆盖
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//分配非直接缓冲区
while(inChannel.read(byteBuffer)!=-1){
byteBuffer.flip();//切换读取模式
outChannel.write(byteBuffer);
byteBuffer.clear();//清空
}
//释放资源
inChannel.close();
outChannel.close();
- 直接缓冲区复制文件
FileChannel inChannel = FileChannel.open(Paths.get("曾经的你.mp3"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("曾经的你2.mp3"), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//建立直接缓冲区
MappedByteBuffer inMap = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMap = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
//把数据读取到缓冲区中
byte[] bytes = new byte[inMap.limit()];
inMap.get(bytes);
outMap.get(bytes);
inChannel.close();
outChannel.close();
- 通道中的数据传输
//通道中的文件传输
FileChannel inChannel = FileChannel.open(Paths.get("曾经的你.mp3"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("曾经的你2.mp3"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//1.站在输入通道的角度
inChannel.transferTo(0,inChannel.size(),outChannel);
//2.站在输出通道的角度
outChannel.transferFrom(inChannel,0,inChannel.size());
- 分配多个缓冲区来进行文件的传输
FileChannel inChannel = FileChannel.open(Paths.get("MyTest3.java"), StandardOpenOption.READ);
FileChannel outChannel = FileChannel.open(Paths.get("MyTest.java"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//分配多个缓冲区
ByteBuffer byteBuffer1 = ByteBuffer.allocate(100);
ByteBuffer byteBuffer2= ByteBuffer.allocate(1024 * 2);
//定义一个数组
ByteBuffer[] byteBuffers ={byteBuffer1,byteBuffer2};
inChannel.read(byteBuffers);
//聚集
for (ByteBuffer byteBuffer : byteBuffers) {
byteBuffer.flip();//转换成读取模式
}
outChannel.write(byteBuffers);//写出数据
inChannel.close();
outChannel.close();//释放资源
- 获取通道的第三种方式
JDK1.7之后提供了一个工具类Files newByteChannel()
FileChannel in =(FileChannel) Files.newByteChannel(Paths.get("demo.txt"), StandardOpenOption.READ);
FileChannel out =(FileChannel)Files.newByteChannel(Paths.get("demo111.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
五、Files类
1.Files类中复制文件的方法
Files.copy(Paths.get("a.txt"), Paths.get("b.txt"), StandardCopyOption.REPLACE_EXISTING);
Files.copy(new FileInputStream("a.txt"), Paths.get("b.txt"), StandardCopyOption.REPLACE_EXISTING);
Files.copy(Paths.get("a.txt"), new FileOutputStream("b.txt"));
//复制文件,参1;源文件路径,参数2:目标文件路径
//StandardCopyOption.REPLACE_EXISTING 可选参数,文件存在,就覆盖
//不给第三个参数,文件存在就报错,也就是多次复制,就报错
Files.move(Paths.get("a.txt"), Paths.get("D:\\b.txt"));//移动剪切文件