热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java.io包_Java源码解析——JavaIO包

一、基础知识:1.JavaIO一般包含两个部分:1)java.io包中阻塞型IO;2)java.nio包中的非阻塞型IO,通

一、基础知识:

1. Java IO一般包含两个部分:1)java.io包中阻塞型IO;2)java.nio包中的非阻塞型IO,通常称为New IO。这里只考虑到java.io包中堵塞型IO;

2. Java.io包简单地分类。

2.1 Java的IO主要包含三个部分:

1)流式部分――IO的主体部分;

2)非流式部分――主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类;

3)文件读取部分的与安全相关的类,如:SerializablePermission类。以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。

2.2 流式部分可以概括:

1)字节流(Byte Stream)和字符流(Char Stream)的对应;

2)输入和输出的对应。

3)从字节流到字符流的桥梁。对应于输入和输出为InputStreamReader和OutputStreamWriter。

2.3 流的具体类中又可以具体分为:

1)介质流(Media Stream或者称为原始流Raw Stream)――主要指一些基本的流,他们主要是从具体的介质上,如:文件、内存缓冲区(Byte数组、Char数组、StringBuffer对象)等,读取数据;

2)过滤流(Filter Stream)――主要指所有FilterInputStream/FilterOutputStream和FilterReader/FilterWriter的子类,主要是对其包装的类进行某些特定的处理,如:缓存等。

2.4 节点流和处理流

1)节点流是FileInputStream、ByteArrayInputStream这些直接从某个地方获取流的类;

2)处理流则是BufferedInputStream这种可以装饰节点流,来实现特定功能的类。因此,节点流可以理解为装饰者模式中的被装饰者,处理流则是装饰者。

fd0f2a943db823ef7f92f3e30eb4e029.png

二、类的分析

1、输入字节流

IO中输入字节流的继承图:

-InputStream-ByteArrayInputStream //将内存中的Byte数组适配为一个InputStream

-FileInputStream       //最基本的文件输入流。主要用于从文件中读取信息

-FilterInputStream      //给其它被装饰对象提供额外功能的抽象类

-BufferedInputStream  //使用该对象阻止每次读取一个字节都会频繁操作IO。将字节读取一个缓存区,从缓存区读取。

-DataInputStream     //使用它可以读出基本数据类型

-LineNumberInputStream //跟踪输入流中的行号。可以得到和设置行号。

-PushbackInputStream //可以在读取最后一个byte 后将其放回到缓存中。

-ObjectInputStream-PipedInputStream    //在流中实现了管道的概念读取PipedOutputStream写入的数据。

-SequenceInputStream     //将2个或者多个InputStream 对象转变为一个InputStream

-StringBufferInputStream  //将内存中的字符串适配为一个InputStream(废弃)

1)InputStream是抽象类,是所有字节输入流的超类。

2)ByteArrayInputStream、StringBufferInputStream、FileInputStream是三种基本的介质流,它们分别将Byte数组、StringBuffer、和本地文件中读取数据。PipedInputStream是从与其它线程共用的管道中读取数据;

3)ObjectInputStream和所有FilterInputStream的子类都是装饰流(装饰器模式的主角)。

4)FileInputStream 文件输入流,用于读取本地文件中的字节数据。

2. IO中的输出字节流

IO中输出字节流的继承图:

-OutputStream-ByteArrayOutputStream   //在内存中创建一个buffer。所有写入此流中的数据都被放入到此buffer中。

-FileOutputStream      //将信息写入文件中。

-FilterOutputStream     //实现OutputStream装饰器功能的抽象类。

-BufferedOutputStream //使用该对象阻止每次读取一个字节都会频繁操作IO。将字节读取一个缓存区,从缓存区读取。

-DataOutputStream    //使用它可以写入基本数据类型。

-PrintStream       //产生具有格式的输出信息。(一般地在java程序中DataOutputStream用于数据的存储,即J2EE中持久层完成的功能,PrintStream完成显示的功能,类似于J2EE中表现层的功能)

-BufferedOutputStream //使用它可以避免频繁地向IO写入数据,数据一般都写入一个缓存区,在调用flush方法后会清空缓存、一次完成数据的写入。

-PipedOutputStream   //任何写入此对象的信息都被放入对应PipedInputStream 对象的缓存中,从而完成线程的通信,实现了“管道”的概念。

1)OutputStream是所有的输出字节流的父类,它是一个抽象类。

2)ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte数组、和本地文件中写入数据。PipedOutputStream是向与其它线程共用的管道中写入数据。

3)ObjectOutputStream和所有FilterOutputStream的子类都是装饰流。

3. 字节流的输入与输出的对应

1)LineNumberInputStream主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。

2)PushbackInputStream的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream几乎实现相近的功能。

