本文连接:https://www.jianshu.com/p/a23ca155f949
本文作者:gks09@qq.com
仓促成文,还请指正。
FileInputStream典型代码
public static void main(String[] args) {
System.out.println(System.getProperty("user.dir"));
File file = new File(System.getProperty("user.dir") + "/src/oio/file.txt");
System.out.println("file name: " + file.getName());
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
byte[] bytes = new byte[(int) file.length()];
int len = inputStream.read(bytes);
System.out.println("bytes len :" + len + " detail: " + new String(bytes));
} catch (IOException e) {
e.printStackTrace();
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
FileChannel典型代码
public class NIOTest {
public static void main(String[] args) throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(4);//①
Path path = Paths.get(System.getProperty("user.dir") + "/assets/file.txt");
FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);//②
int len = fileChannel.read(byteBuffer);//③
while (len != -1) {
byteBuffer.flip();//④
while (byteBuffer.hasRemaining()){
System.out.print((char) byteBuffer.get());//⑤
}
byteBuffer.clear();//⑥
len = fileChannel.read(byteBuffer);//⑦
}
}
}
FileInputStream和FileChannel的深度分析
FileInputStream的read方法,调用了native的read0
-
http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/share/native/java/io/io_util.c的
jint[] readSingle(JNIEnv *env, jobject this, jfieldID fid)
:
jint
readSingle(JNIEnv *env, jobject this, jfieldID fid) {
jint nread;
char ret;
FD fd = GET_FD(this, fid);
if (fd == -1) {
JNU_ThrowIOException(env, "Stream Closed");
return -1;
}
nread = IO_Read(fd, &ret, 1);
if (nread == 0) { /* EOF */
return -1;
} else if (nread == -1) { /* error */
JNU_ThrowIOExceptionWithLastError(env, "Read error");
}
return ret & 0xFF;
}
核心是IO_Read方法。
- http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/windows/native/java/io/io_util_md.h定义了宏
#define IO_Read handleRead
- http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/windows/native/java/io/io_util_md.c的handleRead方法:
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;
}
核心方法是ReadFile方法。
FileChannel的read方法
- 使用FIleChannelImpl作为FileChannel的实现类,read方法:
public int read(ByteBuffer dst) throws IOException {
ensureOpen();
if (!readable)
throw new NonReadableChannelException();
synchronized (positionLock) {
int n = 0;
int ti = -1;
try {
begin();
ti = threads.add();
if (!isOpen())
return 0;
do {
n = IOUtil.read(fd, dst, -1, nd);
} while ((n == IOStatus.INTERRUPTED) && isOpen());
return IOStatus.normalize(n);
} finally {
threads.remove(ti);
end(n > 0);
assert IOStatus.check(n);
}
}
}
核心方法是IOUtil.read。
- 进入IOUtil类:
static int read(FileDescriptor fd, ByteBuffer dst, long position,
NativeDispatcher nd)
throws IOException
{
if (dst.isReadOnly())
throw new IllegalArgumentException("Read-only buffer");
if (dst instanceof DirectBuffer)
return readIntoNativeBuffer(fd, dst, position, nd);
// Substitute a native buffer
ByteBuffer bb = Util.getTemporaryDirectBuffer(dst.remaining());
try {
int n = readIntoNativeBuffer(fd, bb, position, nd);
bb.flip();
if (n > 0)
dst.put(bb);
return n;
} finally {
Util.offerFirstTemporaryDirectBuffer(bb);
}
}
核心方法是readIntoNativeBuffer。
- 进入readIntoNativeBuffer方法
private static int readIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,
long position, NativeDispatcher nd)
throws IOException
{
int pos = bb.position();
int lim = bb.limit();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
if (rem == 0)
return 0;
int n = 0;
if (position != -1) {
n = nd.pread(fd, ((DirectBuffer)bb).address() + pos,
rem, position);
} else {
n = nd.read(fd, ((DirectBuffer)bb).address() + pos, rem);
}
if (n > 0)
bb.position(pos + n);
return n;
}
核心方法是nd.pread(或nd.read,本质上一样)。这里的nd是抽象类sun.nio.ch.NativeDispatcher
,具体类是sun.nio.ch.FileDispatcherImpl
。
- 进入
sun.nio.ch.FileDispatcherImpl
类:
int read(FileDescriptor var1, long var2, int var4) throws IOException {
return read0(var1, var2, var4);
}
最终进入了一个native的read0方法。
- 根据openjdk1.7,查看这个native方法,在http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/9b8c96f96a0f/src/windows/native/sun/nio/ch/FileDispatcherImpl.c中(以windows为例):
JNIEXPORT jint JNICALL
Java_sun_nio_ch_FileDispatcherImpl_read0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len)
{
DWORD read = 0;
BOOL result = 0;
HANDLE h = (HANDLE)(handleval(env, fdo));
if (h == INVALID_HANDLE_VALUE) {
JNU_ThrowIOExceptionWithLastError(env, "Invalid handle");
return IOS_THROWN;
}
result = ReadFile(h, /* File handle to read */
(LPVOID)address, /* 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 IOS_EOF;
}
if (error == ERROR_NO_DATA) {
return IOS_UNAVAILABLE;
}
JNU_ThrowIOExceptionWithLastError(env, "Read failed");
return IOS_THROWN;
}
return convertReturnVal(env, (jint)read, JNI_TRUE);
}
核心方法是ReadFile方法。
结论
FileInputStream和FileChannel最终均调用了native的ReadFile方法,本质是一样的!