JavaSe笔记-IO流

目录

流的概念

输入输出流

系统流

字符编码

File类

获取文件属性

创建和删除文件

创建和删除目录

RandomAccessFile类

字节流的使用

字节输入流

字节输出流

字节数组输入流

字节数组输出流

文件输入流

文件输出流

字符流的使用

字符输入流

字符输出流

字符文件输入流

字符文件输出流

字符缓冲区输入流

字符缓冲区输出流

转换流


流的概念

输入输出流

        在 Java 中所有数据都是使用流读写的。流是一组有序的数据序列,将数据从一个地方带到另一个地方。根据数据流向的不同,可以分为输入(Input)流和输出(Output)流两种。

        在学习输入和输出流之前,我们要明白为什么应用程序需要输入和输出流。例如,我们平时用的 Office 软件,对于 Word、Excel 和 PPT 文件,我们需要打开文件并读取这些文本,和编辑输入一些文本,这都需要利用输入和输出的功能。在现实生活中,输入和输出的例子比比皆是。

        Java 程序通过流来完成输入/输出,所有的输入/输出以流的形式处理。因此要了解 I/O 系统,首先要理解输入/输出流的概念。

        输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设备,又可以作为输出设备。

        数据流是 Java 进行 I/O 操作的对象,它按照不同的标准可以分为不同的类别。 ​ 1、按照流的方向主要分为输入流和输出流两大类。 ​ 2、数据流按照数据单位的不同分为字节流和字符流。 ​ 3、按照功能可以划分为节点流和处理流。

系统流

        每个 Java 程序运行时都带有一个系统流,系统流对应的类为 java.lang.System。Sytem 类封装了 Java 程序运行时的 3 个系统流,分别通过 in、out 和 err 变量来引用。这 3 个系统流如下所示: ​ System.in:标准输入流,默认设备是键盘。 ​ System.out:标准输出流,默认设备是控制台。 ​ System.err:标准错误流,默认设备是控制台。

字符编码

        Java 中常见编码说明如下: ​ ISO8859-1:属于单字节编码,最多只能表示 0~255 的字符范围。 ​ GBK/GB2312:中文的国标编码,用来表示汉字,属于双字节编码。GBK 可以表示简体中文和繁体中文,而 GB2312 只能表示 简体中文。GBK 兼容 GB2312。 ​ Unicode:是一种编码规范,是为解决全球字符通用编码而设计的。UTF-8 和 UTF-16 是这种规范的一种实现,此编码不兼容 I SO8859-1 编码。Java 内部采用此编码。 ​ UTF:UTF 编码兼容了 ISO8859-1 编码,同时也可以用来表示所有的语言字符,不过 UTF 编码是不定长编码,每一个字符的长 度为 1~6 个字节不等。一般在中文网页中使用此编码,可以节省空间。

        在程序中如果处理不好字符编码,就有可能出现乱码问题。例如现在本机的默认编码是 GBK,但在程序中使用了 ISO8859-1 编码,则就会出现字符的乱码问题。就像两个人交谈,一个人说中文,另外一个人说英语,语言不同就无法沟通。为了避免产生乱码,程序编码应与本地的默认编码保持一致。

        现在操作系统的默认编码是 GBK

File类

        在 Java 中,File 类是 java.io 包中唯一代表磁盘文件本身的对象,也就是说,如果希望在程序中操作文件和目录,则都可以通过 File 类来完成。File 类定义了一些方法来操作文件,如新建、删除、重命名文件和目录等。

        File 类不能访问文件内容本身,如果需要访问文件内容本身,则需要使用输入/输出流。

        File 类提供了如下三种形式构造方法。 ​ File(String path):如果 path 是实际存在的路径,则该 File 对象表示的是目录;如果 path 是文件名,则该 File 对象表示的是文 件。 ​ File(String path, String name):path 是路径名,name 是文件名。 ​ File(File dir, String name):dir 是路径对象,name 是文件名。

注意:可以看到 File 类的常量定义的命名规则不符合标准命名规则,常量名没有全部大写,这是因为 Java 的发展经过了一段相当长的时间,而命名规范也是逐步形成的,File 类出现较早,所以当时并没有对命名规范有严格的要求,这些都属于 Java 的历史遗留问题。

import java.io.File;

public class Demo01 {
    public static void main(String[] args) {
        /**
         * File类常用方法
         */
        File f1 = new File("C:/WorkAndStudy/java/demo01.txt");
        System.out.println(f1);
        // 测试应用程序是否能从指定的文件中进行读取
        System.out.println(f1.canRead());

        // 测试应用程序是否能写当前文件
        System.out.println(f1.canWrite());

        // 删除当前对象指定的文件
//        boolean delete = f1.delete();
//        System.out.println("删除文件: " + delete);

        // 测试当前 File 是否存在
        System.out.println(f1.exists());

        // 返回由该对象表示的文件的绝对路径名
        System.out.println(f1.getAbsolutePath());

        // 返回表示当前对象的文件名或路径名(如果是路径,则返回最后一级子路径名)
        System.out.println(f1.getName());

        // 返回当前 File 对象所对应目录(最后一级子目录)的父目录名
        System.out.println(f1.getParent());

        // 判断当前文件对象路径是否为绝对路径
        System.out.println(f1.isAbsolute());

        // 测试当前 File 对象表示的文件是否为一个路径
        System.out.println(f1.isDirectory());

        // 判断当前file对象是否为文件
        System.out.println(f1.isFile());

        // 返回当前 File 对象表示的文件最后修改的时间
        System.out.println(f1.lastModified());

        // 返回当前 File 对象表示的文件长度
        System.out.println(f1.length());


    }

获取文件属性

