作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可。

上一章我们已经分析过FileInputStream的构建过程,接下来我们就来看一下read方法的读取过程。

我们先来看下FileInputStream中的四个有关read的方法的源码,如下。

    public native int read() throws IOException;

    private native int readBytes(byte b[], int off, int len) throws IOException;

    public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
} public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}

可以看到,其中有两个本地方法,两个不是本地方法,但是还是内部还是调用的本地方法,那么我们研究的重点就是这两个本地方法到底是如何实现的。

下面是这两个本地方法的源码,非常简单,各位请看。

JNIEXPORT jint JNICALL
Java_java_io_FileInputStream_read(JNIEnv *env, jobject this) {
return readSingle(env, this, fis_fd);//每一个本地的实例方法默认的两个参数,JNI环境与对象的实例
} JNIEXPORT jint JNICALL
Java_java_io_FileInputStream_readBytes(JNIEnv *env, jobject this,
jbyteArray bytes, jint off, jint len) {//除了前两个参数,后三个就是readBytes方法传递进来的,字节数组、起始位置、长度三个参数
return readBytes(env, this, bytes, off, len, fis_fd);
}

可以看到,这两个本地方法的实现只是将任务又转给了两个方法,readSingle和ReadBytes,请注意,在调用这两个方法时,除了常用的env和this对象,以及从JAVA环境传过来的参数之外,还多了一个参数fis_fd,这个对象就是上一章中FileInputStream类中的fd属性的地址偏移量了。

那么下面我们首先来看readSingle方法的实现,如下。

/*
env和this参数就不再解释了
fid就是FileInputStream类中fd属性的地址偏移量
通过fid和this实例可以获取FileInputStream类中fd属性的内存地址
*/
jint
readSingle(JNIEnv *env, jobject this, jfieldID fid) {
jint nread;//存储读取后返回的结果值
char ret;//存储读取出来的字符
FD fd = GET_FD(this, fid);//这个获取到的FD其实就是之前handle属性的值,也就是文件的句柄
if (fd == -) {
JNU_ThrowIOException(env, "Stream Closed");
return -;//如果文件句柄等于-1,说明文件流已关闭
}
nread = (jint)IO_Read(fd, &ret, );//读取一个字符,并且赋给ret变量
//以下根据返回的int值判断读取的结果
if (nread == ) { /* EOF */
return -;//代表流已到末尾,返回-1
} else if (nread == JVM_IO_ERR) { /* error */
JNU_ThrowIOExceptionWithLastError(env, "Read error");//IO错误
} else if (nread == JVM_IO_INTR) {
JNU_ThrowByName(env, "java/io/InterruptedIOException", NULL);//被打断
}
return ret & 0xFF;//与0xFF做按位的与运算,去除高于8位bit的位
}

可以看到,这个方法其实最关键的就是IO_Read这个宏定义的处理,而IO_Read其实只是代表了一个方法名称叫handleRead,我们去看一下handleRead的源码。

/*
fd就是handle属性的值
buf是收取读取内容的数组
len是读取的长度,可以看到,这个参数传进来的是1
函数返回的值代表的是实际读取的字符长度
*/
JNIEXPORT
size_t
handleRead(jlong fd, void *buf, jint len)
{
DWORD read = ;
BOOL result = ;
HANDLE h = (HANDLE)fd;
if (h == INVALID_HANDLE_VALUE) {//如果句柄是无效的,则返回-1
return -;
}
//这里ReadFile又是一个现有的函数,和上一章的CreateFile是一样的
//都是WIN API的函数,可以百度搜索它的作用与参数详解,理解它并不难
result = ReadFile(h, /* File handle to read */ //文件句柄
buf, /* address to put data */ //存放数据的地址
len, /* number of bytes to read */ //要读取的长度
&read, /* number of bytes read */ //实际读取的长度
NULL); /* no overlapped struct */ //只有对文件进行重叠操作时才需要传值
if (result == ) {//如果没读取出来东西,则判断是到了文件末尾返回0,还是报错了返回-1
int error = GetLastError();
if (error == ERROR_BROKEN_PIPE) {
return ; /* EOF */
}
return -;
}
return read;
}

到此,基本上就完全看完了无参数的read方法的源码,它的原理其实很简单,就是利用handle这个句柄,使用ReadFile的WIN API函数读取了一个字符,不过值得注意的是,这些都是windows系统下的实现方式,所以不可认为这些源码代表了所有系统下的情况。

然而对于带有参数的read方法,其原理与无参read方法是一样的,而且最终也是调用的handleRead这个方法,只是读取的长度不再是1而已。

由此可以看出,文件输入流只是已只读方式打开了一个文件流,而且这个文件流只能依次向后读取,因为在之前的设计模式系列装饰器模式一文中,LZ已经提到过,对于FileInputStream进行包装而支持回退,标记重置等操作的输入流,都只是在内存里创建缓冲区造成的假象,我们真正的文件输入流是不支持这些操作的。

好了,有关FileInputstream的源码内容就分享到此了,如果有兴趣的猿友,可以继续看一下其它的本地方法是如何实现的。

感谢各位的收看。

