字节流 字符流
输入流 InputStream Reader read()
输出流 OutputStream Writer write()
对上面名词的解释
字节流:一次读1个字节,1个字节=8个二进制位
字符流:一次读1个字符,1个字符=2个字节=一个char类型字符
‘a ‘、’中’、’?’
列举你知道的流
FileInputStream/FileOutputStream
FileReader/FileWriter
BufferedInputStream/BufferedOutputStream
BufferedReader/BufferedWriter
InputStreamReader/OutputStreamWriter
PrintWriter……
BufferedReader读一行的数据:String readLine();
PrintWriter写一行的数据:println(String);
使用IO流进行文件复制
public static void main(String[] args) {
//1、确定源文件的路径
String srcFilePath = "D:/abc.txt";
//2、确定目标文件路径
String destFilePath = "D:/abc-copy.txt";
//3、创建读取源文件的FileInputStream
FileInputStream fis = null;
//4、创建写入目标文件的FileOutputStream
FileOutputStream fos = null;
try {
//5、创建流的对象
fis = new FileInputStream(srcFilePath);
fos = new FileOutputStream(destFilePath);
//6、循环读取,并且边读边写
int b;//每次读取1个字符,可以自动转换为int
while((b = fis.read())!=-1){
fos.write(b);
//补充:代码写完并没有真正写进去
//必须调用flush或close才可以真正写入
fos.flush();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
这种方式可以复制任何类型的文件,字节流只能处理ASCII(可以简单理解为键盘直接敲出来的,不依赖任何输入法的)
而字符流是不靠谱的,不能用字符流复制文件,字符流可以处理带中文或非ascii的文件
举个例子
‘中’字是1个字符,对应2个字节,也就是16个二进制位,比如0100 1110 0010 1101
字节流一次读1个字节,所以它把中这个字拆开来读了
现在我们复制一个大一点的文件,在刚才的代码上加上
long start = System.currentTimeMillis();
//1、确定源文件的路径
String srcFilePath = "D:/yasea-master.zip";
//2、确定目标文件路径
String destFilePath = "D:/yasea-master-copy.zip";
......
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
运行程序
开始文件大小对比,在程序运行中,被copy文件一直是0kb
运行结束时,输出日志
耗时:54667
直到flush之后才有大小
可以看到1个9M的文件复制了50多秒,效率很低,我们来进行改进
改进一
改为用BufferedInputStream和BufferedOutputStream
public static void main(String[] args) {
long start = System.currentTimeMillis();
//1、确定源文件的路径
String srcFilePath = "D:/yasea-master.zip";
//2、确定目标文件路径
String destFilePath = "D:/yasea-master-copy.zip";
//3、创建读取源文件的FileInputStream
FileInputStream fis = null;
//4、创建写入目标文件的FileOutputStream
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//5、创建流的对象
fis = new FileInputStream(srcFilePath);
fos = new FileOutputStream(destFilePath);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//6、循环读取,并且边读边写
int b;//每次读取1个字符,可以自动转换为int
while((b = bis.read())!=-1){
bos.write(b);
//补充:代码写完并没有真正写进去
//必须调用flush或close才可以真正写入
bos.flush();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
if(bis != null){
try {
bis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(bos != null){
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
复制完成,输出日志
耗时:28082
改进二
我们将数据读入缓冲区
public static void main(String[] args) {
long start = System.currentTimeMillis();
//1、确定源文件的路径
String srcFilePath = "D:/yasea-master.zip";
//2、确定目标文件路径
String destFilePath = "D:/yasea-master-copy.zip";
//3、创建读取源文件的FileInputStream
FileInputStream fis = null;
//4、创建写入目标文件的FileOutputStream
FileOutputStream fos = null;
try {
//5、创建流的对象
fis = new FileInputStream(srcFilePath);
fos = new FileOutputStream(destFilePath);
//6、循环读取,并且边读边写
/*int b;//每次读取1个字符,可以自动转换为int
while((b = fis.read())!=-1){
fos.write(b);
//补充:代码写完并没有真正写进去
//必须调用flush或close才可以真正写入
fos.flush();
}*/
//读的时候会读入缓冲区的数组
byte[] b = new byte[1024];
int length;
while((length = fis.read(b))!=-1){
//写的时候直接写缓冲区的数组
fos.write(b);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
if(fis != null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start));
}
}
运行完成,查看日志
耗时:79
这种做法是牺牲空间换取时间,是划算的
关于缓冲区,有2点要说
1、文件越大,给的缓冲区越大
2、缓冲区大到一定程度,效果就不明显,推荐1024*8
刚才的程序可能有bug
例如一个文件2000byte,我们的缓冲区设为1024
第一次读取的时候读了1024,第二次只能读976,但是我们write的时候还是1024,所以文件大小可能会不对
所以最后一点要改进的就是
//读的时候会读入缓冲区的数组
byte[] b = new byte[1024];
int length;
while((length = fis.read(b))!=-1){
//写的时候直接写缓冲区的数组
fos.write(b,0,length);
}