IO流
1.流的概念和作用
流是一组有序的,有起点和终点的字节的集合,是对数据传输的总称或者抽象说法。即数据在两个设备之间传输称为流,流的本质是数据传输;
分类
-
按处理数据单元分类
-
字节流:以字节单位获取数据,命名上以stream结尾的流一般是字节流,eg:FileInputStream,FileOutputStream
-
字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,eg:FileReader,FileWriter
-
区别:
(1):读写单位不同,字节流以字节为单位(8bit),字符流以字符为单位,根据码表映射字符,一次可读多个字节。
(2):处理对象不同,字节流能处理所有类型的数据,图片,avi等;字符流只能处理字符类型的数据;
-
结论:只要是纯文本输出 使用字符流,除此之外都是用字节流;
-
-
按处理对象不同分类
-
节点流:可以直接从数据源或目的地读写数据
操作文件 操作数组 操作字符串 操作管道 FileInputStream ByteArrayInputStream StringReader PipedInputStream FileOutputStream ByteArrayOutputStream StringWriter PipedOutputStream FileReader CharArrayReader / PipedReader FileWriter CharArrayWriter / PipedWriter -
处理流:也叫缓冲流,不直接链接到数据源或者目地,使用在节点流上的流称为处理流,通过对其他流的处理提升程序性能,处理流的构造方法总要带一个其他流的对象作为参数,一个流的对象经过其他流的包装称为流的链接。
(1):缓冲流:BufferedInputStream,BufferedOutputStream ,BufferReader,BufferWriter增加缓冲功能,避免频繁读写硬盘。
(2)转换流:InputStreamReader,OutputStreamReader 实现了字节流和字符流之间的转换。
(3):数据流:DataInputStream,DataOutputStream 等将其他基础数据类型写入到文件中,或者读取出来。
-
转换流:
InputStreamReader,OutputStreamWriter 需要InputStream或OutputStream作为参数,实现从字节流到到字符流的转换。
-
节点流处于IO操作的第一线,所有操作必须通过他们进行;处理流可以对节点流进行包装,提高性能或者提高程序的灵活性
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N8x274UE-1620533981839)(C:\Users\Mr.zhang\Desktop\截图\stream.png)]
-
2.四大抽象类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uTj1jp47-1620533981842)(C:\Users\Mr.zhang\Desktop\截图\io1.png)]
- InputStream 是所有输入字节流的父类,抽象类
- ByteArrayInputStream从byte数组中读取数据
- StringBufferInputStream从StringBuffer中读取数据
- FileInputStream从本地文件中读取数据
- ObjectInputStream和所有FileInputStream的子类都是装饰流
- OutputStream 操作字节输出流
- ByteArrayOutputStream,FileOutputStream是两种基本的介质流,分别向Byte数组和本地文件中写入数据
- Reader操作字符输入流
- Writer操作字符输出流
3.总结
- 输入流:InputStream和Reader,从文件读取 到程序中
- 输出流:OutputStream和Writer ,从程序中写入到文件
4.FileInputStream,FileOutputStream,读写取文件内容。
public static void main(String[] args) {
String filePath = "C:\\Users\\Mr.zhang\\Desktop\\MarkDown\\ioTest\\iotest.txt";
//测试写入文件
writeFile(filePath,"今天也是做好人的一天,你很好");
//测试从文件中读取内容
System.out.println(readFile(filePath));
}
/**
* 将content写入指定文件
* @param filePath
* @param content
*/
public static void writeFile(String filePath,String content){
FileOutputStream fileOutputStream = null;
try {
//1.根据文件路径创建输出流
fileOutputStream = new FileOutputStream(filePath);
//2.把String转换成byte数组
byte[] bytes = content.getBytes();
//3.把bytes数组写入输出流中
fileOutputStream.write(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 读取文件内容
* @return
*/
public static String readFile(String filePath){
FileInputStream fileInputStream = null;
String result = "";
try {
//根据path路径实例化一个输入流的对象
fileInputStream = new FileInputStream(filePath);
//返回这个输入流可以被都剩下的bytes字节的剩余量
int available = fileInputStream.available();
//创建一个接收数组
byte[] bytes = new byte[available];
fileInputStream.read(bytes);
result = new String(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
运行结果
今天也是做好人的一天,你很好
Process finished with exit code 0
5.File
文件和目录路径名的抽象表示,可以完成对所有文件的操作。
-
构造
public File(String pathname) //文件的绝对路径 public File(URI uri) //文件的URI地址 public File(String parent, String child) //指定父文件绝对路径、子文件绝对路径 public File(File parent, String child) //指定父文件、子文件相对路径 //下面这两个是File类中私有的构造函数,外面不能调用 private File(String child, File parent) private File(String pathname, int prefixLength)
-
感兴趣的方法 可以读取指定目录下的文件,可以通过FileFilter 过滤指定后缀的文件
static void flieListFilter(){ MyFileFlter myFileFlter =new MyFileFlter(".png"); File file = new File("C:\\Users\\Mr.zhang\\Desktop\\截图"); File[] files = file.listFiles(myFileFlter); for (File f : files) { System.out.println(f.getName()); } } static class MyFileFlter implements FileFilter{ String type; //文件类型 MyFileFlter(){ } MyFileFlter(String type){ this.type = type; } @Override public boolean accept(File pathname) { if(!pathname.isDirectory()){ pathname.getName().endsWith(type); return true; } return false; } }
6.缓冲流BufferedInputStream,BufferOutputStream
BufferedInputStream和BufferedOutputStream这两个类分别是FilterInputStream和FilterOutputStream的子类,作为装饰器子类,使用它们可以防止每次读取/发送数据时进行实际的写操作,代表着使用缓冲区。
我们有必要知道不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!
同时正因为它们实现了缓冲功能,所以要注意在使用BufferedOutputStream写完数据后,要调用flush()方法或close()方法,强行将缓冲区中的数据写出。否则可能无法写出数据。与之相似还BufferedReader和BufferedWriter两个类。
现在就可以回答在本文的开头提出的问题:
BufferedInputStream
和BufferedOutputStream
类就是实现了缓冲功能的输入流/输出流。使用带缓冲的输入输出流,效率更高,速度更快。
BufferedInputStream本质上是通过一个内部缓冲区数组实现的,BufferInputStream会将输入流的数据分批填入缓冲区中,每当缓冲区的数据被读取完之后,输入流会再次填入到缓冲区,如此反复;
那么什么时候flush()才有效呢?
答案是:当OutputStream是BufferedOutputStream时。
当写文件需要flush()的效果时,需要
FileOutputStream fos = new FileOutputStream(“c:\a.txt”);
BufferedOutputStream bos = new BufferedOutputStream(fos);
也就是说,需要将FileOutputStream作为BufferedOutputStream构造函数的参数传入,然后对BufferedOutputStream进行写入操作,才能利用缓冲及flush()。
查看BufferedOutputStream的源代码,发现所谓的buffer其实就是一个byte[]。
BufferedOutputStream的每一次write其实是将内容写入byte[],当buffer容量到达上限时,会触发真正的磁盘写入。
而另一种触发磁盘写入的办法就是调用flush()了。
BufferedOutputStream
在close()
时会自动flush
BufferedOutputStream
在不调用close()
的情况下,缓冲区不满,又需要把缓冲区的内容写入到文件或通过网络发送到别的机器时,才需要调用flush.
public static void main(String[] args) {
//复制图片
copuFile("d:\\io2.png","C:\\Users\\Mr.zhang\\Desktop\\截图\\io1.png");
}
/**
* 复制图片
*/
static void copuFile(String newFilePath,String oldFilePath){
InputStream inputStream = null;
BufferedInputStream bis = null;
OutputStream outputStream = null;
BufferedOutputStream bos = null;
File newFile = new File(newFilePath);
File oldFile = new File(oldFilePath);
try {
inputStream = new FileInputStream(oldFile); //创建一个输入流
bis = new BufferedInputStream(inputStream); //对接缓冲流
outputStream = new FileOutputStream(newFile); //创建一个输出流
bos = new BufferedOutputStream(outputStream); //对接缓冲流
byte[] inputByte = new byte[1024]; //代表一次读取1kb 暂存从输入流读取的数据
int temp = 0; //代表实际读取的字节数
while ((temp=bis.read(inputByte))!=-1){
bos.write(inputByte,0,temp);
}
//主动刷新,将缓冲区的内容写入到文件
bos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
close()方法的作用
关闭输入流,并且释放系统资源
BufferedInputStream装饰一个 InputStream 使之具有缓冲功能,is要关闭只需要调用最终被装饰出的对象的 close()方法即可,因为它最终会调用真正数据源对象的 close()方法。因此,可以只调用外层流的close方法关闭其装饰的内层流。
7.缓冲流BufferedReader,BufferWriter
方法
void close() // 关闭此流,但要先刷新它。
void flush() //刷新该流的缓冲。
void newLine() //写入一个行分隔符。
void write(char[] cbuf, int off, int len) //写入字符数组的某一部分。
void write(int c) //写入单个字符。
void write(String s, int off, int len) //写入字符串的某一部分。
Test
public static void main(String[] args) {
copyFile("D:/newTest.txt","C:\\Users\\Mr.zhang\\Desktop\\MarkDown\\ioTest\\iotest.txt");
}
/**
* 复制txt文件
* @param newFileStr
* @param oldFileStr
*/
static void copyFile(String newFileStr,String oldFileStr){
FileReader fileReader = null;
BufferedReader bufferedReader = null;
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
File newFile = new File(newFileStr);
File oldFile = new File(oldFileStr);
try {
fileReader = new FileReader(oldFile); //创建FileReader读字符流
bufferedReader = new BufferedReader(fileReader); //接入缓冲流
fileWriter = new FileWriter(newFile); //创建FileWriter写字符流
bufferedWriter = new BufferedWriter(fileWriter); //接入缓冲流
String result = null; //每次读取内容
while (( result = bufferedReader.readLine() ) != null){
bufferedWriter.write(result);
bufferedWriter.newLine(); //换行
}
bufferedWriter.flush(); //强制把数组内容写入文件
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(bufferedWriter!=null){
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bufferedReader!=null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
8.转换流 InputStreamReader,OutputStreamWriter
InputStreamReader简介 是字符流Reader的子类,是字节流通向字符流桥梁,你可以在构造器中指定编码的方式,如果不指定将采用底层操作系统的默认方式,要启用从字节到字符的转换,可以提前从底层读取更多的字节,使其超过满足当前读取操作所需的字节。
-
构造
InputStreamReader(Inputstream in) //创建一个使用默认字符集的 InputStreamReader。 InputStreamReader(Inputstream in,Charset cs) //创建使用给定字符集的 InputStreamReader。 InputStreamReader(InputStream in, CharsetDecoder dec) //创建使用给定字符集解码器的 InputStreamReader。 InputStreamReader(InputStream in, String charsetName) //创建使用指定字符集的 InputStreamReader。
-
方法
void close() // 关闭该流并释放与之关联的所有资源。 String getEncoding() //返回此流使用的字符编码的名称。 int read() //读取单个字符。 int read(char[] cbuf, int offset, int length) //将字符读入数组中的某一部分。 boolean ready() //判断此流是否已经准备好用于读取。
OutputStreamWriter简介 是字符流Writer的子类,是字符流通向字节流的桥梁,每次调用write()方法会导致在给定字符上调用编码转换器,再写入之前,得到的这些字节流将在缓冲区累计。
-
构造
OutputStreamWriter(OutputStream out) //创建使用默认字符编码的 OutputStreamWriter OutputStreamWriter(OutputStream out, String charsetName) //创建使用指定字符集的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, Charset cs) //创建使用给定字符集的 OutputStreamWriter。 OutputStreamWriter(OutputStream out, CharsetEncoder enc) //创建使用给定字符集编码器的 OutputStreamWriter。
-
方法
void write(int c) //写入的字符长度 void write(char cbuf[]) //写入的字符数组 void write(String str) //写入的字符串 void write(String str, int off, int len) //应该写入的字符串,开始写入的索引位置,写入的长度 void close() //关闭该流并释放与之关联的所有资源。
public static void main(String[] args) {
copyFile("D:/new2Test.txt","C:\\Users\\Mr.zhang\\Desktop\\MarkDown\\ioTest\\iotest.txt");
}
/**
* 复制txt文件
* @param newFileStr
* @param oldFileStr
*/
static void copyFile(String newFileStr,String oldFileStr){
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
OutputStream outputStream = null;
OutputStreamWriter outputStreamWriter = null;
File newFile = new File(newFileStr);
File oldFile = new File(oldFileStr);
try {
inputStream = new FileInputStream(oldFile); //创建输入流
inputStreamReader = new InputStreamReader(inputStream); //创建转换输入流
outputStream = new FileOutputStream(newFile); //创建输出流
outputStreamWriter = new OutputStreamWriter(outputStream); //创建转换输出流
int temp = 0; //每次读取内容
while (( temp = inputStreamReader.read() ) != -1){
System.out.println(temp);
outputStreamWriter.write(temp); //读取二进制数据
}
outputStreamWriter.flush(); //强制把数组内容写入文件
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(outputStreamWriter!=null){
try {
outputStreamWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStreamReader!=null){
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 字节数组ByteArrayInputStream,ByteArrayOutputStream
ByteArrayInputStream 可以将字节数组转换为输入流;
ByteArrayOutputStream 可以捕获内存缓冲区中的数据,转换成字节数组;
-
ByteArrayInputStream
构造
public ByteArrayInputStream(byte buf[])
public ByteArrayInputStream(byte buf[], int offset, int length)
一般方法
void close() // 关闭该流并释放与之关联的所有资源。
String getEncoding() //返回此流使用的字符编码的名称。
int read() //读取单个字符。
int read(char[] cbuf, int offset, int length) //将字符读入数组中的某一部分。
boolean ready() //判断此流是否已经准备好用于读取。
String msg = "hello,world";
byte[] b = msg.getBytes();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(b);
int temp = -1;
while ((temp=byteArrayInputStream.read())!=-1){
System.out.println((char)temp);
}
try {
byteArrayInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
- ByteArrayOutputStream
-构造
~~~java
public ByteArrayOutputStream()
public ByteArrayOutputStream(int size)
~~~
-一般方法
void write(int b)
void write(byte b[], int off, int len)
void writeTo(OutputStream out)
byte toByteArray()[]
void close()
public static void main(String[] args) throws IOException {
String mes = "你好,world" ;
byte[] b = mes.getBytes() ;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
FileOutputStream fileOutputStream = null;
try {
byteArrayOutputStream.write(b);
fileOutputStream = new FileOutputStream("D:/byteArrayoutputstreamTest.txt");
byteArrayOutputStream.writeTo(fileOutputStream);
fileOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fileOutputStream!=null){
fileOutputStream.close();
}
if(byteArrayOutputStream!=null){
byteArrayOutputStream.close();
}
}
}
10.FileUtils 工具类
Commons io
Apache Commons IO是Apache基金会创建并维护的Java函数库。它提供了许多类使得开发者的常见任务变得简单,同时减少重复代码,这些代码可能遍布于每个独立的项目中,你却不得不重复的编写。
这里集成有很多对文件操作的方法,包括对集合的操作;
-
复制文件
-
复制文件夹
-
下载文件copyURLToFile(final URL source, final File destination)
-
字符串写入文件writeStringToFile(final File file, final String data, final String encoding) writeStringToFile(final File file, final String data, final Charset encoding, final boolean append)
-
把字节数组写入文件
void writeByteArrayToFile(final File file, final byte[] data) void writeByteArrayToFile(final File file, final byte[] data, final boolean append) void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len) void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len, final boolean append)
-
把集合里面的内容写入文件
void writeLines(final File file, final Collection<?> lines) void writeLines(final File file, final Collection<?> lines, final boolean append) void writeLines(final File file, final String encoding, final Collection<?> lines) void writeLines(final File file, final String encoding, final Collection<?> lines, final boolean append) void writeLines(final File file, final String encoding, final Collection<?> lines, final String lineEnding) void writeLines(final File file, final String encoding, final Collection<?> lines, final String lineEnding, final boolean append) void writeLines(final File file, final Collection<?> lines, final String lineEnding) void writeLines(final File file, final Collection<?> lines, final String lineEnding, final boolean append)