Java基础(26)——I/O流相关知识详解及示例分析(字节流)


版权声明

  • 本文原创作者:清风不渡
  • 博客地址:https://blog.csdn.net/WXKKang

一、I/O流

  上篇我们学习了文件(File)类的相关知识,但是File对象只是操作文件或目录的元数据,如果我们想要得到文件的内容的时候该怎么办呢?这就需要引入I/O流进行处理了
  “流”表示任何有能力产出数据的数据源对象,或者是有能力接收数据的接收端对象,“流”屏蔽了实际的I/O设备中处理数据的细节。所以我们通常使用I/O流用来处理设备之间的数据传输,这里的设备通常是指硬盘、内存,键盘录入,网络等
在这里插入图片描述
  大部分程序都需要输出/输入处理,比如从键盘读取数据、向屏幕中输出数据、从文件中读取或向文件中写入数据、在一个网络连接上进行读写操作等,在Java中,把这些不同类型的输入源、输出源抽象为流(Stream),而其中输入或输出的数据则称之为数据流(Data Stream),用统一的抽象类来表示,从而使程序设计简单明了
  学习I/O流的时候很多人会有一个疑问,我要从设备上读数据为什么是输入流呢?应该是输出流呀,而我向设备写入数据的时候又为什么是输出流呢?应该是输入流呀,这里的问题其实和物理上的相对速度是一样一样的,我们所谓的输入流与输出流,不是相对于设备而言的,而是相对于内存而言的,只要把这个思想转变成功就显得很简单了,我们从设备上读取数据,虽然从设备上看是输出,但是相对于内存而言它就是把数据输入到程序里,所以使用的就是输入流,同样,相应的输出流亦如此

1、输入流与输出流

  根据数据流动的方向不同,可以将流分为输入流与输出流。输入流是指将数据源中的数据输入到应用程序(内存)中,输出流是指应用程序(内存)将数据输出到数据源中
在这里插入图片描述
  输入流、输出流和数据源是没有关系的,它由数据传输的方向来决定的。例如:
  一个文件,当向其中写数据的时候,它就是一个输出流的数据源;当从其中读取数据时,它就是一个输入流的数据源
  当然,键盘只是一个输入流的数据源,而屏幕只是一个输出流的数据源
  简单理解就是:将硬盘已有的数据读取到内存的是输入流,用来操作将内存中的数据存储到硬盘中的是输出流。输入流进行读操作,输出流进行写操作

2、字节流和字符流

  按照数据单元可将流分为字节流和字符流
  以字节为单位进行读写操作的我们称之为字节流;以字符为单位进行读写操作的我们称之为字符流。字节流可以读写任意资源(文字、图片、音频、视频都可以),而字符流是为了更便捷的读写文字
  字节流:
在这里插入图片描述
  字符流:
在这里插入图片描述

3、I/O流操作步骤

  与流相关的类及函数我们的前辈已经为我们封装好啦,大部分对象都在java.io包中
  所有流相关的编程都是分为三步:
  第一步:在操作前打开流(即创建流)
  第二步:在流中执行读写操作,其中会产生大量的异常,通常使用try…catch…finally结构处理异常
  第三步:在操作完成后必须要关闭流,释放相关资源,大致结构如下:

try {
	第一步:打开流,即创建流
		第二步:通过流读取内容,这里主要是读、写操作
	} catch (...) {
		异常处理
	}finally {
		第三步:关闭流,释放资源
	}

  注意:使用流就像使用水管一样,要打开就要关闭,所以打开流和关闭流的动作是必不可少的。如何关闭流?使用close方法就可以释放掉十分有限的操作系统资源。如果一个应用程序打开了过多的流而没有关闭它们,那么系统资源将被耗尽,之后就无法获得流资源了

二、字节流

1、什么是字节流

  计算机中的信息,比如文档、音频、视频、图片,都是作为字节(二进制)存在的,也就是说字节流处理的数据类型非常多
  我们是不能直接读懂二进制文件的,因为二进制文件是由程序来读取的,二进制文件的优势在于它的处理效率比文本文件高,例如,Java的源程序(.java源文件)存储在文本文件中,可以使用文本编辑器阅读,但是Java的类(字节码文件)存储在二进制文件中,可以被Java虚拟机阅读

2、字节流API

  字节流有两个抽象基类,输入流是java.io.InputStream,输出流是java.io.OutputStream,这两个类都是抽象类,不能直接被实例化。实际开发时,从这两个抽象基类派生各种流的子类实现,这些子类的名字都是以其父类名作为子类名的后缀,如FileInputStream、ByteArrayInputStream等
  字节流类结构:
在这里插入图片描述

3、InputStream

  InputStream是字节输入流的抽象类,最核心的方法是read,主要方法如下:
在这里插入图片描述
  现在我们就通过一个例子使用一下输入流来将存储在设备中的文本文件中的内容打印在控制台上,注意不要包含中文内容,因为中文需要使用字符流来处理
  在D盘根目录下放了一个txt文件名为:demo.txt,其内容如下图所示,我们需要使用I/O流来将内容读取并打印在控制台上:
在这里插入图片描述
  代码如下:

