详解Java的readBytes是怎么实现的

 更新时间:2023年07月30日 11:14:03   作者:darcy_yuan  
众所周知,Java是一门跨平台语言,针对不同的操作系统有不同的实现,下面小编就来从一个非常简单的api调用带大家来看看Java具体是怎么做的吧

1.前言

众所周知,Java是一门跨平台语言,针对不同的操作系统有不同的实现。本文从一个非常简单的api调用来看看Java具体是怎么做的.

2.源码分析

从FileInputStream.java中看到readBytes最后是native调用

/**
     * Reads a subarray as a sequence of bytes.
     * @param b the data to be written
     * @param off the start offset in the data
     * @param len the number of bytes that are written
     * @exception IOException If an I/O error has occurred.
     */
    private native int readBytes(byte b[], int off, int len) throws IOException; // native调用
    /**
     * Reads up to <code>b.length</code> bytes of data from this input
     * stream into an array of bytes. This method blocks until some input
     * is available.
     *
     * @param      b   the buffer into which the data is read.
     * @return     the total number of bytes read into the buffer, or
     *             <code>-1</code> if there is no more data because the end of
     *             the file has been reached.
     * @exception  IOException  if an I/O error occurs.
     */
    public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }

从jdk源码中,我们找到FileInputStream.c(/jdk/src/share/native/java/io),此文件定义了对应文件的native调用.

// FileInputStream.c
JNIEXPORT jint JNICALL
Java_java_io_FileInputStream_readBytes(JNIEnv *env, jobject this,
        jbyteArray bytes, jint off, jint len) {
    return readBytes(env, this, bytes, off, len, fis_fd);
}

我们观察下当前的目录,可以看到java 对典型的四种unix like的系统(bsd, linux, macosx, solaris), 以及windows 提供了特殊实现。share是公用部分。

在头部获取文件fd field (fd 是非负正整数,用来标识打开文件)

// FileInputStream.c
JNIEXPORT void JNICALL
Java_java_io_FileInputStream_initIDs(JNIEnv *env, jclass fdClass) {
    fis_fd = (*env)->GetFieldID(env, fdClass, "fd", "Ljava/io/FileDescriptor;"); /* fd field,后面用来获取 fd */
}

继续调用readBytes

// ioutil.c
jint
readBytes(JNIEnv *env, jobject this, jbyteArray bytes,
          jint off, jint len, jfieldID fid)
{
    jint nread;
    char stackBuf[BUF_SIZE];
    char *buf = NULL;
    FD fd;
    if (IS_NULL(bytes)) {
        JNU_ThrowNullPointerException(env, NULL);
        return -1;
    }
    if (outOfBounds(env, off, len, bytes)) { /* 越界判断 */
        JNU_ThrowByName(env, "java/lang/IndexOutOfBoundsException", NULL);
        return -1;
    }
    if (len == 0) {
        return 0;
    } else if (len > BUF_SIZE) {
        buf = malloc(len); /* 缓冲区不足,动态分配内存 */
        if (buf == NULL) {
            JNU_ThrowOutOfMemoryError(env, NULL);
            return 0;
        }
    } else {
        buf = stackBuf;
    }
    fd = GET_FD(this, fid); /* 获取fd */
    if (fd == -1) {
        JNU_ThrowIOException(env, "Stream Closed");
        nread = -1;
    } else {
        nread = IO_Read(fd, buf, len); /* 执行read,系统调用 */
        if (nread > 0) {
            (*env)->SetByteArrayRegion(env, bytes, off, nread, (jbyte *)buf);
        } else if (nread == -1) {
            JNU_ThrowIOExceptionWithLastError(env, "Read error");
        } else { /* EOF */
            nread = -1;
        }
    }
    if (buf != stackBuf) {
        free(buf); /* 失败释放内存 */
    }
    return nread;
}

我们继续看看IO_Read的实现,是个宏定义

#define IO_Read handleRead

handleRead有两种实现

solaris实现:

// /jdk/src/solaris/native/java/io/io_util_md.c
ssize_t
handleRead(FD fd, void *buf, jint len)
{
    ssize_t result;
    RESTARTABLE(read(fd, buf, len), result);
    return result;
}
/*
 * Retry the operation if it is interrupted
 */
#define RESTARTABLE(_cmd, _result) do { \
    do { \
        _result = _cmd; \
    } while((_result == -1) && (errno == EINTR)); \ /* 如果是中断,则不断重试,避免进程调度等待*/
} while(0)

read方法可以参考unix man page

windows实现:

// jdk/src/windows/native/java/io/io_util_md.c
JNIEXPORT
jint
handleRead(FD fd, void *buf, jint len)
{
    DWORD read = 0;
    BOOL result = 0;
    HANDLE h = (HANDLE)fd;
    if (h == INVALID_HANDLE_VALUE) {
        return -1;
    }
    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) {
        int error = GetLastError();
        if (error == ERROR_BROKEN_PIPE) {
            return 0; /* EOF */
        }
        return -1;
    }
    return (jint)read;
}

