字节流的概念
在计算机中,无论文本、图片、音频还是视频,所有文件都是以二进制(字节)形式存在的。IO流中针对字节的输入输出提供了一系列的流,统称为字节流。字节流是程序中最常用的流,根据数据的传输方向可将其分为字节输入流和字节输出流。在JDK中,提供了两个抽象类InputStream和OutputStream,它们是字节流的顶级父类,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream。
InputStream被看成一个输入管道,OutputStream被看成一个输出管道,数据通过InputStream从源设备输入到程序,通过OutputStream从程序输出到目标设备,从而实现数据的传输。IO流中的输入输出都是相对于程序而言的
InputStream的常用方法
public static read()
从此字节流中读取一个数据字节
public static read(byte[] b)
从此输入流中将最多b.length个字节的数据读入一个byte数组中
public static read(byte[] b,int off,int len)
指定byte数组中从偏移量off开始的len个字节读出此文件流
os.close()
关闭此文件输出流并释放与此流有关的所有系统资源
OutputStream的常用方法
public void write(int b)
将指定字节写入此文件输出流
public void write(byte[] b)
将b.length个字节从指定byte数组写入到文件输出流中去
public void write(byte[] b,int off,int len)
指定byte数组中从偏移量off开始的len个字节写入此文件输出流
flush()
刷新缓存区并强制写出所有缓冲的输出字节
os.close()
关闭此文件输入流并释放与此流有关的所有系统资源
InputStream和OutputStream这两个类虽然提供了一系列的读写数据有关的方法,但是这两个类是抽象类,不能被实例化,因此,针对不同的功能,InputStream和OutputStream提供了两个不同的子类,这些子类形成了一个体系结构
字节流读写文件
由于计算机中的数据基本都保存在硬盘的文件中,因此操作文件中的数据时一种很常见的操作。在操作文件时,最常见的就是从文件中读取数据并将数据写入文件,即文件的读写。针对文件的读写,JDK专门提供了两个类,分别是FileInputStream和FileOutputStream。
FileInputStream是InputStream的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。由于从文件中读取数据时重复性的操作,因此需要通过循环语句来实现数据的持续读取。
接下来通过一个安妮来实现字节流对文件数据的读取
public class Example01 {
public static void main(String[] args) throws Exception {
//创镌一个文件字节输入流对象
FileInputStream in = new FileInputStream("test.txt");
int b=0; //定义一个int类型的变量b,记住每次读取的一个字节
while(true) {
b=in.read(); //变量b记住读取的一个字节
if(b==-1) { //如果读取的字节为-1,跳出while循环
break;
}
System.out.println(b); //否则将b输出
}
}
}
与FileInputStream对应的是FileOutputStream。FileOutputStream是OutputStream的子类,他是操作文件的字节输出流,专门把输入写入文件。
接下来通过一个案例来演示如何将数据写入文件
public class Example02 {
public static void main(String[] args) throws IOException {
FileOutputStream out = new FileOutputStream("example.txt");
String str="波音飞机";
byte[] b = str.getBytes();
for (int i = 0; i < b.length; i++) {
out.write(b[i]);
}
out.close();
}
}
需要注意的是,如果是通过FileOutputStream向一个已经存在的文件中写入数据,那么该文件中的数据首先会被情况,再写入数据。若希望在已存在的文件内容之后追加新内容,则可使用FileOutputStrem的构造函数FileOutputStream(String file, boolean append)来创建文件输出流对象,并把append参数的值设置为true。
接下来通过一个案例来演示如何将数据追加到文件末尾
public class Example01 {
public static void main(String[] args) throws Exception {
//创镌一个文件字节输入流对象
FileInputStream in = new FileInputStream("test.txt");
int b=0; //定义一个int类型的变量b,记住每次读取的一个字节
while(true) {
b=in.read(); //变量b记住读取的一个字节
if(b==-1) { //如果读取的字节为-1,跳出while循环
break;
}
System.out.println(b); //否则将b输出
}
}
}
由于IO流在进行数据读写操作时会出现异常,为了代码的简洁,在上面的程序中使用了throws关键字将异常抛出。然而一遇到IO异常,IO流的close()方法将无法得到执行,流对象所占用的系统资源将得不到释放,因此,为了保证IO流的close()方法必须执行,通常将关闭流的操作写在finally代码块中
finally{
try{
if(in!=null){
in.close;
}catch(Exception e){
e.printStackTrace();
}
}
}
文件的拷贝
在应用程序中,IO流通常都是成对出现的,即输入流和输出流一起使用。例如文件的拷贝就需要通过输入流来读取文件中的数据,通过文件输出流写入文件。
接下来通过一个案例来演示如何进行文件内容的拷贝
public class Example01 {
public static void main(String[] args) throws Exception {
//创建一个字节输入流,用于读取当前目录下sourse文件夹中的map3文件
FileInputStream in = new FileInputStream("sourse\\周深 - 大鱼.mp3");
//创建一个字节输出流,用于把读取的字节写入到target目录下的文件中
FileOutputStream out = new FileOutputStream("target\\周深 - 大鱼.mp3");
int len; //定义一个整型类型的变量len,记住每次读取的一个字节
long starttime = System.currentTimeMillis(); //获取拷贝文件前的系统时间
while((len=in.read())!=-1) {
out.write(len); //将读到的字节写入文件
}
long endtime = System.currentTimeMillis();
System.out.println("拷贝时间: "+(endtime-starttime)+"毫秒");
in.close();
out.close();
}
}
字节流的缓冲区
在拷贝文件时,可以一次性读取多个字节的数据,并保存在字节数组中,然后将字节而数组中的数据一次性写入文件。
接下来通过修改文件来学习如何使用缓冲区拷贝文件
public class Example02 {
public static void main(String[] args) throws Exception {
//创建一个文件输入流,用于读取当前目录下sourse目录下的mp3文件
FileInputStream in = new FileInputStream("sourse\\周深 - 大鱼.mp3");
//创建一个文件输出流,用于将读取的数据写入当前目录下的target文件中
FileOutputStream out = new FileOutputStream("target\\周深 - 大鱼.mp3");
//以下是用缓冲区读写文件
byte[] buff = new byte[1024]; //定义一个字节数组,作为缓冲区
int len;
long startTime = System.currentTimeMillis();
while((len=in.read(buff))!=-1) { //判断是否读到文件末尾
out.write(buff, 0, len); //从第一个字节开始,想文件写入len个字符
}
long endTime = System.currentTimeMillis();
System.out.println("拷贝文件所用的时间:"+(endTime-startTime));
in.close();
out.close();
}
}
在拷贝过程中,使用while循环逐渐实现字节文件的拷贝,每循环一次,就从文件读取若干字节填充字节数组,并通过变量len记住读入数组的字节数,然后从数组的第一个字节开始,将len个字节一次写入文件。循环往复,当len值为-1时,说明已经读到了文件的末尾,循环会结束,整个拷贝过程也就结束了。
程序中的缓冲区就是一块内存,该内存主要用于存放暂时输入输出的数据,由于使用缓冲区减少了对文件的操作次数,所有可以提高读写数据的效率。
字节缓冲流
在IO包中提供两个带缓冲的字节流,分别是BufferedInputStream和BufferedOutputStream,它们的构造方法中分别接收InputStream和OutputStream类型的参数作为对象,在读写数据时提供缓冲功能。
接下来通过一个案例来学习BufferedInputStream和BufferedOutputStream这两个流的用法
public class Example03 {
public static void main(String[] args) throws Exception {
//创建一个带缓冲区的输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src.txt"));
//创建一个带缓冲区的输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("des.txt"));
int len;
while((len=bis.read())!=-1){
bos.write(len);
}
bis.close();
bos.close();
}
}
当调用了read()或者write()方法读写数据时,首先将读写的数据存入定义好了的字节数组,然后将字节数组一次性写入到文件中。从而提高了数据的读写效率