詳解Java的readBytes是怎么實現的
1.前言
眾所周知,Java是一門跨平臺語言,針對不同的操作系統(tǒng)有不同的實現。本文從一個非常簡單的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的系統(tǒng)(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 */
}繼續(xù)調用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); /* 緩沖區(qū)不足,動態(tài)分配內存 */
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); /* 執(zhí)行read,系統(tǒng)調用 */
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;
}我們繼續(xù)看看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語言)系統(tǒng)調用,c語言能夠在更低的層面做非常多的優(yōu)化。如果我們了解了這些底層的系統(tǒng)調用,就能看到問題的本質。
到此這篇關于詳解Java的readBytes是怎么實現的的文章就介紹到這了,更多相關Java readBytes內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot 2.0 整合sharding-jdbc中間件實現數據分庫分表
這篇文章主要介紹了SpringBoot 2.0 整合sharding-jdbc中間件,實現數據分庫分表,本文圖文并茂給大家介紹的非常詳細,具有一定的參考借鑒價值 ,需要的朋友可以參考下2019-06-06
Spring Security中的 @PreAuthorize 注解使用方法和示例代碼
@PreAuthorize是SpringSecurity中用于方法級權限控制的重要注解,本文詳細介紹了@PreAuthorize的使用方法,通過合理使用@PreAuthorize,可以實現細粒度的權限控制,確保應用程序的安全性,感興趣的朋友跟隨小編一起看看吧2026-01-01
SpringBoot集成jersey打包jar找不到class的處理方法
這篇文章主要介紹了SpringBoot集成jersey打包jar找不到class的處理方法,文中通過代碼示例介紹的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下2024-03-03
JAVA如何判斷上傳文件后綴名是否符合規(guī)范MultipartFile
這篇文章主要介紹了JAVA判斷上傳文件后綴名是否符合規(guī)范MultipartFile,文中通過實例代碼介紹了java實現對上傳文件做安全性檢查,需要的朋友可以參考下2023-11-11
Spring Boot 中使用 Drools 規(guī)則引擎的完整步驟
規(guī)則引擎主要用于將業(yè)務邏輯從應用程序代碼中分離出來,提高系統(tǒng)的靈活性和可維護性,規(guī)則引擎通過預定義的規(guī)則來處理輸入數據并做出相應的決策,從而實現業(yè)務邏輯的自動化和動態(tài)調整,本文給大家介紹Spring Boot中使用 Drools 規(guī)則引擎的指南,感興趣的朋友一起看看吧2025-04-04

