Java IO流之文件描述符FileDescriptor

简介

POSIX标准

先了解一下POSIX标准,POSIX标准是操作系统为应用程序提供的接口,是电子和电气工程协会(简称IEEE)为要在各种UNIX操作系统上运行软件而定义的一系列API标准总称,目的是为了保证应用程序的可移植性.也就是说为一个POSIX操作系统编写的应用程序,在另外一个POSIX操作系统里面依旧可以运行.比如:POSIX标准接口:Open()打开文件,create()创建文件,read()读取文件,write()写入文件等接口,如果在以POSIX标准的操作系统编写的应用程序调用到了Open()接口(操作系统提供接口),那么应用程序移植到别的以POSIX标准的操作系统,同样也可以运行.因为调用操作系统接口都是一样的.都是Open().

文件描述符

对内核而言,所有打开的文件(或者socket)都是由文件描述符引用,打开一个现存的文件或者是创建一个新文件时,内核会向当前进程返回一个文件描述符,那么根据文件描述符就可以找到对应的文件。比如读写一个文件的简单步骤:

  1. 应用程序调用POSIX标准操作系统接口open()或者create(),方法将会返回文件描述符来标识该文件,
  2. 将文件描述符的值传递给应用程序中的read()或者write().文件描述符的值结合流就可以读取数据或者写入数据到文件中。

文件描述符是一个非负的整数,在POSIX标准的应用程序中,整数0,1,2分别表示的是标准输入,标准输出,标准错误输出的描述符.最早期的UNIX版本中最大值是19(也就是容许每个进程打开20个文件),现在系统都有所增加,比如Linux是1024,在java中虽然设计使用的是抽象度更高的流,但是依然需要文件描述符与操作系统进行交互.

FileDescriptor介绍

在java中FileDescriptor表示的是"文件描述符",对于文件描述符中0,1,2分别表示如下:

  整数值    名称
0 标准输入描述符

1

标准输出描述符
2 标准错误输出描述符

对于三个"文件描述符",用法基本上是相似的.比如标准输出描述符,看FileDescriptor源码,调用的方式是FileDescriptor.out.但是由于FileDescriptor没有直接输出屏幕的接口,需要借助输出流对象,通过调用输出流的方法write()方法,可以将对应的字符输出到控制台上,案例如下:

public class FileDescriptorDemo {
  public static void main(String[] args) throws Exception {
    FileOutputStream fos = new FileOutputStream(FileDescriptor.out);
    fos.write("abcd".getBytes());
    fos.close();
  }
}

运行的结果是:

abcd

当然在java中已经有相应封装的接口,可以直接输出到控制台:System.out.println("abcd").其余标准输入描述符对应是System.in,而标准错误输出对应的是System.err.

源码分析

public final class FileDescriptor {
    //打开的文件或者socket时,简单说操作系统会返回一个fd值,通过该值可以对文件,socket等进行相关操作,
    private int fd;
    
    //关联的流只有一个,则保存到parent中
    private Closeable parent;
    
    //关联的流很多,则保存到集合otherParents中
    private List otherParents;
    
    //用于判断是否释放文件文件引用
    private boolean closed;
 
    //无参数构造方法,fd赋值为-1, 由于java中文件描述符是大于0的整数,所以无意义.
    public  FileDescriptor() {
        fd = -1;
    }
    //有参构造方法,由于修饰符是private,无法设置fd
    private  FileDescriptor(int fd) {
        this.fd = fd;
    }
 
    //标准的输入,文件描述符用0表示
    public static final FileDescriptor in = new FileDescriptor(0);
 
    //标准的输出,文件描述符用1表示
    public static final FileDescriptor out = new FileDescriptor(1);
 
    //标准错误输出,文件描述符用2表示
    public static final FileDescriptor err = new FileDescriptor(2);
    
 
    //判断是否有效,非-1有效.所以无参构造方法中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集合中.
    //结合下面closeAll()方法,释放文件描述符的引用,同时也会将对应所有的流关闭
    //因为文件描述符释放的情况下,流的存在也无意义
    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);
        }
    }
 
    //关闭所有与文件描述符关联的所有的流对象
    @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;
            }
        }
    }
}

总结:

1.文件描述符就是打开文件或socket时,操作系统返回的一个int类型值.会根据文件描述符结合流操作对应的文件或者socket.

2.文件描述符是一个非负的整数,其中0,1,2分别表示的标准输入,标准输出和标准错误输出.在java中已经有相关接口封装,分别是System.in;System.out;System.err.

 

 

 

你可能感兴趣的:(Java,IO流)