一、字节流输入输出:
下面是一个字节流的输入输出案例练习,包括的类有OutputStream(输出流)、BufferedOutputStream(缓冲输出流)、InputStream(输入流)、BufferedInputStream(缓冲输输入流):
public class xxPutStreamTest {
public static void main(String[] args) throws IOException {
String currentPath = "src/main/java/ssl/ioTest/byteStream/document/";
//输出字节流
File forWriteFile = new File(currentPath + "forWrite.txt");
StringBuilder sb = new StringBuilder();
//缓冲区一次读取8192(2的13次方)个字节Byte
for (int i = 0; i < 1368; i++) {
sb.append("write");
}
byte[] forWriteBytes = sb.toString().getBytes();
long outputStreamStart = System.currentTimeMillis();
write(forWriteFile, forWriteBytes);
long outputStreamEnd = System.currentTimeMillis();
System.out.println("普通输出字节流耗时:" + (outputStreamEnd - outputStreamStart) + " ms");
long bufferedOutputStreamStart = System.currentTimeMillis();
bufferedWrite(forWriteFile, forWriteBytes);
long bufferedOutputStreamEnd = System.currentTimeMillis();
System.out.println("缓冲输出字节流耗时:" + (bufferedOutputStreamEnd - bufferedOutputStreamStart) + " ms");
//输入字节流
File forReadFile = new File(currentPath + "forRead.txt");
byte[] forReadBytes = new byte[120000000];
long inputStreamStart = System.currentTimeMillis();
read(forReadFile,forReadBytes);
long inputStreamEnd = System.currentTimeMillis();
System.out.println("普通输入字节流耗时:" + (inputStreamEnd - inputStreamStart) + " ms");
long bufferedInputStreamStart = System.currentTimeMillis();
bufferedRead(forReadFile,forReadBytes);
long bufferedInputStreamEnd = System.currentTimeMillis();
System.out.println("缓冲输入字节流耗时:" + (bufferedInputStreamEnd - bufferedInputStreamStart) + " ms");
}
/**
* 普通输出字节流OutputStream
* @param file 输出文件路径
* @param bytes 要输出的内容
* @throws IOException
*/
public static void write(File file, byte[] bytes) throws IOException {
OutputStream outputStream = new FileOutputStream(file);
outputStream.write(bytes);
outputStream.close();
}
/**
* 缓冲输出字节流BufferedOutputStream
*/
public static void bufferedWrite(File file, byte[] bytes) throws IOException {
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
bufferedOutputStream.write(bytes);
bufferedOutputStream.close();
}
/**
* 普通输入字节流InputStream
* @return resultLength 返回读到的byte数组的长度
*/
public static int read(File file, byte[] bytes) throws IOException {
InputStream inputStream = new FileInputStream(file);
int resultLength = inputStream.read(bytes);
inputStream.close();
return resultLength;
}
/**
* 缓冲输入字节流BufferedInputStream
*/
public static int bufferedRead(File file, byte[] bytes) throws IOException {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
int resultLength = bufferedInputStream.read(bytes);
bufferedInputStream.close();
return resultLength;
}
}
输出结果:
普通输出字节流耗时:7 ms
缓冲输出字节流耗时:2 ms
普通输入字节流耗时:53 ms
缓冲输入字节流耗时:50 ms
经过多次输出,对结果进行分析,使用缓冲输入(出)流一般要比普通输入(出)流所使用的时间更短,即速度更快,但是当输入或者输出字符串的长度过长时,两者之间的速度差异会变小,即缓冲输入(出)流的速度优势会减弱,个人对其原因进行初步分析,下面是BufferedOutPutStream中的其中一个write方法的源码:
public synchronized void write(byte b[], int off, int len) throws IOException {
if (len >= buf.length) {
/* If the request length exceeds the size of the output buffer,
flush the output buffer and then write the data directly.
In this way buffered streams will cascade harmlessly. */
flushBuffer();
out.write(b, off, len);
return;
}
if (len > buf.length - count) {
flushBuffer();
}
System.arraycopy(b, off, buf, count, len);
count += len;
}
方法中注释翻译:如果请求长度超过了输出缓冲区的大小,刷新输出缓冲区,然后直接写入数据。以这种方式缓冲的流将无害地级联。
那么缓冲区的大小到底是多少呢,我们在新建BufferedOutputStream的时候使用的构造函数:
/**
* Creates a new buffered output stream to write data to the
* specified underlying output stream.
*
* @param out the underlying output stream.
*/
public BufferedOutputStream(OutputStream out) {
this(out, 8192);
}
/**
* Creates a new buffered output stream to write data to the
* specified underlying output stream with the specified buffer
* size.
*
* @param out the underlying output stream.
* @param size the buffer size.
* @exception IllegalArgumentException if size <= 0.
*/
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
可以看到在新建的时候就给定了缓冲区的大小(size)为8192,至于为什么是8192(2的13次方)而不是其他长度的原因我并不清楚,只是看到一篇博客说是跟网络请求套接字有关系,但是java本地文件的传输好像用不到网络传输,所以此点暂时存疑。
同样缓冲输入流也是一样的,在下面BufferedInputStream类的源码中同样通过构造方法限制了输入缓冲流的数组的长度为8192:
private static int DEFAULT_BUFFER_SIZE = 8192;
/**
* Creates a <code>BufferedInputStream</code>
* and saves its argument, the input stream
* <code>in</code>, for later use. An internal
* buffer array is created and stored in <code>buf</code>.
*
* @param in the underlying input stream.
*/
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
/**
* Creates a <code>BufferedInputStream</code>
* with the specified buffer size,
* and saves its argument, the input stream
* <code>in</code>, for later use. An internal
* buffer array of length <code>size</code>
* is created and stored in <code>buf</code>.
*
* @param in the underlying input stream.
* @param size the buffer size.
* @exception IllegalArgumentException if {@code size <= 0}.
*/
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
二、字符输入输出流
package ssl.ioTest.characterStream;
import com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBaseIterators;
import java.io.*;
public class Filexx {
public static void main(String[] args) throws IOException {
// 数据准备
String currentPath = "src/main/java/ssl/ioTest/characterStream/document/";
File data = new File(currentPath + "data.txt");
dataReady(data);
File a = new File(currentPath + "a.txt");
File b = new File(currentPath + "b.txt");
File c = new File(currentPath + "c.txt");
long start = System.currentTimeMillis();
copy(data, a);
long end = System.currentTimeMillis();
System.out.println("普通字节流1耗时:" + (end - start) + " ms,文件大小:" + a.length() / 1024 + " kb");
long start2 = System.currentTimeMillis();
copyChars(data, b);
long end2 = System.currentTimeMillis();
System.out.println("普通字节流2耗时:" + (end2 - start2) + " ms,文件大小:" + b.length() / 1024 + " kb");
long start3 = System.currentTimeMillis();
bufferedCopy(data, c);
long end3 = System.currentTimeMillis();
System.out.println("缓冲字节流耗时:" + (end3 - start3) + " ms,文件大小:" + c.length() / 1024 + " kb");
}
// 普通字符流不使用数组
public static void copy(File in, File out) throws IOException {
Reader reader = new FileReader(in);
Writer writer = new FileWriter(out);
int ch = 0;
while ((ch = reader.read()) != -1) {
writer.write((char) ch);
}
reader.close();
writer.close();
}
// 普通字符流使用字符流
public static void copyChars(File in, File out) throws IOException {
Reader reader = new FileReader(in);
Writer writer = new FileWriter(out);
char[] chs = new char[1024];
while ((reader.read(chs)) != -1) {
writer.write(chs);
}
reader.close();
writer.close();
}
// 缓冲字符流
public static void bufferedCopy(File in, File out) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(in));
BufferedWriter bw = new BufferedWriter(new FileWriter(out));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
// 释放资源
bw.close();
br.close();
}
// 数据准备
public static void dataReady(File file) throws IOException {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 600000; i++) {
sb.append("read&write");
}
OutputStream os = new FileOutputStream(file);
os.write(sb.toString().getBytes());
os.close();
System.out.println("数据填充结束");
}
}