前言:
流的浅显总结(总结了常用的流的操作和一些关键点):
之前我对流的概念理解很片面,认为所谓输入输出流,都是面对程序和文件的概念
但是,我发现其实比较片面,而且疏漏点很多,其实也有程序和非本程序范围内的内存之间,最终不是都结束于文件和外存,在对已经加载到了内存中的数据的读和写也占了很大的一部分。
如若不全,更多详细方法:请查找JDK文档。
首先请参考于:
什么是native method:
wikeqi的博客CSDN
http://blog.csdn.net/wike163/article/details/6635321
还有对于Input和Output中的概念,是对于程序来讲的,我们应该从程序的角度来看,我们从外部数据读取到程序中,为input;我们将程序中的数据输出到外部,为output。
InputStream
ByteArrayInputStream
ByteArrayInputStream把字节数组中的字节以流的形式读出
还是相对于程序来讲的: 从 字节数组 à程序(内存中的数据)
ByteArrayOutputStream把内存中的数据读到字节数组中,而ByteArrayInputStream又把字节数组中的字节以流的形式读出,实现了对同一个字节数组的操作.
ByteArrayInputStream相对于ByteArrayOutputStream的(可以将程序中的数据读到自己内置的大小任意的缓冲区中,再写到一个Byte数组中)功能,显得比较鸡肋,在JDK介绍文档中介绍的都比较少(将字节数组中的数据,读到流中的缓冲区中,再反馈给程序)该缓冲区中包含从流中读取的字节。内部计数器跟踪read方法要提供的下一个字节。关闭ByteArrayInputStream无效(ByteArrayOutputStream相同)此类中的方法,在关闭此流之后,仍可以被调用,而不会产生IOException。
public ByteArrayInputStream(byte[] buf)
参数:
buf
- 输入缓冲区。
public ByteArrayInputStream(byte[] buf,
int offset,
int length)
参数:
buf
- 输入缓冲区。
offset
- 缓冲区中要读取的第一个字节的偏移量。
length
- 从缓冲区中读取的最大字节数。
用途:
/*
在网络中读取数据包,用于数据包是定长的,我们可以先分配一个够大的byte数组,例如: byte[] buffer = new byte[1024];
然后再通过网络编程中的操作:
*/
Socket s = …
DataInputStream dis = new DataInputStream(s.getInputStream());
// 先将所有的数据存到buffer中
dis.read(buffer);
// 把刚才的部分视为输入流
ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
DataInputStream dis_2 = new DataInputStream(bais);
现在就可以使用dis_2的各种read方法,读取制定的字节
比如: dis_2.readByte();
and so on
上面的示例两次包转,看起来多次一举,但是中间使用了ByteArrayInputStream的好处是关掉了流依然存在。
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
public class ByteArrayInputStreamDemo {
public static void main(String[] args)throws Exception{
Data data = new Data();
byte[] temp = data.getData();
InputStream inputStream = new ByteArrayInputStream(temp);
byte[] arr =new byte[temp.length];
inputStream.read(arr);
System.out.println(new String(arr));
}
}
class Data{
byte[] data = "abc".getBytes();
public byte[] getData() {
returndata;
}
}
FileInputStream
三个核心方法,也就是Override(重写)了抽象类InputStream的read方法。intread() 方法,即:
1. publicint read() throws IOException
代码实现中很简单,一个try中调用本地native的read0()方法,直接从文件输入流中读取一个字节。IoTrace.fileReadEnd(),字面意思是防止文件没有关闭读的通道,导致读文件失败,一直开着读的通道,会造成内存泄露。
/* 从此输入流中读取一个数据字节 */
public int read() throws IOException {
Object traceContext = IoTrace.fileReadBegin(path);
int b = 0;
try {
b = read0();
} finally {
IoTrace.fileReadEnd(traceContext, b == -1 ? 0 : 1);
}
return b;
}
2. int read(byte b[]) 方法,即
public int read(byte b[]) throws IOException
代码实现也是比较简单的,也是一个try中调用本地native的readBytes()方法,直接从文件输入流中读取最多b.length个字节到byte数组b中。
/* 从此输入流中读取多个字节到byte数组中。 */
public int read(byte b[]) throws IOException {
Object traceContext = IoTrace.fileReadBegin(path);
int bytesRead = 0;
try {
bytesRead = readBytes(b, 0, b.length);
} finally {
IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead);
}
return bytesRead;
}
3. int read(byte b[], int off, int len) 方法,即
public int read(byte b[], int off, int len) throwsIOException
代码实现和 int read(byte b[])方法一样,直接从文件输入流中读取最多len个字节到byte数组b中。
/* 从此输入流中读取最多len个字节到byte数组中。 */
public int read(byte b[], int off, int len) throws IOException {
Object traceContext = IoTrace.fileReadBegin(path);
int bytesRead = 0;
try {
bytesRead = readBytes(b, off, len);
} finally {
IoTrace.fileReadEnd(traceContext, bytesRead == -1 ? 0 : bytesRead);
}
return bytesRead;
}
值得一提的native方法:
上面核心方法中为什么实现简单,因为工作量都在native方法里面,即JVM里面实现了。native倒是不少一一列举吧:
native void open(String name) // 打开文件,为了下一步读取文件内容
native int read0() // 从文件输入流中读取一个字节
native int readBytes(byte b[], int off, int len) //从文件输入流中读取,从off句柄开始的len个字节,并存储至b字节数组内。
native void close0() // 关闭该文件输入流及涉及的资源,比如说如果该文件输入流的FileChannel对被获取后,需要对FileChannel进行close。
FileInputStream和FileOutputStream的应用(后不再贴):
第一层文本文档copy:
packageday7.bytestream;
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
/**
* 这里的路径没有写绝对路径使用的是相对路径
* 这里的test.txt文档自动生成在我的teacher_yang目录下
* 和src是同级的
* 补充:在windows中.txt .doc等扩展名只是标志这个文件是什么类型的软件产生的
* 但是其实这个扩展名是无关重要的,只是提示你用什么软件打开,一定能正常显示
* 只是可能不同软件使用的编码方式不同,会导致文件出错,但是纠其本质,文件都
* 是一串二进制的数字存储的。
* @author半步疯子
*
* 这个程序还有很多缺陷,就是这个程序进行程序拷贝的时候,是你的源文件有多大
* 它就会去形成多大的临界缓冲区,假如我们这个文件过大的话,就会导致我们的内
* 存占用过大,造成电脑死机等等问题。
* (但是我们也得明白:缓冲区的大小越大,必然拷贝搬运文件的速度就越快
* 但是缓冲区的大小最大就是需要拷贝文件的大小,再大的话拷贝的速度也不会再
* 提升了)
*/
public classFileCopy1 {
public static voidmain(String[] args) {
FileInputStreamfis = null;
FileOutputStreamfos = null;
try {
//源文件
fis= new FileInputStream("test.txt");
//目标文件
fos = newFileOutputStream("test1.txt");
//得到源文件的长度
intlen = fis.available();
//构造一个和源文件大小相同的buffer(临界区)
byte[]buffer =new byte[len];
//将源文件的数据读到程序中(buffer临界区内)
fis.read(buffer);
//将临界区buffer中的数据写到目标文件中去
fos.write(buffer);
} catch(FileNotFoundException e) {
//文件可能找不到的情况
e.printStackTrace();
}catch(IOException e) {
//输入输出异常
e.printStackTrace();
}finally {
if(fis!=null) {
try {
fis.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(fos!=null) {
try {
fos.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
第二层自定义缓冲区,拷贝大文件:
packageday7.bytestream;
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.FileOutputStream;
importjava.io.IOException;
public classFileCopy2 {
public static voidmain(String[] args) {
FileInputStreamfis = null;
FileOutputStreamfos = null;
try {
fis = newFileInputStream("E:\\安装包\\mysql-5.6.31-winx64.zip");
fos = newFileOutputStream("a.rar");
// 创建一个缓冲区
// 缓冲区越大,copy的速度越快,最快速度为缓冲区大小等于copy文件大小的时候
byte[] buffer =new byte[1024*8];
System.out.println("文件正在复制,请稍后...");
longbegin = System.currentTimeMillis();
while(true) {
/*
* read方法返回的是一个int值
* 表示读入缓冲区(buffer)的字节总数
* 因为各种方面的原因,read到缓冲区中
* 的字节数不一定每次都装满了len长度。
* 如果当前源文件中不存在可以读的字节
* 的时候,read的返回值为-1。
*/
intlen = fis.read(buffer);
if(len<0){
break;
}
fos.write(buffer,0, len);
/*
*fos的flush方法是手动清空buffer(缓冲区,或者临界区)
*在完成copy操作之后,会自动flush
*/
fos.flush();
}
longend = System.currentTimeMillis();
System.out.println("文件复制完成,耗时:"+(end-begin)+"毫秒");
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} finally {
if(fos!=null) {
try {
fos.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(fis!=null) {
try {
fis.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
源码:
/**
* FileInputStream 从文件系统的文件中获取输入字节流。文件取决于主机系统。
* 比如读取图片等的原始字节流。如果读取字符流,考虑使用 FiLeReader。
*/
public class FileInputStream extends InputStream{
/* 文件描述符类---此处用于打开文件的句柄 */
private final FileDescriptor fd;
/* 引用文件的路径 */
private final String path;
/* 文件通道,NIO部分 */
private FileChannel channel = null;
private final Object closeLock = new Object();
private volatile boolean closed = false;
private static final ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<>();
private static boolean isRunningFinalize() {
Boolean val;
if ((val = runningFinalize.get()) != null)
return val.booleanValue();
return false;
}