深入分析java I/O

简介: I/O 是最为基础重要的一门工程,它是数据信息交换的渠道方式。在爆炸性海量数据的互联网时代,I/O问题尤其重要,往往影响我们系统的性能祸首之一。通过研读jdk代码,画出类层次结构图以及小实例代码说明,便于读者理解

I/O 是最为基础重要的一门工程,它是数据信息交换的渠道方式。在爆炸性海量数据的互联网时代,I/O问题尤其重要,往往影响我们系统的性能祸首之一。
常常有网络I/O网络数据上传与下载能力, 磁盘I/O即是我们硬盘的读写能力。
而I/O类主要在JDK包java.io下,类比较繁多,我们只需要挑一两个来study。根据数据流的格式和方式如下分类:
1)基于字节(抽象类):InputStream 和 OutputStream
2)基于字符(抽象类):Writer 和 Reader
3)基于磁盘文件(类):File
4)基于网络传输(类):Socket

基于字节的I/O操作
InputStream类的相关层级关系
image

代码1


    private static void inputStreamForFile(File file) throws IOException {
        InputStream fis = null;
        try {
            fis = new FileInputStream(file);
/*        int result;
        while ((result = fis.read()) != -1) {
            System.out.print((char)result);
        }*/

            byte[] buffer = new byte[(fis.available())]; // 缓存大小跟文件字节数一致
            fis.read(buffer);
            System.out.println(new String(buffer));
        } finally {
            fis.close();
        }
    }

与InputStream输入字节流相对应的是OutputStream输出字节流
image

代码2

public static void outputStreamToFile(File file) {
    String bufferStr = new String("\n hello from outStream!");
    byte buffer[] = bufferStr.getBytes();
    OutputStream out = null;
    try {
        // 以追加的方式写入文件
        out = new FileOutputStream(file, true);
        out.write(buffer);
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        try {
            out.close();
        } catch (IOException ioEx) {
            System.out.println(ioEx.toString());
        }
    }
}

其中,FileOutputStream(File file, boolean append)
以追加的方式,添加入文件的末尾。
通过类层次可以知道,FileOutputStream 的子类有SocketOutputStream, 即网络输出字节流其实也是以文件流形式进行传输。

基于字符的I/O操作
最小的存储单元是字节而不是字符,所以I/O操作都是针对字节进行存储。那为啥需要引入字符呢?那是因为在我们的程序中,通常操作的数据都是以字符形式,为了操作方便需要提供一个直接写字符的 I/O 接口。

Reader 字符流,它的类层次结构如下
image

代码3

private static void readForFile(File file) throws IOException {
    StringBuffer sb;
    FileReader fileReader = null;
    try {
        sb = new StringBuffer();
        char[] buf = new char[1024];

        fileReader = new FileReader(file);
        while (fileReader.read(buf) > 0) {
            sb.append(buf);
        }
    } finally {
        fileReader.close();
    }
    System.out.println("readForFile,  " + sb.toString());
}

Writer的类层次结构
image

代码4

private static void writeForFile(File file) throws IOException {
    FileWriter fw = null;
    try {
        fw = new FileWriter(file, true);
        fw.write("this is from write");
    } finally {
        fw.close();
    }
}

刚才说了,底层是以最小单元为字节进行存储,而程序中经常以字符来进行操作,所以存在字节与字符的转化,拿InputStream 输入字节流举例,类结构如下
image

如果转换时,没有指定编码,会以默认的字符编码进行转义,默认的是“UTF-8”

  public static Charset defaultCharset() {
    if (defaultCharset == null) {
        synchronized (Charset.class) {
            String csn = AccessController.doPrivileged(
                new GetPropertyAction("file.encoding"));
            Charset cs = lookup(csn);
            if (cs != null)
                defaultCharset = cs;
            else
                defaultCharset = forName("UTF-8");
        }
    }
    return defaultCharset;
}

