1. 字节流,字符流基本概念
File类不支持文件内容处理,如果要处理文件内容,必须要通过流的操作模式来完成
在java.io包中,流分为两种:字节流与字符流
字节流:读写数据以字节为基本单位(处理二进制文件/数据的时候使用) 字符流:独写数据以字符为基本单位(处理文本文档/数据的时候使用)
2. 字节流
1)InputStream:(输入)把输入设备读取到内存中 2)OutputStream:(输出)把内存中的数据写入到输出设备中
a)FileInputStream和FileOutputStream
FileInputStream 从文件系统中的某个文件中获得输入字节。 FileInputStream 用于读取诸如图像数据之类的原始字节流。
示例:复制图片 1)初阶:
package dqy0305;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class IODemo2 {
//实现文件的复制
public static void main(String[] args) throws IOException {
copyFile("D:/file_dir/1010.png","D:/file_dir/10102.png");
}
private static void copyFile(String srcPath, String destPath) throws IOException {
//1.先打开文件(先创建InputStream和OutputStreaam对象的过程)
FileInputStream fileInputStream = new FileInputStream(srcPath);
FileOutputStream fileOutputStream = new FileOutputStream(destPath);
//2.需要读取srcPath文件对应的内容
byte[] buffer = new byte[1024];
int len = -1;
//如果已经读完了就返回-1.如果返回一个正数,就相当于一共有这个整数的长度
while((len = fileInputStream.read(buffer)) != -1){
//读取成功
//3.把读取到的内容写入desrPath对应的文件中
fileOutputStream.write(buffer,0,len);
}
//4.close关闭文件,如果没有关闭就会造成文件资源泄露
fileInputStream.close();
fileOutputStream.close();
}
}
这时对应的文件里就已经复制好了一个图片
以上方法有可能正在执行过程中触发了异常,那么还是无法执行close(),并且在实例化fileInputStream和fileOutputStream的时候,如果指定位置的文件不存在,就会导致实例化失败,也就是说还是null,这时在调用close方法的时候就会触发空指针异常,也就有了以下的改进版本:
2)改进版
public static void main(String[] args) throws IOException {
copyFile2("D:/file_dir/1010.png","D:/file_dir/10102.png");
}
private static void copyFile2(String srcPath, String destPath){
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
//1.先打开文件(先创建InputStream和OutputStreaam对象的过程)
fileInputStream = new FileInputStream(srcPath);
//但是如果路径对应的文件不存在,此时构造方法也会抛出异常,说明构造尚未成功,还是null,
// 那么接下来调用close方法就会触发空指针异常
fileOutputStream = new FileOutputStream(destPath);
//2.需要读取srcPath文件对应的内容
byte[] buffer = new byte[1024];
int len = -1;
//如果已经读完了就返回-1.如果返回一个正数,就相当于一共有这个整数的长度
while((len = fileInputStream.read(buffer)) != -1){
//读取成功
//3.把读取到的内容写入desrPath对应的文件中
fileOutputStream.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//4.close关闭文件,如果没有关闭就会造成文件资源泄露
if (fileInputStream != null) {
fileInputStream.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
但是以上代码在异常处理方面过于冗杂,为了提高代码的可读性,就有了以下版本: 3)常用版
public static void main(String[] args) throws IOException {
copyFile3();
}
private static void copyFile3(){
//当代码写成这个样子的时候,就不需要显示调用close
//try语句会在代码执行完毕后,自动调用close方法(前提是这个类必须实现closeable接口)
try(FileInputStream fileInputStream = new FileInputStream("D:/file_dir/1010.png");
FileOutputStream fileOutputStream = new FileOutputStream("D:/file_dir/10102.png")){
byte[] buffer = new byte[1024];
int len = -1;
while ((len = fileInputStream.read(buffer)) != -1){
fileOutputStream .write(buffer,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}
b)字节缓冲流 BufferedInputStream 和 BufferedOutputStream(内置了缓冲区)
问题一:为什么需要有缓冲区?
答:当我们用read()读取文件时,每读一个字节,访问一次磁盘,效率很低 。文件过大时,操作起来也不是很方便。因此我们需要用到buffer缓存流,当创建buffer对象时,会创建一个缓冲区数组。当我们读一个文件时,先从磁盘中读到缓冲区,然后直接从缓冲区输出即可,效率会更高
实例:复制图片 1)初版
public class IODemo3 {
public static void main(String[] args) throws IOException {
copyFile();
}
private static void copyFile() throws IOException {
//需要创建的实例 BufferedInputStream 和 BufferedOutputStream,就先要创建FileInputStream和FileOutputStream
FileInputStream fileInputStream = new FileInputStream("D:/file_dir/1010.png");
FileOutputStream fileOutputStream = new FileOutputStream("D:/file_dir/10102.png");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
byte[] buffer = new byte[1024];
int length = -1;
while ((length = bufferedInputStream.read(buffer)) != -1){
bufferedOutputStream.write(buffer,0,length);
}
//有四个流对象,只需要关闭这两个,然后就会自动关闭包含的FileInputStream和FileOutputStream
bufferedInputStream.close();
bufferedOutputStream.close();
}
}
2)改进版
private static void copyFile2() {
try( BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("D:/file_dir/1010.png"));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream( new FileOutputStream("D:/file_dir/10102.png"))) {
byte[] buffer = new byte[1024];
int length = -1;
while ((length = bufferedInputStream.read(buffer)) != -1){
bufferedOutputStream.write(buffer,0,length);
}
} catch (IOException e) {
e.printStackTrace();
}
}
c)带缓冲区和不带缓冲区的对比
private static void testNoBuffer() {
long beg = System.currentTimeMillis();
try ( FileInputStream fileInputStream = new FileInputStream("D:/file_dir/1010.png")){
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:/file_dir/1010.png"))){
int ch = -1;
while ((ch = bufferedInputStream.read()) != -1){
}
} catch (IOException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("buffer:"+(end-beg)+"ms");
}
执行主函数,就会发现,读取一个15MB的文件,不带缓冲区大概花了6s左右,但是带缓冲区大概只花了50ms
3. 字符流
1)Reader:(输入)把输入设备读取到内存中 2)Writer:(输出)把内存中的数据写入到输出设备中
字符流与字节流用法基本一致,一个典型的区别是: 字节流的读写操作以byte为单位,缓冲区就是byte[] 字符流的读写操作以char为单位,缓冲区就是char[]
示例: 1)不带缓冲区
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//字符流:可以来处理文本文件
public class IODemo5 {
public static void main(String[] args) {
copyFile();
}
private static void copyFile(){
try(FileReader fileReader = new FileReader("d:/test.txt");
FileWriter fileWriter = new FileWriter("d:/test2.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();
}
}
}
2)带缓冲区
//带缓冲区
private static void copyFile2(){
try(BufferedReader bufferedReader = new BufferedReader(new FileReader("d:/test.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("d:/test2.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();
}
}
3)字符流中有特殊的用法:按行读取
private static void copyFile3(){
try(BufferedReader bufferedReader = new BufferedReader(new FileReader("d:/test.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("d:/test2.txt"))) {
String line = "";//读一行,读到换行符为止
while ((line = bufferedReader.readLine()) != null){
bufferedWriter.write(line+ "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}