文章目录
一、IO流的基本概念
IO流
流是一种概念,是对数传输的总称,也就是数据在设备间的传输。
IO流是指数据的传输流动,按照流动的方向,以内存为基准,分为输入流input 和输出流output ,即流向内存是输入流,流出内存的输出流。
流的分类
1)按照数据流向
输入流是将存储设备中的内容读入搭配内存。输入流只能读取数据、不能写入数据,
输出流是将内存的内容写出到存储设备。只能写出数据、不能读取数据。
2)按照数据类型
字节流,以字节为单位, 可以读写所有数据。其中字节流操作的数据单元是8位的字节,
字符流,以字符为单位, 只能读写文本数据。操作的数据单元是16位的字符。
3)按照处理功能
节点流可以直接从/向一个特定的IO设备(磁盘、网络等)读/写数据,也称为低级流,
处理流是对节点流的连接或封装,用于简化数据读/写功能或提高效率,也称为高级流。
IO流的作用
用来处理设备间数据传输问题
常见的应用:文件复制、文件上传、文件下载
如果操作的是纯文本文件,优先使用字符流
如果操作的是二进制文件,优先使用字节流
如果不确定,则优先使用字节流
二、NIO
概念
Java NIO全称java non-blocking IO, 是指JDK提供的新API。从JDK1.4开始,Java提供了一系列改进的输入/输出的新特性,被统称为NIO(即New IO),是同步非阻塞的
NIO有三大核心部分: Channel(通道), Buffer(缓冲区),Selector(选择器)
NIO是面向缓冲区,或者面向块编程的。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞式的高伸缩性网络。
Channel(通道)
NIO的通道类似,但与流又有区别:通道可以进行读写,而流只能读或者写;通道可以支持异步读写。
Buffer(缓冲区)
Channel 提供从文件、网络读取数据的渠道,但是读取或写入的数据都必须经由Buffer。
Selector(选择器)
基本上,所有的IO在NIO中都从一个Channel开始,数据可以从Channel读到Buffer中,也可以从Buffer写到Channel中。Channel有好几种类型,其中比较常用的有FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel等,这些通道涵盖了UDP和TCP网络IO以及文件IO。
Buffer本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。Java NIO里关键的Buffer实现有CharBuffer、ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。这些Buffer覆盖了你能通过IO发送的基本数据类型,即byte、short、int、long、float、double、char。
Buffer对象包含三个重要的属性,分别是capacity、position、limit,其中position和limit的含义取决于Buffer处在读模式还是写模式。但不管Buffer处在什么模式,capacity的含义总是一样的。
capacity:作为一个内存块,Buffer有个固定的最大值,就是capacity。Buffer只能写capacity个数据,一旦Buffer满了,需要将其清空才能继续写数据往里写数据。
position:当写数据到Buffer中时,position表示当前的位置。初始的position值为0。当一个数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity–1。当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0。当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
limit:在写模式下,Buffer的limit表示最多能往Buffer里写多少数据,此时limit等于capacity。当切换Buffer到读模式时, limit表示你最多能读到多少数据,此时limit会被设置成写模式下的position值。
三、文件
File类并不是代表文件,它可以代表特定文件的名称,也可以是某个目录。
这里的名称就是路径,因为在Java中“\”被用作转义字符,所有Windows风格的路径最好用"\“来分隔,如:“c:\temp\test.txt”, 但在Linux下此路径就应该这样写: c:/temp/test.txt”。
如果要考虑跨平台,则最好这样写:
“c:”+File.separator+“temp” +File.separator+“文件名”
文件file 相关方法使用
package com.wdy.file;
import java.io.File;
import java.util.Date;
/**
* 文件file 相关方法使用
* @author wdy
*
*/
public class FileDemo {
public static void main(String[] args) throws Exception {
File file = new File("D:\\abc.txt"); //创建文件对象
file.createNewFile(); //该路径不存在的同名文件情况下,创建文件
System.out.println("是否是文件夹: " +file.isDirectory());
System.out.println("是否是文件: " +file.isFile());
System.out.println(file.isHidden()); //文件是否隐藏
System.out.println(new Date(file.lastModified()));//文件创建时间
System.out.println(file.getTotalSpace()/1024/1024/1024+"G");// 字节/kb/M/G 计算硬盘总空间
System.out.println(file.getFreeSpace()/1024/1024/1024+"G");//计算硬盘剩余空间
File f = new File("C:\\");
File[] fs = f.listFiles(); //.listFiles() 获取C盘下所有的文件和文件夹,放入数组
for (File f2 : fs) {
System.out.println(f2.getName()); //遍历显示
}
//file.delete(); //删除文件
}
}
Demo 通过递归 查找c盘下所有文件
package com.wdy.file;
import java.io.File;
/**
* file文件
* 通过递归 查找c盘下所有文件
* @author wdy
*
*/
public class FileDemo02 {
static int count = 0;
public static void main(String[] args) {
File file = new File("C:\\");
find(file);
System.out.println("图片: " + count);
}
public static void find(File file) {
if(file!=null && file.isFile()) {//如果是文件 递归终止条件
String fileName = file.getName();
// if(fileName.endsWith(".jpg")||fileName.endsWith(".png")) { //.endswith() 查找 .jpg 和 . png
// count++;
// System.out.println(fileName);
// }
return;
}
File[] fs = file.listFiles(); //查找C盘下的所有文件夹
if(fs!=null) {
for (File f : fs) {
find(f); //对每个子文件夹进行 递归调用
}
}
}
}
File和流
File类关心得是磁盘上存储的文件,而流是指程序运行中数据的通道信道,流类关心的是文件的内容。
流
输入流和输出流
-
输入流就是从外部读取数据进入程序,然后由程序处理。通过read方法把内容读到流管道。InputStream类是所有输入流类的基类。InputStream类是抽线类,没有构造方法,一般使用它的子类来实现读入,如FileInputStream类、
-
输出流是以程序为起点输出数据。通过write方法把内容写到流管道。OutputStream类是所有输出流的基类。也是一个抽象类。
字节流和字符流
- 字节流以字节的形式来处理数据
- 字符流以字符的形式来处理数据
输入流 Demo: 读取指定文件内容
获取当前文件位置的方法:
1)右击java文件 ——Properties——查看绝对路径
2)利用线程获取bin文件夹位置,再进行修改
用字节流读取出现乱码(中文乱码)如何解决?
1)采用包装模式:字节流——>字符流
InputStreamReader
2)直接用字符流读取 Reader类
try…with…resource 捕获异常 :
使用条件:出现异常的类实现了Closeable接口
如Reader类实现了Closeable接口
package com.wdy.stream;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
/*
* 把FileDemo02读入到流。(输入流)
* 循环在程序中显示读到的内容
* 利用线程获取当前文件夹的位置
* 包装模式。字节流——>字符流
*/
public class InputStreamDemo {
public static void main(String[] args) {
String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();//当前线程。类加载器。从根目录开始获取 bin。显示为字符串的形式
System.out.println(path);
path =path.substring(0, path.length()-4); //截取字符串
System.out.println(path);
String fileName = path+"src/io/file/FileDemo02.java"; //拼接字符串 尾部
// 包装模式
// InputStreamReader(类) 把字节流(FileInputStream)包装成字符流 否则中文会乱码
try(InputStreamReader r = new InputStreamReader(new FileInputStream(fileName));){
int n = 0;
while((n=r.read())!=-1) {
System.out.print((char)n);
}
}catch(Exception e) {
e.printStackTrace(); //打印异常
}
}
}
用字符流读取:Reader
package com.wdy.stream;
import java.io.FileReader;
import java.io.Reader;
/*
* 使用字符流读取
*/
public class ReaderDemo {
public static void main(String[] args) {
String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
System.out.println(path);
path =path.substring(0, path.length()-4);
System.out.println(path);
String fileName = path+"src/io/file/FileDemo02.java";
//读取字符流
try(Reader in = new FileReader(fileName);){//try...with...resource 捕获异常 使用条件:Reader类型实现了Closeable接口
int n = 0;
while((n = in.read())!=-1){
System.out.print((char)n);
}
}catch(Exception e) {
e.printStackTrace();
}
}
}
输出流
1)输出流:即在指定文件写内容的操作方法
2)OutputStream底层实现了Closeable接口 (关闭的接口) 、flushable接口(缓存的接口)
3)提高拷贝效率的方式 :
缓存:创建1024字节大小的数组中。讲数据先读入数组(缓存)中
4).close() 底层包含了flush()方法,flush()方法可以省略
5)音视频拷贝使用字节流
输入流demo1:用字节流完成输出流 。在文件中写入一个字符。通过write方法把内容写到流管道
package com.wdy.stream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
/*
* 字节输出流的两种写法
*/
public class OutStreamDemo {
public static void main(String[] args) throws Exception {
// try(OutputStream out = new FileOutputStream("D:\\out.txt", false);){
// out.write('a');
//
// }catch (Exception e) {
// e.printStackTrace();
// }
OutputStream out = new FileOutputStream("D:\\out.txt", false);
out.write('c');
out.flush();
out.close();
}
}
输入流demo2:用字符流完成输入流 。在文件中写入字符串
package com.wdy.stream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class WriterDemo {
public static void main(String[] args) throws Exception {
Writer out = new FileWriter("D:\\outw.txt", true); //true和false false取决于是在文件中直接写入字符流 true是再文件内容尾部追加
out.write("test...");
// out.flush();//字符流相当于包装的字节流。字符流写入文件需要一个缓存时间
out.close();//关闭 close()底层调用了flush()方法,flush可以神略
}
}
输入流demo3:视频拷贝。利用缓存提高效率
package com.wdy.stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
/*
* demo进行视频拷贝
* 输入流 先读取一个视频(视频用字节流)
* 再写入一个视频
*/
public class CopyDemo {
public static void main(String[] args) throws Exception {
InputStream in = new FileInputStream("D:\\dyit\\20220202\\video\\IO-File.mp4");//输入流
OutputStream out = new FileOutputStream("D:\\copyMovie.mp4"); //输出流
byte[] buffer = new byte[1024];//缓存概念 创建一个缓存数组
long s1 = System.currentTimeMillis();// 拷贝时间
while(in.read(buffer)!=-1) { // .read() 将字符读入数组中的某一部分。
out.write(buffer); //写入缓存(数组)中
}
long s2 = System.currentTimeMillis();
System.out.println("拷贝耗时: " + (s2-s1));
out.close();//关闭 已经包含flush()
in.close();
}
}