package qfbd.com;
/*
	原创作者:清风不渡
	博客地址:https://blog.csdn.net/WXKKang
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Demo {

	public static void main(String[] args) {
		String path = "D:" + File.separatorChar + "demo.txt";
		File file = new File(path);
		FileInputStream fileInputStream = null;
		int len;
		try {
			fileInputStream = new FileInputStream(file);

			while ((len = fileInputStream.read()) != -1) {
				System.out.print((char) len);
			}

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(fileInputStream!=null){
				try {
					fileInputStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

  执行结果如下:
在这里插入图片描述

3、OutputStream

  OutputStream是字节输出流的抽象类,最核心的方法是write、flush,其主要方法如下:
在这里插入图片描述
  现在我们就使用输出流来向一个txt文件中写入数据,可以看到txt文件中初始是没有数据的:
在这里插入图片描述
  代码如下:

package qfbd.com;

/*
	原创作者:清风不渡
	博客地址:https://blog.csdn.net/WXKKang
*/
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo {

	public static void main(String[] args) {
		String path = "D:" + File.separatorChar + "demo.txt";
		File file = new File(path);
		FileOutputStream fileOutputStream = null;
		try {
			fileOutputStream = new FileOutputStream(file);
			fileOutputStream.write('a');
			fileOutputStream.write('b');
			fileOutputStream.write('c');
			fileOutputStream.write('d');
			fileOutputStream.write('e');
			fileOutputStream.write('f');
			fileOutputStream.write('g');
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			if(fileOutputStream != null){
				try {
					fileOutputStream.flush();
					fileOutputStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

  执行结果如下:
在这里插入图片描述
  执行这段代码的时候我们会发现,每次执行代码,前面向文件写入的数据就会被覆盖掉,那么如何实现追加数据呢?其实很简单,其构造函数FileOutputStream(File file,boolean append)可以实现追加功能,第二个参数append如果为true则是追加,它的默认值为false,所以我们只需在创建对象时稍稍变动上面的代码即可
  并且,如果demo.txt不存在时,它会自动创建该文件,但是这种方式只是创建文件本身,如果文件所在的目录也不存在,它是不会为文件创建多级目录的

三、文件拷贝

  现在,我们可以通过I/O流来读出文件里的数据,也可以将数据写入到文件中,那么我们可不可以通过结合这两种方式而实现文件的拷贝呢?如果可以的话那也太炫了吧,当然是可以的,我们可以这样做:

package qfbd.com;
/*
	原创作者:清风不渡
	博客地址:https://blog.csdn.net/WXKKang
*/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo {

	public static void main(String[] args) {
		String path1 = "D:" + File.separatorChar + "demo.txt";
		String path2 = "D:" + File.separatorChar + "demoCopy.txt";
		FileInputStream fileInputStream = null;
		FileOutputStream fileOutputStream = null;
		try {
			fileInputStream = new FileInputStream(path1);
			fileOutputStream = new FileOutputStream(path2);
			byte[] buffer = new byte[1 * 1024];
			int len = 0;
			while ((len = fileInputStream.read(buffer)) != -1) {
				fileOutputStream.write(buffer,0,len);
			}

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			if(fileInputStream!=null){
				try {
					fileInputStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			if(fileOutputStream!=null){
				try {
					fileOutputStream.flush();
					fileOutputStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}
}

  执行完毕之后,目录下将会新增加一个文件,原文件的内容也会被拷贝进去,如果要拷贝图片使用字节流也是可以的:
在这里插入图片描述

四、字节缓冲流

  在介绍FileInputStream 和FileOutputStream的例子中,使用了一个byte数组来作为数据读入的缓冲区,以文件存取为例,硬盘存取的速度远低于内存中的数据存取速度。为了减少对硬盘的存取,通常从文件中一次读入一定长度的数据,而写入时也是一次写入一定
长度的数据,这可以增加文件存取的效率
  BufferedInputStream与BufferedOutputStream 可以为InputStream、 OutputStream 类的对象增加缓冲区功能,构建BufferedInputStream实例时,需要给定一个InputStream类型的实例,实现BufferedInputStream时,实际上最后是实现InputStream实例,同样地,在构建BufferedOutputStream时,也需要给定一个OutputStream实例,实现BufferedOutputStream时,实际上最后是实现OutputStream实例
工作原理:
  BufferedInputStream内置了一个缓冲区(数组), 从BufferedInputStream中读取字节时,BufferedInputStream 会一次性从文件中读取8192个,存在缓冲区中,程序再次读取时,就不用找文件了,直接从缓冲区中获取,直到缓冲区中所有的都被使用过,才重新从文件中读取8192个
  BufferedOutputStream也内置了一个缓冲区(数组),程序向流中写出字节时,不会直接写到文件,先写到缓冲区中,直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。
  这样就可以大大加快数据的读写速度,那么我们就使用字节缓冲流来实现文件拷贝实践一下吧,代码如下:
  当前有图片文件如下:
在这里插入图片描述
  代码如下:

package qfbd.com;
/*
原创作者:清风不渡
博客地址:https://blog.csdn.net/WXKKang
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo {

	public static void main(String[] args) throws IOException {
		String path1 = "D:" + File.separatorChar +"demo"+ File.separatorChar + "demo.jpg";
		String path2 = "D:" + File.separatorChar +"demo"+ File.separatorChar + "demoCopy.jpg";
		Demo demo = new Demo();
		demo.copyDemo(path1, path2);

	}
	
	public void copyDemo(String path,String destPath) throws IOException{
		//打开输入流输出流
		FileInputStream fileInputStream = new FileInputStream(path);
		FileOutputStream fileOutputStream = new FileOutputStream(destPath);
		//使用缓冲流
		BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
		BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
		//读取和写入信息
		byte[] buffer = new byte[8*1024];
		int len = 0;
		while((len = bufferedInputStream.read(buffer)) != -1){
			bufferedOutputStream.write(buffer, 0, len);
		}
		//关闭流,释放资源
		bufferedInputStream.close();
		bufferedOutputStream.flush();
		bufferedOutputStream.close();
	}
}

  执行结果如下:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值