3)SequenceInputStream可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO包中去除,还完全不影响IO包的结构,却让其更“纯洁”――纯洁的Decorator模式。

4)PrintStream也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO包!System.out和System.out就是PrintStream的实例!

5)ObjectInputStream/ObjectOutputStream和DataInputStream/DataOutputStream主要是要求写对象/数据和读对象/数据的次序要保持一致,否则轻则不能得到正确的数据,重则抛出异常;

6)PipedInputStream/PipedOutputStream在创建时一般就一起创建,调用它们的读写方法时会检查对方是否存在,或者关闭。

4. 输入字符流

IO中输入字符流的继承图:

-Reader-BufferedReader-LineNumberReader-CharArrayReader-FilterReader-PushbackReader-InputStreamReader-FileReader-PipedReader-StringReader

1)Reader是所有的输入字符流的父类,它是一个抽象类。

2)CharReader、StringReader是两种基本的介质流,它们分别将Char数组、String中读取数据。PipedReader是从与其它线程共用的管道中读取数据。

3)BufferedReader很明显就是一个装饰器,它和其子类负责装饰其它Reader对象。

4)FilterReader是所有自定义具体装饰流的父类,其子类PushbackReader对Reader对象进行装饰,会增加一个行号。

5)InputStreamReader是字节流向字符流转化的桥梁,它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。其构造方法的默认参数为InputStream 对象。

使用方法:

InputStreamReader(InputStream in),

InputStreamReader(Inputstreamin, charset cs)

为了达到最高效率,InputStreamReader通常用法为:

BufferedReader in = new BufferedReader(newInputStreamReader(System.in));

BufferedReader:缓冲输入流,包装其他字符输入流,提高读取效率,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。Reader的读取操作开销大,为提高效率使用BufferedReader包装其他Reader(如FileReader和InputStreamReader)

5. 输出字符流:

IO中输出字符流的继承图:

-Writer-BufferedWriter-CharArrayWriter-FilterWriter-OutputStreamWriter-FileWriter-PipedWriter-PrintWriter-StringWriter

1)Writer是所有的输出字符流的父类,它是一个抽象类。

2)CharArrayWriter、StringWriter是两种基本的介质流,它们分别向Char数组、String中写入数据。PipedWriter是向与其它线程共用的管道中写入数据。

3) BufferedWriter是一个装饰器为Writer提供缓冲功能。

4)PrintWriter和PrintStream极其类似,功能和使用也非常相似。

5)OutputStreamWriter:是字符流通向字节流的桥梁,它使用指定的 charset 读取字符并将其解码为字节。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

使用方法:

其构造方法的默认参数为OutputStream 对象,

OutputStreamReader(InputStream in),

OutputStreamReader(Inputstream in, charset cs)

为了达到最高效率,OutputStreamReader通常用法为:

BufferedWriter out = new BufferedWriter(newOutputStreamWriter(System.in));

BufferedWriter:缓冲输出流,包装其他字符输出流,提高读取效率,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入Writer的读取操作开销大,为提高效率使用BufferedWriter包装其他Writer(如FileWriter和InputStreamWriter)

6. 序列化与反序列化:ObjectInputStream,ObjectOutputStream

JAVA提出序列化是为了将对象在ObjectOutputStream:对象输出流,它的writeObject(Object obj)方法可以对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

序列化过程:

File file = newFile(“path”);

OutputStream fos= newFileOutputStream(file);

ObjectOutputStream oos= newObjectOutputStream(“fos”);将输出流对象输出到file对象中。

oos.writeObject(Object obj);

oos.flush();

oos.close();

ObjectInputStream:对象输入流,它的readObject()方法可以序列化文件进行反序列化,把字节序列文件转化为对象。

反序列化过程:

File file = newFile(“path”)

InputStream fis= newFileInputStream(file);

ObjectInputStream ois= newObjectInputStream(fis);

Class obj= (class)ois.readObject();

Ois.close();

实现序列化的两种方式:

1)类实现Serializable接口,类只有实现了serializable接口,ObjectOutputstream才会去将类的对象序列化,否则会抛出NotSerializableException异常

2)类继承Externalizable类。

三、主要源码实现:

1.InputStream:

public abstract class InputStream implementsCloseable {private static final int SKIP_BUFFER_SIZE = 2048; //用于skip方法,和skipBuffer相关

private static byte[] skipBuffer; //skipBuffer is initialized in skip(long), if needed.//从输入流中读取下一个字节,//正常返回0-255,到达文件的末尾返回-1//在流中还有数据,但是没有读到时该方法会阻塞(block)//Java IO和New IO的区别就是阻塞流和非阻塞流//抽象方法!不同的子类不同的实现!

public abstract int read() throwsIOException;//将流中的数据读入放在byte数组的第off个位置先后的len个位置中//放回值为放入字节的个数。//这个方法在利用抽象方法read,某种意义上简单的Templete模式。

public int read(byte b[], int off, int len) throwsIOException {//检查输入是否正常。一般情况下,检查输入是方法设计的第一步

if (b == null) {throw newNullPointerException();

}else if (off <0 || len <0 || len > b.length -off) {throw newIndexOutOfBoundsException();

}else if (len &#61;&#61; 0) {return 0;

}//读取下一个字节

int c &#61;read();//到达文件的末端返回-1

if (c &#61;&#61; -1) { return -1; }//放回的字节downcast

b[off] &#61; (byte)c;//已经读取了一个字节

int i &#61; 1;try{//最多读取len个字节&#xff0c;所以要循环len次

for (; i

c &#61;read();//到达末尾&#xff0c;理所当然放回-1

if (c &#61;&#61; -1) { break; }//读到就放入byte数组中

b[off &#43; i] &#61; (byte)c;

}

}catch(IOException ee) { }returni;

}//利用上面的方法read(byte[] b)

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

}//方法内部使用的、表示要跳过的字节数目&#xff0c;

public long skip(long n) throwsIOException {long remaining &#61;n;intnr;if (skipBuffer &#61;&#61; null)//初始化一个跳转的缓存

skipBuffer &#61; new byte[SKIP_BUFFER_SIZE];//本地化的跳转缓存

byte[] localSkipBuffer &#61;skipBuffer;//检查输入参数&#xff0c;应该放在方法的开始

if (n <&#61; 0) { return 0; }//一共要跳过n个&#xff0c;每次跳过部分&#xff0c;循环

while (remaining > 0) {

nr&#61; read(localSkipBuffer, 0, (int) Math.min(SKIP_BUFFER_SIZE, remaining));//利用上面的read(byte[],int,int)方法尽量读取n个字节//读到流的末端&#xff0c;则返回

if (nr <0) { break; }//没有完全读到需要的&#xff0c;则继续循环

remaining -&#61;nr;

}return n - remaining;//返回时要么全部读完&#xff0c;要么因为到达文件末端&#xff0c;读取了部分

}//查询流中还有多少可以读取的字节//该方法不会block。在java中抽象类方法的实现一般有以下几种方式&#xff1a;//1.抛出异常(java.util)&#xff1b;2.“弱”实现。像上面这种。子类在必要的时候覆盖它。//3.“空”实现。

public int available() throwsIOException {return 0;

}//关闭当前流、同时释放与此流相关的资源//关闭当前流、同时释放与此流相关的资源

public void close() throwsIOException {}//markSupport可以查询当前流是否支持mark

public synchronized void mark(intreadlimit) {}//对mark过的流进行复位。只有当流支持mark时才可以使用此方法。

public synchronized void reset() throwsIOException {throw new IOException("mark/reset not supported");

}//查询是否支持mark//绝大部分不支持&#xff0c;因此提供默认实现&#xff0c;返回false。子类有需要可以覆盖。

public booleanmarkSupported() {return false;

}

}

2.FilterInputStream

这是字节输入流部分装饰器模式的核心。是在装饰器模式中的Decorator对象&#xff0c;主要完成对其它流装饰的基本功能&#xff1a;

public class FilterInputStream extendsInputStream {//装饰器的代码特征&#xff1a;被装饰的对象一般是装饰器的成员变量

protected volatile InputStream in; //将要被装饰的字节输入流

protected FilterInputStream(InputStream in) { //通过构造方法传入此被装饰的流

this.in &#61;in;

}//下面这些方法&#xff0c;完成最小的装饰――0装饰&#xff0c;只是调用被装饰流的方法而已

public int read() throwsIOException {returnin.read();

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

}public int read(byte b[], int off, int len) throwsIOException {returnin.read(b, off, len);

}public long skip(long n) throwsIOException {returnin.skip(n);

}public int available() throwsIOException {returnin.available();

}public void close() throwsIOException {

in.close();

}public synchronized void mark(intreadlimit) {

in.mark(readlimit);

}public synchronized void reset() throwsIOException {

in.reset();

}public booleanmarkSupported() {returnin.markSupported();

}

}

ByteArray到ByteArrayInputStream的适配&#xff1a;

ByteArrayInputStream内部有一个byte类型的buffer。很典型的适配器模式的应用――将byte数组适配流的接口。

public class ByteArrayInputStream extendsInputStream {protected byte buf[]; //内部的buffer&#xff0c;一般通过构造器输入

protected int pos; //当前位置的cursor。从0至byte数组的长度。//byte[pos]就是read方法读取的字节

protected int mark &#61; 0; //mark的位置。

protected int count; //流中字节的数目。//构造器&#xff0c;从一个byte[]创建一个ByteArrayInputStream

public ByteArrayInputStream(bytebuf[]) {//初始化流中的各个成员变量

this.buf &#61;buf;this.pos &#61; 0;this.count &#61;buf.length;

}//构造器

public ByteArrayInputStream(byte buf[], int offset, intlength) {this.buf &#61;buf;this.pos &#61; offset; //与上面不同

this.count &#61; Math.min(offset &#43;length, buf.length);this.mark &#61; offset; //与上面不同

}//从流中读取下一个字节

public synchronized intread() {//返回下一个位置的字节//流中没有数据则返回-1

return (pos

}//ByteArrayInputStream要覆盖InputStream中可以看出其提供了该方法的实现//某些时候&#xff0c;父类不能完全实现子类的功能&#xff0c;父类的实现一般比较通用。//当子类有更有效的方法时&#xff0c;我们会覆盖这些方法。

public synchronized int read(byte b[], int off, intlen) {//首先检查输入参数的状态是否正确

if(b&#61;&#61;null){throw newNullPointerException();

}else if (off <0 || len <0 || len > b.length -off) {throw newIndexOutOfBoundsException();

}if (pos >&#61; count) { return -1; }if (pos &#43; len > count) { len &#61; count -pos; }if (len <&#61; 0) { return 0; }//java中提供数据复制的方法//出于速度的原因&#xff01;他们都用到System.arraycopy方法

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

pos&#43;&#61;len;returnlen;

}//下面这个方法&#xff0c;在InputStream中也已经实现了。//但是当时是通过将字节读入一个buffer中实现的&#xff0c;好像效率低了一点。//比InputStream中的方法简单、高效

public synchronized long skip(longn) {//当前位置&#xff0c;可以跳跃的字节数目

if (pos &#43; n > count) { n &#61; count -pos; }//小于0&#xff0c;则不可以跳跃

if (n <0) { return 0; }//跳跃后&#xff0c;当前位置变化

pos &#43;&#61;n;returnn;

}//查询流中还有多少字节没有读取。

public synchronized intavailable() {return count -pos;

}//ByteArrayInputStream支持mark所以返回true

public booleanmarkSupported() {return true;

}//在流中当前位置mark。

public void mark(intreadAheadLimit) {

mark&#61;pos;

}//重置流。即回到mark的位置。

public synchronized voidreset() {

pos&#61;mark;

}//关闭ByteArrayInputStream不会产生任何动作。

public void close() throwsIOException { }

}

3.BufferedInputStream//该类主要完成对被包装流&#xff0c;加上一个缓存的功能

public class BufferedInputStream extendsFilterInputStream {private static int defaultBufferSize &#61; 8192; //默认缓存的大小

protected volatile byte buf[]; //内部的缓存

protected int count; //buffer的大小

protected int pos; //buffer中cursor的位置

protected int markpos &#61; -1; //mark的位置

protected int marklimit; //mark的范围//原子性更新。和一致性编程相关

private static finalAtomicReferenceFieldUpdater bufUpdater &#61;AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class,"buf");//检查输入流是否关闭&#xff0c;同时返回被包装流

private InputStream getInIfOpen() throwsIOException {

InputStream input&#61;in;if (input &#61;&#61; null) throw new IOException("Stream closed");returninput;

}//检查buffer的状态&#xff0c;同时返回缓存

private byte[] getBufIfOpen() throwsIOException {byte[] buffer &#61;buf;//不太可能发生的状态

if (buffer &#61;&#61; null) throw new IOException("Stream closed");returnbuffer;

}//构造器

publicBufferedInputStream(InputStream in) {//指定默认长度的buffer

this(in, defaultBufferSize);

}//构造器

public BufferedInputStream(InputStream in, intsize) {super(in);//检查输入参数

if(size<&#61;0){throw new IllegalArgumentException("Buffer size <&#61; 0");

}//创建指定长度的buffer

buf &#61; new byte[size];

}//从流中读取数据&#xff0c;填充如缓存中。

private void fill() throwsIOException {//得到buffer

byte[] buffer &#61;getBufIfOpen();if (markpos <0)//mark位置小于0&#xff0c;此时pos为0

pos &#61; 0;//pos大于buffer的长度

else if (pos >&#61;buffer.length)if (markpos > 0) {int sz &#61; pos - markpos;

System.arraycopy(buffer, markpos, buffer, 0, sz);

pos&#61;sz;

markpos&#61; 0;

}else if (buffer.length >&#61;marklimit) {//buffer的长度大于marklimit时&#xff0c;mark失效

markpos &#61; -1;//丢弃buffer中的内容

pos &#61; 0;

}else{//buffer的长度小于marklimit时对buffer扩容

int nsz &#61; pos * 2;if (nsz >marklimit)

nsz&#61; marklimit;//扩容为原来的2倍&#xff0c;太大则为marklimit大小

byte nbuf[] &#61; new byte[nsz];//将buffer中的字节拷贝如扩容后的buf中

System.arraycopy(buffer, 0, nbuf, 0, pos);if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {//在buffer在被操作时&#xff0c;不能取代此buffer

throw new IOException("Stream closed");

}//将新buf赋值给buffer

buffer &#61;nbuf;

}

count&#61;pos;int n &#61; getInIfOpen().read(buffer, pos, buffer.length -pos);if (n > 0) count &#61; n &#43;pos;

}//读取下一个字节

public synchronized int read() throwsIOException {//到达buffer的末端

if (pos >&#61;count) {//就从流中读取数据&#xff0c;填充buffer

fill();

//读过一次&#xff0c;没有数据则返回-1

if (pos >&#61; count) return -1;

}//返回buffer中下一个位置的字节

return getBufIfOpen()[pos&#43;&#43;] & 0xff;

}//将数据从流中读入buffer中

private int read1(byte[] b, int off, int len) throwsIOException {int avail &#61; count - pos; //buffer中还剩的可读字符//buffer中没有可以读取的数据时

if(avail<&#61;0){//将输入流中的字节读入b中

if (len >&#61; getBufIfOpen().length && markpos <0) {returngetInIfOpen().read(b, off, len);

}

fill();//填充

avail &#61; count -pos;if (avail <&#61; 0) return -1;

}//从流中读取后&#xff0c;检查可以读取的数目

int cnt &#61; (avail

System.arraycopy(getBufIfOpen(), pos, b, off, cnt);

pos&#43;&#61;cnt;returncnt;

}public synchronized int read(byte b[], int off, int len)throwsIOException {

getBufIfOpen();//检查buffer是否open//检查输入参数是否正确

if ((off | len | (off &#43; len) | (b.length - (off &#43; len))) <0) {throw newIndexOutOfBoundsException();

}else if (len &#61;&#61; 0) {return 0;

}int n &#61; 0;for(;;) {int nread &#61; read1(b, off &#43; n, len -n);if (nread <&#61; 0) return (n &#61;&#61; 0) ?nread : n;

n&#43;&#61;nread;if (n >&#61; len) returnn;

InputStream input&#61;in;if (input !&#61; null && input.available() <&#61; 0) returnn;

}

}public synchronized long skip(long n) throwsIOException {//检查buffer是否关闭

getBufIfOpen();//检查输入参数是否正确

if (n <&#61; 0) { return 0; }//buffered中可以读取字节的数目

long avail &#61; count -pos;//可以读取的小于0&#xff0c;则从流中读取

if (avail <&#61; 0) {//mark小于0&#xff0c;则mark在流中

if (markpos <0) returngetInIfOpen().skip(n);//从流中读取数据&#xff0c;填充缓冲区。

fill();//可以读的取字节为buffer的容量减当前位置

avail &#61; count -pos;if (avail <&#61; 0) return 0;

}long skipped &#61; (avail

pos&#43;&#61; skipped;

//当前位置改变

returnskipped;

}//该方法不会block&#xff01;返回流中可以读取的字节的数目。//该方法的返回值为缓存中的可读字节数目加流中可读字节数目的和

public synchronized int available() throwsIOException {return getInIfOpen().available() &#43; (count -pos);

}//当前位置处为mark位置

public synchronized void mark(intreadlimit) {

marklimit&#61;readlimit;

markpos&#61;pos;

}public synchronized void reset() throwsIOException {//缓冲去关闭了&#xff0c;肯定就抛出异常&#xff01;程序设计中经常的手段

getBufIfOpen();if (markpos <0) throw new IOException("Resetting to invalid mark");

pos&#61;markpos;

}//该流和ByteArrayInputStream一样都支持mark

public booleanmarkSupported() {return true;

}//关闭当前流同时释放相应的系统资源。

public void close() throwsIOException {byte[] buffer;while ( (buffer &#61; buf) !&#61; null) {if (bufUpdater.compareAndSet(this, buffer, null)) {

InputStream input&#61;in;

in&#61; null;if (input !&#61; null) input.close();return;

}//Else retry in case a new buf was CASed in fill()

}

}

}

4.PipedOutputStream

PipedOutputStream一般必须和一个PipedInputStream连接。共同构成一个pipe。即必须连接输入部分。

其原理为&#xff1a;PipedInputStream内部有一个Buffer&#xff0c; PipedInputStream可以使用InputStream的方法读取其Buffer中的字节。PipedInputStream中Buffer中的字节是PipedOutputStream调用PipedInputStream的方法放入的。

public class PipedOutputStream extendsOutputStream {//包含一个PipedInputStream

privatePipedInputStream sink;//带有目的地的构造器

public PipedOutputStream(PipedInputStream snk)throwsIOException {

connect(snk);

}//默认构造器&#xff0c;必须使用下面的connect方法连接

publicPipedOutputStream() { }public synchronized void connect(PipedInputStream snk) throwsIOException {//检查输入参数的正确性

if(snk&#61;&#61;null){throw newNullPointerException();

}else if (sink !&#61; null ||snk.connected) {throw new IOException("Already connected");

}//一系列初始化工作

sink &#61;snk;

snk.in&#61; -1;

snk.out&#61; 0;

snk.connected&#61; true;

}//向流中写入数据

public void write(int b) throwsIOException {if (sink &#61;&#61; null) { throw new IOException("Pipe not connected"); }//本质上是&#xff0c;调用PipedInputStream的receive方法接受此字节

sink.receive(b);

}public void write(byte b[], int off, int len) throwsIOException {//首先检查输入参数的正确性

if (sink &#61;&#61; null) {throw new IOException("Pipe not connected");

}else if (b &#61;&#61; null) {throw newNullPointerException();

}else if ((off <0) || (off > b.length) || (len <0) || ((off &#43; len) > b.length) || ((off &#43; len) <0)) {throw newIndexOutOfBoundsException();

}else if (len &#61;&#61; 0) {return;

}//调用PipedInputStream的receive方法接受

sink.receive(b, off, len);

}//flush输出流

public synchronized void flush() throwsIOException {if (sink !&#61; null) {//本质是通知输入流&#xff0c;可以读取

synchronized(sink) { sink.notifyAll(); }

}

}//关闭流同时释放相关资源

public void close() throwsIOException {if (sink !&#61; null) { sink.receivedLast(); }

}

}

PipedInputStream

public class PipedInputStream extendsInputStream {//标识有读取方或写入方关闭

boolean closedByWriter &#61; false;volatile boolean closedByReader &#61; false;//是否建立连接

boolean connected &#61; false;//标识哪个线程

Thread readSide;

Thread writeSide;//缓冲区的默认大小

protected static final int PIPE_SIZE &#61; 1024;//缓冲区

protected byte buffer[] &#61; new byte[PIPE_SIZE];//下一个写入字节的位置。0代表空&#xff0c;in&#61;&#61;out代表满

protected int in &#61; -1;//下一个读取字节的位置

protected int out &#61; 0;//给定源的输入流

public PipedInputStream(PipedOutputStream src) throwsIOException {

connect(src);

}//默认构造器&#xff0c;下部一定要connect源

publicPipedInputStream() { }//连接输入源

public void connect(PipedOutputStream src) throwsIOException {//调用源的connect方法连接当前对象

src.connect(this);

}//只被PipedOuputStream调用

protected synchronized void receive(int b) throwsIOException {//检查状态&#xff0c;写入

checkStateForReceive();//永远是PipedOuputStream

writeSide &#61;Thread.currentThread();//输入和输出相等&#xff0c;等待空间

if (in &#61;&#61;out) awaitSpace();if (in <0) {

in&#61; 0;

out&#61; 0;

}//放入buffer相应的位置

buffer[in&#43;&#43;] &#61; (byte)(b & 0xFF);//in为0表示buffer已空

if (in >&#61; buffer.length) { in &#61; 0; }

}synchronized void receive(byte b[], int off, int len) throwsIOException {

checkStateForReceive();//从PipedOutputStream可以看出

writeSide &#61;Thread.currentThread();int bytesToTransfer &#61;len;while (bytesToTransfer > 0) {//满了&#xff0c;会通知读取的&#xff1b;空会通知写入

if (in &#61;&#61;out) awaitSpace();int nextTransferAmount &#61; 0;if (out

nextTransferAmount&#61; buffer.length -in;

}else if (in

in&#61; out &#61; 0;

nextTransferAmount&#61; buffer.length -in;

}else{

nextTransferAmount&#61; out -in;

}

}if (nextTransferAmount > bytesToTransfer) nextTransferAmount &#61;bytesToTransfer;assert(nextTransferAmount > 0);

System.arraycopy(b, off, buffer, in, nextTransferAmount);

bytesToTransfer-&#61;nextTransferAmount;

off&#43;&#61;nextTransferAmount;

in&#43;&#61;nextTransferAmount;if (in >&#61; buffer.length) { in &#61; 0; }

}

}//检查当前状态&#xff0c;等待输入

private void checkStateForReceive() throwsIOException {if (!connected) {throw new IOException("Pipe not connected");

}else if (closedByWriter ||closedByReader) {throw new IOException("Pipe closed");

}else if (readSide !&#61; null && !readSide.isAlive()) {throw new IOException("Read end dead");

}

}//Buffer已满&#xff0c;等待一段时间

private void awaitSpace() throwsIOException {//in&#61;&#61;out表示满了&#xff0c;没有空间

while (in &#61;&#61;out) {//检查接受端的状态

checkStateForReceive();//通知读取端

notifyAll();try{

wait(1000);

}catch(InterruptedException ex) {throw newjava.io.InterruptedIOException();

}

}

}//通知所有等待的线程()已经接受到最后的字节

synchronized voidreceivedLast() {

closedByWriter&#61; true; //notifyAll();

}public synchronized int read() throwsIOException {//检查一些内部状态

if (!connected) {throw new IOException("Pipe not connected");

}else if(closedByReader) {throw new IOException("Pipe closed");

}else if (writeSide !&#61; null && !writeSide.isAlive()&& !closedByWriter && (in <0)) {throw new IOException("Write end dead");

}//当前线程读取

readSide &#61;Thread.currentThread();//重复两次&#xff1f;&#xff1f;&#xff1f;

int trials &#61; 2;while (in <0) {//输入断关闭返回-1

if (closedByWriter) { return -1; }//状态错误

if ((writeSide !&#61; null) && (!writeSide.isAlive()) && (--trials <0)) {throw new IOException("Pipe broken");

}

notifyAll();//空了&#xff0c;通知写入端可以写入 try {

wait(1000);

}catch(InterruptedException ex) {throw newjava.io.InterruptedIOException();

}

}int ret &#61; buffer[out&#43;&#43;] & 0xFF; if (out >&#61; buffer.length) { out &#61; 0; }//没有任何字节

if (in &#61;&#61; out) { in &#61; -1; }returnret;

}public synchronized int read(byte b[], int off, int len) throwsIOException {//检查输入参数的正确性

if (b &#61;&#61; null) {throw newNullPointerException();

}else if (off <0 || len <0 || len > b.length -off) {throw newIndexOutOfBoundsException();

}else if (len &#61;&#61; 0) {return 0;

}//读取下一个

int c &#61;read();//已经到达末尾了&#xff0c;返回-1

if (c <0) { return -1; }//放入外部buffer中

b[off] &#61; (byte) c;//return-len

int rlen &#61; 1;//下一个in存在&#xff0c;且没有到达len

while ((in >&#61; 0) && (--len > 0)) {//依次放入外部buffer

b[off &#43; rlen] &#61; buffer[out&#43;&#43;];

rlen&#43;&#43;;//读到buffer的末尾&#xff0c;返回头部

if (out >&#61; buffer.length) { out &#61; 0; }//读、写位置一致时&#xff0c;表示没有数据

if (in &#61;&#61; out) { in &#61; -1; }

}//返回填充的长度

returnrlen;

}//返回还有多少字节可以读取

public synchronized int available() throwsIOException {//到达末端&#xff0c;没有字节

if(in <0)return 0;else if(in &#61;&#61;out)//写入的和读出的一致&#xff0c;表示满

returnbuffer.length;else if (in >out)//写入的大于读出

return in -out;else

//写入的小于读出的

return in &#43; buffer.length -out;

}//关闭当前流&#xff0c;同时释放与其相关的资源

public void close() throwsIOException {//表示由输入流关闭

closedByReader &#61; true;//同步化当前对象&#xff0c;in为-1

synchronized (this) { in &#61; -1; }

}

}



推荐阅读
  • 本题库精选了Java核心知识点的练习题,旨在帮助学习者巩固和检验对Java理论基础的掌握。其中,选择题部分涵盖了访问控制权限等关键概念,例如,Java语言中仅允许子类或同一包内的类访问的访问权限为protected。此外,题库还包括其他重要知识点,如异常处理、多线程、集合框架等,全面覆盖Java编程的核心内容。 ... [详细]
  • 本书详细介绍了在最新Linux 4.0内核环境下进行Java与Linux设备驱动开发的全面指南。内容涵盖设备驱动的基本概念、开发环境的搭建、操作系统对设备驱动的影响以及具体开发步骤和技巧。通过丰富的实例和深入的技术解析,帮助读者掌握设备驱动开发的核心技术和最佳实践。 ... [详细]
  • 本文首先对信息漏洞的基础知识进行了概述,重点介绍了几种常见的信息泄露途径。具体包括目录遍历、PHPINFO信息泄露以及备份文件的不当下载。其中,备份文件下载涉及网站源代码、`.bak`文件、Vim缓存文件和`DS_Store`文件等。目录遍历漏洞的详细分析为后续深入研究奠定了基础。 ... [详细]
  • Ceph API微服务实现RBD块设备的高效创建与安全删除
    本文旨在实现Ceph块存储中RBD块设备的高效创建与安全删除功能。开发环境为CentOS 7,使用 IntelliJ IDEA 进行开发。首先介绍了 librbd 的基本概念及其在 Ceph 中的作用,随后详细描述了项目 Gradle 配置的优化过程,确保了开发环境的稳定性和兼容性。通过这一系列步骤,我们成功实现了 RBD 块设备的快速创建与安全删除,提升了系统的整体性能和可靠性。 ... [详细]
  • 技术日志:深入探讨Spark Streaming与Spark SQL的融合应用
    技术日志:深入探讨Spark Streaming与Spark SQL的融合应用 ... [详细]
  • 本文探讨了在Linux 2.6内核中实现进程隐藏的技术方法与实践。通过分析系统调用 `sys_getdents` 的工作原理,提出了一种有效的方法来隐藏指定的进程。该方法通过对内核模块进行修改,拦截并过滤掉目标进程的相关信息,从而在常用的进程查看命令(如 `ps` 和 `top`)中无法显示这些隐藏的进程。实验结果表明,该方法在实际应用中具有较高的隐蔽性和稳定性。 ... [详细]
  • 在Unity3D的第13天学习中,我们深入探讨了关节系统和布料模拟技术。关节系统作为Unity中的关键物理组件,能够实现游戏对象间的动态连接,如刚体间的关系、门的开合动作以及角色的布娃娃效果。铰链关节涉及两个刚体的交互,能够精确模拟复杂的机械运动,为游戏增添了真实感。此外,布料模拟技术则进一步提升了角色衣物和环境装饰物的自然表现,增强了视觉效果的真实性和沉浸感。 ... [详细]
  • 在VC环境中,掌握高效的调试技巧和高级应用对于提高开发效率至关重要。本文详细介绍了如何通过检查程序中的括号匹配来避免常见的语法错误。具体操作包括将光标置于待检测的括号(如大括号 {}、方括号 [] 和圆括号 ())上,系统会自动高亮显示对应的配对括号,从而帮助开发者快速定位和修复问题。此外,文章还探讨了其他实用的调试工具和方法,如断点设置、变量监视和调用堆栈分析,以全面提升代码调试的准确性和效率。 ... [详细]
  • 本文介绍了Android动画的基本概念及其主要类型。Android动画主要包括三种形式:视图动画(也称为补间动画或Tween动画),主要通过改变视图的属性来实现动态效果;帧动画,通过顺序播放一系列预定义的图像来模拟动画效果;以及属性动画,通过对对象的属性进行平滑过渡来创建更加复杂的动画效果。每种类型的动画都有其独特的应用场景和实现方式,开发者可以根据具体需求选择合适的动画类型。 ... [详细]
  • 本文详细探讨了Java集合框架的使用方法及其性能特点。首先,通过关系图展示了集合接口之间的层次结构,如`Collection`接口作为对象集合的基础,其下分为`List`、`Set`和`Queue`等子接口。其中,`List`接口支持按插入顺序保存元素且允许重复,而`Set`接口则确保元素唯一性。此外,文章还深入分析了不同集合类在实际应用中的性能表现,为开发者选择合适的集合类型提供了参考依据。 ... [详细]
  • 深入解析JWT的实现与应用
    本文深入探讨了JSON Web Token (JWT) 的实现机制及其应用场景。JWT 是一种基于 RFC 7519 标准的开放性认证协议,用于在各方之间安全地传输信息。文章详细分析了 JWT 的结构、生成和验证过程,并讨论了其在现代 Web 应用中的实际应用案例,为开发者提供了全面的理解和实践指导。 ... [详细]
  • 2019年后蚂蚁集团与拼多多面试经验详述与深度剖析
    2019年后蚂蚁集团与拼多多面试经验详述与深度剖析 ... [详细]
  • MongoDB Aggregates.group() 方法详解与编程实例 ... [详细]
  • 在使用sbt构建项目时,遇到了“对象apache不是org软件包的成员”的错误。本文详细分析了该问题的原因,并提供了有效的解决方案,包括检查依赖配置、清理缓存和更新sbt插件等步骤,帮助开发者快速解决问题。 ... [详细]
  • 深入解析 Spring MVC 的核心原理与应用实践
    本文将详细探讨Spring MVC的核心原理及其实际应用,首先从配置web.xml文件入手,解析其在初始化过程中的关键作用,接着深入分析请求处理流程,包括控制器、视图解析器等组件的工作机制,并结合具体案例,展示如何高效利用Spring MVC进行开发,为读者提供全面的技术指导。 ... [详细]
author-avatar
泉州联合网2534
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有