InputStream类、OutputStream类详解

InputStream类的定义

​public abstract class InputStream implements Closeable

分析:抽象类,实现了Closeable接口,而Closeable接口又拓展了AutoCloseable接口,因此所有的InputStream及其子类都可以用于Java 7新引入的带资源的try语句。

方法使用: 读入字节之前,我们可能想知道还有多少数据可用,调用available方法完成,具体的读入由read()及其重载方法完成,skip方法用于跳过某些字节,同时定义了几个有关标记(mark)的方法,读完数据用close方法关闭流,释放资源。下列具体介绍:

1.available方法
public int available() throws IOException {return 0;}

​ 假设方法返回的值为a,a代表的是在不阻塞的情况下,可以读入或者跳过(skip)的字节数。即,在该对象下一次调用读入方法读入a个字节,或者skip方法跳过a个字节时,不会出现阻塞(block)的情况。这个调用可以由相同线程调用,也可以是其他线程调用。但是在下次读入或跳过的时候,实际读入(跳过)的可能不止a字节。当遇到流结尾的时候,返回0。如果出现I/0错误,抛出IOException异常。

​ 由于方法实现只是简单地返回0,因此子类必须重写该方法注意:Java异常机制中,子类的方法不能throws父类方法没有throws的异常(构造器除外),因此在父类中先指出throws IOException,然后允许子类方法抛出IOException。

​ 使用意义:一般用于在读入或者跳过之间先探测一下有多少可用字节

2.读入方法:read 有3种重载的形式

1)read()
public abstract int read () throws IOException

​ 读取输入流的下一个字节。为抽象方法,不提供实现,子类必须实现这个方法。该方法读取下一个字节,返回一个0-255之间的int类型整数。如果到达流的末端,返回-1,调用该方法的时候,方法阻塞直到出现下列其中一种情况:a.遇到流的尾部 b.有数据可以读入 c.抛出异常 。面向字节的操作时,可能需要像这样比较底层的字节操作。

2)read(byte[] b)
public int read(byte[] b) throws IOException

​ 试图读入多个字节,存入字节数组b,返回实际读入的字节数。如果传输的是一个空数组(注意数组长度可以为0,即空数组。比如,byte[] b = new byte[0];或者byte[] b = {}?,那什么也没读入,返回0。

​ 如果到达流尾部,没有字节可读,返回-1;实际读入的第一个字节存在b[0],往后依次存入数组,读入的字节数最多不能超过数组b的长度。如果读入的字节数小于b的长度,剩余的数组元素保持不变。具体地,如果读入的字节数为k,则k个字节分别存在b[0]到b[k-1],而b[k]到b[b.length-1]保持原来的数据。

3)read(byte[] b,int off, int len)
public int read(byte[] b, int off, int len) throws IOException{ return read(b,0,b.length); }

​ 与上一个方法功能类似,除了读入的数据存储到b数组是从off开始的,len是试图读取的字节数,返回的是实际读入的字节数。如果遇到流尾部,返回-1,否则至少读入一个字节。

​ 假设实际读入k个字节,则k个字节分别存储在b[off]到b[off+k-1],而b[off+k]往后的元素保持不变,b[off]之前的也保持不变。InputStream中的源码

public int read(byte[] b, int off, int len) throws IOException{
		if(b == null) {//检测参数是否为null
			throw new NullPointerException();
		}else if(off < 0 || len <0 || len > b.length -off) {
			throw new IndexOutOfBoundsException(); //数组越界检测
		}else if(len == 0) {
			return 0; //如果b为空数组,返回0
		}
		
		int c = read(); //调用read()方法获取下一个字节
		if(c == -1) {
			return -1;
		}				//遇到流尾部,返回-1
		b[off] = (byte) c; //读入的第一个字节存入b[off]
		
		int i=1;
		try {
			for(;i<len;i++) { //循环调用read,直到流尾部
				c=read();
				if(c == -1) {
					break;
				}
				b[off + i] = (byte) c; //一次存入字节数组
			}
		}catch(IOException ee) {
			
		}
		
		return i; //返回实际存入的字节数
	}

