我是靠谱客的博主 刻苦台灯,最近开发中收集的这篇文章主要介绍Java 中的 IO 流的基本使用1. IO 流简介2. IO 流的分类3. Java 中的 IO 流,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1. IO 流简介

何为 IO 流?

在计算机中,内存和磁盘需要进行数据传输(内存从磁盘中读入数据,进行处理,然后写入磁盘中),而数据传输需要通道。所以,这里的 “IO 流”就是数据传输的通道。

内存和磁盘数据交互图如下:

在这里插入图片描述
大致意思:内存通过输入流从磁盘中读取数据,这个过程也称为读;内存通过输出流将数据保存到磁盘中,这个过程也称为写。

所以,简单来说,IO 流的作用就是 通过 IO 流,可以完成对磁盘文件的读和写。

2. IO 流的分类

  1. 按流的方向分(以内存为参照物):输入流(往内存中去)、输出流(从内存中出来)
  2. 按数据的读取方式:字节流、字符流

字节流和字符流的区别?

  1. 字节流:按照字节方式读取,一次读一个字节,可以读取任意类型的文件。如:文本文件、图片、视频文件,等…
  2. 字符流:按照字符方式读取,一次读取一个字符。它是为了更方便读取普通的文本文件,无法读取图片、word文件、视频文件等…

举个例子:

有一个 test.txt 文件,它里面的内容是:z爱中国

用字节流读:

第一次读:读一个字节,正好读到 ‘a’
第二次读:读一个字节,正好读到 ‘中’ 的一半
第三次读:读一个字节,读到 ‘中’ 的另一半

Tips:在 Windows 系统中,字母只占一个字节(在 Java 中,字母(char) 占用两个字节,但这个文件与 Java 无关,它只是 Windows 操作系统上的一个文件),而汉字占两个字节。

用字符流读:

第一次读:读一个字符,正好读到 ‘a’
第而次读:读一个字节,正好读到 ‘中’

3. Java 中的 IO 流

Java 中的 IO 流都在 java.io.* 包下,这块被称为“四大家族”(所有的 IO 类只有四类),四大家族的首领(这四类 IO 流的顶级父类)如下:

  • java.io.InputStream:字节输入流
  • java.io.OutputStream:字节输出流
  • java.io.Reader:字符输入流
  • java.io.Writer:字符输出流

特点:

  1. 四大家族的首领都是抽象类
  2. 所有的 IO 流都实现了 java.io.Closeable 接口,都是可关闭的(close() 方法),用完之后要关闭,不然会占用很多资源
  3. 所有的输出流都实现了 java.io.Flushable 接口,都是可刷新的(flush() 方法),用完之后,记得刷新

Java 中需要掌握的 IO 流有16个,如下:

文件专属(操作文件):

  • java.io.FileInputStream
  • java.io.FileOutputStream
  • java.io.FileReader
  • java.io.FileWriter

转换流(将字节流转换为字符流):

  • java.io.InputStreamReader
  • java.io.OutputStreamWriter

缓冲专属:

  • java.io.BufferedInputStream
  • java.io.BufferedOutputStream
  • java.io.BufferedReader
  • java.io.BufferedWriter

数据流专属:

  • java.io.DataInputStream
  • java.io.DataOutputStream

标准输出流:

  • java.io.PrintStream
  • java.io.PrintWriter

对象专属流:

  • java.io.ObjectInputStream
  • java.io.ObjectOutputStream

这里重点以“FileInputStream/FileOutputStream”举例,因为其它流与之相似

3.1 FileInputStream/FileOutputStream

3.1.1 FileInputStream

FileInputStream:文件字节输入流

场景:使用文件字节流读取磁盘中的一个文件temp.txt,其内容为:abcdef

示例一:

public class FileStreamDemo {

    public static void main(String[] args) {
        // 文件绝对路径名
        String name = "E:\zzc\temp.txt";
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(name);

            // 开始读
            // read() 方法返回的是:读取到的“字节”本身。
            // 例如:读取到的字符是'a',则返回97
            int data = fis.read();
            System.out.println(data); // 97

            data = fis.read();
            System.out.println(data); // 98

            data = fis.read();
            System.out.println(data); // 99

            data = fis.read();
            System.out.println(data); // 100

            data = fis.read();
            System.out.println(data); // 101

            data = fis.read();
            System.out.println(data); // 102

            // 读取到文件末尾返回 -1
            data = fis.read();
            System.out.println(data); // -1
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != fis) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

如果一个文件内容巨多,照上面那种方式肯定就不行了。所以,下面用循环的方式进行读取。

示例二:

public class FileStreamDemo {

