python fileinputstream_浅析FileInputStream的read(e)和ByteArrayOutputStream()的write(e,0,len)

之前在项目中,写过一段对二进制文件进行解析【解析成16进制字符串】的代码。今天回头做总结的时候发现里面有大学问。话不多说,先上代码。

1 public static byte[] loadFile(String fileNm) {2 File file = newFile(fileNm);3 FileInputStream fis = null;4 ByteArrayOutputStream baos = null;5 byte[] data = null;6

7 try{8 fis = newFileInputStream(file);9 //baos = new ByteArrayOutputStream((int)file.length());

10 baos = new ByteArrayOutputStream(2048);11 byte[] e = new byte[1024];12

13 intlen;14 while ((len = fis.read(e)) != -1) {15 baos.write(e, 0, len);16 }17

18 data =baos.toByteArray();19 System.out.println("此时的data is:" +Arrays.toString(data));20 } catch(IOException var15) {21 var15.printStackTrace();22 } finally{23 try{24 if (fis != null) {25 fis.close();26 fis = null;27 }28 if (null !=baos) {29 baos.close();30 baos = null;31 }32 } catch(IOException var14) {33 var14.printStackTrace();34 }35 }36 returndata;37 }

View Code

1.可以看到,先用FileInputStream文件字节流对文件进行了读取。

fis = new FileInputStream(file);

2.然后用ByteArrayOutputStream字节数组输出流将其输出到JVM内存里,供后续程序调用。注意这里用的是输出流,因为是需要将从文件里面读到的字节流输出到内存里【不是输出到控制台】。

baos = new ByteArrayOutputStream(2048);

通过看ByteArrayOutputStream()的构造函数.

public ByteArrayOutputStream(int size) {

if (size < 0) {

throw new IllegalArgumentException("Negative initial size: "

+ size);

}

buf = new byte[size];

}

可以看到,相当于对baos=new byte[2048];也就是baos的初值是一个2k大小的字节数组。

下面就是定义了一个1k大小的字节数组e。

byte[] e = new byte[1024];

3.文件字节流对象fis的read(e)返回的是一个整数【这样设计个人觉得是为了统一】,当返回-1时,代表它读完了。

public int read(byte b[]) throwsIOException {return readBytes(b, 0, b.length);

}

private native int readBytes(byte b[], int off, int len) throws IOException;

可以发现,read调用的还是readBytes(b, 0, b.length)。从该输入流中读取数据的字节,并将其读入字节数组。如果输入不可用,此方法将阻塞。

这个方法会返回读到读入缓冲区的字节总数【但是注意返回长度的有可能小于字节数组的长度】。

继续追踪readBytes,发现此方法被native修饰,没有源码。经查阅得知,native修饰符代表的原生,本地。具体实现不在JDK里面,它通常是有C或者C++实现的。也就意味着,native修饰的方法是java与底层硬件【通常是C或者C++】交互的接口。

在项目代码里,它是一个读1k。

4.然后就是写入内存【JVM】里。

baos.write(e, 0, len);

就是这个方法有大学问。表面上,是往内存里写一个长度为len字节数组e。但是仔细一想,数组是有固定长度的,它是如何保证数据不会覆盖,不会溢出的呢?

打开这个方法的源码。

public synchronized void write(byte b[], int off, intlen) {if ((off < 0) || (off > b.length) || (len < 0) ||((off+ len) - b.length > 0)) {throw newIndexOutOfBoundsException();

}

ensureCapacity(count+len);

System.arraycopy(b, off, buf, count, len);

count+=len;

}

可以看到,当前条件下,if条件是不会走的。

然后我看到是ensureCapacity(count+len);看名字像是扩容,但是count是什么呢?内部具体实现又是什么呢?

首先,在ByteArrayOutputStream源码里看到

protected int count;

count在这个地方被定义。但是并没有赋值,由于count是成员变量,默认初值是0;

于是上面代码就变成了ensureCapacity(len);打开ensureCapacity();

private void ensureCapacity(intminCapacity) {//overflow-conscious code

if (minCapacity - buf.length > 0)

grow(minCapacity);

}

这里的buf也是成员变量。

protected byte buf[];

默认是没有数据的。

因此,传入的len【minCapacity】>0;走到了grow(minCaptacity).打开grow()的源码。

private void grow(intminCapacity) {//overflow-conscious code

int oldCapacity =buf.length;int newCapacity = oldCapacity << 1;if (newCapacity - minCapacity < 0)

newCapacity=minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity=hugeCapacity(minCapacity);

buf=Arrays.copyOf(buf, newCapacity);

}

首先,因为buf.length=0;所以oldCapacity =0;它左移一位的newCapacity还是0;所以newCapacity = minCapacity;打开copyOf(buf,newCapacity);

public static byte[] copyOf(byte[] original, intnewLength) {byte[] copy = new byte[newLength];

System.arraycopy(original,0, copy, 0,

Math.min(original.length, newLength));returncopy;

}

copy为一个1k大小的数组。打开arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));

public static native void arraycopy(Object src, intsrcPos,

Object dest,intdestPos,int length);

这里又是一个被native修饰的方法。同上,就不展开了。直接举例说明arraycopy。

arrayCopy( arr1, 2, arr2, 5, 10);

意思是;将arr1数组里从索引为2的元素开始, 复制到数组arr2里的索引为5的位置, 复制的元素个数为10个.

Int[] arr1 ={1,2,3,4,5};

arrayCopy(arr1, 3, arr1, 2, 2);

意思是:将arr1从数字4开始 拷贝到arr1的数字3的位置, 拷贝2个数, 也就是说将4和5 拷贝到数字3的位置,相当于删除数字3.

此处返回的copy还是它本身的大小。

5.读的细心的朋友可以发现,ensureCapacity下面还有一个

System.arraycopy(b, off, buf, count, len);

由上可知,是将b数组从0开始赋值到buf的count【0】这个位置。并且复制len个元素。然后

count += len;

需要注意的是count是同步改变的,意味着不会被其他地方改变。

6.while这个循环第一遍就结束了,当进行第二遍的时候,由于count已经改变,这个时候的

System.arraycopy(b, off, buf, count, len);

就相当于把 新读到的1k大小的字节数组追加到了原来读字节数组的后面。

至此,关于字节流的读取就结束了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值