java访问磁盘文件


转载,务必写上原文链接 !(尊重与你分享知识的人)


目录


文件

数据在磁盘中的唯一最小描述就是 文件 ,也就是说应用程序只能通过操控 文件 来操作磁盘上的数据;


File 对象 VS FileDescriptor 对象

初学 java IO 流的时候,基本都被告诉 java 里面用 File 来表示一个文件对象;

其实 File 并不代表一个真实存在的文件对象,当我们给定路径字符串的时候,返回的 File 对象仅仅是代表这个路径下的 虚拟对象,至于这个路径是一个文件还是一个文件夹,或者文件存在不存在,File 根本不关心 ;

只要当真正要读取这个文件的时候,才会检查给定路径的文件存在不存在(这个判断发生在读取流的构造器里面) ;

那么 java 里面,用什么来描述磁盘的文件 —— FileDescriptor 对象,它代表一个磁盘上的真实文件对象;


文件讲解java访问磁盘文件过程

// 创建一个字符读取流
FileReader fileReader = new FileReader("xxxxxx");

0、这一行代码后面都发生了什么事情;

我们要看下,这个 FileReader 到底是怎么工作的,跟进去看下源代码 ;


下面开始源码跟进分析


public FileReader(String fileName) throws FileNotFoundException {
    // 调用父类构造器,传进去的参数是 FileInputStream
        super(new FileInputStream(fileName));
    }
----------------------------------------
// FileReader 的父类是 字节转换流
public class FileReader extends InputStreamReader

1、发现 FileReader 构造器中,是调用其父类 InputStreamReader 的构造器;

分析父类构造器参数发现,给父类构造器传递的参数是一个 FileInputStream 对象;我们同样需要看下这个类的构造器源代码;

public FileInputStream(String name) throws FileNotFoundException {
    // 跟进传进来的字符串,创建对应的 虚拟对象 File
        this(name != null ? new File(name) : null);
    }

2、可以看见 FileInputStream 根据传进来的字符串,创建了 File 对象,然后将这个 File 对象,传给了同类的其他构造器

public FileInputStream(File file) throws FileNotFoundException {
    // 获取文件的路径
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(name);
        }
        // 如果name为null,说明传进来的file就是null。抛出空指针异常
        if (name == null) {
            throw new NullPointerException();
        }
        // 判断文件是否有效,也就是判断给File对象的路径下面的文件是否真的存在的;
        if (file.isInvalid()) {
            throw new FileNotFoundException("Invalid file path");
        }
        // 创建一个代表真实文件的对象
        fd = new FileDescriptor();
        fd.incrementAndGetUseCount();
        this.path = name;
        // 打开路径下面的文件,是一个本地方法
        open(name);
    }

3、经过上面的代码分析,我们可以发现 FileInputStream 创建对象的时候,会对 File 构造器的路径参数进行检查,判断路径对应的文件到底存在与否;并且还会创建一个代表真实文件的 FileDescriptor 对象 ;

创建完 FileInputStream 对象以后,继续回到 FileReader 构造器上面 ;我们在上面的代码,知道 FileReader 是调用了父类 InputStreamReader 的构造器,现在我们关注一下它的父类构造器;

public FileReader(String fileName) throws FileNotFoundException {
    // 调用父类构造器,传进去的参数是 FileInputStream
        super(new FileInputStream(fileName));
    }

----------------------------------------
public InputStreamReader(InputStream in) {
    // 调用了父类构造器,里面没啥好看的,就加了锁
    // 加锁的原因是因为,同一时刻一个文件只允许一个人来读取
        super(in);
        try {
        // 创建一个 StreamDecoder 解码对象,将字节解码为字符
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            // The default encoding should always be available
            throw new Error(e);
        }
    }

4、查看源码,知道创建 InputStreamReader 对象,会同时创建 StreamDecoder 对象,因为我们是用字节流,最后需要的字符,所有需要它来解码

其中关于 StreamDecoder 笔者之前分析过—— StreamDecoder 对象分析(可点击)


fileReader.read()

分析完 上面,我们再看下 read()方法;看看到底是谁实现了read()接口 ;

 FileReader fileReader = new FileReader("xxxxxx");
 // 跟进去看看 read()方法到底是谁实现的;
 fileReader.read();
 ---------------------------------
 // 跟进去发现是调用了 一个 sd变量的read方法;
 public int read() throws IOException {
     return sd.read();
 }
-------------------------------------
// sd的read()方法实现
 private int read0() throws IOException {
        Object var1 = this.lock;
        synchronized(this.lock) {
            if (this.haveLeftoverChar) {
                this.haveLeftoverChar = false;
                return this.leftoverChar;
            } else {
                char[] var2 = new char[2];
                int var3 = this.read(var2, 0, 2);
                switch(var3) {
                case -1:
                    return -1;
                case 0:
                default:
                    assert false : var3;

                    return -1;
                case 2:
                    this.leftoverChar = var2[1];
                    this.haveLeftoverChar = true;
                case 1:
                    return var2[0];
                }
            }
        }
    }

 ---------------------------------
 // 跟进发现 这个sd 是StreamDecoder 对象
private final StreamDecoder sd;

至此,我们发现底层工作都是 StreamDecoder 完成的,这个在背后默默工作的低调者,我们应该记住它;


图解java访问磁盘文件过程

吃饭去,饿死了


posted @ 2018-05-10 19:24  Yiaz  阅读(501)  评论(0编辑  收藏  举报