IO流
在Java当中可以使用流来完成读写文件等操作,那么什么是流呢?
1. 什么是流?
- Java当中的流大概的分为输入流和输出流两类,输入流,就是从不同的数据源当中读取数据,数据源可以是磁盘文件、内存数据等。
输出流就是可以将数据写入到不同的数据宿当中。
- Java中,对于不同的数据源,可以使用不同的工具类来完成数据的读取操作;对于不同的数据宿,可以使用不同的工具类开完成数据的写入操作。
- Java中根据流向的不同可以分为输入流和输出流,根据处理单位不同可以分为字节流和字符流。
2. 字节流
2.1 字节输入流-InputStream
- InputStream类是所有字节输入流的父类,它是一个抽象类,所以不能直接实例化,需要子类为其实例化,InputStream类的常用子类有下面几种:
java.io.FileInputStream : 文件操作字节输入流,通常用于读取非字符类型的文件,例如图片、视频等等。
java.io.ObjectInputStream : 通常使用在序列化操作中,将对象反序列化读取出来。
java.io.BufferedInputStream : 通常用于字节缓冲输入流当中。
在这里我们使用FileInputStream文件操作字节输入流为父类实例化对象。
- Constructor:
FileInputStream(File file):
FileInputStream(String pathName): - Method:
public int read():
public int read(byte[ ] bs):
public int read(byte[ ] bs,int offset,int length):
/*
* 文件操作输入字节流
* 1. 确认读取哪一个文件
* 2. 创建对应文件的FileInputStream
* 3. 读取数据
* 4. 关闭资源 【重点】
*/
public class Demo1 {
public static void main(String[] args) {
//1.确定操作的文件
File file = new File("E:/aaa/1.txt");
FileInputStream fileInputStream = null;
try {
//2.得到对应的流对象
fileInputStream = new FileInputStream(file);
//3.读取数据
int length = -1;
byte[] bb = new byte[1024 * 4];
while((length = fileInputStream.read(bb)) != -1) {
System.out.println(new String(bb,0,length));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
2.2 字节输出流-OutputStream
- OutputStream类是所有字节输出流的父类,它是一个抽象类,所以不能直接实例化,需要子类为其实例化,OutputStream类的常用子类有下面几种:
java.io.FileOutputStream: 文件操作字节输出流,通常用于写入非字符类型的文件,例如图片、视频等等。
java.io.ObjectInputStream : 通常使用在序列化操作中,将对象序列化写入到文件当中。
java.io.BufferedInputStream : 通常用于字节缓冲输出流当中。
在这里我们使用FileOutputStream文件操作字节输入流为父类实例化对象。
FileOutputStream构造方法具有创建文件的能力,如果要操作的文件不存在但是路径合法的话,会自动创建该文件。
- Constructor:
FileOutputStream(File file):
FileOutputStream(String pathName):
FileOutputStream(File file,boolean append):
FileOutputStream(String pathName,boolean append): - Method:
public void write():
public void write(byte[ ] bs):
public void write(byte[ ] bs,int offset,int length):
/*
* 文件操作输出字节流
* 1. 确定文件
* 2. 创建FileOutputStream
* 3. 写入数据到文件中
* 4. 关闭资源
*/
public class Demo2 {
public static void main(String[] args) {
//1.确定操作的文件
File file = new File("E:/aaa/1.txt");
FileOutputStream fileOutputStream = null;
try {
//2.创建对应的流对象
fileOutputStream = new FileOutputStream(file);
//3.写数据
byte[] bb = {67,68,69,70};
fileOutputStream.write(bb,0,2);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
2.3 使用字节流完成拷贝程序
/*
* 文件拷贝程序
*/
public class Demo3 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
//1,创建两个流对象,确定操作的文件
fileInputStream = new FileInputStream("E:/aaa/1.txt");
fileOutputStream = new FileOutputStream("E:/aaa/2.txt");
//2.边读边写
int length = -1;
byte[] b = new byte[1024 * 4];
//读取数据
while((length = fileInputStream.read(b)) != -1) {
//写数据
fileOutputStream.write(b);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
//先开后关,后开先关
if(fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
3. 字符流
3.1 字符输入流-Reader
- Reader类是所有字符输入流的父类,它是一个抽象类,所以不能直接实例化,需要子类为其实例化,Reader类的常用子类有下面几种:
java.io.FileReader: 文件操作字符输入流,用于从文件中读取字符。
java.io.BufferedReader: 字符缓冲输入流。
java.io.InputStreamReader : 该类是字节输入流转换到字符输入流之间的桥梁。
在这里我们使用FileReader文件操作字符输入流为父类实例化对象。
- Constructor:
FileReader(File file):
FileReader(String pathName): - Method:
public int read():
public int read(char[ ] ch):
public int read(char[ ] ch,int offset,int length):
/*
* 文件操作输入字符流
* 1. 确定文件
* 2. 创建FileReader
* 3. 读取数据
* 4. 关闭资源
*/
public class Demo4 {
public static void main(String[] args) {
fileReaderTest2();
}
private static void fileReaderTest2() {
FileReader fileReader = null;
try {
fileReader = new FileReader(new File("E:/aaa/1.txt"));
char[] buf = new char[1024 * 4];
int length = -1;
while ((length = fileReader.read(buf)) != -1) {
System.out.println(new String(buf, 0, length));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
3.2 字符输出流-Writer
- Writer类是所有字符输出流的父类,它是一个抽象类,所以不能直接实例化,需要子类为其实例化,Writer类的常用子类有下面几种:
java.io.FileWriter: 文件操作字符输出流,通常用于写入字符类型的文件,例如图片、视频等等。
java.io.OutputStreamWriter: 该类是字节输出流转换到字符输出流的桥梁。
java.io.BufferedWriter : 通常用于字符缓冲输出流当中。
java.io.PrintWriter: 打印流。
在这里我们使用FileWriter文件操作字符输出流为父类实例化对象。
FileWriter构造方法具有创建文件的能力,如果要操作的文件不存在但是路径合法的话,会自动创建该文件。
- Constructor:
FileWriter(File file):
FileWriter(String pathName):
FileWriter(File file,boolean append):
FileWriter(String pathName,boolean append): - Method:
public void write():
public void write(char[ ] ch):
public void write(char[ ] ch,int offset,int length):
public void write(String str):
public void write(String str,int offset,int length):
/*
* 字符输出流
*/
public class Demo1 {
public static void main(String[] args) {
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(new File("E:/aaa/3.txt"),true);
char[] ch = {'世','界','加','油'};
fileWriter.write(ch);
fileWriter.write(",疫情一定会过去的");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(fileWriter != null) {
try {
fileWriter.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
3.3 使用字符流完成拷贝程序
/*
* 使用文件操作字符流拷贝程序
*/
public class Demo2 {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader(new File("E:/aaa/3.txt"));
fw = new FileWriter(new File("E:/aaa/6.txt"));
char[] ch = new char[1024 * 4];
int length = -1;
while((length = fr.read(ch)) != -1) {
fw.write(ch);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(fw != null) {
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fr != null) {
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
4. 缓冲流
- 起到的作用:使用缓冲数组之后,整体的读取,写入的效率提升很大,降低了CPU通过内存方法磁盘的次数,提高效率,降低磁盘损耗。
- 特点:所有的缓冲流都没有任何的读取写入文件的能力,都需要对应的输入输出流作为参数,来提供对应的能力,所以在创建缓冲流对象时,需要传入对应的输入输出流对象。缓冲流底层就是提供了一个默认大小的缓冲数组,用来提高效率。
4.1 字节缓冲流
4.1.1 字节输入缓冲流-BufferedInputStream
- Constructor:
BufferedInputStream(InputStream in): - Method:
所使用的方法都是所传进来的参数对应的字节输入流对象中的方法。
/*
* 字节缓冲输入流
*/
public class Demo1 {
public static void main(String[] args) {
BufferedInputStream bis = null;
try {
//1.首先创建一个字节缓冲输入流对象
bis = new BufferedInputStream(new FileInputStream
(new File("E:/aaa/3.txt")));
//2.执行读取数据
byte[] bb = new byte[1024];
@SuppressWarnings("unused")
int length = -1;
while((length = bis.read(bb)) != -1) {
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.1.2 字节输出缓冲流-BufferedOutputStream
- Constructor:
BufferedOutputStream(OutputStream out): - Method:
所使用的方法都是所传进来的参数对应的字节输出流对象中的方法。
/*
* 字节输出缓冲流
*/
public class Demo2 {
public static void main(String[] args) {
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(
new File("E:/aaa/6.txt"),true));
bos.write('s');
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(bos != null ) {
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
4.1.3 使用字节缓冲流完成拷贝程序
/*
* 使用字节输入输出缓冲流拷贝程序
*/
public class Demo3 {
public static void main(String[] args) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(new File("E:/aaa/6.txt")));
bos = new BufferedOutputStream(new FileOutputStream(new File("E:/aaa/7.txt")));
int length = -1;
while((length = bis.read()) != -1) {
bos.write(length);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(bos != null) {
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(bis != null) {
try {
bis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
4.2 字符缓冲流
4.2.1 字符输入缓冲流-BufferedReader
- Constructor:
BufferedReader(Reader reader): - Method:
所使用的方法大部分都是所传进来的参数对应的字符输入流对象中的方法。
新增方法:String readLine():每次读取一行数据。
/*
* 字符缓冲输入流
*/
public class Demo4 {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(new File("E:/aaa/6.txt")));
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
System.out.println(br.readLine());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
4.2.2 字符输出缓冲流-BufferedWriter
- Constructor:
BufferedWriter(Writer writer): - Method:
- 所使用的方法大部分都是所传进来的参数对应的字符输出流对象中的方法。
新增方法:void newLine():换行。
/*
* 字符缓冲输出流
*/
public class Demo4 {
public static void main(String[] args) {
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(new File("E:/aaa/6.txt")));
bw.write("红烧猪蹄");
//换行
bw.newLine();
bw.write("酱肘子");
bw.newLine();
bw.write("小炒肉");
bw.newLine();
bw.write("酸辣土豆丝");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(bw != null) {
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
4.3 性能(效率)问题
- 在底层有一个默认大小是8KB的byte类型的缓冲数组。
- 在每一次调用read方法读取数据之前,都会检查缓冲数组中是否有数据,如果缓冲数组中没有数据,那么会先调用fill方法,从磁盘中读取数据填充到缓冲数组当中。
- 每次从磁盘中读取的数据容量与缓冲数组的容量一致,调用所有的read方法都是从缓冲数组中读取数据,这样子数据的交互全都是在内存当中进行,极大的降低了CPU通过内存访问磁盘的次数,提高了效率。
- 而数据在写入文件时,并不是直接写入到磁盘当中,而是先保存到缓冲数组当中,如果缓冲数组被填满,会直接flush缓冲区,将数据保存到磁盘当中,同时清空整个缓冲区,为下一次的写入做好准备。
- 关闭缓冲流的时候,会首先释放对应的缓冲数组空间,归还到内存当中,并且关闭创建的对应的缓冲流对象。