FileInputStream和FileOutputStream详解
一.引子
文件,作为常见的数据源。关于操作文件的字节流就是 FileInputStream & FileOutputStream。它们是Basic IO字节流中重要的实现类。
二、FileInputStream源码分析
FileInputStream源码如下:
/**
* 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;
} /* 通过文件路径名来创建FileInputStream */
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
} /* 通过文件来创建FileInputStream */
public FileInputStream(File file) throws FileNotFoundException {
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 new NullPointerException();
}
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 new IOException();
} /* 获取此文件输入流的唯一FileChannel对象 */
publicFileChannel getChannel() {
synchronized(this) {
if(channel == null) {
channel = FileChannelImpl.open(fd, path, true, false, this);
fd.incrementAndGetUseCount();
}
returnchannel;
}
} privatestaticnativevoidinitIDs(); privatenativevoidclose0() 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);
}
}
}
}
1. 三个核心方法
三个核心方法,也就是Override(重写)了抽象类InputStream的read方法。int read() 方法,即
public int read() throws IOException
代码实现中很简单,一个try中调用本地native的read0()方法,直接从文件输入流中读取一个字节。IoTrace.fileReadEnd(),字面意思是防止文件没有关闭读的通道,导致读文件失败,一直开着读的通道,会造成内存泄露。
int read(byte b[]) 方法,即
public int read(byte b[]) throws IOException
代码实现也是比较简单的,也是一个try中调用本地native的readBytes()方法,直接从文件输入流中读取最多b.length个字节到byte数组b中。
int read(byte b[], int off, int len) 方法,即
public int read(byte b[], int off, int len) throws IOException
代码实现和 int read(byte b[])方法 一样,直接从文件输入流中读取最多len个字节到byte数组b中。
2. 值得一提的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。
其他还有值得一提的就是,在jdk1.4中,新增了NIO包,优化了一些IO处理的速度,所以在FileInputStream和FileOutputStream中新增了FileChannel getChannel()的方法。即获取与该文件输入流相关的 java.nio.channels.FileChannel对象。
FileOutputStream 源码如下:
/**
* 文件输入流是用于将数据写入文件或者文件描述符类
* 比如写入图片等的原始字节流。如果写入字符流,考虑使用 FiLeWriter。
*/
publicclassSFileOutputStream extendsOutputStream{
/* 文件描述符类---此处用于打开文件的句柄 */
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 final ThreadLocal<Boolean> runningFinalize =
new ThreadLocal<>(); private static boolean isRunningFinalize() {
Boolean val;
if ((val = runningFinalize.get()) != null)
return val.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 ? new File(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 new NullPointerException();
}
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) throws IOException {
Object traceContext = IoTrace.fileWriteBegin(path);
int bytesWritten = 0;
try {
writeBytes(b, off, len, append);
bytesWritten = len;
} finally {
IoTrace.fileWriteEnd(traceContext, bytesWritten);
}
} /* 关闭此文件输出流并释放与此流有关的所有系统资源 */
publicvoidclose() throwsIOException {
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() throws IOException {
if(fd != null) returnfd;
thrownewIOException();
} publicFile Channel getChannel() {
synchronized(this) {
if(channel == null) {
channel = FileChannelImpl.open(fd, path, false, true, append, this); fd.incrementAndGetUseCount();
}
returnchannel;
}
} protected void finalize() throws IOException {
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();
} }
1. 三个核心方法
三个核心方法,也就是Override(重写)了抽象类OutputStream的write方法。void write(int b) 方法,即
public void write(intb) throws IOException
代码实现中很简单,一个try中调用本地native的write()方法,直接将指定的字节b写入文件输出流。IoTrace.fileReadEnd()的意思和上面FileInputStream意思一致。
void write(byte b[]) 方法,即
public void write(byteb[]) throws IOException
代码实现也是比较简单的,也是一个try中调用本地native的writeBytes()方法,直接将指定的字节数组写入该文件输入流。
void write(byte b[], int off, int len) 方法,即
public void write(byte b[], int off, int len) throws IOException
代码实现和 void write(byte b[]) 方法 一样,直接将指定的字节数组写入该文件输入流。
2. 值得一提的native方法
上面核心方法中为什么实现简单,因为工作量都在native方法里面,即JVM里面实现了。native倒是不少一一列举吧:
native void open(String name) // 打开文件,为了下一步读取文件内容
native void write(int b, boolean append) // 直接将指定的字节b写入文件输出流
native native void writeBytes(byte b[], int off, int len, boolean append) // 直接将指定的字节数组写入该文件输入流。
native void close0() // 关闭该文件输入流及涉及的资源,比如说如果该文件输入流的FileChannel对被获取后,需要对FileChannel进行close。
FileInputStream和FileOutputStream详解的更多相关文章
- Java输出流FileOutputStream使用详解
Java输出流FileOutputStream使用详解 http://baijiahao.baidu.com/s?id=1600984799323133994&wfr=spider&f ...
- JNI探秘-----FileInputStream的read方法详解
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章我们已经分析过File ...
- Android图片缓存之Bitmap详解
前言: 最近准备研究一下图片缓存框架,基于这个想法觉得还是先了解有关图片缓存的基础知识,今天重点学习一下Bitmap.BitmapFactory这两个类. 图片缓存相关博客地址: Android图片缓 ...
- Java 序列化Serializable详解
Java 序列化Serializable详解(附详细例子) Java 序列化Serializable详解(附详细例子) 1.什么是序列化和反序列化Serialization(序列化)是一种将对象以一连 ...
- java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET
java中的io系统详解 - ilibaba的专栏 - 博客频道 - CSDN.NET 亲,“社区之星”已经一周岁了! 社区福利快来领取免费参加MDCC大会机会哦 Tag功能介绍—我们 ...
- Apache POI使用详解
Apache POI使用详解 1.POI结构与常用类 (1)POI介绍 Apache POI是Apache软件基金会的开源项目,POI提供API给Java程序对Microsoft Office格式档案 ...
- org.apache.common.io-FileUtils详解
org.apache.common.io---FileUtils详解 getTempDirectoryPath():返回临时目录路径; public static String getTempDire ...
- POI使用详解
Apache POI使用详解 1.POI结构与常用类 (1)POI介绍 Apache POI是Apache软件基金会的开源项目,POI提供API给Java程序对Microsoft Office格式档案 ...
- Java 序列化Serializable详解(附详细例子)
Java 序列化Serializable详解(附详细例子) 1.什么是序列化和反序列化 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization ...
随机推荐
- 共通css初次尝试
1.网页的主要的html <@fn.html css=["${basePath}/css/help/guideCommon.css${versionControl}"]tit ...
- build.gradle代码
Android Studio 这么强大的工具,就算我们不懂 gradle, groovy, 也照样能借助AS对 Android 项目进行编译.调试.运行.打包等操作.build.gradle 这个文件 ...
- oracle删除字段中的空格、回车及指定字符
create or replace procedure PROC_test is --Description:删除字段中的指定字符(回车chr(13).换行chr(10)) --By LiChao - ...
- Laravel查询构造器的使用方法整理
1.结果集 1.1从一张表获取所有行,get方法获取所有行 $users = DB::table('users')->get(); 获取列的值 foreach ($users as $user) ...
- jQuery入门:认识jQuery
jQuery是一个轻量级的JavaScript库,拥有独特的选择器.出色的DOM操作.可靠的事件处理.完善的兼容性.链式操作以及方便的ajax等功能.jQuery的最新版本可在官方网站(http:// ...
- 微信小程序,前端大梦想(二)
微信小程序的视图与渲染 今天我们从四个方面来了解小程序: •组件的基本使用 •数据绑定 •渲染标签 •模板的使用 一.组件的基本使用: 微信小程序为我们的开发提供了丰富的UI组件 ...
- MySQL两个最简单的delimiter的使用demo
今天复习MySQL,使用的工具是Navicat for MySQL,写了两个简单的delimiter的demo. 第一个,获取当前时间的年月日时分秒格式的时间: delimiter $$ drop f ...
- 你的计算机也可以看懂世界——十分钟跑起卷积神经网络(Windows+CPU)
众所周知,如果你想研究Deep Learning,那么比较常用的配置是Linux+GPU,不过现在很多非计算机专业的同学有时也会想采用Deep Learning方法来完成一些工作,那么Linux+GP ...
- Git协作
前面的话 本文将详细介绍Git多人协作的具体内容 远程仓库 当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin. ...
- 【Linux配置】vim配置文件内容
vim的配置 文件:~/.vimrc 在自己的家目录中的.vimrc文件进行编辑配置 设置如下: set nu "序号 set tabstop= "tab键的大小 set show ...