        假设有一个文件位于 C:\windows\notepad.exe。编写 Java 程序获取并显示该文件的长度、是否可写、最后修改日期以及文件路径等属性信息。实现代码如下:

public class Test02 {
    public static void main(String[] args) {
        String path = "C:/windows/"; // 指定文件所在的目录
        File f = new File(path, "notepad.exe"); // 建立File变量,并设定由f变量引用
        System.out.println("C:\\windows\\notepad.exe文件信息如下:");
        System.out.println("============================================");
        System.out.println("文件长度:" + f.length() + "字节");
        System.out.println("文件或者目录:" + (f.isFile() ? "是文件" : "不是文件"));
        System.out.println("文件或者目录:" + (f.isDirectory() ? "是目录" : "不是目录"));
        System.out.println("是否可读:" + (f.canRead() ? "可读取" : "不可读取"));
        System.out.println("是否可写:" + (f.canWrite() ? "可写入" : "不可写入"));
        System.out.println("是否隐藏:" + (f.isHidden() ? "是隐藏文件" : "不是隐藏文件"));
        System.out.println("最后修改日期:" + new Date(f.lastModified()));
        System.out.println("文件名称:" + f.getName());
        System.out.println("文件路径:" + f.getPath());
        System.out.println("绝对路径:" + f.getAbsolutePath());
    }

创建和删除文件

        File 类不仅可以获取已知文件的属性信息,还可以在指定路径创建文件,以及删除一个文件。创建文件需要调用 createNewFile() 方法,删除文件需要调用 delete() 方法。无论是创建还是删除文件通常都先调用 exists() 方法判断文件是否存在。

创建文件

import java.io.File;
import java.util.Arrays;

public class Demo04 {
    public static void main(String[] args) {
        /**
         * 删除非空目录a
         */
        String patten = "D:/LianXi/java";
        // 创建非空目录a的File对象
        File f = new File(patten);
        // 获取当前对象下所有内容
        String[] result = f.list();
        System.out.println(Arrays.toString(result));

        // 获取当前对像下的所有file对象
        File[] files = f.listFiles();
        for (File file : files) {
            System.out.println(file);
        }

        // 判断是否为文件或者目录

    }

删除文件 

import java.io.File;

public class Demo05 {
    public static void main(String[] args) {
        /**
         * 使用递归删除C:\WorkAndStudy\java
         * 目录
         */
        // 创建file对象
        File f = new File("C:\\WorkAndStudy\\java");
        // 调用递归删除方法
        del(f);
    }

    /**
     * 删除文件或者目录
     * @param file 文件或者目录对象
     */
    public static void del(File file){
        // 判断file对象是否为目录
        if (file.isDirectory()) {
            // 是目录,获取当前对象下的所有file对象
            File[] files = file.listFiles();
            for (File f : files) {
                // 调用del函数执行删除
                del(f);
            }
        }

        // 非目录可直接删除
        file.delete();
        System.out.println("删除成功");
    }

创建和删除目录

        File 类除了对文件的创建和删除外,还可以创建和删除目录。创建目录需要调用 mkdir() 方法,删除目录需要调用 delete() 方法。无论是创建还是删除目录都可以调用 exists() 方法判断目录是否存在。

import java.io.File;
import java.util.Arrays;

public class Demo04 {
    public static void main(String[] args) {
        /**
         * 删除非空目录a
         */
        String patten = "C:/WorkAndStudy/java/a";
        // 创建非空目录a的File对象
        File f = new File(patten);
        // 获取当前对象下所有内容
        String[] result = f.list();
        System.out.println(Arrays.toString(result));

        // 获取当前对像下的所有file对象
        File[] files = f.listFiles();
        for (File file : files) {
            System.out.println(file);
        }

        // 判断是否为文件或者目录


    }

RandomAccessFile类

        所谓动态读取是指从文件的任意位置开始访问文件,而不是必须从文件开始位置读取到文件末尾。动态读取需要用到 Java 中的 RandomAccessFile 类。

        RandomAccessFile 是 Java 输入/输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。由于 RandomAccessFile 可以从任意位置访问文件,所以在只需要访问文件部分内容的情况下,使用 RandonAccessFile 类是一个很好的选择。

