Java_io体系之BufferedInputStream、BufferedOutputStream简介、走进源码及示例——10
Java_io体系之BufferedInputStream、BufferedOutputStream简介、走进源码及示例——10
一:BufferedInputStream
1、类功能简介:
缓冲字节输入流、作为FilterInputStream的一个子类、他所提供的功能是为传入的底层字节输入流提供缓冲功能、他会通过底层字节输入流(in)中的字节读取到自己的buffer中(内置缓存字节数组)、然后程序调用BufferedInputStream的read方法将buffer中的字节读取到程序中、当buffer中的字节被读取完之后、BufferedInputStream会从in中读取下一个数据块到buffer中供程序读取、直到in中数据被读取完毕、这样做的好处一是提高了读取的效率、二是减少了打开存储介质的连接次数、详细的原因下面BufferedOutputStream有说到。其有个关键的方法fill()就是每当buffer中数据被读取完之后从in中将数据填充到buffer中、程序从内存中读取数据的速度是从磁盘中读取的十倍!这是一个很恐怖的效率的提升、同时我们也不能无禁止的指定BufferedInputStream的buffer大小、毕竟、一次性读取in中耗时较长、二是内存价格相对昂贵、我们能做的就是尽量在其中找到合理点。一般也不用我们费这个心、创建BufferedInputStream时使用buffer的默认大小就好。
2、BufferedInputStream API简介:
A:关键字段
private static int defaultBufferSize = 8192;//内置缓存字节数组的大小 protected volatile byte buf[]; 内置缓存字节数组 protected int count; 当前buf中的字节总数、注意不是底层字节输入流的源中字节总数 protected int pos; 当前buf中下一个被读取的字节下标 protected int markpos = -1; 最后一次调用mark(int readLimit)方法记录的buf中下一个被读取的字节的位置 protected int marklimit; 调用mark后、在后续调用reset()方法失败之前云寻的从in中读取的最大数据量、用于限制被标记后buffer的最大值
B:构造方法
BufferedInputStream (InputStream in); 使用默认buf大小、底层字节输入流构建bis BufferedInputStream (InputStream in, int size); 使用指定buf大小、底层字节输入流构建bis
C:一般方法
int available(); 返回底层流对应的源中有效可供读取的字节数 void close(); 关闭此流、释放与此流有关的所有资源 boolean markSupport(); 查看此流是否支持mark、此方法一直返回true void mark(int readLimit); 标记当前buf中读取下一个字节的下标 int read(); 读取buf中下一个字节 int read(byte[] b, int off, int len); 读取buf中下一个字节 void reset(); 重置最后一次调用mark标记的buf中的位子 long skip(long n); 跳过n个字节、 不仅仅是buf中的有效字节、也包括in的源中的字节
3、源码分析:
package com.chy.io.original.code; import java.io.IOException; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; /** * BufferedInputStream 缓冲字节输入流、作为FilterInputStream的一个实现类、为子类添加缓冲功能、自带一个缓冲数组、 * 是为传入的InputStream的实现类(简称in)提供缓冲读取功能、他的原理是从in中一次性读取一个数据块放入自带的缓冲数组中、 * 当这个缓冲数组中的数据被读取完时、BufferedInputStream再从in中读取一个数据块、这样循环读取。 * 缓存数组是放在内存中的、当程序从buf中读取字节的时候、相当于从内存中读取、从内存中读取的效率至少是从磁盘等存储介质的十倍以上! * 这里会有人想干嘛不一次性读取所有in中的字节放入内存中? * 1)读取全部字节可能耗费的时间较长、 * 2)内存有限、不想磁盘等存储介质存储空间那么大、价格也相对昂贵的多! * */ public class BufferedInputStream extends FilterInputStream { // BufferedInputStream自带的数组大小 private static int defaultBufferSize = 8192; /** *自带数组、如果在根据传入的in构造BufferedInputStream没有传入时、其大小为8192、 *如果传入则使用传入的大小。 */ protected volatile byte buf[]; /** * 缓存数组的原子更新器。 * 该成员变量与buf数组的volatile关键字共同组成了buf数组的原子更新功能实现, * 即,在多线程中操作BufferedInputStream对象时,buf和bufUpdater都具有原子性(不同的线程访问到的数据都是相同的) */ private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, "buf"); /** *当前缓冲字节数组中的有效可供读取的字节总数。 *注意这里是缓冲区、即缓冲数组buf中的有效字节、而不是in中有效的字节 */ protected int count; /** * 用于标记buf中下一个被读取的字节的下标、即下一个被读取的字节是buf[pos]、pos取值范围一般是 0 到count、 * 如果pos = count则说明这个buf中的字节被读取完、那么需要从in中读取下一个数据块用于读取。 * 同样:这里指的是缓冲字节数组中字节的下标、而不是in中的字节的下标 */ protected int pos; /** * 当前缓冲区的标记位置、 * mark() reset()结合使用步骤(单独使用没有意义) * 1)调用mark()、将标记当前buf中下一个要被读取的字节的索引 pos保存到 markpos中 * 2)调用reset()、将markpos的值赋给pos、这样再次调用read()的时候就会从最后一次调用mark方法的位置继续读取。 */ protected int markpos = -1; /** * 调用mark方法后、在后续调用reset()方法失败之前所允许的最大提前读取量。 */ protected int marklimit; /** * 检测传入的基础流in是否关闭。若没有关闭则返回此流。 */ private InputStream getInIfOpen() throws IOException { InputStream input = in; if (input == null){ throw new IOException("Stream closed"); } return input; } /** *检测BufferedInputStream是否关闭、如果没有关闭则返回其自带缓存数组buf。 */ private byte[] getBufIfOpen() throws IOException { byte[] buffer = buf; if (buffer == null) throw new IOException("Stream closed"); return buffer; } /** * 使用BufferedInputStream默认的缓冲字节数组大小及传入的基础输入字节流in创建BufferedInputStream。 */ public BufferedInputStream(InputStream in) { this(in, defaultBufferSize); } /** * 使用传入的缓冲字节数组大小及传入的基础输入字节流in创建BufferedInputStream。 */ public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } /** * 从输入流in中获取字节、填充到缓冲字节数组中。这里由于时间关系没有过于纠结内部实现、有兴趣的可以自己研究下 */ private void fill() throws IOException { byte[] buffer = getBufIfOpen(); if (markpos < 0){ pos = 0; /* no mark: throw away the buffer */ }else if (pos >= buffer.length){ /* no room left in buffer */ if (markpos > 0) { /* can throw away early part of the buffer */ int sz = pos - markpos; System.arraycopy(buffer, markpos, buffer, 0, sz); pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { markpos = -1; /* buffer got too big, invalidate mark */ pos = 0; /* drop buffer contents */ } else { /* grow buffer */ int nsz = pos * 2; if (nsz > marklimit){ nsz = marklimit; } byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { // Can't replace buf if there was an async close. // Note: This would need to be changed if fill() // is ever made accessible to multiple threads. // But for now, the only way CAS can fail is via close. // assert buf == null; throw new IOException("Stream closed"); } buffer = nbuf; } } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0){ //根据从输入流中读取的实际数据的多少,来更新buffer中数据的实际大小 count = n + pos; } } /** * 读取下一个字节、并以整数形式返回。 */ public synchronized int read() throws IOException { if (pos >= count) { // 若已经读完缓冲区中的数据,则调用fill()从输入流读取下一部分数据来填充缓冲区 fill(); //读取完in最后一个字节。 if (pos >= count) return -1; } return getBufIfOpen()[pos++] & 0xff; } /** * 将缓存字节数组中的字节写入到从下标off开始、长度为len的byte[]b 中。 */ private int read1(byte[] b, int off, int len) throws IOException { int avail = count - pos; if (avail <= 0) { /** * 如果传入的byte[]的长度len大于buf的size、并且没有markpos、就直接从原始流中读取len个字节放入byte[]b中 * 避免不必要的copy:从原始流中读取buf.length个放入buf中、再将buf中的所有字节放入byte[]b 中 * 再清空buf、再读取buf.length个放入buf中 、还要放入byte[]b中直到 len个。 */ if (len >= getBufIfOpen().length && markpos < 0) { return getInIfOpen().read(b, off, len); } //若缓冲区数据被读取完、则调用fill()填充buf。 fill(); avail = count - pos; if (avail <= 0) return -1; } int cnt = (avail < len) ? avail : len; System.arraycopy(getBufIfOpen(), pos, b, off, cnt); pos += cnt; return cnt; } /** * 将buf中的字节读取到下标从off开始、len长的byte[]b 中 */ public synchronized int read(byte b[], int off, int len) throws IOException { getBufIfOpen(); // Check for closed stream if ((off | len | (off + len) | (b.length - (off + len))) < 0) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } /** * 不断的读取字节、放入b中、有以下几种情况 * 1)buf中有len个可供读取、则将len个字节copy到b中 * 2)buf中不足len个、从源输入流中读取下一个数据块 * a)源输入流中有可供读取的有效的、读取并填充buf、继续填充到b中 * b)读到结尾、依然不够、将所有读取的填充到b中 */ int n = 0; for (;;) { int nread = read1(b, off + n, len - n); if (nread <= 0) return (n == 0) ? nread : n; n += nread; if (n >= len) return n; // if not closed but no bytes available, return InputStream input = in; if (input != null && input.available() <= 0) return n; } } /** * 跳过n个字节、返回实际跳过的字节数。 */ public synchronized long skip(long n) throws IOException { getBufIfOpen(); // Check for closed stream if (n <= 0) { return 0; } long avail = count - pos; if (avail <= 0) { // If no mark position set then don't keep in buffer if (markpos <0) return getInIfOpen().skip(n); // Fill in buffer to save bytes for reset fill(); avail = count - pos; if (avail <= 0) return 0; } long skipped = (avail < n) ? avail : n; pos += skipped; return skipped; } /** * 返回in中有效可供被读取的字节数。从这里也可以看出、有效字节是in中现有有效字节与buf中剩余字节的和。 * 即buf中是从in中读取的一个供程序读取的数据块。 */ public synchronized int available() throws IOException { return getInIfOpen().available() + (count - pos); } /** * @param readlimit 在mark方法方法失效前最大允许读取量 */ public synchronized void mark(int readlimit) { marklimit = readlimit; markpos = pos; } /** * 将最后一次调用mark标记的位置传递给pos、使得此流可以继续接着最后一次mark的地方读取。 * 如果没有调用mark、或者mark失效则抛出IOException。 */ public synchronized void reset() throws IOException { getBufIfOpen(); // Cause exception if closed if (markpos < 0) throw new IOException("Resetting to invalid mark"); pos = markpos; } /** * 查看此流是否支持markSupport * @return true 此流支持mark。 */ public boolean markSupported() { return true; } /** * 关闭此流并释放所有资源。 */ public void close() throws IOException { byte[] buffer; while ( (buffer = buf) != null) { if (bufUpdater.compareAndSet(this, buffer, null)) { InputStream input = in; in = null; if (input != null) input.close(); return; } } } }
4、实例演示:
二:BufferedOutputStream
1、类功能简介:
BufferedOutputStream、缓存字节输出流、作为FilterInputStream的一个子类、他的功能是为传入的底层字节输出流提供缓存功能、同样当使用底层字节输出流向目的地中写入字节或者字节数组时、没写入一次就要打开一次到目的地的连接、这样频繁的访问不断效率底下、也有可能会对存储介质造成一定的破坏、比如当我们向磁盘中不断的写入字节时、夸张一点、将一个非常大单位是G的字节数据写入到磁盘的指定文件中的、没写入一个字节就要打开一次到这个磁盘的通道、这个结果无疑是恐怖的、而当我们使用BufferedOutputStream将底层字节输出流、比如FileInputStream包装一下之后、我们可以在程序中先将要写入到文件中的字节写入到BufferedOutputStream的内置缓存空间中、然后当达到一定数量时、一次性写入FileInputStream流中、此时、FileInputStream就可以打开一次通道、将这个数据块写入到文件中、这样做虽然不可能达到一次访问就将所有数据写入磁盘中的效果、但也大大提高了效率和减少了磁盘的访问量!这就是其意义所在、
他的具体工作原理在这里简单提一下:这里可能说的比较乱、具体可以看源码、不懂再回头看看这里、当程序中每次将字节或者字节数组写入到BufferedOutputStream中时、都会检查BufferedOutputStream中的缓存字节数组buf(buf的大小是默认的或者在创建bos时指定的、一般使用默认的就好)是否存满、如果没有存满则将字节写入到buf中、如果存满、则调用底层的writer(byte[] b, int off, int len)将buf中的所有字节一次性写入到底层out中、如果写入的是字节数组、如果buf中已满则同上面满的时候的处理、如果能够存下写入的字节数组、则存入buf中、如果存不下、并且要写入buf的字节个数小于buf的长度、则将buf中所有字节写入到out中、然后将要写入的字节存放到buf中(从下标0开始存放)、如果要写入out中的字节超过buf的长度、则直接写入out中、
2、BufferedOutputStream API简介:
A:关键字段
protected byte[] buf; 内置缓存字节数组、用于存放程序要写入out的字节 protected int count; 内置缓存字节数组中现有字节总数
B:构造方法
BufferedOutputStream(OutputStream out); 使用默认大小、底层字节输出流构造bos BufferedOutputStream(OutputStream out, int size); 使用指定大小、底层字节输出流构造bos
C:一般方法
//在这里提一句、BufferedOutputStream没有自己的close方法、当他调用父类FilterOutputStrem的方法关闭时、会间接调用自己实现的flush方法将buf中残存的字节flush到out中、再out.flush()到目的地中、DataOutputStream也是如此、 void flush(); 将写入bos中的数据flush到out指定的目的地中、注意这里不是flush到out中、因为其内部又调用了out.flush() write(byte b); 将一个字节写入到buf中 write(byte[] b, int off, int len); 将b的一部分写入buf中
3、源码分析:
package com.chy.io.original.code; import java.io.IOException; /** * 为传入的基础输出流(以下简称 out)提供缓冲功能、先将要写入out的数据写入到BufferedOutputStream缓冲数组buf中、 * 当调用bufferedOutputStream的flush()时、或者调用flushBuffer()时一次性的将数据使用out.write(byte[]b , 0, count)写入out指定的目的地中、 * 避免out.write(byte b)方法每写一个字节就访问一次目的地。 * */ public class BufferedOutputStream extends FilterOutputStream { /** * BufferedOutputStream内置的缓存数组、当out进行写入时、BufferedOutputStream会先将out写入的数据 * 放到buf中、 * 注意:不一定buf[]被写满之后才会写入out中。 */ protected byte buf[]; /** * 缓存数组中现有的可供写入的有效字节数、 * 注意:这里是buf中的、而不是out中的。 */ protected int count; /** * 使用BufferedOutputStream默认的缓存数组大小与基础输出流out创建BufferedOutputStream。 */ public BufferedOutputStream(OutputStream out) { this(out, 8192); } /** * 使用指定的的缓存数组大小与基础输出流out创建BufferedOutputStream。 */ public BufferedOutputStream(OutputStream out, int size) { super(out); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } /** 如果buf中有的数据、则立刻将buf中所有数据写入目的地中。 并且将count重置为0。*/ private void flushBuffer() throws IOException { if (count > 0) { out.write(buf, 0, count); count = 0; } } /** * 将要写入out中的数据先写入到buf中。 */ public synchronized void write(int b) throws IOException { if (count >= buf.length) { flushBuffer(); } buf[count++] = (byte)b; } /** * 将从下标off开始、长度为len个字节的byte[]b写入buf中、 */ public synchronized void write(byte b[], int off, int len) throws IOException { if (len >= buf.length) { /* 如果写入的字节个数超过buf的长度、 * 那么直接调用out.write(byte b[] , int off, int len)避免重复操作、即读到buf中但buf立马装满 * 此时将buf写入out中又将剩下的填充到buf中。 */ flushBuffer(); out.write(b, off, len); return; } //如果写入的个数大于buf中现有剩余空间、则将buf中的现有count个字节写入out中、注意:flushBuffer之后、count变成了0。 //再将len个字节写入从count下标开始长度为len的buf中。 //同时将count 变成 count +=len. if (len > buf.length - count) { flushBuffer(); } System.arraycopy(b, off, buf, count, len); count += len; } /** * 1)将buf中现有字节写入out中 * 2)将out中的现有字节flush到目的地中。 */ public synchronized void flush() throws IOException { flushBuffer(); out.flush(); } }
4、实例演示:
package com.chy.io.original.test; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class BufferedStreamTest { private static final byte[] byteArray = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A }; public static void testBufferedOutputStream() throws IOException{ BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("D:\\bos.txt"))); bos.write(byteArray[0]); bos.write(byteArray, 1, byteArray.length-1); //bos.write(byteArray); 注意:这个方法不是BufferedOutputStream的方法、此方法是调用FilterOutputStream的write(byte[] b) //write(byte[] b)调用本身的write(byte[]b , 0, b.length)方法、而此方法的本质是循环调用传入的out的out.write(byte b)方法. bos.flush(); } public static void testBufferedInpuStream() throws IOException{ BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("D:\\bos.txt"))); for(int i=0; i<10; i++){ if(bis.available() >=0){ System.out.println(byteToString((byte)bis.read())); } } if(!bis.markSupported()){ return; } bis.mark(6666);//标记"当前索引位子" 即第11个字节 k bis.skip(10);//丢弃10个字节。 //读取剩下的b.length个字节 byte[] b = new byte[1024]; int n1 = bis.read(b, 0, b.length); System.out.println("剩余有效字节 : " +n1); printByteValue(b); bis.reset();//重置输入流最后一次调用mark()标记的索引位置 int n2 = bis.read(b, 0, b.length); System.out.println("剩余有效字节 : " +n2); printByteValue(b); } private static void printByteValue(byte[] buf) { for(byte b : buf){ if(b != 0){ System.out.print(byteToString(b) + " "); } } } private static String byteToString(byte b){ byte[] bAray = {b}; return new String (bAray); } public static void main(String[] args) throws IOException { testBufferedOutputStream(); testBufferedInpuStream(); } }
总结:
对于BufferedInputStream、BufferedOutputStream、本质就是为底层字节输入输出流添加缓冲功能、先将底层流中的要读取或者要写入的数据先以一次读取一组的形式来讲数据读取或者写入到buffer中、再对buffer进行操作、这样不但效率、还能节省资源。最后、在程序中、出于效率的考虑、也应为低级流使用这两个类进行装饰一下、而不是直接拿着流直接上、觉得能实现就行。这两个流说的有些粗糙、有时间回头修理、不足之处请见谅、、、、
相关推荐
本资源包“Java_se_io学习 Java学习资料源码”包含了深入学习Java I/O系统的相关资料和源码,对于Java开发者来说,这是一个非常宝贵的参考资料。 I/O流是Java中用于读写数据的对象,它们分为字节流和字符流两大类。...
这篇博客“Java学习之IO总结及mina和netty”探讨了Java IO的基础知识,并深入到两个高级网络通信框架——Mina和Netty。Mina和Netty都是基于NIO(非阻塞IO)的高性能网络应用框架,它们简化了复杂网络编程的实现。 *...
在这个`Java_file_send.rar`压缩包中,可能包含了一个简单的Java程序示例,用于演示如何完成这一功能。下面将详细解释这个过程,并涵盖相关的Java知识点。 首先,文件在网络上传输的基本原理是通过TCP/IP协议,将...
这个示例源码提供了关于Java二进制IO类和文件复制操作的实际应用,对于学习和理解这一主题非常有用。通过分析和实践这些代码,开发者可以更好地掌握Java的文件处理能力,为日常开发工作奠定坚实基础。
在Java编程语言中,"javaGetFileDemo_JAVA源码_" 涉及到的核心知识点是网络编程,尤其是从网络下载文件。这是一个常见的任务,特别是在开发Web应用或爬虫程序时。下面将详细介绍如何使用Java实现这个功能,并涵盖...
9. **示例代码**:压缩包中的"codefans.net"可能包含了一个名为CodeFan的网站上的Java源码示例,可能讲解了如何使用以上技术进行文件复制。通过阅读和学习这些示例,你可以更好地理解和应用这些概念。 总结来说,这...
这个"Java从网络取得文件Demo源码.zip"压缩包包含了一个示例项目,展示了如何使用Java实现这一功能。下面我们将深入探讨相关的Java知识点。 1. **URL类**:在Java中,`java.net.URL`类用于表示统一资源定位符,它是...
在Java编程语言中,`java.io`包是处理输入输出操作的核心部分,它包含了大量用于读写数据的类。这个包中的类广泛应用于文件操作、网络通信、标准输入输出流等场景。本篇文章将深入探讨`java.io`包中一些常用的类,并...
这篇博客“java(IO)第二部分”可能深入探讨了Java IO体系结构的高级概念,特别是那些在第一部分没有涵盖的部分。虽然具体的博文内容无法直接提供,但我们可以基于常见的Java IO主题进行详细的解释。 首先,Java ...
同时,理解Java IO的源码有助于深入学习其内部工作机制,为编写更高效、更稳定的代码打下基础。 在博客链接(https://xiayingjie.iteye.com/blog/778564)中,可能包含了关于Java IO的更深入解析、示例代码以及实践...
这个压缩包"基于Java的实例源码-二进制IO类与文件复制操作实例.zip"提供了一些实际的代码示例,帮助开发者了解如何使用Java进行二进制文件的读写和复制操作。在这个实例中,我们将深入探讨Java中的关键二进制IO类...
Java的IO(Input/Output)系统是其核心特性之一,为程序处理数据的输入与输出提供了强大而灵活的工具。本文将深入探讨Java中的输入输出流以及字符集的相关知识点。 一、Java IO流概述 Java的IO流模型是基于管道的...
《疯狂Java讲义》是Java编程领域的一本经典教材,其源码分为多个部分,这里主要探讨的是第六部分,涵盖了第11至13章的内容。这三章涉及了Java高级特性和面向对象编程的深入理解,对于进阶Java开发者来说至关重要。...
标题中的“jsp .java中输入输出流 java IO 下载 流的应用先01 > 02 > 01”指的是一个关于Java编程中输入输出流(IO)在JSP(JavaServer Pages)和Java应用程序中的使用,特别是针对文件下载场景的知识点讲解。...
同时,`下载及使用说明.txt`可能提供了如何运行这些源码的指导,而`更多Java资料学习.url`则可能是一个链接,指向更多关于Java学习的资源。 总的来说,通过学习这个资料包,你将能够掌握Java中字节流与图片转化的...
本篇文章将深入探讨如何使用Java实现带密码的ZIP文件压缩和解压缩功能,结合给定的文件名列表,我们将讲解核心概念、源码分析以及实际应用。 首先,我们需要了解Java中的`java.util.zip`包,它提供了对ZIP和GZIP...
这个“Java从网络上下载数据Java实用源码整理learn”集合提供了一些源码示例,可以帮助开发者理解并实现相关功能。下面将详细探讨相关知识点。 1. **URL与URLConnection** - `java.net.URL` 类是Java提供的用于...
本篇文章将深入探讨如何使用Java来实现ZIP文件的压缩和解压缩功能,以及相关的Java源码实践。 一、Java压缩文件到ZIP 在Java中,我们可以使用`java.util.zip`包中的`ZipOutputStream`类来实现文件或文件夹的压缩。...
`java.io`包提供了各种输入/输出流,如FileInputStream和FileOutputStream用于读写文件,而BufferedInputStream和BufferedOutputStream则提高了流的效率。解压缩源码会用到这些类来读取和写入文件内容。 2. **...
这个压缩包文件“安卓Android源码——(游戏保存Stream).rar”显然是一个关于如何利用Stream来保存和读取游戏数据的示例源码。Stream在Java中是一种处理数据流的方式,它允许程序员以一种声明性方式处理数据,尤其...