File对象可以表示存在的文件或文件夹,也可以表示不存在的。我们想要得到文件怎么办,File只是操作文件,文件的内容如何处理就需要使用IO流技术了。
例如在C盘下有一个名称为a.txt的文本文件。想要通过Java程序读出来文件中的内容,需要使用IO流技术。同样想要将程序中的数据,保存到硬盘的文件中,也需要IO流技术。
IO:Input和Output两个单词的缩写,input是输入,output是输出。
IO流
IO分类
分类的方式有两种:按照功能可以分类,按照流向也可以分类。
按照功能分类:
- 字节流:可以直接操作字节的流对象
- 字符流:可以直接操作字符的流对象
按照流向分类:
- 输入流:其他设备流到内存的流对象
- 输出流:内存流到其他设备的流对象
IO流的体系结构,根据分类,有四种流对象的类型。
字节流:
- 字节输入流:InputStream
- 字节输出流:OutputStream
字符流:
- 字符输入流:Reader
- 字符输出流:Writer
字节流
(1)概述
- 可以直接操作字节信息的流对象
- 根据流向,可以分成字节输入流和字节输出流
- 顶层抽象父类分别是:InputStream 和 OutputStream
(2)InputStream
字节输入流的顶层抽象父类。根据交互设备的不同,有不同的具体子类
(3)常用方法
1、常用方法
read():从当前的字节输入流中,获取一个字节 read(byte[] arr):将arr.length个字节,读取到arr中 |
InputStream是一个抽象类,不能直接创建对象,只能由子类创建对象,最常用子类 FileInputStream,用于和磁盘上的文件进行交互。
2、FileInputStream构造方法
FileInputStream(File f):将File对象封装成字节输入流,将来可以读取这个文件中的信息 FileInputStream(String path):将字符串封装成字节输入流,将来可以读取信息 |
示例代码
InputStream is = new FileInputStream("F:/aaa.txt");
File file = new File("F:/aaa.txt"); InputStream is = new FileInputStream(file); |
综合代码示例:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Arrays;
public class Demo1 {
public static void main(String[] args) throws Exception {
File f = new File("F:\\FILEandIO/2.txt");
FileInputStream fis = new FileInputStream(f);
//这里是进行一个字节一个字节的读取
/*int read = fis.read();
System.out.println(read);*/
//这里进行连续读取
/*
int i = fis.read();
while((i=fis.read())!=-1) {
System.out.println((char)i);
} */
//下面是利用字符数组加速读取
byte[] buf = new byte[2];
int len = 0;
while((len=fis.read(buf))!=-1) {
//这里的输出有两种方式,这种缺陷为当读取到最后可能会有数组后面的几个元素没有被覆盖然后继续输出上一轮的元素
/*for(byte b:buf) {
System.out.println((char)b);
}*/
//这里利用字符串来弥补上面的缺陷,没有被注释的是比较快速较好的方法
String str = new String(buf,0,len);
System.out.print(str);
}
}
}
read(byte[] arr)注意事项:
使用read方法的时候,流需要读一次就处理一次,可以将读到的数据装入到字节数组中,一次性的操作数组,可以提高效率。
read方法返回的是往数组中存了多少字节。
示例代码
byte[] byt = new byte[5]; int len = fis.read(byt); |
问题1:当文档中的内容很多,byte[5] 显然不能承装全部数据。
解决方式:增大数组缓存区,把数组设置为 1024
byte[] byt = new byte[1024]; int len = fis.read(byt); |
问题2:数组过大,文件中的数据不能装满数组,当遍历这个数组时,会出现很多0。
byte[] byt = new byte[1024]; int len = fis.read(byt); for (int i = 0; i <byt.length; i++) { System.out.print(byt[i]); } |
解决方式:
byte[] byt = new byte[1024]; int len = fis.read(byt); for (int i = 0; i <len; i++) { System.out.print(byt[i]); } |
设置合适大小的数组
示例代码
byte[] b = new byte[is.available()]; |
5、skip(n)
跳过n个字节
示例代码
is.skip(3); System.out.println(is.read()); |
6注意:
使用完流后,一定要关闭。 close()
可以使用try catch finally结构 确保流会关闭
(3)OutputStream
字节输出流的顶层抽象父类,最常用子类FileOutputStream。
FileOutputStream构造方法
FileOutputStream(File f):将f描述的路径文件封装成字节输出流对象 FileOutputStream(String path):将path描述的文件路径封装成字节输出流对象 |
常用方法
write(int b) 一次写一个字节。例如: write(65),write(‘a’)。 write(byte[] b) 使用数组缓冲区。 write(“abc”.getBytes()) 也可以写一个字符串:将字符串转换成字节数组。 |
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class Demo02 {
public static void main(String[] args) throws Exception {
//输出流关联的文件不存在,会自动地创建
//如果该文件存在,自动创建一个新的文件覆盖原有的文件
//boolean append:如果设置为true,不会覆盖原有的文件,在此文件基础之上进行追加
FileOutputStream fos = new FileOutputStream("1.txt",true);
//fos.write(97);
//fos.write(99);
fos.write(98);
byte[] buf = {99,100,101,123,122,12};
fos.write(buf);//把数组中的全部输出
fos.write(buf,0,4);//选择输出范围
//释放资源
fos.close();
}
}
练习:进行文件的传输(类似复制粘贴)
一个字节一个字节进行传输:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class 视频传输 {
public static void main(String[] args) throws Exception {
File f1 = new File("F:/FILEandIO/重命名/a/08高效缓冲流.mp4");
FileInputStream fis = new FileInputStream(f1);
File f2 = new File("F:/FILEandIO/重命名/b/09高效缓冲流.mp4");
FileOutputStream fos = new FileOutputStream(f2);
long startTime = System.currentTimeMillis();
int i = 0;
while((i=fis.read())!=-1) { //这里的i返回的是这个字节的ASCII的值
fos.write(i);
System.out.println((int)(((double)f2.length()/(double)f1.length())*100)+"%");
}
long endTime = System.currentTimeMillis();
System.out.println("传输完成:时间为"+(endTime-startTime));
fis.close();
fos.close();
}
}
博主电脑太菜,文件大概是120M,传输了大概13分钟
利用数组进行传输:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class 视频传输2 {
public static void main(String[] args) throws Exception {
File f1 = new File("F:/FILEandIO/重命名/a/08高效缓冲流.mp4");
FileInputStream fis = new FileInputStream(f1);
File f2 = new File("F:/FILEandIO/重命名/b/09高效缓冲流.mp4");
FileOutputStream fos = new FileOutputStream(f2);
byte[] b = new byte[1024*1024*10];
int len = 0;
long startTime = System.currentTimeMillis();
while((len=fis.read(b))!=-1) { //这里的len指的是读取到数据的长度
fos.write(b, 0, len);
System.out.println((int)(((double)f2.length()/(double)f1.length())*100)+"%");
}
long endTime = System.currentTimeMillis();
System.out.println("传输完成:时间为"+(endTime-startTime)+"ms");
}
}
结果:立竿见影。
上述程序中我们为了提高流的使用效率,自定义了字节数组,作为缓冲区。Java其实提供了专门的字节流缓冲来提高效率。
BufferedInputStream和BufferedOutputStream
BuffereInputStream和BufferedOutputStream类可以通过减少读写次数来提高输入和输出的速度。它们内部有一个缓冲区,用来提高处理效率。查看API文档,发现可以指定缓冲区的大小。其实内部也是封装了字节数组。没有指定缓冲区大小,默认的字节是8192。
显然缓冲区输入流和缓冲区输出流要配合使用。首先缓冲区输入流会将读取到的数据读入缓冲区,当缓冲区满时,或者调用flush方法,缓冲输出流会将数据写出。
注意:当然使用缓冲流来进行提高效率时,对于小文件可能看不到性能的提升。但是文件稍微大一些的话,就可以看到实质的性能提升了。
原理:
1、BufferedInputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用read()方法想获取一个字节的时候,该对象从文件中一次性读取了8192个字节到数组中,只返回了第一个字节给调用者。将来调用者再次调用read方法时,当前对象就不需要再次访问磁盘,只需要从数组中取出一个字节返回给调用者即可,由于读取的是数组,所以速度非常快。当8192个字节全都读取完成之后,再需要读取一个字节,就得让该对象到文件中读取下一个8192个字节了。
2、BufferedOutputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用write方法想写出一个字节的时候,该对象直接将这个字节存储到了自己的数组中,而不刷新到文件中。一直到该数组所有8192个位置全都占满,该对象才把这个数组中的所有数据一次性写出到目标文件中。如果最后一次循环过程中,没有将数组写满,最终在关闭流对象的时候,也会将该数组中的数据刷新到文件中。
构造方法
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
BufferedOutputStream(OutputStream out)
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
再来一个转换视频的方法:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class 高速缓冲区 {
public static void main(String[] args) throws Exception {
File f = new File("F:/FILEandIO/重命名/a/08高效缓冲流.mp4");
FileInputStream fis = new FileInputStream(f);
BufferedInputStream bis = new BufferedInputStream(fis);
File f1 = new File("F:/FILEandIO/重命名/b/08高效缓冲流.mp4");
FileOutputStream fos = new FileOutputStream(f1);
System.out.println(f1.getAbsolutePath());
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] b = new byte[1024*1024*10];
int len = 0;
long startTime = System.currentTimeMillis();
while((len=bis.read(b))!=-1) {
bos.write(b,0,len);
System.out.println((int)(((double)f1.length()/(double)f.length())*100)+"%");
}
long endTime = System.currentTimeMillis();
System.out.println("传输完成:时间为"+(endTime-startTime)+"ms");
}
}
这次速度大约为200ms。
使用字节流完成文件拷贝,看起来是一个普通程序,但是它使用的是一种低级的IO。
如果针对的内容由字符组成,最好使用字符流,字节流只适用于最为原始的IO。当我们用中文和英文交互使用的时候,因为中文占两个字节,英文占一个字节,一次一次进行判断,是很麻烦的事情,所以我们需要字符流来帮助我们进行字符内存容量的冲突处理。
Java平台使用Unicode来存储字符。字符流自动在本地字符集和Unicode这种国际通用格式之间转换。例如在西方国家,本地字符集通常是ASCII,在应对国际化的时候,程序员不用做额外的处理。
Reader
Reader是抽象类,常用实现类FileReader。
常用方法
int read() 读取一个字符,-1表示读到文件的末尾。 int read(char[] cbuf) 将字符读入数组,返回值表示读到了多少个字符。 close()关闭流 |