zoukankan      html  css  js  c++  java
  • Java 语法 -- IO 流

    Java 的 IO 流分为两大类:

    • 字节流
    • 字符流

    从超类到实现类,Java IO 流的继承关系大致如下图:

    InputStream

    InputStream 就是 Java 标准库提供的最基本的字节输入流。它位于java.io这个包里。java.io包提供了所有同步IO的功能。

    要特别注意的一点是,InputStream 并不是一个接口,而是一个抽象类,它是所有输入流的超类。

    InputStream 类定义的一个最重要的方法就是 int read(),这个方法会读取输入流的下一个字节,并返回字节表示的 int 值(0~255)。如果已读到末尾,返回 -1 表示不能继续读取了。

    FileInputStream

    FileInputStream 是 InputStream 的一个子类。顾名思义,FileInputStream 就是从文件流中读取数据。下面的代码演示了如何完整地读取一个FileInputStream的所有字节:

    public void readFile() throws IOException {
        // 创建一个FileInputStream对象:
        InputStream input = new FileInputStream("src/readme.txt");
        for (;;) {
            int n = input.read(); // 反复调用read()方法,直到返回-1
            if (n == -1) {
                break;
            }
            System.out.println(n); // 打印byte的值
        }
        input.close(); // 关闭流
    }
    

    InputStream 和 OutputStream 都是通过 close() 方法来关闭流。关闭流就会释放对应的底层资源。

    所有与 IO 操作相关的代码都必须正确处理 IOException, 如果读取过程中发生了 IO 错误,InputStream 就没法正确地关闭,资源也就没法及时释放。

    利用 Java 7引入的新的 try(resource) 的语法,只需要编写 try 语句,让编译器自动为我们关闭资源。推荐的写法如下:

    public void readFile() throws IOException {
        try (InputStream input = new FileInputStream("src/readme.txt")) {
            int n;
            while ((n = input.read()) != -1) {
                System.out.println(n);
            }
        } // 编译器在此自动为我们写入finally并调用close()
    }
    

    OutputStream

    和 InputStream 相反,OutputStream 是 Java 标准库提供的最基本的字节输出流。

    和InputStream类似,OutputStream也是抽象类,它是所有输出流的超类。这个抽象类定义的一个最重要的方法就是void write(int b)

    和InputStream类似,OutputStream也提供了close()方法关闭输出流,以便释放系统资源。要特别注意:OutputStream还提供了一个flush()方法,它的目的是将缓冲区的内容真正输出到目的地。

    FileOutputStream

    用 FileOutputStream 可以从文件获取输出流,这是 OutputStream 常用的一个实现类。

    以 FileOutputStream 为例,演示如何将若干个字节写入文件流:

    public void writeFile() throws IOException {
        OutputStream output = new FileOutputStream("out/readme.txt");
        output.write(72); // H
        output.write(101); // e
        output.write(108); // l
        output.write(108); // l
        output.write(111); // o
        output.close();
    }
    

    每次写入一个字节非常麻烦,更常见的方法是一次性写入若干字节。这时,可以用 OutputStream 提供的重载方法void write(byte[])来实现:

    public void writeFile() throws IOException {
        OutputStream output = new FileOutputStream("out/readme.txt");
        output.write("Hello".getBytes("UTF-8")); // Hello
        output.close();
    }
    

    和InputStream一样,上述代码没有考虑到在发生异常的情况下如何正确地关闭资源。写入过程也会经常发生IO错误,例如,磁盘已满,无权限写入等等。我们需要用try(resource)来保证OutputStream在无论是否发生IO错误的时候都能够正确地关闭:

    public void writeFile() throws IOException {
        try (OutputStream output = new FileOutputStream("out/readme.txt")) {
            output.write("Hello".getBytes("UTF-8")); // Hello
        } // 编译器在此自动为我们写入finally并调用close()
    }
    

    ​​PrintStream

    PrintStream 继承自 FilterOutputStream, FilterOutputStream 又继承自 OutputStream。

    它在OutputStream的接口上,额外提供了一些写入各种数据类型的方法:

    写入int:print(int)
    写入boolean:print(boolean)
    写入String:print(String)
    写入Object:print(Object),实际上相当于print(object.toString())
    

    以及对应的一组println()方法,它会自动加上换行符

    我们经常使用的System.out.println()实际上就是使用PrintStream打印各种数据。其中,System.out是系统默认提供的PrintStream,表示标准输出:

    System.out.print(12345); // 输出12345
    System.out.print(new Object()); // 输出类似java.lang.Object@3c7a835a
    System.out.println("Hello"); // 输出Hello并换行
    System.err是系统默认提供的标准错误输出。
    

    PrintStream和OutputStream相比,除了添加了一组print()/println()方法,可以打印各种数据类型,比较方便外,它还有一个额外的优点,就是不会抛出IOException,这样我们在编写代码的时候,就不必捕获IOException。

    序列化与反序列化

    序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。

    为什么要把Java对象序列化呢?因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了。

    有序列化,就有反序列化,即把一个二进制内容(也就是byte[]数组)变回Java对象。有了反序列化,保存到文件中的byte[]数组又可以“变回”Java对象,或者从网络上读取byte[]并把它“变回”Java对象。

    ObjectOutputStream

    把一个 Java 对象变为 byte[] 数组,需要使用 ObjectOutputStream。它负责把一个 Java 对象写入一个字节流:

    ObjectInputStream

    和ObjectOutputStream相反,ObjectInputStream负责从一个字节流读取Java对象:

    try (ObjectInputStream input = new ObjectInputStream(...)) {
        int n = input.readInt();
        String s = input.readUTF();
        Double d = (Double) input.readObject();
    }
    

    除了能读取基本类型和String类型外,调用readObject()可以直接返回一个Object对象。要把它变成一个特定类型,必须强制转型。

    Java的序列化机制仅适用于Java,如果需要与其它语言交换数据,必须使用通用的序列化方法,例如JSON。

    Reader 接口

    Reader是Java的IO库提供的另一个输入流接口。和InputStream的区别是,InputStream是一个字节流,即以byte为单位读取,而Reader是一个字符流,即以char为单位读取:

    java.io.Reader是所有字符输入流的超类,它最主要的方法是:int read()

    这个方法读取字符流的下一个字符,并返回字符表示的int,范围是0~65535。如果已读到末尾,返回-1。

    ​BufferedReader

    BufferedReader 是缓冲字符输入流。它继承于Reader。

    BufferedReader 的作用是为其他字符输入流添加一些缓冲功能。

    提供了读取一行的功能:readLine()

    ​LineNumberReader

    LineNumberReader 继承自 BufferedReader,并且增加了下面两个功能:

    • 获取行号:getLineNumber()
    • 设置行号:setLineNumber()

    ​InputStreamReader

    InputStreamReader : 是字节流与字符流之间的桥梁,能将字节流输出为字符流,并且能为字节流指定字符集,可输出一个个的字符.

    ​FileReader

    ​FileReader 继承自 InputStreamReader

    它可以打开文件并获取Reader。下面的代码演示了如何完整地读取一个FileReader的所有字符:

    public void readFile() throws IOException {
        // 创建一个FileReader对象:
        Reader reader = new FileReader("src/readme.txt"); 
        for (;;) {
            int n = reader.read(); // 反复调用read()方法,直到返回-1
            if (n == -1) {
                break;
            }
            System.out.println((char)n); // 打印char
        }
        reader.close(); // 关闭流
    }
    

    ​StringReader

    StringReader是接口 Reader 的一个实现类,其用法是读取一个String字符串。

    Writer 接口

    Reader是带编码转换器的InputStream,它把byte转换为char,而Writer就是带编码转换器的OutputStream,它把char转换为byte并输出。

    Writer和OutputStream的区别如下:

    Writer是所有字符输出流的超类,它提供的方法主要有:

    • 写入一个字符(0~65535):void write(int c);
    • 写入字符数组的所有字符:void write(char[] c);
    • 写入String表示的所有字符:void write(String s)。

    ​BufferedWriter

    BufferedWriter 为带有默认缓冲的字符输出流。

    主要方法:

    • void write(char ch);//写入单个字符。

    • void write(char []cbuf,int off,int len)//写入字符数据的某一部分。

    • void write(String s,int off,int len)//写入字符串的某一部分。

    • void newLine()//写入一个行分隔符。

    • void flush();//刷新该流中的缓冲。将缓冲数据写到目的文件中去。

    • void close();//关闭此流,再关闭前会先刷新他。

    ​OutputStreamWriter

    整个IO包实际上分为字节流和字符流,但是除了这两个流之外,还存在一组字节流-字符流的转换类。

    OutputStreamWriter:是Writer的子类,将输出的字符流变为字节流,即将一个字符流的输出对象变为字节流输出对象。

    InputStreamReader:是Reader的子类,将输入的字节流变为字符流,即将一个字节流的输入对象变为字符流的输入对象。

    如果以文件操作为例,则内存中的字符数据需要通过OutputStreamWriter变为字节流才能保存在文件中,读取时需要将读入的字节流通过InputStreamReader变为字符流。过程如下:

    写入数据-->内存中的字符数据-->字符流-->OutputStreamWriter-->字节流-->网络传输(或文件保存)
    读取数据<--内存中的字符数据<--字符流<--InputStreamReader<--字节流<--网络传输(或文件保存)
    

    可以清楚地发现,不管如何操作,最终全部是以字节的形式保存在文件中或者进行网络传输。

    ​FileWriter

    FileWriter 类从 OutputStreamWriter 类继承而来。该类按字符向流中写入数据。

    它的使用方法和FileReader类似:

    try (Writer writer = new FileWriter("readme.txt", StandardCharsets.UTF_8)) {
        writer.write('H'); // 写入单个字符
        writer.write("Hello".toCharArray()); // 写入char[]
        writer.write("Hello"); // 写入String
    }
    

    ​PrintWriter

    PrintStream最终输出的总是byte数据,而PrintWriter则是扩展了Writer接口,它的print()/println()方法最终输出的是char数据。两者的使用方法几乎是一模一样的:

    public class Main {
        public static void main(String[] args)     {
            StringWriter buffer = new StringWriter();
            try (PrintWriter pw = new PrintWriter(buffer)) {
                pw.println("Hello");
                pw.println(12345);
                pw.println(true);
            }
            System.out.println(buffer.toString());
        }
    }
    

    ​StringWriter

    ​StringWriter 是接口 Writer 的一个实现类,主要作用是写入一个字符串。

    每天学习一点点,每天进步一点点。

  • 相关阅读:
    asp.net中分页与存储过程的一些总结
    Ajax与Json的一些总结
    aspx页面与服务器控件间运行原理
    ASP.NET中Server对象的几个方法
    Cookie与Session的一些总结
    ASP.NET的学习之asp.net整体运行机制
    Find offset of first/last found substring
    由于DNS反向解析导致的登录BI启动版速度变慢问题
    4-自定义BI启动版Logon界面
    3-自定义BI启动版是否隐藏CMS名称
  • 原文地址:https://www.cnblogs.com/youcoding/p/13511644.html
Copyright © 2011-2022 走看看