字节流的概念
在计算机中,无论文本、图片、音频还是视频,所有文件都是以二进制(字节)形式存在的。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()方法读写数据时,首先将读写的数据存入定义好了的字节数组,然后将字节数组一次性写入到文件中。从而提高了数据的读写效率