总结:上面三个读方法都可能出现阻塞;理解:方法只是尝试读入我们想要的字节数,但是能否成功,返回的是实际读入的字节数,会受到数据源的影响。另外一点是读入的数据存到哪里,第一个方法作为返回值,第二、三个方法存到指定的数组的指定位置,返回的是实际读入的字节数。后两个方法真正的读入工作都是通过调用read()来完成的,这个抽象方法在子类中实现。

3.skip方法
public long skip(long n) throws IOException

​ 这个方法试图跳过当前流的n个字节,返回实际跳过的字节数。如果n为负数,返回0。一下为遇到流结尾:img

跳过两个字节的时候位置由A跳到B,而如果试图一次跳过6个字节,会发现只能跳过5个字节到达c。

public long skip(long n) throws IOException{
		long remaining = n; //还有多少字节没跳过
		int nr;
		if(n<=0) {
			return 0; //n小于0,简单返回0
		}
		int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); //这里的常数在类中定义为2048
		byte[] skipBuffer = new byte[size]; //新建一个字节数组,如果n<2048,数组大小为n,否则为2048
		while(remaining>0) {
			nr = read(skipBuffer,0,(int)Math.min(size, remaining)); //读入字节,存入数组
			if(nr < 0) {//遇到流尾部跳出循环
				break;
			}
			remaining -= nr;
		}
		
		return n-remaining;
	}

其实现代码,是通过不断地读取字节来完成跳过的任务的。首先建立一个缓冲数组,这个数组的大小不超过2048(MAX_SKIP_BUFFER_SIZE),若跳过的字节大于2048,则数组大小为2048,否则采用要跳过的字节数作为数组长度。接下来不断读取字节,填入数组,如果还有没跳过的字节数超过缓冲数组长度,则读入2048,否则读入还没跳过的字节,完成跳过任务。如果遇到流尾部,跳出循环,返回已经读入的字节个数(n-remaining)。

4.与标记相关的方法

与标注相关的方法使得我们可以重新读入(跳过)那些已经被我们读入或者跳过的字节,再重新来过一次。

1)mark
public void mark(int readlimit)

​ 这个方法用于在流的当前位置做个标记,参数readlimit指定这个标记的“有效期”,如果从标记处开始往后,已经获取或者跳过了readlimit个字节,那么这个标记失效,不允许再重新回到这个位置(通过reset方法)。多次调用这个方法,前面的标记会被覆盖。

2)reset
public void reset() throws IOException

​ 这个方法用于重定位到最近的标记。如果在这之前mark方法从来没被调用,或者标记已经失效,再抛出IOException。如果没有抛出这个异常,将当前位置重新定位到最近的标记位置。

InputStream is = null;
try{
    is = new BufferedInputStream(new FileInputStream("test.txt"));
    is.mark(4);
    is.skip(2);
    is.reset();
    System.out.println((char)is.read());
}finally{
	if(is != null){
		is.close();
    }
}

使用了支持mark的BufferedInputStream,首先一开始做标记,跳过两个自己,然后回到最初的位置。

3)markSupported
public boolean markSupported()

​ 检测当前流对象是否支持标记,是返回true,否则返回false。比如InputStream不支持标记,而BufferedInputStream支持。

5.close方法 public void close() throws IOException

​ 关闭当前流,释放与该流相关的资源,防止资源泄漏。在带资源的try语句中将被自动调用。关闭流之后还试图读取字节,会出现IOException异常。
OutputStream类方法使用

public abstract int write(int b) throws IOException

将(来源,输出内容)参数b的低八位字节(去处)写入此输出流中(int 类型是32位的);

与InputStream中的无参read()方法相对应;

public int write(byte [] b) throws IOException与下面方法效果相同

public int write(byte [] b , int off , int len) throws IOException

将字节数组b中的第off+1元素开始的len个数据,顺序写入此输出流中

  • write()方法所写的数据并没有直接传到输出流相连中的外设上,而是先暂时的存放在流的缓冲区中,等到缓冲区的数据积累到一定数量,再执行一次向外设的写操作把它们全部写到外设上

img
参考自此文章:https://www.cnblogs.com/wxgblogs/p/5647054.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值