前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java-Java I/O流解读之基于字节的I / O和字节流

Java-Java I/O流解读之基于字节的I / O和字节流

作者头像
小小工匠
发布于 2021-08-17 01:59:25
发布于 2021-08-17 01:59:25
1.1K00
代码可运行
举报
文章被收录于专栏:小工匠聊架构小工匠聊架构
运行总次数:0
代码可运行

概述

编写简单的 I / O操作很简单,仅仅涉及到很少的类和方法。 但是编写高效,便携式I / O非常困难,特别是如果我们必须处理不同的字符集。 这就解释了为什么有这么多的I / O包(在JDK 1.7中有9个)!

JDK有两套 I / O 包:

  • 自JDK 1.0引入的基于流的I / O的标准I / O(在包java.io中)
  • 在JDK 1.4中引入的新的I / O(在java.nio包中)用于更有效的基于缓冲区的I / O

JDK 1.5通过新类java.util.Scanner和Formatter引用格式化的文本I / O,并使用格式说明符格式化输出的C-like printf()和format()方法.

JDK 1.7通过新的包java.nio.file及其辅助包中的所谓的NIO.2(非阻塞I / O)来增强对文件I / O的支持。 它还引入了一种新的try-with-resources语法来简化close()方法的编码。


File and Directory

Class java.io.File (Pre-JDK 7)

java.io.File 可以表述一个文件 或者一个目录 (JDK1.7中 引入了一个更加强大的接类 java.nio,file.Path,克服了java.io.File中的很多限制。)

路径字符串用于定位文件或目录。 不幸的是,路径字符串是系统相关的

例如: Windows中的“c:\myproject\java\Hello.java” Unix / Mac中的“/myproject/java/Hello.java”

  • 目录分隔符: Windows使用反斜杠’\’作为目录分隔符; 而Unixes / Mac使用前斜杠’/’。
  • 路径分隔符:Windows使用分号’;’ 作为路径分隔符来分隔路径列表; 而Unixes / Mac使用冒号’:’
  • 行分隔符:Windows中使用“ \r\n”作为行分隔符的文本文件; 而Unix系统使用“ \n”和Mac使用“ \r”
  • 根目录:在“ c:\”或“ \”被称为根。Windows支持多根,每一个映射到一个驱动器(例如,“ c:\”,“ d:\“)。而Unix / Mac有一个根(” \“)

文件路径可以是绝对的(从根开始)或相对(相对于引用目录)。 特殊符号 ...分别表示当前目录和父目录。

java.io.File类维护这些依赖于系统的属性,我们可以编写可移植的程序

  • 目录分隔符: 静态字符串字段 File.separator 和 File.separatorChar [他们没有遵循从JDK 1.2采用的常规Java命名约定。]如上所述,Windows使用反斜杠’\’; 而Unixes / Mac使用正斜杠’/’。
  • 路径分隔符:静态字符串字段 File.pathSeparator和 File.pathSeparatorChar。 如前所述,Windows使用分号’;’ 分隔路径列表; 而Unixes / Mac使用冒号’:’。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SeparatorTest {
    public static void main(String[] args) {
        System.out.println(File.pathSeparator);
        System.out.println(File.pathSeparatorChar);

        System.out.println(File.separator);
        System.out.println(File.separatorChar);
    }
}

windows 环境下输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
;
;
\
\

我们可以使用路径字符串或URI构造一个File实例,如下所示。 请注意,物理文件/目录可能存在或可能不存在。 文件URL采用file:// …的形式,例如file:/// d:/docs/programming/java/test.html。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public File(String pathString)
public File(String parent, String child)
public File(File parent, String child)
// Constructs a File instance based on the given path string.

public File(URI uri)
//Constructs a File instance by converting from the given file-URI "file://...."

举例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// A file relative to the current working directory
File file1 = new File("xgj.txt");

// A file with absolute path D:\workspace\ws-java-base\JavaMaster
File file2 = new File("D:\\xgj.txt");

// A file with parent and child
File parent = new File("D:\\temp");
File child = new File(parent, "xgj.txt");

// A directory
File dir = new File("D:\\temp");

如果你打算将应用封装成jar包,应该使用URL类来引用资源,因为它可以引用磁盘文件以及JAR文件,例如,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java.net.URL url = this.getClass().getResource("icon.png");

Verifying Properties of a File/Directory

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public boolean exists()       // Tests if this file/directory exists.
public long length()          // Returns the length of this file.
public boolean isDirectory()  // Tests if this instance is a directory.
public boolean isFile()       // Tests if this instance is a file.
public boolean canRead()      // Tests if this file is readable.
public boolean canWrite()     // Tests if this file is writable.
public boolean delete()       // Deletes this file/directory.
public void deleteOnExit()    // Deletes this file/directory when the program terminates.
public boolean renameTo(File dest) // Renames this file.
public boolean mkdir()        // Makes (Creates) this directory.

List Directory