        RandomAccessFile 对象包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个 RandomAccessFile 对象时,该对象的文件记录指针位于文件头(也就是 0 处),当读/写了 n 个字节后,文件记录指针将会向后移动 n 个字节。除此之外,RandonAccessFile 可以自由移动该记录指针,既可以向前移动,也可以向后移动。

注意:如果使用 rw 方式声明 RandomAccessFile 对象时,要写入的文件不存在,系统将自动进行创建。

写入文件,代码如下:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo01 {
    public static void main(String[] args) throws Exception {
        /**
         * RandomAccessFile 是 Java 输入/输出流体系中功能最丰富的文件内容访问类,
         * 它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。
         * 由于 RandomAccessFile 可以从任意位置访问文件,所以在只需要访问文件部分内容的情况下,
         * 使用 RandomAccessFile 类是一个很好的选择
         *
         * RandomAccessFile类提供了两个构造方法:
         *      RandomAccessFile(File  file, String mode)
         *          其中file表示文件对象,mode表示文档的属性(常用属性有r:只读模式,w:只写模式,
         *              rw:可读可写模式)
         *
         *      RandomAccessFile(String path, String mode)
         *          其中path表示文件所在路径,mode表示文档的属性(常用属性有r:只读模式,w:只写模式,
         *              rw:可读可写模式)
         *          注意:当指定路径下文件不存在,则会创建改文件对象
         */
        // 任务:将数据写入dir/a.txt文件当中
        // 创建file对象
        File f = new File("dir/a.txt");
        // 创建随机文件对象
        RandomAccessFile raf = new RandomAccessFile(f, "rw");
        // 将hello java写入文件当中
        // String content = "Hello Java";
        String content = "今天安顺天气真好";
        byte[] bytes = content.getBytes("UTF-8");

        /**
         * 注意:
         *      每次写入内容时都会清空原本的内容
         */
        raf.write(bytes);

        raf.write(1);

        System.out.println("写入完毕");

        // 关闭文件
        raf.close();

    }

​​​​读取文件,代码如下:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;

public class RandomAccessFileDemo02 {
    public static void main(String[] args) throws Exception {
        /**
         * 读取文件
         */
        // 创建file对象
        File f = new File("dir/a.txt");
        // 创建随机文件对象
        RandomAccessFile raf = new RandomAccessFile(f, "r");

        // 读取文件
        // int read = raf.read();// 每次读取一个字节
        // String s = raf.readLine();// 读取一行数据
        // 获取文件内容长度
        long length = raf.length();
        byte[] data = new byte[(int) length];
        // 读取数据
        raf.read(data);

        // 将二进制数据转为中文
        String result = new String(data, "Utf-8");

        System.out.println(result);

        // 关闭文件流
        raf.close();

    }

根据上面所学的制作一个简易的记事本程序

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.util.Scanner;

public class RAFDemo03 {
    public static void main(String[] args) throws Exception {
        /**
         * 任务:
         *      制作一个简易的记事本程序
         *      要求:
         *          用户输入记事本名字打开记事本,如果不存在则创建,当用户输入Q或者q的时候
         *      保存并且退出记事本
         */
        // 创建扫描器对象
        Scanner scan = new Scanner(System.in);
        System.out.println("欢迎来到记事本,请输入要打开的记事本名字,如果不存在则会创建:");
        String name = scan.nextLine();

        // 创建随机读取文件对象
        RandomAccessFile raf = new RandomAccessFile("dir/"+name+".txt", "rw");

        String cont = "";

        while (true) {
            System.out.println("请输入内容:");
            cont = scan.nextLine();
            // 判断用户是否退出
            if ("Q".equals(cont) || "q".equals(cont)) {
                // 用户退出
                break;
            }

            // 写入数据
            raf.write(cont.getBytes("utf-8"));
        }


        // 关闭文件
        raf.close();

    }

文件指针

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;

public class Demo04 {
    public static void main(String[] args) throws Exception {
        /**
         * 文件指针
         *      当读取到文件末尾的时候返回-1
         */
        // 打开文件
        RandomAccessFile raf = new RandomAccessFile("dir/a.txt", "rw");
        // 获取当前文件指针的位置
        System.out.println(raf.getFilePointer());
        // 写入数据
        raf.write("今".getBytes("utf-8"));
        // 获取当前文件指针的位置
        System.out.println(raf.getFilePointer());
        // 写入数据
        raf.write("天".getBytes("utf-8"));
        // 获取当前文件指针的位置
        System.out.println(raf.getFilePointer());
        // 写入数据
        raf.write("心".getBytes("utf-8"));
        // 获取当前文件指针的位置
        System.out.println(raf.getFilePointer());
        // 写入数据
        raf.write("情".getBytes("utf-8"));
        // 获取当前文件指针的位置
        System.out.println(raf.getFilePointer());

        System.out.println("************************");
        // 移动指针位置
        raf.seek(3);
        // 获取当前文件指针的位置
        System.out.println(raf.getFilePointer());
    }

文件复制

import java.io.FileNotFoundException;
import java.io.RandomAccessFile;

public class Demo05 {
    public static void main(String[] args) throws Exception {
        /**
         * 文件复制
         *      要求:将dir目录下的patten.txt文件复制到patten_back.txt文件中
          */
        // 创建读文件对象
        RandomAccessFile raf1 = new RandomAccessFile("dir/pattern.txt", "r");
        // 创建写文件对象
        RandomAccessFile raf2 = new RandomAccessFile("dir/pattern_back.txt", "rw");

        int len = -1;
        long start = System.currentTimeMillis();
        while ((len = raf1.read()) != -1) {
            // 写入文件
            raf2.write(len);
        }
        long end = System.currentTimeMillis();
        System.out.println("复制文件完毕,耗时:"+(end - start)+"毫秒");

        // 关闭文件
        raf1.close();
        raf2.close();
    }

字节流的使用

        InputStream 是 Java 所有字节输入流类的父类,OutputStream 是 Java 所有字节输出流类的父类,它们都是一个抽象类,因此继承它们的子类要重新定义父类中的抽象方法。

字节输入流

InputStream 类及其子类的对象表示字节输入流,InputStream 类的常用子类如下。 ​  

        ByteArrayInputStream 类:将字节数组转换为字节输入流,从中读取字节。

        FileInputStream 类:从文件中读取数据。 ​

        PipedInputStream 类:连接到一个 PipedOutputStream(管道输出流)。 ​

        SequenceInputStream 类:将多个字节输入流串联成一个字节输入流。 ​

        ObjectInputStream 类:将对象反序列化。

使用 InputStream 类的方法可以从流中读取一个或一批字节。下表列出了 InputStream 类的常用方法。

 

import java.io.FileInputStream;
import java.io.FileNotFoundException;

/**
 * FileInputStream: 文件字节输入流
 * FileInputStream从文件系统中的文件获取输入字节。 什么文件可用取决于主机环境。
 * FileInputStream用于读取诸如图像数据的原始字节流。 要阅读字符流,请考虑使用FileReader 。
 *
 * 构造方法
 * FileInputStream(File file)
 *      通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
 * FileInputStream(FileDescriptor fdObj)
 *      通过使用文件描述符 fdObj创建 FileInputStream ,该文件描述符表示与文件系统中的实际文件的现有连接。
 * FileInputStream(String name)
 *      通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
 */
public class FileInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        // 创建fileInputStream对象
        FileInputStream fis = new FileInputStream("dir/a.txt");

        // 常用方法
        for (int i = 0; i < 10; i++) {
            int read = fis.read();
            System.out.println(fis.read(new byte[1024]));
            System.out.println(read);

            // 将输入流的指针返回到设置标记的起始处
            // fis.reset();
        }

        // 关闭文件
        fis.close();
    }
}

字节输出流

OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。 ​

        ByteArrayOutputStream 类:向内存缓冲区的字节数组中写数据。 ​

        FileOutputStream 类:向文件中写数据。 ​

        PipedOutputStream 类:连接到一个 PipedlntputStream(管道输入流)。

        ​ObjectOutputStream 类:将对象序列化。

利用 OutputStream 类的方法可以从流中写入一个或一批字节。下表列出了 OutputStream 类的常用方法。

 

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

/**
 * 文件字节输出流 FileOutputStream
 * 文件输出流是用于将数据写入File或FileDescriptor的输出流。
 * 文件是否可用或可能被创建取决于底层平台。 特别是某些平台允许一次只能打开一个文件来写入一个
 * FileOutputStream (或其他文件写入对象)。 在这种情况下,如果所涉及的文件已经打开,
 * 则此类中的构造函数将失败。
 *
 * FileOutputStream用于写入诸如图像数据的原始字节流。 要编写字符流,请考虑使用FileWriter 。
 *
 * 构造函数:
 * FileOutputStream(File file)
 *      创建文件输出流以写入由指定的 File对象表示的文件。
 * FileOutputStream(FileDescriptor fdObj)
 *      创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
 * FileOutputStream(File file, boolean append)
 *      创建文件输出流以写入由指定的 File对象表示的文件。
 *      参数
 *          file - 要打开的文件进行写入。
 *          append - 如果是 true ,则字节将写入文件的末尾而不是开头
 * FileOutputStream(String name)
 *      创建文件输出流以指定的名称写入文件。
 * FileOutputStream(String name, boolean append)
 *      创建文件输出流以指定的名称写入文件。
 *      参数
 *          file - 要打开的文件进行写入。
 *          append - 如果是 true ,则字节将写入文件的末尾而不是开头
 */
public class FileOutputStreamDemo02 {
    public static void main(String[] args) throws Exception {
        // 创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("dir/fos.txt");

        // 写入数据
        String str = "慕然抬首已是秋,方觉岁月太匆匆。";
        fos.write(str.getBytes("utf-8"));

        // 关闭文件
        fos.close();
    }
}

字节数组输入流

        ByteArrayInputStream 类可以从内存的字节数组中读取数据,该类有如下两种构造方法重载形式。

        1、ByteArrayInputStream(byte[] buf):创建一个字节数组输入流,字节数组类型的数据源由参数 buf 指定。

        2、ByteArrayInputStream(byte[] buf,int offse,int length):创建一个字节数组输入流,其中,参数 buf 指定字节数组类型的数据源,offset 指定在数组中开始读取数据的起始下标位置,length 指定读取的元素个数。

字节数组输出流

        ByteArrayOutputStream 类可以向内存的字节数组中写入数据,该类的构造方法有如下两种重载形式。 ​

        1、ByteArrayOutputStream():创建一个字节数组输出流,输出流缓冲区的初始容量大小为 32 字节。 ​

        2、ByteArrayOutputStream(int size):创建一个字节数组输出流,输出流缓冲区的初始容量大小由参数 size 指定。

ByteArrayOutputStream 类中除了有前面介绍的字节输出流中的常用方法以外,还有如下两个方法。 ​

        1、intsize():返回缓冲区中的当前字节数。 ​

        2、byte[] toByteArray():以字节数组的形式返回输出流中的当前内容。

文件输入流

        FileInputStream 是 Java 流中比较常用的一种,它表示从文件系统的某个文件中获取输入字节。通过使用 FileInputStream 可以访问文件中的一个字节、一批字节或整个文件。

        在创建 FileInputStream 类的对象时,如果找不到指定的文件将拋出 FileNotFoundException 异常,该异常必须捕获或声明拋出。

FileInputStream 常用的构造方法主要有如下两种重载形式。 ​

FileInputStream(File file):通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 ​

FileInputStream(String name):通过打开一个到实际文件的链接来创建一个 FileInputStream,该文件通过文件系统中的路 径名 name 指定。

注意:FileInputStream 类重写了父类 InputStream 中的 read() 方法、skip() 方法、available() 方法和 close() 方法,不支持 mark() 方法和 reset() 方法。

​​​​​​​

文件输出流

        FileOutputStream 类继承自 OutputStream 类,重写和实现了父类中的所有方法。        

        FileOutputStream 类的对象表示一个文件字节输出流,可以向流中写入一个字节或一批字节。在创建 FileOutputStream 类的对象时,如果指定的文件不存在,则创建一个新文件;如果文件已存在,则清除原文件的内容重新写入。

FileOutputStream 类的构造方法主要有如下 4 种重载形式。

        FileOutputStream(File file):创建一个文件输出流,参数 file 指定目标文件。

        FileOutputStream(File file,boolean append):创建一个文件输出流,参数 file 指定目标文件,append 指定是否将数据添加到目标文件的内容末尾,如果为 true,则在末尾添加;如果为 false,则覆盖原有内容;其默认值为 false。

        FileOutputStream(String name):创建一个文件输出流,参数 name 指定目标文件的文件路径信息。

        FileOutputStream(String name,boolean append):创建一个文件输出流,参数 name 和 append 的含义同上。

注意:使用构造方法 FileOutputStream(String name,boolean append) 创建一个文件输出流对象,它将数据附加在现有文件的末尾。该字符串 name 指明了原文件,如果只是为了附加数据而不是重写任何已有的数据,布尔类型参数 append 的值应为 true。

对文件输出流有如下四点说明:

1、在 FileOutputStream 类的构造方法中指定目标文件时,目标文件可以不存在。

2、目标文件的名称可以是任意的,例如 D:\abc、D:\abc.de 和 D:\abc.de.fg 等都可以,可以使用记事本等工具打开并浏览这些文件中的内容。

3、目标文件所在目录必须存在,否则会拋出 java.io.FileNotFoundException 异常。

4、目标文件的名称不能是已存在的目录。例如 D 盘下已存在 Java 文件夹,那么就不能使用 Java 作为文件名,即不能使用 D:\Java,否则抛出 java.io.FileNotFoundException 异常。

​​​​​​​

import java.io.FileNotFoundException;
import java.io.FileOutputStream;

/**
 * 文件字节输出流 FileOutputStream
 * 文件输出流是用于将数据写入File或FileDescriptor的输出流。
 * 文件是否可用或可能被创建取决于底层平台。 特别是某些平台允许一次只能打开一个文件来写入一个
 * FileOutputStream (或其他文件写入对象)。 在这种情况下,如果所涉及的文件已经打开,
 * 则此类中的构造函数将失败。
 *
 * FileOutputStream用于写入诸如图像数据的原始字节流。 要编写字符流,请考虑使用FileWriter 。
 *
 * 构造函数:
 * FileOutputStream(File file)
 *      创建文件输出流以写入由指定的 File对象表示的文件。
 * FileOutputStream(FileDescriptor fdObj)
 *      创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
 * FileOutputStream(File file, boolean append)
 *      创建文件输出流以写入由指定的 File对象表示的文件。
 *      参数
 *          file - 要打开的文件进行写入。
 *          append - 如果是 true ,则字节将写入文件的末尾而不是开头
 * FileOutputStream(String name)
 *      创建文件输出流以指定的名称写入文件。
 * FileOutputStream(String name, boolean append)
 *      创建文件输出流以指定的名称写入文件。
 *      参数
 *          file - 要打开的文件进行写入。
 *          append - 如果是 true ,则字节将写入文件的末尾而不是开头
 */
public class FileOutputStreamDemo03 {
    public static void main(String[] args) throws Exception {
        // 创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("dir/fos.txt", true);

        // 写入数据
        String str = "晓看天色,暮看云....";
        fos.write(str.getBytes("utf-8"));

        // 关闭文件
        fos.close();
    }
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Date;

/**
 * 使用文件字节流对象实现复制功能
 */
public class CopyDemo04 {
    public static void main(String[] args) throws Exception {
        // 创建读取文件字节流对象
        FileInputStream fis = new FileInputStream("dir/a.txt");
        // 创建输出文件字节流对象
        FileOutputStream fos = new FileOutputStream("dir/a_back.dat");

        // 定义每次读取的大小
        byte[] data = new byte[1024 * 8];
        int len = -1;

        // 循环读取和写入文件
        while ((len = fis.read(data)) != -1) {
            // 写入数据
            fos.write(data, 0, len);
        }
        // 关闭流
        fis.close();
        fos.close();
    }
}

字符流的使用

        尽管 Java 中字节流的功能十分强大,几乎可以直接或间接地处理任何类型的输入/输出操作,但利用它却不能直接操作 16 位的 Unicode 字符。这就要用到字符流。

字符输入流

        Reader 类是所有字符流输入类的父类,该类定义了许多方法,这些方法对所有子类都是有效的。

Reader 类的常用子类如下。 ​

        CharArrayReader 类:将字符数组转换为字符输入流,从中读取字符。 ​

        StringReader 类:将字符串转换为字符输入流,从中读取字符。 ​

        BufferedReader 类:为其他字符输入流提供读缓冲区。 ​

        PipedReader 类:连接到一个 PipedWriter。 ​

        InputStreamReader 类:将字节输入流转换为字符输入流,可以指定字符编码。

与 InputStream 类相同,在 Reader 类中也包含 close()、mark()、skip() 和 reset() 等方法,这些方法可以参考 InputStream 类的方法。下面主要介绍 Reader 类中的 read() 方法。

 

import javax.imageio.stream.FileImageInputStream;
import java.io.*;

/**
 * BufferedReader 类主要用于辅助其他字符输入流,它带有缓冲区,可以先将一批数据读到内存缓冲区。
 * 接下来的读操作就可以直接从缓冲区中获取数据,而不需要每次都从数据源读取数据并进行字符编码转换
 * ,这样就可以提高数据的读取效率。
 */
public class BufferedReaderDemo01 {
    public static void main(String[] args) throws Exception {
        // 创建字节流对象
        FileInputStream fis = new FileInputStream("dir/osw.txt");
        // 创建字符流对象
        InputStreamReader isr = new InputStreamReader(fis, "utf-8");
        // 创建缓冲流对象
        BufferedReader br = new BufferedReader(isr);

        // 读
        String s = br.readLine();
        System.out.println(s);

        String s1 = br.readLine();
        System.out.println(s1);

        // 读取到文件末尾结果为null
        String s2 = br.readLine();
        System.out.println(s2);

        // 关闭资源
        fis.close();
        isr.close();
        br.close();
    }
}

字符输出流

        与 Reader 类相反,Writer 类是所有字符输出流的父类,该类中有许多方法,这些方法对继承该类的所有子类都是有效的。

Writer 类的常用子类如下。 ​

        CharArrayWriter 类:向内存缓冲区的字符数组写数据。 ​

        StringWriter 类:向内存缓冲区的字符串(StringBuffer)写数据。 ​

        BufferedWriter 类:为其他字符输出流提供写缓冲区。 ​

        PipedWriter 类:连接到一个 PipedReader。 ​

        OutputStreamReader 类:将字节输出流转换为字符输出流,可以指定字符编码。

与 OutputStream 类相同,Writer 类也包含 close()、flush() 等方法,这些方法可以参考 OutputStream 类的方法。下面主要介绍 Writer 类中的 write() 方法和 append() 方法,如下表所示。

import java.io.*;

/**
 * BufferedWriter 类主要用于辅助其他字符输出流,它同样带有缓冲区,可以先将一批数据写入缓冲区,
 * 当缓冲区满了以后,再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。
 */
public class BufferedWriterDemo02 {
    public static void main(String[] args) throws Exception {
        // 创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("dir/bw.txt");
        // 创建字符输出流对象
        OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
        // 创建输出缓冲流对象
        BufferedWriter bw = new BufferedWriter(osw);

        // 写
        bw.write("BufferedWriter 类主要用于辅助其他字符输出流,它同样带有缓冲区,可以先将一批数据写入缓冲区,");
        // 写入一行分隔符
        bw.newLine();
        bw.write("当缓冲区满了以后,再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。");

        // 关闭资源
//        fos.close();
//        osw.close();
        bw.close();

    }
}

字符文件输入流

        为了读取方便,Java 提供了用来读取字符文件的便捷类——FileReader。该类的构造方法有如下两种重载形式。

        1、FileReader(File file):在给定要读取数据的文件的情况下创建一个新的 FileReader 对象。其中,file 表示要从中读取数据的文件。

        2、FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新 FileReader 对象。其中,fileName 表示要从中读取数据的文件的名称,表示的是一个文件的完整路径。

        在用该类的构造方法创建 FileReader 读取对象时,默认的字符编码及字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FilelnputStream 上构造一个 InputStreamReader。

注意:在创建 FileReader 对象时可能会引发一个 FileNotFoundException 异常,因此需要使用 try catch 语句捕获该异常。

        字符流和字节流的操作步骤相同,都是首先创建输入流或输出流对象,即建立连接管道,建立完成后进行读或写操作,最后关闭输入/输出流通道。

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

/**
 * 字符流(高级流)\转换流
 * 作用:专用于与文本的读写
 *
 * 对应的构造方法:
 * 		OutputStreamWriter(OutputStream out) 该构造方法会使用相同默认的字符编码
 * 		OutputStreamWriter(OutputStream out, String charsetName) 该构造方法可以指定字符集
 *
 * OutputStreamWriter是从字符流到字节流的桥梁:使用指定的charset将写入的字符编码为字节。
 * 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
 * 每次调用write()方法都会使编码转换器在给定的字符上被调用。 所得到的字节在写入底层输出流之前累
 * 积在缓冲区中。 请注意,传递给write()方法的字符不会缓冲。
 *为了最大的效率,请考虑在BufferedWriter中包装一个OutputStreamWriter,以避免频繁的转换器调用。
 *
 */
public class OutputStreamWriterDemo01 {
    public static void main(String[] args) throws Exception {

        // 创建字节输出流对象
        FileOutputStream os = new FileOutputStream("dir/osw.txt");
        // 字符输出缓冲流
        OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");

        //
        osw.append("\t");
        // 写出字符串
        String str = "OutputStreamWriter是从字符流到字节流的桥梁";
        osw.write(str);

        osw.append("\n");

        // 写出字符串
        String str2 = "OutputStreamWriter是从字符流到字节流的桥梁";
        osw.write(str2);

        // 刷新流
        osw.flush();



        // 关闭资源
        os.close();
        osw.close();
    }
}

字符文件输出流

        Java 提供了写入字符文件的便捷类——FileWriter,该类的构造方法有如下 4 种重载形式。

        1、FileWriter(File file):在指定 File 对象的情况下构造一个 FileWriter 对象。其中,file 表示要写入数据的 File 对象。

        2、FileWriter(File file,boolean append):在指定 File 对象的情况下构造一个 FileWriter 对象,如果 append 的值为 true,则将字节写入文件末尾,而不是写入文件开始处。

        3、FileWriter(String fileName):在指定文件名的情况下构造一个 FileWriter 对象。其中,fileName 表示要写入字符的文件名,表示的是完整路径。

        4、FileWriter(String fileName,boolean append):在指定文件名以及要写入文件的位置的情况下构造 FileWriter 对象。其中,append 是一个 boolean 值,如果为 true,则将数据写入文件末尾,而不是文件开始处。

        在创建 FileWriter 对象时,默认字符编码和默认字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FileOutputStream 上构造一个 OutputStreamWriter 对象。

        FileWriter 类的创建不依赖于文件存在与否,如果关联文件不存在,则会自动生成一个新的文件。在创建文件之前,FileWriter 将在创建对象时打开它作为输出。如果试图打开一个只读文件,将引发一个 IOException 异常。

注意:在创建 FileWriter 对象时可能会引发 IOException 或 SecurityException 异常,因此需要使用 try catch 语句捕获该异常。

​​​​​​​

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;

/**
 *  高级流(字符流)\转换流
 *  InputStreamReader是从字节流到字符流的桥梁:它读取字节,并使用指定的charset将其解码为字符。
 *  它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
 * 每个调用InputStreamReader的read()方法之一可能会导致从底层字节输入流读取一个或多个字节。
 * 为了使字节有效地转换为字符,可以从底层流读取比满足当前读取操作所需的更多字节。
 *
 * 构造方法    Constructor 描述
 * InputStreamReader(InputStream in)
 *      创建一个使用默认字符集的InputStreamReader。
 * InputStreamReader(InputStream in, String charsetName)
 *      创建一个使用命名字符集的InputStreamReader。
 * InputStreamReader(InputStream in, Charset cs)
 *      创建一个使用给定字符集的InputStreamReader。
 * InputStreamReader(InputStream in, CharsetDecoder dec)
 *      创建一个使用给定字符集解码器的InputStreamReader。
 *
 * 为了最大的效率,请考虑在BufferedReader中包装一个InputStreamReader。
 */
public class InputStreamReaderDemo02 {
    public static void main(String[] args) throws Exception {

        // 字节流对象
        FileInputStream fis = new FileInputStream("dir/osw.txt");

        // 创建字符输入流对象
        InputStreamReader isr = new InputStreamReader(fis, "utf-8");


        // 读取字符
        int read = isr.read(new char[10]);
        System.out.println(read);

        // 关闭 资源
        fis.close();
        isr.close();
    }
}

字符缓冲区输入流

        BufferedReader 类主要用于辅助其他字符输入流,它带有缓冲区,可以先将一批数据读到内存缓冲区。接下来的读操作就可以直接从缓冲区中获取数据,而不需要每次都从数据源读取数据并进行字符编码转换,这样就可以提高数据的读取效率。

BufferedReader 类的构造方法有如下两种重载形式。

        1、BufferedReader(Reader in):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流。

        2、BufferedReader(Reader in,int size):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流,参数 size 则用于指定缓冲区的大小,单位为字符。

        除了可以为字符输入流提供缓冲区以外,BufferedReader 还提供了 readLine() 方法,该方法返回包含该行内容的字符串,但该字符串中不包含任何终止符,如果已到达流末尾,则返回 null。readLine() 方法表示每次读取一行文本内容,当遇到换行(\n)、回车(\r)或回车后直接跟着换行标记符即可认为某行已终止。

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

/**
 * 通过字节缓冲流的方式将给定的内容写入到文件当中去
 *
 * public class BufferedOutputStream extends FilterOutputStream
 * 该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,
 * 而不必为写入的每个字节导致底层系统的调用。
 *
 * 构造方法摘要
 * 构造方法    Constructor 描述
 * BufferedOutputStream(OutputStream out)
 *      创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
 * BufferedOutputStream(OutputStream out, int size)
 *      创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
 */
public class BufferedOutputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        // 创建流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("dir/bos.txt"));

        // 创建需要写入的字符串
        String str = "该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节";

        // 写入数据
        bos.write(str.getBytes("utf-8"));

        // 关闭流
        bos.close();
    }
}

字符缓冲区输出流

​​​​​​​

        BufferedWriter 类主要用于辅助其他字符输出流,它同样带有缓冲区,可以先将一批数据写入缓冲区,当缓冲区满了以后,再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。

BufferedWriter 类的构造方法有如下两种重载形式。

        1、BufferedWriter(Writer out):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流。

        2、BufferedWriter(Writer out,int size):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流,参数 size 则用于指定缓冲区的大小,单位为字符。

该类除了可以给字符输出流提供缓冲区之外,还提供了一个新的方法 newLine(),该方法用于写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行(\n)符。

提示:BufferedWriter 类的使用与 FileWriter 类相同。

转换流

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

/**
 * public class BufferedInputStream extends FilterInputStreamA
 *
 * BufferedInputStream为另一个输入流添加了功能,即缓冲输入并支持mark和reset方法的功能。
 * 创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区
 * 将根据需要从所包含的输入流中重新填充,一次很多字节。 mark操作会记住输入流中的一个点,并且
 * reset操作会导致从最近的mark操作读取的所有字节在从包含的输入流中取出新字节之前重新读取。
 *
 * 构造方法    Constructor 描述
 * BufferedInputStream(InputStream in)
 *      创建一个 BufferedInputStream并保存其参数,输入流 in供以后使用。
 * BufferedInputStream(InputStream in, int size)
 *      创建具有指定缓冲区大小的 BufferedInputStream ,并保存其参数,输入流 in供以后使用。
 */
public class BufferedInputStreamDemo02 {
    public static void main(String[] args) throws Exception {
        // 创建字节输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("dir/bos.txt"));

        // 创建读缓冲区大小
        byte[] data = new byte[1024 * 8];

        // 将读取数据转出字符串输出
        bis.read(data);
        String result = new String(data, "utf-8");
        System.out.println(result);

        // 关闭流
        bis.close();
    }
}

        正常情况下,字节流可以对所有的数据进行操作,但是有些时候在处理一些文本时我们要用到字符流,比如,查看文本的中文时就是需要采用字符流更为方便。所以 Java IO 流中提供了两种用于将字节流转换为字符流的转换流。

        InputStreamReader 用于将字节输入流转换为字符输入流,其中 OutputStreamWriter 用于将字节输出流转换为字符输出流。使用转换流可以在一定程度上避免乱码,还可以指定输入输出所使用的字符集。

        由于 BufferedReader 具有一个 readLine() 方法,可以非常方便地进行一次读入一行内容,所以经常把读入文本内容地输入流包装成 BufferedReader,用来方便地读取输入流的文本内容。

import java.io.FileNotFoundException;
import java.io.PrintWriter;

/**
 * java.io.PrintWriter
 * 具有自动行刷新的缓冲字符流,开发中比较常用的字符高级流
 * 可以按行进行写出字符串
 * @author ASUS
 *
 */
public class PrintWriterDemo01 {
    public static void main(String[] args) throws Exception {
        /**
         * PrintWriter提供了专门针对文件的构造方法
         * PrintWriter(String path)
         * PrintWriter(File file)
         */
        // 创建流对象
        PrintWriter pw = new PrintWriter("dir/pw.txt");

        // 写入数据
        pw.println("这个Java高级流");
        pw.println("PrintWriter提供了专门针对文件的构造方法");

        // 关闭流
        pw.close();
    }
}
import java.io.*;

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

        PrintWriter pw = null;
        try {
            // 创建字节流
            FileOutputStream fos = new FileOutputStream("dir/pw1.txt");
            // 创建转换流,将字节流转为为字符流
            OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
            // 创建缓冲流
            BufferedWriter bw = new BufferedWriter(osw);
            // 创建printWriter高级流
            pw = new PrintWriter(bw);

            // 写入数据
            pw.println("俄罗斯套娃");



        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            if (pw != null) {
                pw.close();
            }

        }

    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值