java 访问磁盘文件
在 java 中通常的 File 并不代表一个真实存在的文件对象,当你指定一个路径描述符时,他就会返回代表这个路径的虚拟对象,它可能是一个真实存在的文件或者一个包含多个文件的文件夹。多数情况下,我们并不关心这是文件是否存在,只关心这个文件到底如何操作。那么什么时候会检查这个文件是否真实存在呢?
只有在真正需要读取文件的时候,才会去检查这个文件是否存在
前文已经提到,文件存盘的最小单位是字节,所以直接和硬盘打交道的是字节流,针对于输入文件字节流FileInputStream,在它实例化时会创建一个 FileDescritor 对象,其实FileDescritor就是代表一个存在的文件对象的描述。当我们在操作一个FileInputStream文件对象时,可以通过其 getFD() 方法来获取FileDescriptor对象。而FileDescriptor是与底层操作系统相关联文件的行为描述。

具体的行为包括如下:

/**
 * Force all system buffers to synchronize with the underlying
 * device.  This method returns after all modified data and
 * attributes of this FileDescriptor have been written to the
 * relevant device(s)…
**/
FileDescriptor.sync() 方法将操作系统缓存中的数据强制刷新到物理磁盘中。
/**
 * Tests if this file descriptor object is valid.
 */
FileDescriptor.valid() 方法将检测描述对象是否有效。

image

当传入一个文件路径时,将会根据这个路径创建一个 File 对象,然后创建StreamDecoder进行编码解码,最后将会真正创建一个关联真实存在的磁盘文件的文件描述符 FileDescriptor,这个对象可以用来操作磁盘文件。

目录
相关文章
|
1月前
|
算法 Java
java面向对象和面向过程分析
java面向对象和面向过程分析
36 0
|
1月前
|
存储 Java 编译器
java和c++的主要区别、各自的优缺点分析、java跨平台的原理的深度解析
java和c++的主要区别、各自的优缺点分析、java跨平台的原理的深度解析
69 0
|
7天前
|
Java 调度
Java中常见锁的分类及概念分析
Java中常见锁的分类及概念分析
13 0
|
7天前
|
Java
Java中ReentrantLock中tryLock()方法加锁分析
Java中ReentrantLock中tryLock()方法加锁分析
9 0
|
24天前
|
人工智能 监控 算法
java智慧城管源码 AI视频智能分析 可直接上项目
Java智慧城管源码实现AI视频智能分析,适用于直接部署项目。系统运用互联网、大数据、云计算和AI提升城市管理水平,采用“一级监督、二级指挥、四级联动”模式。功能涵盖AI智能检测(如占道广告、垃圾处理等)、执法办案、视频分析、统计分析及队伍管理等多个模块,利用深度学习优化城市管理自动化和智能化,提供决策支持。
142 4
java智慧城管源码 AI视频智能分析 可直接上项目
|
2月前
|
算法 前端开发 JavaScript
【JAVA】JDK11新特性个人分析
【JAVA】JDK11新特性个人分析
53 0
|
3月前
|
存储 缓存 Java
深入理解Java中的逃逸分析
深入理解Java中的逃逸分析
45 0
|
7天前
|
Java
Java中关于ConditionObject的分析
Java中关于ConditionObject的分析
17 3
|
11天前
|
设计模式 缓存 安全
分析设计模式对Java应用性能的影响,并提供优化策略
【4月更文挑战第7天】本文分析了7种常见设计模式对Java应用性能的影响及优化策略:单例模式可采用双重检查锁定、枚举实现或对象池优化;工厂方法和抽象工厂模式可通过对象池和缓存减少对象创建开销;建造者模式应减少构建步骤,简化复杂对象;原型模式优化克隆方法或使用序列化提高复制效率;适配器模式尽量减少使用,或合并多个适配器;观察者模式限制观察者数量并使用异步通知。设计模式需根据应用场景谨慎选用,兼顾代码质量和性能。
|
1月前
|
缓存 Java
java使用MAT进行内存分析
java使用MAT进行内存分析