3.java异常初探

// jdk/src/share/native/common/jni_util.c
/**
 * Throw a Java exception by name. Similar to SignalError.
 */
JNIEXPORT void JNICALL
JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg)
{
    jclass cls = (*env)->FindClass(env, name);
    if (cls != 0) /* Otherwise an exception has already been thrown */
        (*env)->ThrowNew(env, cls, msg); /* 调用JNI 接口*/
}
/* JNU_Throw common exceptions */
JNIEXPORT void JNICALL
JNU_ThrowNullPointerException(JNIEnv *env, const char *msg)
{
    JNU_ThrowByName(env, "java/lang/NullPointerException", msg);
}

最后是调用JNI:

// hotspot/src/share/vm/prims/jni.h
jint ThrowNew(jclass clazz, const char *msg) {
        return functions->ThrowNew(this, clazz, msg);
    }
jint (JNICALL *ThrowNew)
      (JNIEnv *env, jclass clazz, const char *msg);

4.总结

很多高级语言,有着不同的编程范式,但是归根到底还是(c语言)系统调用,c语言能够在更低的层面做非常多的优化。如果我们了解了这些底层的系统调用,就能看到问题的本质。

到此这篇关于详解Java的readBytes是怎么实现的的文章就介绍到这了,更多相关Java readBytes内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot 2.0 整合sharding-jdbc中间件实现数据分库分表

    SpringBoot 2.0 整合sharding-jdbc中间件实现数据分库分表

    这篇文章主要介绍了SpringBoot 2.0 整合sharding-jdbc中间件,实现数据分库分表,本文图文并茂给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2019-06-06
  • Spring Security中的 @PreAuthorize 注解使用方法和示例代码

    Spring Security中的 @PreAuthorize 注解使用方法和示例代码

    @PreAuthorize是SpringSecurity中用于方法级权限控制的重要注解,本文详细介绍了@PreAuthorize的使用方法,通过合理使用@PreAuthorize,可以实现细粒度的权限控制,确保应用程序的安全性,感兴趣的朋友跟随小编一起看看吧
    2026-01-01
  • Java实现Excel表单控件的添加与删除

    Java实现Excel表单控件的添加与删除

    本文通过Java代码示例介绍如何在Excel表格中添加表单控件,包括文本框、单选按钮、复选框、组合框、微调按钮等,以及如何删除Excel中的指定表单控件,需要的可以参考一下
    2022-05-05
  • SpringBoot集成jersey打包jar找不到class的处理方法

    SpringBoot集成jersey打包jar找不到class的处理方法

    这篇文章主要介绍了SpringBoot集成jersey打包jar找不到class的处理方法,文中通过代码示例介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-03-03
  • java动态代理示例分享

    java动态代理示例分享

    这篇文章主要介绍了java动态代理示例,需要的朋友可以参考下
    2014-02-02
  • Java编程实现二项分布的采样或抽样实例代码

    Java编程实现二项分布的采样或抽样实例代码

    这篇文章主要介绍了Java编程实现二项分布的采样或抽样实例代码,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • JAVA如何判断上传文件后缀名是否符合规范MultipartFile

    JAVA如何判断上传文件后缀名是否符合规范MultipartFile

    这篇文章主要介绍了JAVA判断上传文件后缀名是否符合规范MultipartFile,文中通过实例代码介绍了java实现对上传文件做安全性检查,需要的朋友可以参考下
    2023-11-11
  • Spring Boot 中使用 Drools 规则引擎的完整步骤

    Spring Boot 中使用 Drools 规则引擎的完整步骤

    规则引擎主要用于将业务逻辑从应用程序代码中分离出来,提高系统的灵活性和可维护性,规则引擎通过预定义的规则来处理输入数据并做出相应的决策,从而实现业务逻辑的自动化和动态调整,本文给大家介绍Spring Boot中使用 Drools 规则引擎的指南,感兴趣的朋友一起看看吧
    2025-04-04
  • Java 日志中 Marker 的使用示例详解

    Java 日志中 Marker 的使用示例详解

    Marker是SLF4J(以及Logback、Log4j 2)提供的一个接口,它本质上是一个命名对象,你可以把它想象成一个可以附加到日志语句上的"标签"或"戳记",本文给大家介绍Java日志中Marker的使用示例,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • Spring无法解决循环依赖的五种场景分析

    Spring无法解决循环依赖的五种场景分析

    本文详细分析Spring框架中五类循环依赖问题(构造器注入、原型作用域、@Async、配置类、BeanPostProcessor),提出应急方案如@Lazy、重构设计,并强调通过单一职责、依赖倒置等设计原则避免循环依赖,需要的朋友可以参考下
    2025-05-05

最新评论