    public static void main(String[] args) {
        // 文件绝对路径名
        String name = "E:\zzc\temp.txt";
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(name);
            int data = 0;
            while ((data = fis.read()) != -1) {
                System.out.println(data);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != fis) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

但是,上面还会有一个问题:就是一次只读取一个字节,那么要读取所有字节,就要读取多次。这就会意味着内存要和磁盘频繁交互,这样性能会严重降低。所以,就要考虑一次能否读取多个字节。

查看 API 文档,可知:调用 read(byte[] b) 方法

示例三:

public class FileStreamDemo {

    public static void main(String[] args) throws Exception{
        // 文件绝对路径名
        String name = "E:\zzc\temp.txt";
        FileInputStream fis = new FileInputStream(name);
        byte[] bytes = new byte[4];
        
        int length = 0;
        while ((length = fis.read(bytes)) != -1) {
            // 将 byte 数组转换成字符串
            System.out.print(new String(bytes, 0, length));
        }

        if (null != fis) {
            fis.close();
        }
    }
}

3.1.2 FileOutputStream

FileOutputStream:文件字节输出流

场景:使用文件输出流向磁盘某个文件写入数据

示例一:

public class FileOutputStreamDemo {

    public static void main(String[] args) throws Exception{
        // temp.txt 文件不存在时,会自动新建
        FileOutputStream fos = new FileOutputStream("temp.txt");
        byte[] bytes = {97, 98, 99, 100};

        fos.write(bytes);
        // 写完之后,一定要刷新
        fos.flush();
        fos.close();
    }
}

注意:如果文件不存在,则会新建文件。而且每次向此文件中写入数据之前,都会清空此文件。

那么,每次向此文件写入数据时,能不能向此文件中追加内容呢?

使用这个构造方法:

  • FileOutputStream(String name, boolean append):创建一个向具有指定 name 的文件中写入数据的输出文件流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。

如何向文件中写入汉字呢?

使用如下方式:

String data = "我爱中国";
// 将字符串转换为字节数组
byte[] bytes = data.getBytes();
fos.write(bytes);

3.1.3 使用文件字节流

场景:使用文件字节流实现文件的赋值:把文件 temp.txt 中的内容复制到 test.txt 文件中去。

示例:

public class FileStreamDemo {

    public static void main(String[] args) throws Exception{
        FileInputStream fis = new FileInputStream("E:\zzc\temp.txt");
        FileOutputStream fos = new FileOutputStream("E:\zzc\test.txt");

        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = fis.read(bytes)) != -1) {
            fos.write(bytes, 0, len);
        }

        if (null != fos) {
            fos.flush();
        }
        if (null != fis) {
            fis.close();
        }
        fos.close();
    }
}

3.2 FileReader/FileWriter

3.2.1 FileReader

FileReader:文件字符输入流,只能读取普通文本,方便、快捷

场景:使用文件字符输入流读取一个txt文件(包含中文)

示例:

public class FileReaderDemo {

    public static void main(String[] args) throws Exception{
        FileReader fr = new FileReader("temp.txt");

        char[] chars = new char[4];
        int length = 0;
        while ((length = fr.read(chars)) != -1) {
            System.out.print(new String(chars, 0, length));
        }
        fr.close();
    }
}

3.2.2 FileWriter

FileWriter:文件字符输出流,只能写普通文本

场景:使用文件字符输出流向一个txt文件写入数据(包含中文)

public class FileWriterDemo {

    public static void main(String[] args) throws Exception {
        FileWriter fw = new FileWriter("fw.txt");
        
        char[] chars = {'我', '是', '中', '国', '人'};
        fw.write(chars);
        fw.flush();
        fw.close();
    }
}

3.3 BufferedReader/BufferedWriter

3.3.1 BufferedReader

BufferedReader:带有缓冲区的字符输入流。使用此流时,不需要自定义 byte/char 数组。自带缓冲。

示例一:

public class BufferedReaderDemo {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader("temp.txt"));

        String line = null;
        while (null != (line = br.readLine())) {
            System.out.print(line);
        }

        br.close();
    }
}

示例二:

public class BufferedReaderDemo {

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("temp.txt")));

        String line = null;
        while (null != (line = br.readLine())) {
            System.out.print(line);
        }

        br.close();
    }
}

示例一与示例二的区别:示例二中使用了字节流来读取 temp.txt,但 BufferedReader 的构造方法中只能传入 Reader 类型的参数。所以,这里使用了 字节输入流来转换。

这里 BufferedWriter 就不说了。

3.4 DataOutputStream/DataInputStream

DataOutputStream:此流可以将数据以及数据类型写入文件中。
注意:此文件不是普通文件(无法用记事本打开)

3.5 PrintStream/PrintWriter

此类流是标准输出流。

3.5.1 PrintStream

PrintStream 是字节输出流,默认输出到控制台。

示例:

public class PrintStreamDemo {

    public static void main(String[] args) {
        String msg = "Hello PS";
        PrintStream out = System.out;
        // System.out.println(msg);
        out.println(msg);
    }
}

既然是默认输出到控制台。那么可以改变标准输出流的方向吗?

当然可以。

public class PrintWriterDemo {

    public static void main(String[] args) throws Exception{
        // 标准输出流不再指向控制台,而是指向 log 文件
        PrintStream ps = new PrintStream(new FileOutputStream("log"));
        // 修改输出方向,将输出方向从控制台修改为 log 文件
        System.setOut(ps);
        ps.print("我是中国人");
    }
}

3.5.2 PrintWriter

示例:

public class PrintWriterDemo {

    public static void main(String[] args) throws Exception{
        // 若此文件不存在,则创建
        PrintWriter pw = new PrintWriter("pw.txt");
        String data = "Hello PrintWriter";
        pw.write(data);

        // 使用字符输出流,则需要调用 flush() 方法
        // 字符内部也是用字节。相当于字符内部有缓存区,如果不调用 flush() 方法,那么数据只会存留在缓冲区中,
        // 并不会输出到文本中去
        pw.flush();
        // 当调用 close() 时,它会先调用 flush() 方法。即使不显示调用 flush() 方法
        pw.close();
    }
}

3.6 ObjectOutputStream/ObjectInputStream

主要用于:对象的序列化和反序列化

示例(序列化):

public class User implements Serializable {
    private static final long serialVersionUID = -2002612691013855369L;

    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    ...
}

User 类实现了 Serializable 接口

public class ObjectOutputStreamDemo {

    public static void main(String[] args) throws Exception{
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("users.txt"));
        User user = new User("zzc", 18);
        oos.writeObject(user);

        oos.flush();
        oos.close();
    }
}

注意:参与序列化和反序列化的对象都要实现 Serializable 接口。

3.7 关于内存的流

public class MemoryStream {

    public static void main(String[] args) throws Exception{
        // 1. 内存字节流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        String data = "Hello Memory";
        // 将数据写入内存
        baos.write(data.getBytes());
        // 获取内存中的数据
        byte[] bytes = baos.toByteArray();
        //System.out.println(new String(bytes));
        baos.close();

        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        int ch;
        while ((ch = bais.read()) != -1) {
            System.out.print((char) ch);
        }
        bais.close();


        // 2. 内存字符数组流
        CharArrayWriter caw = new CharArrayWriter();
        char[] chs = {'H', 'e', 'l', 'l', 'o'};
        caw.write(chs);
        caw.close();

        // 返回内存中数据的引用
        char[] data = caw.toCharArray();
        CharArrayReader car = new CharArrayReader(data);
        int ch;
        while ((ch = car.read()) != -1) {
            System.out.print((char) ch);
        }
        car.close();


        // 3. 内存字符串流
        StringWriter sw = new StringWriter();
        String data = "Hello World";
        sw.write(data);
        sw.close();

        String s = sw.toString();
        StringReader sr = new StringReader(s);
        int ch;
        while((ch = sr.read()) != -1) {
            System.out.print((char) ch);
        }
        sr.close();
    }
}

好了,一些流的基本使用就到这儿了。多看看它们的 API 文档吧。

最后

以上就是刻苦台灯为你收集整理的Java 中的 IO 流的基本使用1. IO 流简介2. IO 流的分类3. Java 中的 IO 流的全部内容,希望文章能够帮你解决Java 中的 IO 流的基本使用1. IO 流简介2. IO 流的分类3. Java 中的 IO 流所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部