一、流是什么?
File中虽然能进行一些常规的文件操作,但是这里少了两个非常核心的操作,java中针对文件操作,又进行了进一步的抽象~。
流是一组类/API,描述了如何来进行文件读写操作。
- 读文件
- 写文件
二、IO流分类
所有 IO流 相关的类,一共分为两大部分:
1.字节流 读写数据以字节为基本单位 字节(byte)。
2.字符流 读写数据以字符为基本单位 字符(char)。
处理文本文件/数据的时候,使用字符流。
处理二进制文件/数据的时候,使用字节流。
三、什么是文本文件?什么是二进制文件?
区分方式很简单:拿一个记事本打开这个文件。
里面的内容能看懂,就是文本文件。
里面的内容看不懂,就是二进制文件。
例如:
(a).docx(word文档)是文本文件,还是二进制文件?
二进制文件
虽然docx中主要内容是文本,但是由于要保存版式这样的基本信息,这些信息都是使用 二进制 的方式来保存的。
(b)excel创建的文件是文本文件还是二进制文件?
xlsx格式是二进制文件。
csv格式是文本文件。
四、字节流 和 字符流
- 字节流
1.InputStream:输入 从输入设备读取数据到内存中
2.OutputStream:输出 把内存中的数据写入到输出设备中
如果发现某个类的名字中有InputStream或 OutputStream,说明这个类就是字符流~
- 字符流:
1.Reader: 输入
2.Writer: 输出
如果发现某个类的名字中有Reader 或 Writer,说明这个类就是字符流~
注意,此处有特例,InputStreamReader,OutputStreamWriter是字符流
这两个类能够把字节流转换为字符流~~
五、流对象的核心操作流程
流对象的核心操作主要有四个:
1.打开文件(构造方法)
2.read : 从文件中把数据读取到内存中
3.write: 把数据从内存写入文件中
4.close: 关闭文件
如果不关闭文件资源,就会造成文件资源泄露。
文件资源泄露的关键在于,文件描述符表是有上限的。如果代码中一直在反复打开新的文件,而没有关闭的话,文件描述符表就会被打满,一旦满了之后,后面再想打开新的文件就会打开失败。
Reader和Writer基本操作:
package com.java.bite.Buffered_IO_Stream2;
import java.io.*;
/**
* @program: IO
* @description
* @author: wenwen
* @create: 2021-08-03 14:42
**/
//字符流 和字节流
public class CharStreamAndbyteStream3 {
private static void copyFile1(){
//处理文本文件,需要使用字符流
try(FileReader fileReader = new FileReader("d:/test_dir/新建文本文档.txt");
FileWriter fileWriter = new FileWriter("d:/test_dir/copyFile1.txt")){
char[] buffer = new char[1024];
int len = -1;
while((len = fileReader.read(buffer)) != -1){
fileWriter.write(buffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}
private static void copyFile2(){
try(BufferedReader bufferedReader = new BufferedReader(new FileReader("d:/test_dir/新建文本文档.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("d:/test_dir/copyFile2.txt"))){
char[] buffer = new char[1024];
int len = -1;
while((len = bufferedReader.read(buffer)) != -1){
bufferedWriter.write(buffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}
private static void copyFile3(){
// 带缓冲区的字符流中有一种特殊的用法,按行读取
try(BufferedReader bufferedReader = new BufferedReader(new FileReader("d:/test_dir/新建文本文档.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("d:/test_dir/copyFile3.txt"))){
String line = "";
//readLine 表示读一行,读到换行符位置,如果读取文件完毕,就会返回null
while((line = bufferedReader.readLine()) != null){
System.out.println("line:"+line);
bufferedWriter.write(line + "\n");
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
// copyFile1();
// copyFile2();
copyFile3();
}
}
FileInputStream和FileOutputStream基本操作
package com.java.bite.byteStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @program: IO
* @description
* @author: wenwen
* @create: 2021-08-03 11:25
**/
public class IODemo3 {
//当代码这样子的时候,就不需要显示调用close了
//try 语句会在代码执行完毕后,自动调用close方法(前提是这个类必须实现 Closable 接口)
private static void copyFiles(){
try(FileInputStream fileInputStream = new FileInputStream("d:/test_dir/1.jgp");
FileOutputStream fileOutputStream = new FileOutputStream("d:/test_dir/3.jpg")){
byte[] buffer = new byte[1024];//这就是缓冲区
int len = -1;
while((len = fileInputStream.read(buffer)) != -1){
fileOutputStream.write(buffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
}
}
六、类自带的缓冲区
BufferedInputStream
BufferedOutputStream
内置了缓冲区,=》本质上就是一块内存空间,缓冲区存在的意义就是为了提高程序的运行效率。
使用如下代码,测试带缓冲区和不带缓冲区的效率。
package com.java.bite.Buffered_IO_Stream2;
import java.io.*;
/**
* @program: IO
* @description
* @author: wenwen
* @create: 2021-08-03 13:55
**/
//分别不使用缓冲区和使用缓冲区读取一个大文件~感受时间上的差异
public class IODemo2 {
public static void main(String[] args) {
// testNoBuffer();
testBuffer();
}
private static void testNoBuffer(){
//读的时候就是一个字节一个字节的读,完全不使用任何缓冲区
long beg = System.currentTimeMillis();
try(FileInputStream fileInputStream = new FileInputStream("d:/test_dir/一寸.jpg")){
int ch = -1;
while((ch = fileInputStream.read()) != -1){
//啥都不干了
}
}catch (IOException e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("no buffer:"+(end - beg)+"ms");
}
private static void testBuffer(){
long beg = System.currentTimeMillis();
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("d:/test_dir/一寸.jpg"));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("d:/test_dir/4"))){
int ch = -1;
while((ch = bufferedInputStream.read()) != -1){
}
}catch(Exception e){
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("buffer"+(end - beg)+"ms");
}
}
package com.java.bite.Buffered_IO_Stream2;
import java.io.*;
/**
* @program: IO
* @description
* @author: wenwen
* @create: 2021-08-03 12:59
**/
//BufferedInputStream 和 BufferedOutputStream
public class BufferedIO {
public static void copyFile1() throws IOException {
//需要创建的实例是 BufferedInputStream 和 BufferedOutputStream
//创建这样的实例,需要先创建 FileInputStream 和 FileOutputStream
FileInputStream fileInputStream = new FileInputStream("d:/test_dir/1/1.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("d:/test_dir/2.jpg");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
byte[] buffer = new byte[1024];
int len = -1;
while((len = bufferedInputStream.read(buffer)) != -1){
bufferedOutputStream.write(buffer,0,len);
}
// 此处涉及 四个流对象
//调用一组 close 时候,就会自动关闭内部包含的 FileInputSteram 和 FileOutputStream
//此处不需要写四次关闭
bufferedInputStream.close();
fileOutputStream.close();
fileInputStream.close();//这不就相当于是调用了两次close吗?阅读源码发现,close关闭两次不会发生副作用
}
//通过try自动关闭
private static void copyFile(){
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("d:/test_dir/1/1.jpg"));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("d:/test_dir/1/4.jpg"))
){
int len = -1;
byte[] buffer = new byte[1024];
// while((len = bufferedInputStream.read(buffer)) != -1){
// System.out.println(len);
// bufferedOutputStream.write(buffer,0,len);
// }
while(true){
if(len == -1){
break;
}
bufferedOutputStream.write(buffer,0,len);
}
}catch(IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) {
copyFile();
}
}
细节:
字节流的读写以byte为单位,缓冲区就是一个byte[]
字符流的读写以char为单位,缓冲区就是一个char[]
字符流可以使用readLine读取一行数据, readLine 读到的一行数据,会自动把最末尾的换行符去掉,把内容写入文件的时候,如果想换行,就需要手动添加一个换行