1 基本概括

2 主要介绍

2.1 read()与read(byte[] b)和read(byte[] b,int off,int len)方法的区别

read(): 从输入流中读取数据的下一个字节,返回0255范围内的int字节值。如果因为已经到达流末尾而没有可用的字节,则返回-1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

read(byte[] b): 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。在不指定读取的起始点时,默认从流中读取b.length长度的字节值到字节数组中去,

完全覆盖字节数组,当然前提是该流长度大于或者等于字节数组,如果小于字节数组就只写入流长度的字节。在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。

如果 b 的长度为 0 ,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b中。

read()方法每次只能读取一个字节,所以也只能读取由ASCII码范围内的一些字符。read(byte[]b) 可以把参数数组b定义为大小为2的数组即可正常读取汉字了。如果此处定义b 的大小为37等奇数,则对于全是汉字的一篇文档则不能全部正常读写了。

write(byte[] b,int off,int len):

1.从读取流读取一定数量的字节,如果比如文件总共是102个字节

2.我们定义的数组长度是10,但是这里我们写read(bytes,0,9)那么每次往里面添加的(将只会是9个长度),就要读12次,最后一次放入3个.

3.所以一般读取流都不用这个而是用上一个方法:read(byte[]);

2.2 write()与write(byte[] b)和write(byte[] b,int off,int len)方法的区别

write():直接往流写入字节形式的(0-255)int值.

write(byte[] bytes):往流里边写入缓冲字节数组中的所有内容,不满整个数组长度的”空余内容”也会加入。

write(byte[] bytes,int off,int len):

1.这个是更严谨的写法,在外部定义len,然后每次len(为的是最后一次的细节长度)都等于流往数组中存放的长度

2.如上述read(bytes),前面每次都放入十个,第十一次放入的是2个,如果用第二种write(bytes),将会写入输出流十一次,每次写入十个长度,造成后面有8个空的,比原来的内容多了

3.所以用write(byte[] bytes,int off,int len);就不会出现多出来的空的情况,因为最后一次len不同

2.3 available( ) 的使用

available( ) :这个方法可以在读写操作前先得知数据流里有多少个字节可以读取。

注意:1 本地文件读取数据时,一般不会遇到问题,但如果是用于网络操作,就经常会遇到一些麻烦。

2 available()方法是用于创建一个大小刚刚适合的缓冲区,不过如果资源文件过大的话,很容易造成内在溢出的问题。

int count = in.available();  byte[] b = new byte[count];in.read(b); 如果有网络延迟,可能数据是分批次发送的,所以对方数据没有送达,就会出现问题,需要改为   int count = 0;  while (count == 0) {  count = in.available();  }  byte[] b = new byte[count];in.read(b);

2.4 flush方法

1 FileOutputStream本身没有重写flush(),是调用的父类(OutputStream)的方法,而OutputStream仅仅是实现了Flushable接口,flush()是个空方法体,什么都没做,所以对于FileOutputStream来说,flush()没有意义的

2 对于例如BufferedOutputStream这样的子类因为重新了flush(),才是有意义,就需要再调用close()前,先调用flush()

2.5 简单总结

1 文件输出流和输入流可以用于读取图像,图片等原始的字节流数据。

2 FileInputStream和FileOutputStream都可以通过文件路径name,文件对象File,文件描述符三种传参方法创建流对象。

3 FileOutputStream可以设置现写入数据是否覆盖原始文件(true表示的是覆盖)

3 应用

3.1 读取文件

