java IO笔记(FileDescriptor)

25 篇文章 0 订阅

由于在看io源码中偶尔看到FileDescriptor,所以本篇决定讲讲FileDescriptor,从字面上来看它就是文件描述符。
官方是这么描述的:


可以看出,FileDescriptor可以看做一种指向文件引用的抽象化概念。它能表示一个开放文件,一个开放的socket或者一个字节的源。它最主要的用途就是去创建FileInputStream或者FileOutputStream。并且也说了不应该创建应用自己的文件描述符。

不得不说每次看这种官方的解释都表示云里雾里,毕竟与那些大神们差距太大,只能细细品味了。

不知道还是否记得前面讲述的System.out,System.in,System.error了,当时它们初始化的时候就用到了FileDescriptor:

当时没有细说,本篇则来探索一下FileDescriptor。

下面先贴上源码,通过源码来进行一定程度地学习:

package java.io;

import java.util.ArrayList;
import java.util.List;

public final class FileDescriptor {
    //封装了一个int型的值fd,每个打开的文件,socket等都会给你一个fd值,通过该值可以对文件,socket等进行相关操作,有点儿类似操作对象的索引
    private int fd;
    //
    private Closeable parent;
    //
    private List<Closeable> otherParents;
    //封装了一个boolean类型变量closed,用来判断fd是否被释放
    private boolean closed;

    //不带参构造,默认fd为-1,目测-1没什么意义,符合官方说的不建议直接创建自己的对象
    public /**/ FileDescriptor() {
        fd = -1;
    }

    //一个带参私有的构造方法,将fd值设为传入的参数
    private /* */ FileDescriptor(int fd) {
        this.fd = fd;
    }

    //定义的一个标准输入,fd值为0
    public static final FileDescriptor in = new FileDescriptor(0);

    //定义的一个标准输出,fd值为1
    public static final FileDescriptor out = new FileDescriptor(1);

    //定义的一个标准错误输出,fd值为2,我想在不同的环境下,标准流都能工作的原因应该就是因为已经在这里限定好了吧
    public static final FileDescriptor err = new FileDescriptor(2);
    

    //判断是否有效,这也证实了前面构造函数中fd赋值为-1是无效的
    public boolean valid() {
        return fd != -1;
    }

    public native void sync() throws SyncFailedException;

    /* This routine initializes JNI field offsets for the class */
    private static native void initIDs();

    static {
        initIDs();
    }

    // Set up JavaIOFileDescriptorAccess in SharedSecrets
    static {
        sun.misc.SharedSecrets.setJavaIOFileDescriptorAccess(
            new sun.misc.JavaIOFileDescriptorAccess() {
                public void set(FileDescriptor obj, int fd) {
                    obj.fd = fd;
                }

                public int get(FileDescriptor obj) {
                    return obj.fd;
                }

                public void setHandle(FileDescriptor obj, long handle) {
                    throw new UnsupportedOperationException();
                }

                public long getHandle(FileDescriptor obj) {
                    throw new UnsupportedOperationException();
                }
            }
        );
    }

    //像otherParents集合中添加元素
    synchronized void attach(Closeable c) {
        if (parent == null) {
            // first caller gets to do this
            parent = c;
        } else if (otherParents == null) {
            otherParents = new ArrayList<>();
            otherParents.add(parent);
            otherParents.add(c);
        } else {
            otherParents.add(c);
        }
    }

    //关闭otherParents所有的元素
    @SuppressWarnings("try")
    synchronized void closeAll(Closeable releaser) throws IOException {
        if (!closed) {
            closed = true;
            IOException ioe = null;
            try (Closeable c = releaser) {
                if (otherParents != null) {
                    for (Closeable referent : otherParents) {
                        try {
                            referent.close();
                        } catch(IOException x) {
                            if (ioe == null) {
                                ioe = x;
                            } else {
                                ioe.addSuppressed(x);
                            }
                        }
                    }
                }
            } catch(IOException ex) {
                /*
                 * 
                 */
                if (ioe != null)
                    ex.addSuppressed(ioe);
                ioe = ex;
            } finally {
                if (ioe != null)
                    throw ioe;
            }
        }
    }
}
因为笔者也是一个初学者,看了源码后,也没有发现什么,只是知道了下其中定义了3个标准分别为in,out,err,java中的标准流就是通过这里来实现的。其次就是fd的值不是我们可以随意修改的,是底层给我们的,我们可以通过该值取进行操作。并且FileDescriptor中需要进行close方法执行,应该是对文件等操作结束后,执行完close以后,才真正释放该索引以及对应的内存读写空间。

下面用1个小例子来展示一下FileDescriptor的使用及特性:

package test;

import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test4 {
	public static void main(String[] args) throws IOException {
		FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
		fos.write("FileDescriptor:这里证明了System.out,就是使用FileDescriptor.out来创建的\r".getBytes());
		System.out.println("System.out:控制台输出");
		FileOutputStream fos1 = new FileOutputStream("./src/file/test.txt");
		FileOutputStream fos2 = new FileOutputStream("./src/file/test.txt");
		System.out.println("fos1:"+fos1.getFD());
		System.out.println("fos2:"+fos2.getFD());
		fos.close();
		fos1.close();
		fos2.close();
	}
}
执行上述代码可以在控制台看到如下打印:


第一句是一个使用了FileDescriptor.out创建的一个输出流打印出来,第二句为System.out打印出来的,两个流都是在控制台输出,再次证实了Sytem的in,out,err是通过FileDescriptor中的in,out,err来实现的。

然后我们看到对同一文件建立的两个不同的流,其fd值是不同的。

最后总结一下,这个类我们好像不怎么能使用到,因为我们无法去定义其中的fd值,所以我们能使用的基本只有其中为我们定义好的in,out,err了,但是众所周知,java已将帮我们封装好了更方便的System.in/out/err了,所以平常我们基本见不到其使用,只有在看源码时,会经常碰到了。

以上为本篇的内容。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

moonfish0607

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值