JNI探秘-----FileInputStream的read方法详解的更多相关文章

  1. C++调用JAVA方法详解

    C++调用JAVA方法详解          博客分类: 本文主要参考http://tech.ccidnet.com/art/1081/20050413/237901_1.html 上的文章. C++ ...

  2. 使用Java操作文本文件的方法详解

    使用Java操作文本文件的方法详解 摘要: 最初java是不支持对文本文件的处理的,为了弥补这个缺憾而引入了Reader和Writer两个类 最初java是不支持对文本文件的处理的,为了弥补这个缺憾而 ...

  3. session的使用方法详解

    session的使用方法详解 Session是什么呢?简单来说就是服务器给客户端的一个编号.当一台WWW服务器运行时,可能有若干个用户浏览正在运正在这台服务器上的网站.当每个用户首次与这台WWW服务器 ...

  4. Kooboo CMS - Html.FrontHtml[Helper.cs] 各个方法详解

    下面罗列了方法详解,每一个方法一篇文章. Kooboo CMS - @Html.FrontHtml().HtmlTitle() 详解 Kooboo CMS - Html.FrontHtml.Posit ...

  5. HTTP请求方法详解

    HTTP请求方法详解 请求方法:指定了客户端想对指定的资源/服务器作何种操作 下面我们介绍HTTP/1.1中可用的请求方法: [GET:获取资源]     GET方法用来请求已被URI识别的资源.指定 ...

  6. ecshop后台增加|添加商店设置选项和使用方法详解

    有时候我们想在Ecshop后台做个设置.radio.checkbox 等等来控制页面的显示,看看Ecshop的设计,用到了shop_config这个商店设置功能 Ecshop后台增加|添加商店设置选项 ...

  7. (转)Spring JdbcTemplate 方法详解

    Spring JdbcTemplate方法详解 文章来源:http://blog.csdn.net/dyllove98/article/details/7772463 JdbcTemplate主要提供 ...

  8. windows.open()、close()方法详解

    windows.open()方法详解:         window.open(URL,name,features,replace)用于载入指定的URL到新的或已存在的窗口中,并返回代表新窗口的Win ...

  9. CURL使用方法详解

    php采集神器CURL使用方法详解 作者:佚名  更新时间:2016-10-21   对于做过数据采集的人来说,cURL一定不会陌生.虽然在PHP中有file_get_contents函数可以获取远程 ...

随机推荐

  1. Linux 配置 hosts

    1. hosts 是什么 维基百科对 hosts 的介绍如下: hosts文件(域名解析文件)是一个用于储存计算机网络中各节点信息的计算机文件. 这个文件负责将主机名称映射到相应的IP地址. host ...

  2. 【转】HTTP学习---TCP和UDP协议的区别与应用

    [原文]https://www.toutiao.com/i6592813624689951239/ 概述 ⊙TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层. 在网络层有IP协议.ICM ...

  3. MySQL二进制日志文件Binlog的三种格式以及对应的主从复制中三种技术

    二进制日志文件Binlog的格式主要有三种: 1.Statement:基于SQL语句级别的Binlog,每条修改数据的SQL都会保存到Binlog里面. 2.ROW:基于行级别,每一行数据的变化都会记 ...

  4. 通过sqli-labs学习sql注入——基础挑战之less1-3

    首先,先看一些基础知识吧!!!!本人只是初学者,记录一下自己的学习过程,有什么错误之处请指出,谢谢!大佬请绕过!!!! url编码:一般的url编码其实就是那个字符的ASCII值得十六进制,再在前面加 ...

  5. python2.7 threading RLock/Condition文档翻译 (RLock/Condition详解)

    RLock Objects 可重入锁是一个同步原语,它可以被同一个线程多次获取.在内部,除了原始锁使用的锁定/解锁状态之外,它还使用“线程拥有”和“递归级别”的概念.在锁定状态下,某些线程拥有锁:在未 ...

  6. 用python实现MRO算法

    引子: 如图反映了python3中,几个类的继承关系和查找顺序.对于类A,其查找顺序为:A,B,E,C,F,D,G,(Object),这并不是一个简单的深度优先或广度优先的规律.那么这个顺序到底是如何 ...

  7. 2017-2018-2 20165318 实验三《Java面向对象程序设计》实验报告

    2017-2018-2 20165318 实验三<Java面向对象程序设计>实验报告 一.实验报告封面 课程:Java程序设计        班级:1653班        姓名:孙晓暄  ...

  8. Android MaterialDesign之水波点击效果的几种实现方法

    什么是水波点击的效果? 下面是几种不同的实现方法的效果图以及实现方法   Video_2016-08-31_003846 如何实现? 方法一 使用官方提供的RippleDrawable类 优点:使用方 ...

  9. Android开发——Android中常见的4种线程池(保证你能看懂并理解)

    0.前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52415337 使用线程池可以给我们带来很多好处,首先通过线程池中线程的重用 ...

  10. WorldWind源码剖析系列:下载队列类DownloadQueue

    下载队列类DownloadQueue代表具有优先级的下载队列,该类的存储下载请求的数组链表专门按一定的优先级来存储下载请求的.该类的类图如下. 下载队列类DownloadQueue各个字段的含义说明如 ...