如果是目录的话,我们可以通过下面的方法列出内容

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public String[] list()     // List the contents of this directory in a String-array
public File[] listFiles()  // List the contents of this directory in a File-array

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.File;

/**
 * 
 * 
 * @ClassName: ListDirectoryRecusive
 * 
 * @Description: Recursively list the contents of a directory
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月6日 下午6:27:01
 */
public class ListDirectoryRecusive {

    public static void main(String[] args) {
        File dir = new File("D:\\OneDrive");
        listDirectoryRecusive(dir);
    }

    /**
     * 
     * 
     * @Title: listDirectoryRecusive
     * 
     * @Description: 遍历目录下的内容(包括子孙目录)
     * 
     * @param dir
     * 
     * @return: void
     */
    public static void listDirectoryRecusive(File dir) {
        if (dir.isDirectory()) {
            File[] items = dir.listFiles();
            for (File item : items) {
                System.out.println(item.getAbsolutePath());
                // Recursive call 如果目录下还有目录
                if (item.isDirectory()) {
                    listDirectoryRecusive(item);
                }
            }
        }
    }
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
D:\OneDrive\desktop.ini
D:\OneDrive\公开
D:\OneDrive\公开\2.txt
D:\OneDrive\图片
D:\OneDrive\图片\1.txt
D:\OneDrive\文档
D:\OneDrive\文档\MasterJava.pptx
D:\OneDrive\文档\新建
D:\OneDrive\文档\新建\2334.mpp

List Directory with Filter

当然了我们也可以将过滤器应用于list()和listFiles(),以仅列出满足特定条件的文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public String[] list(FilenameFilter filter)
public File[] listFiles(FilenameFilter filter)
public File[] listFiles(FileFilter filter)

java.io.FilenameFilter接口定义了一个抽象方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public boolean accept(File dirName, String fileName)

list()和listFiles()方法对所生成的每个文件/子目录执行回调accept()。 我们可以在accept()中编写过滤条件。 不符合的文件/子目录将被排除。

示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.File;
import java.io.FilenameFilter;

import org.junit.Test;

/**
 * 
 * 
 * @ClassName: ListDirectoryWithFilter
 * 
 * @Description: List files that end with "pptx"
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月6日 下午7:50:13
 */
public class ListDirectoryWithFilter {
    /**
     * 
     * 
     * @Title: listDirectoryWithFilter
     * 
     * @Description: 遍历某个目录下所有以pptx结尾的文件或者目录
     * 
     * @param dir
     * 
     * @return: void
     */
    public void listDirectoryWithFilter(File dir) {
        if (dir.isDirectory()) {
            // List only files that meet the filtering criteria
            // programmed in accept() method of FilenameFilter.
            String[] files = dir.list(new FilenameFilter() {
                @Override
                public boolean accept(File dir, String file) {
                    return file.endsWith("pptx");
                }
            });// an anonymous inner class as FilenameFilter

            for (String file : files) {
                System.out.println(file);
            }
        }
    }

    @Test
    public void test() {
        listDirectoryWithFilter(new File("D:\\OneDrive\\文档"));
    }

}

输出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
MasterJava.pptx
pptx

Class java.nio.file.Path (JDK 7)

另外开篇阐述


Stream I/O in Standard I/O (java.io Package) 标准I / O中的流I / O(java.io包)

程序从数据源(例如键盘,文件,网络,存储器缓冲器或另一程序)读取输入,并向数据宿(例如,显示控制台,文件,网络,存储器缓冲器或另一程序)写入输出。

在Java标准I / O中,输入和输出由所谓的流(Stream)处理。

流是连续的单向数据流(就像水或油流过管道)。重要的是要提到Java不区分流I / O中的各种类型的数据源或汇(例如文件或网络)。它们都被视为一个顺序的数据流。输入和输出流可以从任何数据源/汇点(如文件,网络,键盘/控制台或其他程序)建立。

Java程序通过打开输入流从源接收数据,并通过打开输出流将数据发送到宿。

所有Java I / O流都是单向的(除了RandomAccessFile,稍后将讨论)

如果你的程序需要执行输入和输出,则必须打开两个流 - 输入流和输出流。

操作I / O流,分为3步:

  1. 通过构建适当的I / O流实例来打开与物理设备(例如,文件,网络,控制台/键盘)相关联的输入/输出流。
  2. 从已打开的输入流读取,直到遇到“end-of-stream”,或写入打开的输出流(并可选地刷新缓冲输出)。
  3. 关闭输入/输出流。

Java的I / O操作比C / C ++更复杂以支持国际化(i18n)。 Java内部将字符(字符类型)存储在16位UCS-2字符集中。 但外部数据源/接收器可以将字符存储在其他字符集(例如US-ASCII,ISO-8859-x,UTF-8,UTF-16等等)中,固定长度为8位或16位, 位或以1到4字节的可变长度。 [读取“字符集和编码方案”]。

因此,Java需要区分用于处理原始字节或二进制数据的基于字节的I / O以及用于处理由字符组成的文本的基于字符的I / O。


Byte-Based I/O & Byte Streams 基于字节的I / O和字节流

字节流用于从外部设备串行读取/写入原始字节。 所有字节流都是从抽象超类InputStream和OutputStream派生的,如类图所示。


Reading from an InputStream

抽象类 InputStream中声明了一个抽象方法read() 从输入源读取一个数据字节

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract int read() throws IOException;

read()方法:

  • 返回输入字节读取为int,范围为0到255,或
  • 如果检测到“流结束”条件,则返回-1
  • 如果遇到I / O错误,则抛出IOException。

read()方法返回一个int而不是一个字节,因为它使用-1表示流末尾。 read()方法将会block,直到一个字节可用或者发生I / O错误或检测到“end-of-stream”。 术语“block”表示方法(和程序)将被暂停。 只有当方法返回时,程序才会恢复。

在InputStream中实现了两种read()方法,用于将字节块读入字节数组。 它返回读取的字节数. 如果“end-of-stream”遇到,则返回-1。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Read "length" number of bytes, store in bytes array starting from offset of index.
public int read(byte[] bytes, int offset, int length) throws IOException

// Same as read(bytes, 0, bytes.length)
public int read(byte[] bytes) throws IOException

Writing to an OutputStream

与输入对应类似,抽象超类OutputStream声明一个抽象方法write()来将数据字节写入输出接收器。 write()入参为一个int。 int参数的最低有效字节被写出; 高于3字节被丢弃。 如果发生I / O错误(例如,输出流已关闭),则会抛出IOException。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void abstract void write(int unsignedByte) throws IOException

和 InputStream中的read()相似,OutputStream中实现了两种write()方法从字节数组中写入一个字节块

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Write "length" number of bytes, from the bytes array starting from offset of index.
public void write(byte[] bytes, int offset, int length) throws IOException

// Same as write(bytes, 0, bytes.length)
public void write(byte[] bytes) throws IOException

Opening & Closing I/O Streams

我们通过构建流的实例来打开I / O流。 InputStream和OutputStream都提供了一个close()方法来关闭流,该流执行必要的清理操作以释放系统资源。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void close() throws IOException  // close this Stream

通过在try-catch-finally的finally子句中运行close()来明确地关闭I / O流是一个很好的做法,以便在不再需要流时立即释放系统资源。 这可以防止严重的资源泄漏。

不幸的是,close()方法也抛出一个IOException,并且需要包含在一个嵌套的try-catch语句中,如下所示。 这使得代码变得很丑陋。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
InputStream in = null;

