java IO之输入流——InputStream

时间:2023-02-24 21:33:34

java的基本输入流是java.io.InputStream,该抽象类定义了输入流的基本输入操作方法,实现自该抽象类的子类都有定义自己的数据源,例如ByteArrayInputStream的构造函数指定了ByteArrayInputStream输入流的数据源必须是一个字符数组。这就可以有多种不同的数据源,包括:字符数组、String对象、文件、“管道”、一个由其他种类的流组成的序列...

1 public ByteArrayInputStream(byte buf[]) {}
2
3 public ByteArrayInputStream(byte buf[], int offset, int length) {}

 InputStream子类的继承结构如下,这里列举部分子类,还有很多未能列举:

java IO之输入流——InputStream

下面对这些不同的输入流稍加分析

1、ByteArrayInputStream,它包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。 下面是它的源码:

java IO之输入流——InputStreamjava IO之输入流——InputStream
 1 public class ByteArrayInputStream extends InputStream {
2 protected byte buf[];
3 protected int pos;
4 protected int mark = 0;
5 protected int count;
6 public ByteArrayInputStream(byte buf[]) {
7 this.buf = buf;
8 this.pos = 0;
9 this.count = buf.length;
10 }
11 public ByteArrayInputStream(byte buf[], int offset, int length) {
12 this.buf = buf;
13 this.pos = offset;
14 this.count = Math.min(offset + length, buf.length);
15 this.mark = offset;
16 }
17 public synchronized int read() {
18 return (pos < count) ? (buf[pos++] & 0xff) : -1;
19 }
20 public synchronized int read(byte b[], int off, int len) {
21 if (b == null) {
22 throw new NullPointerException();
23 } else if (off < 0 || len < 0 || len > b.length - off) {
24 throw new IndexOutOfBoundsException();
25 }
26
27 if (pos >= count) {
28 return -1;
29 }
30
31 int avail = count - pos;
32 if (len > avail) {
33 len = avail;
34 }
35 if (len <= 0) {
36 return 0;
37 }
38 System.arraycopy(buf, pos, b, off, len);
39 pos += len;
40 return len;
41 }
42 public synchronized long skip(long n) {
43 long k = count - pos;
44 if (n < k) {
45 k = n < 0 ? 0 : n;
46 }
47 pos += k;
48 return k;
49 }
50 public synchronized int available() {
51 return count - pos;
52 }
53 public boolean markSupported() {
54 return true;
55 }
56 public void mark(int readAheadLimit) {
57 mark = pos;
58 }
59 public synchronized void reset() {
60 pos = mark;
61 }
62 public void close() throws IOException {
63 }
64 }
View Code

从源码可以看出,该输入流是一个同步安全的输入流,下面是它的测试代码:

java IO之输入流——InputStreamjava IO之输入流——InputStream
 1 static void byteArrayTest(){
2 String alph="abcddefghijklmnopqrsttuvwxyz";
3 byte[] cha=alph.getBytes();
4 ByteArrayInputStream bais = new ByteArrayInputStream(cha);
5 if (!bais.markSupported()) {
6 System.out.println("make not supported!");
7 return ;
8 }
9 int i=0;
10 while(bais.available()>0){
11 System.out.print((char)bais.read()+" ");
12 i++;
13 }
14 System.out.println("\n"+i);
15 }
View Code

由于该输入流的数据源是一个字节数组,所以要把字符串转换成字符数组。

2、FileInputStream,文件输入流,用于从一个文件中读取字节,它主要用于读取原始字节流,如图片等,如果读取字符流,需要用FileReader.下面是它的源码:

java IO之输入流——InputStreamjava IO之输入流——InputStream
 1 public class FileInputStream extends InputStream
