zoukankan      html  css  js  c++  java
  • 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访问磁盘文件过程

    吃饭去,饿死了


  • 相关阅读:
    [Swift]LeetCode374. 猜数字大小 | Guess Number Higher or Lower
    [Swift]LeetCode371. 两整数之和 | Sum of Two Integers
    [Swift]LeetCode367. 有效的完全平方数 | Valid Perfect Square
    [Swift]LeetCode350. 两个数组的交集 II | Intersection of Two Arrays II
    [Swift]LeetCode349. 两个数组的交集 | Intersection of Two Arrays
    [Swift实际操作]七、常见概念-(9)使用定时组件Timer执行定时任务
    [Swift实际操作]七、常见概念-(8)日历Calendar和时区TimerZone
    浅谈广告交易系统
    浅谈广告交易系统
    6种排序算法的简洁实现:冒泡、选择、插入、归并、快速、堆
  • 原文地址:https://www.cnblogs.com/young-youth/p/11665713.html
Copyright © 2011-2022 走看看