        try {
            in = new FileInputStream(...);
            ....
            ....
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

JDK 1.7引入了一种新的try-with-resources语法,它会在try或catch之后自动关闭所有打开的资源,如下所示。 这样的代码看起来更加的优雅。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Automatically closes all opened resource in try (...).
try (FileInputStream in = new FileInputStream(...)) {
    ...
    ....
} catch (Exception e) {
    e.printStackTrace();
}

Flushing the OutputStream

OutputStream提供了一个flush()方法来从输出缓冲区中刷新剩余的字节。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void flush() throws IOException  // Flush the output

Implementations of abstract InputStream/OutputStream

InputStream和OutputStream是不能被实例化的抽象类。 我们需要选择一个适当的具体子类来建立与物理设备的连接。 例如,可以实例化FileInputStream或FileOutputStream以建立到物理磁盘文件的流。


Layered (or Chained) I/O Streams 分层(或链接)I / O流

I / O流通常与其他I / O流分层或链接,用于缓冲,过滤或数据格式转换(原始字节和原始类型之间)的目的。 例如,我们可以将BufferedInputStream分层到一个FileInputStream进行缓冲输入,并在前面堆叠一个DataInputStream进行格式化数据输入(使用诸如int,double等原语),如下图所示。


File I/O Byte-Streams - FileInputStream & FileOutputStream

FileInputStream和FileOutputStream是抽象类InputStream和OutputStream的具体实现,用于从磁盘文件支持I / O。


Buffered I/O Byte-Streams - BufferedInputStream & BufferedOutputStream

InputStream / OutputStream中的read()/ write()方法旨在读/写每个调用的单字节数据。 这是非常低效的,因为每个调用由底层操作系统处理(可能会触发磁盘访问或其他昂贵的操作)。 在单个I / O操作中,从外部设备读取/写入内存缓冲区的缓冲区通常用于加速I / O。

FileInputStream / FileOutputStream没有缓冲。 它通常链接到BufferedInputStream或BufferedOutputStream,它提供缓冲。 要将流链接在一起,只需将一个流的实例传递到另一个流的构造函数中。

例如,以下代码将FileInputStream链接到BufferedInputStream,最后是一个DataInputStream

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
FileInputStream in = new FileInputStream(new File("D:\\xgj.txt"));

BufferedInputStream bis = new BufferedInputStream(in);

DataInputStream dis = new DataInputStream(bis);
// or 
DataInputStream dis2 = new DataInputStream(
                    new BufferedInputStream(new FileInputStream(new File("D:\\xgj.txt"))));

示例:

Example 1: Copying a file byte-by-byte without Buffering.

maven工程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.junit.Test;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

/**
 * 
 * 
 * @ClassName: FileCopyNoBuffer
 * 
 * @Description: FileCopyNoBuffer
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月6日 下午10:53:17
 */
public class FileCopyNoBufferPreJDK7 { // Pre-JDK 7

    @Test
    public void test() {
        // 利用Spring提供的Resource/ResourceLoader接口操作资源文件
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource resource = resourcePatternResolver
                .getResource("classpath:com/xgj/master/java/io/fileDemo/SQL.Cookbook.pdf");

        FileInputStream in = null;
        FileOutputStream out = null;
        long startTime, elapsedTime; // for speed benchmarking

        try {
            // Print file length
            File fileIn = resource.getFile();
            System.out.println("File size is " + fileIn.length() / 1024 / 1024 + " MB");

            in = new FileInputStream(fileIn);
            out = new FileOutputStream(new File("D:\\NoBufferPreJDK7.pdf"));

            startTime = System.nanoTime();

            int byteRead;
            // Read a raw byte, returns an int of 0 to 255.
            while ((byteRead = in.read()) != -1) {
                // Write the least-significant byte of int, drop the upper 3
                // bytes
                out.write(byteRead);
            }
            // cost time
            elapsedTime = System.nanoTime() - startTime;
            System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // JDK7之前的写法,在finally中关闭流
            try {
                if (in != null)
                    in.close();
                if (out != null)
                    out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}

此示例通过从输入文件读取一个字节并将其写入输出文件来复制文件。 它直接使用FileInputStream和FileOutputStream进行缓冲。

请注意,大多数I / O方法“抛出”IOException,它必须被捕获或声明为抛出。

close()方法在finally子句中,确保try 或者catch后能够关闭流。 然而,close()方法也会抛出一个IOException,因此必须封装在一个嵌套的try-catch块中,这样使代码有点难看。

在这里使用了 JDK 1.5中引入的System.nanoTime()来更准确地测量经过的时间,而不是传统的不精确的System.currentTimeMillis()。

如前所述,JDK 1.7引入了一种新的try-with-resources语法,它会在try或catch之后自动关闭所有打开的资源。 例如,上述示例可以按照如下方式重新编写:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.junit.Test;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

/**
 * 
 * 
 * @ClassName: FileCopyNoBufferJDK7
 * 
 * @Description: FileCopyNoBufferJDK7
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年9月6日 下午11:02:50
 */
public class FileCopyNoBufferJDK7 {

    @Test
    public void test() {
        // 利用Spring提供的ResourcePatternResolver操作资源文件 同时支持Ant风格的资源路径表达式
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource resource = resourcePatternResolver.getResource("classpath:com/xgj/master/java/io/fileDemo/281015.jpg");

        long startTime, elapsedTime; // for speed benchmarking

        // DK 1.7引入了一种新的try-with-resources语法,
        // 它会在try或catch之后自动关闭所有打开的资源,如下所示。
        // 这样的代码看起来更加的优雅。
        try (FileInputStream in = new FileInputStream(resource.getFile());
                FileOutputStream out = new FileOutputStream(new File("D:\\test.jpg"))) {
            // Print file length
            File fileIn = resource.getFile();
            System.out.println("File size is " + fileIn.length() / 1024 / 1024 + " MB");

            startTime = System.nanoTime();

            int byteRead;
            // Read a raw byte, returns an int of 0 to 255.
            while ((byteRead = in.read()) != -1) {
                // Write the least-significant byte of int, drop the upper 3
                // bytes
                out.write(byteRead);
            }
            // cost time
            elapsedTime = System.nanoTime() - startTime;
            System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

运行结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
File size is 26 MB
Elapsed Time is 333595.85495 msec

耗费 333595.85495 毫秒 大约是 333S ,也就是5分半钟……


Example 2: Copying a file with a Programmer-Managed Buffer. 使用自定义的Buf

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import org.junit.Test;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

public class FileCopyUserBufferPreJDK7 {

    @Test
    public void test() {
        // 利用Spring提供的ResourcePatternResolver操作资源文件 同时支持Ant风格的资源路径表达式
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource = resolver.getResource("com/xgj/master/java/io/fileDemo/SQL.Cookbook.pdf");

        FileInputStream in = null;
        FileOutputStream out = null;

        long startTime, elapsedTime; // for speed benchmarking

        try {

            // Print file length
            File fileIn = resource.getFile();
            System.out.println("File size is " + fileIn.length() / 1024 / 1024 + " MB");

            in = new FileInputStream(fileIn);
            out = new FileOutputStream(new File("D:\\FileCopyUserBufferPreJDK7.pdf"));

            startTime = System.nanoTime();

            // 4K byte-buffer 每次读取4K
            byte[] byteBuf = new byte[4096];

            int numByteRead;
            while ((numByteRead = in.read(byteBuf)) != -1) {
                out.write(byteBuf, 0, numByteRead);
            }
            // cost time
            elapsedTime = System.nanoTime() - startTime;
            System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流 切记
            try {
                if (in != null)
                    in.close();
                if (out != null)
                    out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

运行结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
File size is 26 MB
Elapsed Time is 198.8157 msec

此示例再次直接使用FileInputStream和FileOutputStream。 但是,它不是一次读取/写入一个字节,而是读/写一个4KB块。 这个程序只花了198毫秒 , 和第一个不使用缓冲 ,速度相比提高了1600多倍,效率不言而喻。

较大的缓冲区大小,达到一定限度,通常会提高I / O性能。 然而,在加速和内存使用之间存在权衡。 对于文件复制,肯定建议使用大型缓冲区。 但是,从文件读取几个字节,大型缓冲区只会浪费内存。 我使用JDK 1.7重新编写程序,并尝试在26MB的更大的文件上的各种缓冲区大小,如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.junit.Test;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

public class FileCopyUserBufferLoopJDK7 {

    @Test
    public void test() {

        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource = resolver.getResource("com/xgj/master/java/io/fileDemo/SQL.Cookbook.pdf");

        long startTime = 0, elapsedTime; // for speed benchmarking

        int[] bufSizeKB = { 1, 2, 4, 8, 16, 32, 64, 256, 1024 }; // in KB
        int bufSize; // in bytes

        for (int run = 0; run < bufSizeKB.length; ++run) {
            bufSize = bufSizeKB[run] * 1024;
            try (FileInputStream in = new FileInputStream(resource.getFile());
                    FileOutputStream out = new FileOutputStream("D:\\FileCopyUserBufferLoopJDK7.pdf")) {
                startTime = System.nanoTime();
                byte[] byteBuf = new byte[bufSize];
                int numBytesRead;
                while ((numBytesRead = in.read(byteBuf)) != -1) {
                    out.write(byteBuf, 0, numBytesRead);
                }
                elapsedTime = System.nanoTime() - startTime;
                System.out.printf("%4dKB: %6.2fmsec%n", bufSizeKB[run], (elapsedTime / 1000000.0));

            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   1KB: 560.22msec
   2KB: 265.76msec
   4KB: 130.43msec
   8KB:  77.81msec
  16KB:  52.49msec
  32KB:  38.99msec
  64KB:  30.66msec
 256KB:  31.00msec
1024KB:  73.45msec

从结果中可以看出一些端倪,并不是缓冲区越大效率要高,存在某个平衡。


Example 3: Copying a file with Buffered Streams.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import org.junit.Test;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

public class FileCopyBufferedStreamPreJDK7 {

    @Test
    public void test() {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource = resolver.getResource("com/xgj/master/java/io/fileDemo/SQL.Cookbook.pdf");

        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        long startTime = 0, elapsedTime; // for speed benchmarking

        try {
            bis = new BufferedInputStream(new FileInputStream(resource.getFile()));
            bos = new BufferedOutputStream(new FileOutputStream("D:\\artisan.pdf"));

            startTime = System.nanoTime();

            int byteRead;
            // Read byte-by-byte from buffer
            while ((byteRead = bis.read()) != -1) {
                bos.write(byteRead);
            }
            elapsedTime = System.nanoTime() - startTime;
            System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally { // always close the streams
            try {
                if (bis != null) {
                    bis.close();
                }
                if (bos != null) {
                    bis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Elapsed Time is 1432.864941 msec

在这个例子中,我用BufferedInputStream连接FileInputStream,使用BufferedOutputStream连接FileOutputStream,并逐个字节读/写。 JRE决定缓冲区大小。 该程序花费了1432.864941 毫秒,比例1高出,但慢于我们自定义管理的缓冲区。

JDK 1.7版本的例子如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.junit.Test;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

public class FileCopyBufferedStreamJDK7 {

    @Test
    public void test() {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource = resolver.getResource("com/xgj/master/java/io/fileDemo/SQL.Cookbook.pdf");

        long startTime = 0, elapsedTime; // for speed benchmarking

        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(resource.getFile()));
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("D:\\xgj_2.pdf")))) {

            // Check file length
            File fileIn = resource.getFile();
            System.out.println("File size is " + fileIn.length() + " bytes");

            startTime = System.nanoTime();
            int byteRead;
            while ((byteRead = bis.read()) != -1) {
                bos.write(byteRead);
            }
            elapsedTime = System.nanoTime() - startTime;
            System.out.println("Elapsed Time is " + (elapsedTime / 1000000.0) + " msec");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Formatted Data-Streams: DataInputStream & DataOutputStream

DataInputStream和DataOutputStream可以堆叠在任何InputStream和OutputStream之上,以解析原始字节,以便以所需的数据格式(如int和double)执行I / O操作。

DataInputStream 是数据输入流。它继承于FilterInputStream。 DataInputStream 是用来装饰其它输入流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。 应用程序可以使用DataOutputStream(数据输出流)写入由DataInputStream(数据输入流)读取的数据。

要使用DataInputStream进行格式化输入,可以按如下方式链接输入流:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DataInputStream in = new DataInputStream(
                        new BufferedInputStream(
                           new FileInputStream("in.dat")));

DataInputStream实现了DataInput接口,它提供了读取格式化的原始数据和字符串的方法,如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 8 Primitives
public final int readInt() throws IOExcpetion;       // Read 4 bytes and convert into int
public final double readDoube() throws IOExcpetion;  // Read 8 bytes and convert into double 
public final byte readByte() throws IOExcpetion;
public final char readChar() throws IOExcpetion;
public final short readShort() throws IOExcpetion;
public final long readLong() throws IOExcpetion;
public final boolean readBoolean() throws IOExcpetion;    // Read 1 byte. Convert to false if zero
public final float readFloat() throws IOExcpetion;

public final int readUnsignedByte() throws IOExcpetion;   // Read 1 byte in [0, 255] upcast to int
public final int readUnsignedShort() throws IOExcpetion;  // Read 2 bytes in [0, 65535], same as char, upcast to int
public final void readFully(byte[] b, int off, int len) throws IOException;
public final void readFully(byte[] b) throws IOException;

// Strings
public final String readLine() throws IOException;
     // Read a line (until newline), convert each byte into a char - no unicode support.
public final String readUTF() throws IOException;
    // read a UTF-encoded string with first two bytes indicating its UTF bytes length

public final int skipBytes(int n)  // Skip a number of bytes

同样,你可以按如下方式堆叠DataOutputStream:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
DataOutputStream out = new DataOutputStream(
                          new BufferedOutputStream(
                             new FileOutputStream("out.dat")));

DataOutputStream实现DataOutput接口,它提供写入格式化的原始数据和String的方法。 例如,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 8 primitive types
public final void writeInt(int i) throws IOExcpetion;      // Write the int as 4 bytes
public final void writeFloat(float f) throws IOExcpetion;
public final void writeDoube(double d) throws IOExcpetion; // Write the double as 8 bytes
public final void writeByte(int b) throws IOExcpetion;     // least-significant byte
public final void writeShort(int s) throws IOExcpetion;    // two lower bytes
public final void writeLong(long l) throws IOExcpetion;
public final void writeBoolean(boolean b) throws IOExcpetion;
public final void writeChar(int i) throws IOExcpetion;

// String
public final void writeBytes(String str) throws IOExcpetion;  
     // least-significant byte of each char
public final void writeChars(String str) throws IOExcpetion;
     // Write String as UCS-2 16-bit char, Big-endian (big byte first)
public final void writeUTF(String str) throws IOException;   
     // Write String as UTF, with first two bytes indicating UTF bytes length

public final void write(byte[] b, int off, int len) throws IOException
public final void write(byte[] b) throws IOException
public final void write(int b) throws IOException     // Write the least-significant byte

举例: 以下程序将一些原始语写入磁盘文件。 然后,它读取原始字节以检查原始语的存储方式。 最后,它读取数据作为原始语。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.xgj.master.java.io.fileDemo;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

public class DataIOStream {
    public static void main(String[] args) {

        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource = resolver.getResource("com/xgj/master/java/io/fileDemo/data-out.txt");
        String message = "Hi,您好!";

        // Write primitives to an output file
        try (DataOutputStream out = new DataOutputStream(
                new BufferedOutputStream(new FileOutputStream(resource.getFile())))) {
            out.writeByte(127);
            out.writeShort(0xFFFF); // -1
            out.writeInt(0xABCD);
            out.writeLong(0x1234_5678); // JDK 7 syntax
            out.writeFloat(11.22f);
            out.writeDouble(55.66);
            out.writeBoolean(true);
            out.writeBoolean(false);
            for (int i = 0; i < message.length(); ++i) {
                out.writeChar(message.charAt(i));
            }
            out.writeChars(message);
            out.writeBytes(message);
            out.flush();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        // Read raw bytes and print in Hex
        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(resource.getFile()))) {
            int inByte;
            while ((inByte = in.read()) != -1) {
                System.out.printf("%02X ", inByte); // Print Hex codes
            }
            System.out.printf("%n%n");
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        // Read primitives
        try (DataInputStream in = new DataInputStream(
                new BufferedInputStream(new FileInputStream(resource.getFile())))) {
            System.out.println("byte:    " + in.readByte());
            System.out.println("short:   " + in.readShort());
            System.out.println("int:     " + in.readInt());
            System.out.println("long:    " + in.readLong());
            System.out.println("float:   " + in.readFloat());
            System.out.println("double:  " + in.readDouble());
            System.out.println("boolean: " + in.readBoolean());
            System.out.println("boolean: " + in.readBoolean());

            System.out.print("char:    ");
            for (int i = 0; i < message.length(); ++i) {
                System.out.print(in.readChar());
            }
            System.out.println();

            System.out.print("chars:   ");
            for (int i = 0; i < message.length(); ++i) {
                System.out.print(in.readChar());
            }
            System.out.println();

            System.out.print("bytes:   ");
            for (int i = 0; i < message.length(); ++i) {
                System.out.print((char) in.readByte());
            }
            System.out.println();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

输出结果分析:

存储在磁盘中的数据与内部Java程序的格式完全相同(例如,用于字符的UCS-2)。 字节顺序是big-endian(大字节优先,最低地址中最高有效字节)。


代码

代码已托管到Github—> https://github.com/yangshangwei/JavaMaster

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017/09/06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
前端工程师所需要了解的WebView
| 导语 现如今,在做移动端 H5 开发时,少不了与 Native 之间进行交互。而在Native中,H5的承载容器为 WebView,其核心是使用 WebView 控件实现加载 url。
秋风的笔记
2021/03/29
2.3K0
前端工程师所需要了解的WebView
JS Bridge 通信原理
之前介绍了移动端开发的相关技术,这一篇主要是从 Hybrid 开发的 JS Bridge 通信讲起。
尹光耀
2022/03/22
5K0
JS Bridge 通信原理
JSBridge 技术原理分析
PhoneGap(Codova 的前身)作为 Hybrid 鼻祖框架,是一个开源的移动开发框架,允许你用标准的web技术-HTML5,CSS3和JavaScript做跨平台的Hybird WebUI开发,应该是最先被开发者广泛认知的 JSBridge 的应用场景。而对于 JSBridge 的应用在国内真正兴盛起来,则是因为杀手级应用微信的出现。
玄姐谈AGI
2021/11/23
1.9K0
H5和移动端通信
弹窗包括:alert、confirm、prompt, 和url拦截类似都在回调方法中处理
无忧366
2022/10/21
1.4K0
JSBridge深度剖析
概述 做过混合开发的人都知道Ionic和PhoneGap之类的框架,这些框架在web基础上包装一层Native,然后通过Bridge技术的js调用本地的库。 在讲JSBridge技术之前,我们来看一下传统的实现方式。 Android端 Native调JS native调用js比较简单,只要遵循:”javascript: 方法名(‘参数,需要转为字符串’)”的规则即可。 在4.4之前,调用的方式: // mWebView = new WebView(this); mWebView.lo
xiangzhihong
2018/02/06
3.9K0
JSBridge深度剖析
看完就懂的Hybrid框架设计方案
本篇文章探讨“基于 Webview,如何在 App 内实现带离线包能力的 H5”。在当下这个主题似乎有些过时,但 H5 技术以其良好的跨端一致性,长期来看会占据一席之地,希望整理一个较完整的方案,从基本的实现原理到业务具体应用,让不了解的同学对“离线 H5"有一个较完整的视角。
腾讯云开发者
2024/04/12
2.6K0
看完就懂的Hybrid框架设计方案
Hybrid App技术解析 -- 原理篇
引言 随着 Web 技术和移动设备的快速发展,Hybrid 技术已经成为一种最主流最常见的方案。一套好的 Hybrid架构方案 能让 App 既能拥有极致的体验和性能,同时也能拥有 Web技术 灵活的
前端教程
2018/07/27
1.7K0
Hybrid App技术解析 -- 原理篇
WKWebView 那些坑
QQ空间开发团队
2017/08/15
17.6K2
JSBridge小科普
做Hybird APP开发的同学,应该对JSBridge不陌生,它用于H5页面和Native(Android或者iOS)通信。常用的三方库如Dsbridge系列(https://github.com/wendux/DSBridge-Android)。那么,你知道JSBridge到底是如何在两端进行通信的吗?
娜姐
2020/09/22
2.9K0
JSBridge小科普
H5如何与原生App通信?
为了提高开发效率,开发人员往往会使用原生app里面嵌套前端h5页面的快速开发方式,这就要涉及到h5和原生的相互调用,互相传递数据,接下来就实践项目中的交互方式做一个简单的记录分享,废话不多说,直接上正文:
用户9253515
2021/12/17
6.2K0
JSB 原理与实践
我们开发的 h5 页面运行在端上的 WebView 容器之中,很多业务场景下 h5 需要依赖端上提供的信息/能力,这时我们需要一个可以连接原生运行环境和 JS 运行环境的桥梁 。 这个桥梁就是 JSB,JSB 让 Web 端和 Native 端得以实现双向通信。
用户6835371
2021/08/05
1.4K0
技术 | Hybrid载体的变化(一)
时至今日,我都在想“微信小程序”为什么不能做成Web式,而是要去加那么一层隔离,终归其原因,还是随着时间向前走,Hybrid的载体也发生了变化,不然该卡的还是一样卡的一逼。从iOS的角度上来说载体从UIWebView变成了WKWebView,Android有着他们自研的X5当然原生的内核,如果你用着Android7.0也不见得会卡,这才是小程序能出来的根本原因,没有载体,一切都是空谈。
icepy
2019/06/24
9100
oc和js交互_js调用api
UIWebView 在 iOS12 中已经被废弃,同时WKWebView在iOS8中已经出现。所以无特殊情况的话,我们一般应该也是用不到前者了! UIWebView 相比于 WKWebView 的使用上会简单很多。
全栈程序员站长
2022/11/01
67.6K0
深入小程序系列之一:小程序核心原理及模拟
小程序是一种新的移动应用程序格式,是一种依赖 Web 技术,但也集成了原生应用程序功能的混合解决方案。
凡泰极客
2020/03/09
2K0
深入小程序系列之一:小程序核心原理及模拟
JSBridge原理解析—以WebviewJavascriptBridge实现方式为例
JSBridge 是一种 webview 侧和 native 侧进行通信的手段,webview 可以通过 jsb 调用 native 的能力,native 也可以通过 jsb 在 webview 上执行一些逻辑。
zz_jesse
2021/07/12
1.8K0
JSBridge原理解析—以WebviewJavascriptBridge实现方式为例
浅谈Hybrid
随着 Web 技术和移动设备的飞速发展,各种 APP 层出不穷,极速的业务扩展提高了团队对开发效率的要求,这个时候使用 IOS/Andriod 开发一个 APP 似乎成本有点过高了,而 H5 的低成本、高效率、跨平台等特性马上被利用起来形成了一种新的开发模式:Hybrid APP。
前端森林
2020/04/23
7.1K0
浅谈Hybrid
iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge
上一篇文章介绍了UIWebView 如何通过WebViewJavascriptBridge 来实现JS 与OC 的互相调用,这一篇来介绍一下WKWebView 又是如何通过WebViewJavascriptBridge 来实现JS 与OC 的互相调用的。WKWebView 下使用WebViewJavascriptBridge与UIWebView 大同小异。主要是示例化的类不一样,一些与webView 相关的API调用不一样罢了。
Haley_Wong
2018/08/22
4.9K0
iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge
WebViewJavascriptBridge
Web 页面中的 JS 与 iOS Native 如何交互?JS 和 iOS Native 就好比两块没有交集的大陆,如果想要使它们相互通信就必须要建立一座“桥梁”。
全栈程序员站长
2022/11/19
1.3K0
WebViewJavascriptBridge
WebGL与APP之间的通讯方
WebGL 是在 Web 浏览器环境中运行的 JavaScript API,而“APP”通常指的是原生移动应用(iOS/Android App)或混合应用。WebGL 与 APP 之间的通讯方式,核心在于如何在 Web 环境(WebGL 运行其中)与原生环境之间建立桥梁。
数字孪生开发者
2025/05/23
370
WebGL与APP之间的通讯方
JsBridge实现JavaScript和Java的互相调用
前端网页JavaScript(下文简称Js)和Java互相调用在手机应用中越来越常见,JsBridge是最常用的解决方案。 1. Js调用Java,Java调用Js 在Android开发中,能实现Js调用Java,有4种方法: 1.JavascriptInterface 2.WebViewClient.shouldOverrideUrlLoading() 3.WebChromeClient.onConsoleMessage() 4.WebChromeClient.onJsPrompt() 1.1 Javas
QQ音乐技术团队
2018/01/30
2.5K0
JsBridge实现JavaScript和Java的互相调用
相关推荐
前端工程师所需要了解的WebView
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 概述
  • File and Directory
    • Class java.io.File (Pre-JDK 7)
      • Verifying Properties of a File/Directory
      • List Directory
      • List Directory with Filter
    • Class java.nio.file.Path (JDK 7)
  • Stream I/O in Standard I/O (java.io Package) 标准I / O中的流I / O(java.io包)
  • Byte-Based I/O & Byte Streams 基于字节的I / O和字节流
    • Reading from an InputStream
    • Writing to an OutputStream
    • Opening & Closing I/O Streams
    • Flushing the OutputStream
    • Implementations of abstract InputStream/OutputStream
    • Layered (or Chained) I/O Streams 分层(或链接)I / O流
    • File I/O Byte-Streams - FileInputStream & FileOutputStream
    • Buffered I/O Byte-Streams - BufferedInputStream & BufferedOutputStream
  • 示例:
    • Example 1: Copying a file byte-by-byte without Buffering.
    • Example 2: Copying a file with a Programmer-Managed Buffer. 使用自定义的Buf
    • Example 3: Copying a file with Buffered Streams.
  • Formatted Data-Streams: DataInputStream & DataOutputStream
  • 代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档