认知IO流之 — FileDescriptor

举报
cxuan 发表于 2021/07/15 02:09:43 2021/07/15
【摘要】 关注公众号回复002,有你想要的一切 这是 cxuan 的第 35篇原创文章 FileDescriptor 是什么 FileDescriptor 顾名思义是文件描述符,FileDescriptor 可以被用来表示开放文件、开放套接字等。比如用 FileDescriptor 表示文件来说: 当...

关注公众号回复002,有你想要的一切

640?wx_fmt=jpeg

这是 cxuan 的第 35篇原创文章

FileDescriptor 是什么

FileDescriptor 顾名思义是文件描述符,FileDescriptor 可以被用来表示开放文件、开放套接字等。比如用 FileDescriptor 表示文件来说: 当 FileDescriptor 表示文件时,我们可以通俗的将 FileDescriptor 看成是该文件。但是,我们不能直接通过 FileDescriptor 对该文件进行操作。

若需要通过 FileDescriptor 对该文件进行操作,则需要新创建 FileDescriptor 对应的 FileOutputStream或者是 FileInputStream,再对文件进行操作,应用程序不应该创建他们自己的文件描述符

下面让我们用两个例子来演示一下 FileDescriptor 分别与 FileInputStream 和 FileOutputStream 的使用


    
  1. public class FileDescriptorExample {
  2. public static void main(String[] args) {
  3. try (FileInputStream fileInputStream = new FileInputStream("/Users/mr.l/Desktop/test")) {
  4. // 返回 FileDescriptor 对象代表着文件系统中的真实文件的链接。
  5. FileDescriptor fd = fileInputStream.getFD();
  6. System.out.println("File descriptor of the file /Users/mr.l/Desktop/test.txt : "
  7. + fd.hashCode());
  8. } catch (FileNotFoundException e) {
  9. e.printStackTrace();
  10. } catch (IOException e) {
  11. e.printStackTrace();
  12. }
  13. try (FileOutputStream fileOutputStream = new FileOutputStream("/Users/mr.l/Desktop/test2")) {
  14. FileDescriptor fd = fileOutputStream.getFD();
  15. System.out.println("File descriptor of the file /Users/mr.l/Desktop/test2.txt : " + fd.hashCode());
  16. } catch (FileNotFoundException e) {
  17. e.printStackTrace();
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }

FileDescriptor 结构

FileDescriptor 有三种输出方式:in、out、error,分别代表

public static final FileDescriptor in = new FileDescriptor(0);

   

一个标准输入流的句柄。通常情况下,这个文件描述符不会直接使用,而是通过称为System.in的输入流。

public static final FileDescriptor out = new FileDescriptor(1);

   

一个标准的输出流句柄。通过 System.out 来使用

public static final FileDescriptor err = new FileDescriptor(2);

   

一个标准的错误流句柄。通过 System.err 来使用

那么如何创建这三种输出流呢?在 FileInputStreamFileOutputStream 中使用其对应的构造方法来创建。

public FileInputStream(FileDescriptor fdObj) {...}

   

文件描述符的创建

我们可以通过如下的方式来创建文件描述符


    
  1. FileInputStream fileInputStream = new FileInputStream(FileDescriptor.in);
  2. fileInputStream.read();
  3. fileInputStream.close();

这段代码创建了一个标准输入流,让你可以从控制台输入信息,它的作用等同于System.in

类似的,outerror 都是文件输出流,你可以按照下面这种方式创建


    
  1. FileOutputStream out = new FileOutputStream(FileDescriptor.out);
  2. out.write('A');
  3. out.close();

它们用于向控制台输出消息,out 的作用等于 System.out,err 的作用等于 System.err。

因此,我们可以等价的将上面的程序转换为如下代码:System.out.print('A'); System.err.print('A');

FileDescriptor 方法与使用

FileDescriptor 的方法比较少,下面就一起看一下 FileDescriptor 都包含了哪些方法

sync

public native void sync() throws SyncFailedException;

   

此方法是一个 native 方法,由 C 语言实现,它的主要用处是说:强制所有系统缓冲区与基础设备同步。该方法在此 FileDescriptor 的所有修改数据和属性都写入相关设备后返回。如果此 FileDescriptor 引用物理存储介质,比如文件系统中的文件,则一直要等到将与此 FileDesecriptor 有关的缓冲区的所有内存中修改副本写入物理介质中,sync 方法才会返回。sync 方法由要求物理存储(比例文件)处于某种已知状态下的代码使用。例如,提供简单事务处理的类可以使用 sync 来确保某个文件所有由给定事务造成的更改都记录在存储介质上。sync 只影响此 FileDescriptor 的缓冲区下游。如果正通过应用程序(例如,通过一个 BufferedOutputStream 对象)实现内存缓冲,那么必须在数据受 sync 影响之前将这些缓冲区刷新,并转到 FileDescriptor 中(例如,通过调用 OutputStream.flush)。

它的一般用法是


    
  1. public static void main(String[] args) throws IOException {
  2. FileDescriptor descriptor = null;
  3. FileOutputStream outputStream = null;
  4. byte[] buffer = {71,69,69,75,83};
  5. try {
  6. outputStream = new FileOutputStream("/Users/mr.l/Desktop/test3");
  7. descriptor = outputStream.getFD();
  8. outputStream.write(buffer);
  9. descriptor.sync();
  10. }catch(Exception excpt) {
  11. excpt.printStackTrace();
  12. }
  13. finally{
  14. if(outputStream != null)
  15. outputStream.close();
  16. }
  17. }

valid

测试此文件描述符对象是否有效。如果文件描述符对象代表着 有效的开放文件,套接字或者其他有效的 I/O 连接 则返回true ,其他返回 false。它的用法如下


    
  1. FileDescriptor descriptor = null;
  2. try(FileInputStream inputStream = new FileInputStream("/Users/mr.l/Desktop/test3")) {
  3. boolean check = false;
  4. descriptor = inputStream.getFD();
  5. check = descriptor.valid();
  6. System.out.println("check = " + check);
  7. }catch (Exception e){
  8. e.printStackTrace();
  9. }

打印输出 check = true, 因为此文件目前处于开放状态。如果把 valid() 方法放在 inputStream.close()方法后呢,就如下所表示的


    
  1. FileInputStream inputStream = null;
  2. FileDescriptor descriptor = null;
  3. boolean check = false;
  4. try {
  5. inputStream = new FileInputStream("/Users/mr.l/Desktop/test3");
  6. descriptor = inputStream.getFD();
  7. check = descriptor.valid();
  8. System.out.println("check = " + check);
  9. }catch (Exception e){
  10. e.printStackTrace();
  11. } finally {
  12. inputStream.close();
  13. check = descriptor.valid();
  14. System.out.println("check = " + check);
  15. }

会输出两条消息

check = true check = false

因为在流关闭之后,文件不在有效,故返回false。

attach

解析 attach 方法前首先来看一下两个接口 Closeable 接口和 AutoCloseable 接口

  • Closeable 接口:Closeable 表示一个资源或者数据能够被关闭,close 方法被调用用来释放对象持有的资源,如果资源已经关闭了,那么调用 close 方法不会再产生作用。

然后回到 FileDescriptor 的描述中来,FileDescriptor 有三个属性


    
  1. private Closeable parent;
  2. private List<Closeable> otherParents;
  3. private boolean closed;

有一个 Closeable 对象的 parent,表示用来关闭单个资源,List<Closeable> otherParents,需要关闭对象的集合,下面源码中会用到, closed用来判断资源是否已经关闭。

attach 源码:


    
  1. synchronized void attach(Closeable c) {
  2. if (parent == null) {
  3. parent = c;
  4. } else if (otherParents == null) {
  5. otherParents = new ArrayList<>();
  6. otherParents.add(parent);
  7. otherParents.add(c);
  8. } else {
  9. otherParents.add(c);
  10. }
  11. }

此方法用于追踪需要关闭的对象,如果只有单个需要关闭的对象,那么直接调用后面的 closeAll() 方法即可,如果多个流指向同一个相同的描述符,FileDescriptor 会把需要关闭的资源放在 otherParents 的集合中,我们会循环list 中的每个引用,并且把它们添加到 parent 后面,这个 parent 顾名思义相当于就是 第一个需要被关闭的资源,这个方法主要为下面的 closeAll() 方法做铺垫。

closeAll


    
  1. synchronized void closeAll(Closeable releaser) throws IOException {
  2. if (!closed) {
  3. closed = true;
  4. IOException ioe = null;
  5. try (Closeable c = releaser) {
  6. if (otherParents != null) {
  7. for (Closeable referent : otherParents) {
  8. try {
  9. referent.close();
  10. } catch(IOException x) {
  11. if (ioe == null) {
  12. ioe = x;
  13. } else {
  14. ioe.addSuppressed(x);
  15. }
  16. }
  17. }
  18. }
  19. } catch(IOException ex) {
  20. if (ioe != null)
  21. ex.addSuppressed(ioe);
  22. ioe = ex;
  23. } finally {
  24. if (ioe != null)
  25. throw ioe;
  26. }
  27. }
  28. }

在资源没有被关闭的时候,在需要关闭的资源为 null 的情况下,会对需要关闭的资源集合循环遍历进行关闭操作

相关链接

640?wx_fmt=png
点个好看再走吧

文章来源: cxuan.blog.csdn.net,作者:程序员cxuan,版权归原作者所有,如需转载,请联系作者。

原文链接:cxuan.blog.csdn.net/article/details/102634183

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。