public static void test1() throws IOException{File file = new File("./test.txt");if(!file.exists()){try {file.createNewFile();} catch(IOException e) {e.printStackTrace();}}FileInputStream fis =null;try {fis = new FileInputStream(file);//从输入流中读取数据// int tmp;// while((tmp = fis.read()) != -1){// System.out.println(tmp);// }byte[] bytes = new byte[fis.available()];//返回当前文件字节数fis.read(bytes);System.out.println(Arrays.toString(bytes));} catch(FileNotFoundException e) {e.printStackTrace();}catch (IOException e) {e.printStackTrace();} finally {try {fis.close();} catch(IOException e) {e.printStackTrace();}}}

3.2 把一张图片复制到指定文件夹

public void t2() throws IOException{FileInputStream fis=null;FileOutputStream fos=null;//创建输入流输出流fis=new FileInputStream("C:\\Users\\1\\Desktop\\2018-06-04-V3.png");fos=new FileOutputStream("C:\\Users\\1\\Desktop\\2018-06-04-V4.png");//先定义一个字节缓冲区,减少I/O次数,提高读写效率byte[] n = new byte[1024];int len;//这里是1024个字节的读取,所以在read()里面放的是每次读取的字节数while((len = fis.read(n)) != -1){fos.write(n,0, len);}fos.close();fis.close();}

4 源码分析

4.1 FileInputStream源码分析

/*** FileInputStream 从文件系统的文件中获取输入字节流。文件取决于主机系统。* 比如读取图片等的原始字节流。如果读取字符流,考虑使用 FiLeReader。*/public class SFileInputStream extends InputStream{/* 文件描述符类---此处用于打开文件的句柄 */private finalFileDescriptor 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;}/* 通过文件路径名来创建FileInputStream */public FileInputStream(String name) throws FileNotFoundException {this(name != null ? new File(name) : null);}/* 通过文件来创建FileInputStream */public FileInputStream(File file) throwsFileNotFoundException{String name = (file != null ? file.getPath() : null);SecurityManager security = System.getSecurityManager();if (security != null) {security.checkRead(name);}if (name == null) {throw new NullPointerException();}if (file.isInvalid()) {throw new FileNotFoundException("Invalid file path");}fd = new FileDescriptor();fd.incrementAndGetUseCount();this.path = name;open(name);}/* 通过文件描述符类来创建FileInputStream */public FileInputStream(FileDescriptor fdObj) {SecurityManager security = System.getSecurityManager();if (fdObj == null) {throw newNullPointerException();}if (security != null) {security.checkRead(fdObj);}fd = fdObj;path = null;fd.incrementAndGetUseCount();}/* 打开文件,为了下一步读取文件内容。native方法 */private native void open(String name) throws FileNotFoundException;/* 从此输入流中读取一个数据字节 */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;}/* 从此输入流中读取一个数据字节。native方法 */private native int read0() throws IOException;/* 从此输入流中读取多个字节到byte数组中。native方法 */private native int readBytes(byte b[], int off, int len) throws IOException;/* 从此输入流中读取多个字节到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;}/* 从此输入流中读取最多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;}public native long skip(long n) throws IOException;/* 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 */public native int available() throws IOException;/* 关闭此文件输入流并释放与此流有关的所有系统资源。 */public void close() throws IOException {synchronized (closeLock) {if (closed) {return;}closed =true;}if (channel != null) {fd.decrementAndGetUseCount();channel.close();}int useCount = fd.decrementAndGetUseCount();if((useCount <=0) || !isRunningFinalize()) {close0();}}public final FileDescriptor getFD() throws IOException {if (fd != null) return fd;throw newIOException();}/* 获取此文件输入流的唯一FileChannel对象 */public FileChannel getChannel() {synchronized (this) {if (channel == null) {channel = FileChannelImpl.open(fd, path,true, false, this);fd.incrementAndGetUseCount();}return channel;}}private static native void initIDs();private native void close0() throwsIOException;static {initIDs();}protected void finalize() throws IOException {if ((fd != null) && (fd != FileDescriptor.in)) {runningFinalize.set(Boolean.TRUE);try {close();} finally {runningFinalize.set(Boolean.FALSE);}}}}

4.2 FileOutputStream 源码分析

/*** 文件输入流是用于将数据写入文件或者文件描述符类* 比如写入图片等的原始字节流。如果写入字符流,考虑使用 FiLeWriter。*/public class SFileOutputStream extends OutputStream{/* 文件描述符类---此处用于打开文件的句柄 */private final FileDescriptor fd;/* 引用文件的路径 */private final String path;/* 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处 */private final boolean append;/* 关联的FileChannel类,懒加载 */private FileChannel channel;private final Object closeLock = new Object();private volatile boolean closed = false;private static finalThreadLocal<Boolean> runningFinalize =new ThreadLocal<>();private static boolean isRunningFinalize() {Boolean val;if ((val = runningFinalize.get()) != null)returnval.booleanValue();return false;}/* 通过文件名创建文件输入流 */public FileOutputStream(String name) throws FileNotFoundException {this(name != null ? new File(name) : null, false);}/* 通过文件名创建文件输入流,并确定文件写入起始处模式 */public FileOutputStream(String name, boolean append)throws FileNotFoundException{this(name != null ? newFile(name) :null, append);}/* 通过文件创建文件输入流,默认写入文件的开始处 */public FileOutputStream(File file) throws FileNotFoundException {this(file, false);}/* 通过文件创建文件输入流,并确定文件写入起始处 */public FileOutputStream(File file, boolean append)throws FileNotFoundException{String name = (file != null ? file.getPath() : null);SecurityManager security = System.getSecurityManager();if (security != null) {security.checkWrite(name);}if (name == null) {throw newNullPointerException();}if (file.isInvalid()) {throw new FileNotFoundException("Invalid file path");}this.fd = new FileDescriptor();this.append = append;this.path = name;fd.incrementAndGetUseCount();open(name, append);}/* 通过文件描述符类创建文件输入流 */public FileOutputStream(FileDescriptor fdObj) {SecurityManager security = System.getSecurityManager();if (fdObj == null) {throw new NullPointerException();}if (security != null) {security.checkWrite(fdObj);}this.fd = fdObj;this.path = null;this.append = false;fd.incrementAndGetUseCount();}/* 打开文件,并确定文件写入起始处模式 */private native void open(String name, boolean append)throws FileNotFoundException;/* 将指定的字节b写入到该文件输入流,并指定文件写入起始处模式 */private native void write(int b, boolean append) throws IOException;/* 将指定的字节b写入到该文件输入流 */public void write(int b) throws IOException {Object traceContext = IoTrace.fileWriteBegin(path);int bytesWritten = 0;try{write(b, append);bytesWritten =1;} finally {IoTrace.fileWriteEnd(traceContext, bytesWritten);}}/* 将指定的字节数组写入该文件输入流,并指定文件写入起始处模式 */private native void writeBytes(byte b[], int off, int len, boolean append)throws IOException;/* 将指定的字节数组b写入该文件输入流 */public void write(byte b[]) throws IOException {Object traceContext = IoTrace.fileWriteBegin(path);int bytesWritten = 0;try {writeBytes(b, 0, b.length, append);bytesWritten = b.length;} finally {IoTrace.fileWriteEnd(traceContext, bytesWritten);}}/* 将指定len长度的字节数组b写入该文件输入流 */public void write(byte b[], int off, int len) throwsIOException{Object traceContext = IoTrace.fileWriteBegin(path);int bytesWritten = 0;try{writeBytes(b, off, len, append);bytesWritten = len;}finally {IoTrace.fileWriteEnd(traceContext, bytesWritten);}}/* 关闭此文件输出流并释放与此流有关的所有系统资源 */public void close() throws IOException {synchronized (closeLock) {if (closed) {return;}closed = true;}if (channel != null) {fd.decrementAndGetUseCount();channel.close();}intuseCount = fd.decrementAndGetUseCount();if ((useCount <= 0) || !isRunningFinalize()) {close0();}}public final FileDescriptor getFD() throwsIOException{if (fd != null) return fd;throw new IOException();}public FileChannel getChannel() {synchronized (this) {if (channel == null) {channel = FileChannelImpl.open(fd, path,false, true, append, this);fd.incrementAndGetUseCount();}return channel;}}protected void finalize() throwsIOException{if (fd != null) {if (fd == FileDescriptor.out || fd == FileDescriptor.err) {flush();} else {runningFinalize.set(Boolean.TRUE);try{close();}finally {runningFinalize.set(Boolean.FALSE);}}}}private native void close0() throws IOException;private static native void initIDs();static {initIDs();}}
分类: 百科知识 标签: 暂无标签

评论

暂无评论数据

暂无评论数据

目录