黑马程序员-IO(字节流的理解)

本文详细介绍了Java字节流与字符流的使用方法,包括流的操作数据类型、流向、返回类型解析及具体实例演示。重点解释了返回类型为int与null的原因及应用场景,并探讨了字节流与字符流缓冲区的使用方法及效率提升策略。

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

 

流按操作数据分为两种:字节流与字符流

流按流向分为:输入流,输出流。

首先明确一下

字节流的继承结构

 

字节流的抽象基类:InputStreamOutputStream

字节流的使用和字符流相差无几

格式如下

FileOutputStream fs = new FileOutputStream("File.txt");
//创建流对象,建立数据存放文件
fs.write("asfsdf".getBytes());
//这里接收的数据类型必须是字节,所以调用字符串的转换字节的方法,把字符串转换成字节
//调用流对象的写入方法,将数据写入流
fs.close();



 

对比字节流和字符流的使用,发现字节流操作不用刷新动作,这个详细的内部实现后面再详细解释

问题二:返回值到底代表的是什么,为什么有时候能够要定义一个int型的变量去接收返回值,为什么有时候定义String型去接收返回值

对于字符流

BufferedReader有三个Read(),分别是:

int read():返回int型的数据,理解为把字符强转成int返回,即数据的ASCII码,如果到达流末尾返回“-1”

int read(char[] buf):返回读到的字符数量,到达流末尾返回“-1”

String readLine():返回读到的字符串,如果到达流末尾返回“null”//只有用readLine()才会用null,其余一律用“-1

FileReader也有三个Read(),分别是:

int read():返回int型的数据,理解为把字符强转成int返回,即数据的ASCII码,如果到达流末尾返回“-1”

int read(char[] buf):返回读到的字符数量,到达流末尾返回“-1”

int read(char[],int off,int len ):返回读到的字符数量,到达流末尾返回“-1”

对于字节流

FileInputStream有两个read(),分别是

int read(byte[] bt):返回读到的字节数量,到达流末尾返回“-1”

int read():返回下一字节数,到达流末尾返回“-1”

BufferedInputStream有两个read(),分别是

int read(byte[],int off,int len):返回读到的字节数量,到达流末尾返回“-1”

int read():返回下一字节数,到达流末尾返回“-1”

总结:

1)只有用BufferedReaderreadLine()才会用到null,其余一律用“-1”作为结束条件

2)对于有参数的read()方法里有数组形参“byte[]”“char ch”“byte[],int off,intlen”这三种的,返回值都是代表读取到的数据的数目

3read()无参数的,分两种讨论

1:字符流:返回int型的数据,理解为把字符强转成int返回,即数据的ASCII

2:字节流:返回下一字节数

 2)到底什么时候定义int ch;

While((ch=fd.read(char))!=-1)

{}

什么时候定义string str=null;

While((str=bufr.readLine())!=null)

{}

观察上面的代码能够得出规律,用字符缓冲区的readLine()读取行数据时,会有一个返回值,返回值是一行字符串,String型接收,判断用“!=null”;

用字符流对象直接读取数据时,也有一个返回值,但是这个返回值是读取的个数,用int型接收,判断用“!=-1

弄明白记清楚返回值类型和其代表的意义,可以知道什么时候用int什么时候用string

available()方法能够返回准备读取数据的个数,这个个数把\r\n也计算在内

这个方法的作用是,当数据量比较小的时候,能够用作定义一个恰当的数组,避免资源的浪费

但是一般不建议使用,数据量大的时候,会造成很大的内存消耗,还是以定义确定的数组大小最好

byte[] bt = new byte[1024];
		int ch = 0;
		int num = fis.available();
		while((ch=fis.read(bt))!=-1)
		{
			fos.write(bt);
		} 
		使用了available()方法之后可以修改为下面代码

		int num = fis.available();
byte[] bt = new byte[num];
		int ch = 0;
		while((ch=fis.read(bt))!=-1)
		{
			fos.write(bt);
		}



 

但是一般不建议使用,数据量大的时候,会造成很大的内存消耗

还是以定义确定的数组大小最好

问题:BufferedInputStreamread()每次只读一个字节,而FileInputStreamread()也是每次读一个字节,那么BufferedInputStream的效率是如何提高的?

答:BufferedInputStream的内部维护着一个8K的数组,BufferedInputStream一次性把8K的字节读进数组(相当于读进内存),然后read()每次都从数组中取一个字节数据

FileInputStream每次调用read()都是从硬盘中直接读取数据

从内存中读取数据的速度要远远大于从硬盘读数据

字节流缓冲区的使用和字符流的区别

BufferedInputStream

BufferedOutputStream

用法和字符流的是一样的,都要创建缓冲区对象,然后关联文件

但是这里有一个区别,就是字节流的读取不用定义一个用于临时储存的数组,字节是直接操作数据的

注意是指缓冲区的使用不用定义临时数组,而不是说所有字节流都不用定义,仅限于缓冲区

操作字节流控制循环需要注意的地方

以下这个代码是在自定义实现BufferedInputStream的功能时的代码

 

class MyBufferedInputStream
{
	private InputStream in;
	private byte[] buf = new byte[1024*4];
	private int pos = 0, count = 0;
	MyBufferedInputStream(InputStream in)
	{
		this.in = in;
	}
	//一次读一个字节,从缓冲区(字节数组)获取。
	public int myRead()throws IOException   //为什么读取的是byte类型返回值却是int类型
	{
		//通过in对象读取硬盘上数据,并存储buf中。
		if(count==0)
		{
			count = in.read(buf);
			if(count<0)
				return -1;
			pos = 0;
			byte b = buf[pos];

			count--;
			pos++;
			return b&255;            //为什么要&255
		}
		else if(count>0)
		{
			byte b = buf[pos];
			count--;
			pos++;
			return b&0xff;           // b&0xff;十六进制也是表示255
		}
		return -1;

	}
	public void myClose()throws IOException
	{
		in.close();
	}
}
	public static void copy_2()throws IOException
	{
		MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\9.mp3"));
		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\3.mp3"));
		
		int by = 0;

		//System.out.println("第一个字节:"+bufis.myRead());

		while((by=bufis.myRead())!=-1)
		{
			bufos.write(by);
		}

		bufos.close();
		bufis.myClose();
	}


 

 

视频讲解+我的理解

由于是直接操作字节,那么在判断循环结束条件的时候,在读到流的末尾的时候,人为地设置返回“-1”,标记到读取数据达了末尾,但是实际字节数据在内存中的存储是以二进制码的形式的,有可能会存在恰好读到“1111 1111

这个特殊的数据,“1111 1111”转换成十进制就是“-1”,也就是循环结束的标志

问题就出来了,如何解决读到“1111 1111”而不退出呢

publicint myRead()标志返回值类型的时候用了int,强制把读取到的byte数据类型转成了int,18位二进制提升到48位。以读到特殊字节11111111为例。

“(byte)11111111&255”这个步骤就能够把(byte)11111111---->(int)11111111 11111111 11111111  11111111

因为255的二进制的值就是(int) 00000000 00000000  00000000 11111111

通过&运算,就得到48位的二进制00000000 00000000  00000000 11111111

这样就能够避免在判断循环结束条件的时候为“-1

可是真正的有效位是最低8位,这个在bufos.write(by);查阅API文档,

OutputStreamwrite(int num)读取只保留最低8

结论:

字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。

因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.

那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。

所以,为了避免这种情况将读到的字节进行int类型的提升。

并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。

而OutputStream在写入数据时,只写该int类型数据的最低8位。

 

 

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.youkuaiyun.com 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值