2 {
3 private final FileDescriptor fd;
4 private final String path;
5 private FileChannel channel = null;
6 private final Object closeLock = new Object();
7 private volatile boolean closed = false;
8 public FileInputStream(String name) throws FileNotFoundException {
9 this(name != null ? new File(name) : null);
10 }
11 public FileInputStream(File file) throws FileNotFoundException {
12 String name = (file != null ? file.getPath() : null);
13 SecurityManager security = System.getSecurityManager();
14 if (security != null) {
15 security.checkRead(name);
16 }
17 if (name == null) {
18 throw new NullPointerException();
19 }
20 if (file.isInvalid()) {
21 throw new FileNotFoundException("Invalid file path");
22 }
23 fd = new FileDescriptor();
24 fd.attach(this);
25 path = name;
26 open(name);
27 }
28 public FileInputStream(FileDescriptor fdObj) {
29 SecurityManager security = System.getSecurityManager();
30 if (fdObj == null) {
31 throw new NullPointerException();
32 }
33 if (security != null) {
34 security.checkRead(fdObj);
35 }
36 fd = fdObj;
37 path = null;
38 fd.attach(this);
39 }
40 private native void open0(String name) throws FileNotFoundException;
41 private void open(String name) throws FileNotFoundException {
42 open0(name);
43 }
44 public int read() throws IOException {
45 return read0();
46 }
47 private native int read0() throws IOException;
48 private native int readBytes(byte b[], int off, int len) throws IOException;
49 public int read(byte b[]) throws IOException {
50 return readBytes(b, 0, b.length);
51 }
52 public int read(byte b[], int off, int len) throws IOException {
53 return readBytes(b, off, len);
54 }
55 public native long skip(long n) throws IOException;
56 public native int available() throws IOException;
57 public void close() throws IOException {
58 synchronized (closeLock) {
59 if (closed) {
60 return;
61 }
62 closed = true;
63 }
64 if (channel != null) {
65 channel.close();
66 }
67
68 fd.closeAll(new Closeable() {
69 public void close() throws IOException {
70 close0();
71 }
72 });
73 }
74 public final FileDescriptor getFD() throws IOException {
75 if (fd != null) {
76 return fd;
77 }
78 throw new IOException();
79 }
80 public FileChannel getChannel() {
81 synchronized (this) {
82 if (channel == null) {
83 channel = FileChannelImpl.open(fd, path, true, false, this);
84 }
85 return channel;
86 }
87 }
88 private static native void initIDs();
89 private native void close0() throws IOException;
90 static {
91 initIDs();
92 }
93 protected void finalize() throws IOException {
94 if ((fd != null) && (fd != FileDescriptor.in)) {
95 close();
96 }
97 }
98 }
View Code

该输入流的数据源是一个文件,从源代码中可以看出,它使用了一个native方法,该方法的底层实现是C代码。下面是文件输入流的一个例子:

java IO之输入流——InputStreamjava IO之输入流——InputStream
 1 static void fileInputTest(){//读取一个图片的字节信息
2 try{
3 FileInputStream fis=new FileInputStream("a.PNG");
4 try{
5 byte[] bf=new byte[1024];
6 while(fis.read(bf)>0){
7 for(byte by:bf){
8 System.out.print(by+" ");
9 }
10 System.out.println();
11 }
12 }catch(IOException e){
13 e.printStackTrace();
14 }
15 }catch(Exception e){
16 e.printStackTrace();
17 }
18 }
View Code

这里读取一张照片,然后将读取出的字节输出来。

3、FilterInputStream,它是用来提供装饰器接口,用以控制特定的输入流,在装饰模式中,装饰器能够动态地给一个对象添加额外的职责,FilterInputStream的构造函数是Protected类型的,这说明,它只想让它的子类以及那些和它在同一个包下的类访问它,我们是不能够直接创建它的对象的,这里用该类的一个子类BufferedInputStream为例进行分析,以下是BufferedInputStream的源代码:

java IO之输入流——InputStreamjava IO之输入流——InputStream
  1 public class BufferedInputStream extends FilterInputStream {
2 private static int DEFAULT_BUFFER_SIZE = 8192;
3 private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
4 protected volatile byte buf[];
5 private static final
6 AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
7 AtomicReferenceFieldUpdater.newUpdater
8 (BufferedInputStream.class, byte[].class, "buf");
9 protected int count;
10 protected int pos;
11 protected int markpos = -1;
12 protected int marklimit;
13 private InputStream getInIfOpen() throws IOException {
14 InputStream input = in;
15 if (input == null)
16 throw new IOException("Stream closed");
17 return input;
18 }
19 private byte[] getBufIfOpen() throws IOException {
20 byte[] buffer = buf;
21 if (buffer == null)
22 throw new IOException("Stream closed");
23 return buffer;
24 }
25 public BufferedInputStream(InputStream in) {
26 this(in, DEFAULT_BUFFER_SIZE);
27 }
28 public BufferedInputStream(InputStream in, int size) {
29 super(in);
30 if (size <= 0) {
31 throw new IllegalArgumentException("Buffer size <= 0");
32 }
33 buf = new byte[size];
34 }
35 private void fill() throws IOException {
36 byte[] buffer = getBufIfOpen();
37 if (markpos < 0)
38 pos = 0; /* no mark: throw away the buffer */
39 else if (pos >= buffer.length) /* no room left in buffer */
40 if (markpos > 0) { /* can throw away early part of the buffer */
41 int sz = pos - markpos;
42 System.arraycopy(buffer, markpos, buffer, 0, sz);
43 pos = sz;
44 markpos = 0;
45 } else if (buffer.length >= marklimit) {
46 markpos = -1; /* buffer got too big, invalidate mark */
47 pos = 0; /* drop buffer contents */
48 } else if (buffer.length >= MAX_BUFFER_SIZE) {
49 throw new OutOfMemoryError("Required array size too large");
50 } else { /* grow buffer */
51 int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
52 pos * 2 : MAX_BUFFER_SIZE;
53 if (nsz > marklimit)
54 nsz = marklimit;
55 byte nbuf[] = new byte[nsz];
56 System.arraycopy(buffer, 0, nbuf, 0, pos);
57 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
58 throw new IOException("Stream closed");
59 }
60 buffer = nbuf;
61 }
62 count = pos;
63 int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
64 if (n > 0)
65 count = n + pos;
66 }
67 public synchronized int read() throws IOException {
68 if (pos >= count) {
69 fill();
70 if (pos >= count)
71 return -1;
72 }
73 return getBufIfOpen()[pos++] & 0xff;
74 }
75 private int read1(byte[] b, int off, int len) throws IOException {
76 int avail = count - pos;
77 if (avail <= 0) {
78 /* If the requested length is at least as large as the buffer, and
79 if there is no mark/reset activity, do not bother to copy the
80 bytes into the local buffer. In this way buffered streams will
81 cascade harmlessly. */
82 if (len >= getBufIfOpen().length && markpos < 0) {
83 return getInIfOpen().read(b, off, len);
84 }
85 fill();
86 avail = count - pos;
87 if (avail <= 0) return -1;
88 }
89 int cnt = (avail < len) ? avail : len;
90 System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
91 pos += cnt;
92 return cnt;
93 }
94 public synchronized int read(byte b[], int off, int len)
95 throws IOException
96 {
97 getBufIfOpen(); // Check for closed stream
98 if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
99 throw new IndexOutOfBoundsException();
100 } else if (len == 0) {
101 return 0;
102 }
103
104 int n = 0;
105 for (;;) {
106 int nread = read1(b, off + n, len - n);
107 if (nread <= 0)
108 return (n == 0) ? nread : n;
109 n += nread;
110 if (n >= len)
111 return n;
112 // if not closed but no bytes available, return
113 InputStream input = in;
114 if (input != null && input.available() <= 0)
115 return n;
116 }
117 }
118 public synchronized long skip(long n) throws IOException {
119 getBufIfOpen(); // Check for closed stream
120 if (n <= 0) {
121 return 0;
122 }
123 long avail = count - pos;
124
125 if (avail <= 0) {
126 // If no mark position set then don't keep in buffer
127 if (markpos <0)
128 return getInIfOpen().skip(n);
129
130 // Fill in buffer to save bytes for reset
131 fill();
132 avail = count - pos;
133 if (avail <= 0)
134 return 0;
135 }
136
137 long skipped = (avail < n) ? avail : n;
138 pos += skipped;
139 return skipped;
140 }
141 public synchronized int available() throws IOException {
142 int n = count - pos;
143 int avail = getInIfOpen().available();
144 return n > (Integer.MAX_VALUE - avail)
145 ? Integer.MAX_VALUE
146 : n + avail;
147 }
148 public synchronized void mark(int readlimit) {
149 marklimit = readlimit;
150 markpos = pos;
151 }
152 public synchronized void reset() throws IOException {
153 getBufIfOpen(); // Cause exception if closed
154 if (markpos < 0)
155 throw new IOException("Resetting to invalid mark");
156 pos = markpos;
157 }
158 public boolean markSupported() {
159 return true;
160 }
161 public void close() throws IOException {
162 byte[] buffer;
163 while ( (buffer = buf) != null) {
164 if (bufUpdater.compareAndSet(this, buffer, null)) {
165 InputStream input = in;
166 in = null;
167 if (input != null)
168 input.close();
169 return;
170 }
171 }
172 }
173 }
View Code

注意,这里BufferedInputStream的构造函数的参数是InputStream的对象,这说明BufferedInputStream的数据来源是一个由InputStream类型的流组成的序列,可以是FileInputStream,这样在读文件的时候,就具备了缓冲能力,这就是“装饰”模式带来的效果,在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。 用法如下面代码所示:

java IO之输入流——InputStreamjava IO之输入流——InputStream
 1     static void bufferedInputTest(){
2 try{//BufferedInputStream 测试
3 FileInputStream fis=new FileInputStream("a.PNG");
4 BufferedInputStream buf=new BufferedInputStream(fis);
5 try{
6 byte[] bf=new byte[1024];
7 while(buf.read(bf)>0){
8 for(byte by:bf){
9 System.out.print(by+" ");
10 }
11 System.out.println();
12 }
13 }catch(IOException e){
14 e.printStackTrace();
15 }
16 }catch(Exception e){
17 e.printStackTrace();
18 }
19 }
View Code

现仅给出以上三个示例,其它类的使用方法可参考java api文档。向着明天,加油!