java io总结

一、File类

1.1 概述

使用Java io流经常会用到File类,这里先对File类做个总结。

File类是Java.io包下代表与平台无关的文件和目录。可通过File对象新建、重命名、文件和目录,但不能通过File类访问文件内容本身,此操作需通过Java输入/输出流

File 类是对文件系统的映射,并不是硬盘上真实的文件, new File("filePath") 只是创建一个与硬盘中filePath指定的文件或目录(不管存不存在)对应的File对象,并不会在硬盘中创建文件,即一个File对象只是和硬盘里面的一个文件/目录相关联。如果所指向的文件不存在,调用exist()将返回false,可以通过createNewFile()方法在硬盘中创建对应文件或目录。

1.2 创建File对象

创建File对象最常用的方法是使用构造器new File(String pathName)。pathName可以是绝对路径和相对路径。可以通过传入空字符串来创建一个当前路径对应的File对象:File file = new File("");,可以通过该方法来获取当前路径

File类还包含其他构造器,具体查看官方文档或者File类源码。

1.3 常用API

  • File类的一些静态变量:

    • separator(String):代表当前系统对应的文件路径分隔符

    • separatorChar(char):与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。此字符串只包含一个字符

    • pathSeparatorChar(char):与系统有关的路径分隔符,为了方便,它被表示为一个字符串

    • pathSeparator(String):此字符用于分隔以路径列表形式给定的文件序列中的文件名。在UNIX 系统上此字段为:;在 Windows系统上,它为 ;

  • 获取文件名

    • String getName():返回文件名(最后一级子路径名)
    • String getPath():返回文件(若是目录则返回空字符串)对应的路径名
    • String getAbsolutePath():返回绝对路径名
    • String getParent():返回父目录名
    • boolean renameTo(File newName):修改文件名,成功返回true,否则false(文件不存在时返回false)
  • 检测文件

    • boolean exist():是否存在
    • boolean canWrite():是否可写
    • boolean canRead():是否可读
    • boolean isFile():是否是文件而不是目录
    • boolean isDirectory():是否是目录而不是文件
  • 获取文件信息

    • long lastModified():返回最后修改的时间
    • long length():返回文件长度
  • 文件操作:

    • boolean createNewFile():若对应文件不存在,创建对应的新文件。创建成功返回true,否则false
    • boolean delete():删除对应文件
    • static File createTempFile(String prefix, String suffix):静态方法。根据前缀,后缀以及系统生成的随机数在默认的临时文件目录中创建一个空文件。prefix至少3字节,suffix可为null
    • static File createTempFile(String prefix, String suffix, File directory):在指定目录创建对应文件
    • void deleteOnExit():注册删除钩子。当虚拟机退出时,删除该文件
  • 目录操作:

    • boolean mkdir():创建File对象对应的目录
    • String[] list():返回File的所有子文件名和路径名的String数组
    • File[] listFiles():返回File的所有子文件名和路径名的File数组
    • static File[] listRoots():返回系统所有的根路径的File数组

1.4 文件过滤器

在使用list()方法所有子文件名和路径名时,可以传入一个FilenameFilter(接口)参数,代表文件过滤器。该接口包含一个boolean accept(File dir, String name)方法,该方法将依次对指定File对象的子文件名和路径名进行迭代筛选,返回true表示符合要求。

示例:

public static void main(String[] args) throws IOException {
    File f1 = new File("E:\\temp");

    String[] list1 = f1.list();
    System.out.println("该目录下的所有文件:");
    for (String s : list1) {
        System.out.println(s);
    }

    System.out.println("过滤掉不以.txt结尾的子文件:");
    String[] list2 = f1.list(((dir, name) -> name.endsWith(".txt")));
    for (String s : list2) {
        System.out.println(s);
    }
}
   
输出:
该目录下的所有文件:
a.txt
a.txt.bak
answers.txt
b.txt
b.txt.bak
c.txt
c.txt.bak
expFile.txt
过滤掉不以.txt结尾的子文件:
a.txt
answers.txt
b.txt
c.txt
expFile.txt

二、Java IO

2.1 概述

Java的IO流是实现输入/输出的基础, 它可以方便地实现数据的输入/输出操作, 在Java中把不同的输入/输出源(键盘、文件、网络连接等) 抽象表述为“流”(stram) , 通过流的方式允许Java程序使用相同的方式来访问不同的输入/输出源。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

Java io流的类层次图,其中的类皆来自java.io包(图片来自菜鸟教程):

2.2 流的分类

2.2.1 输入流与输出流

根据数据流向不同可分为输入流和输出流。

输入流:只能从中读取数据。输入流主要以InputStream、Reader抽象类作为基类

输出流:只能向其中输入数据。输出流主要以OutputStream、Writer抽象类作为基类。

这里的输入输出是从程序运行所在角度来划分的

2.2.2 字节流和字符流

根据处理数据类型的不同可分为字节流和字符流。

两者的区别:

  • 传输数据的单位不同,字节流为8位的字节,字符流为16位的字符

  • 处理对象不同,字节流能处理所有类型的数据,而字符流只能处理字符类型的数据

  • 字节流在操作的时候本身是不会用到缓冲区的,是在文件本身直接操作;而字符流是通过缓冲区来操作文件的

  • 结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。

字节流以InputStream、OutputStream为基类。字符流以Reader、Writer为基类

2.2.3 节点流和处理流

  • 节点流:可以从/向一个特定的IO设备(磁盘,网络等)读/写数据的流。即程序直接连接到数据源

  • 处理流:对一个已存在的节点流进行连接和封装,封装后的流提供了更简便强大的数据读写功能。关闭流时,只关闭处理流即可,外层的流将会关闭内层的节点流


以下分为字节流和字符流对一些较常用的流进行总结(每一小节总结对应的一对输入输出流,标题只写输入流)。

2.3 字节流

字节流基类是InputStream和OutputStream。InputStream类是所有字节输入流类的抽象父类。OutputStream类是所有字节输出流的抽象父类。InputStream和OutputStream基类提供了read()write()系列方法进行读写,可以一次读/写一个字节,也可多个字节。在处理图片、音乐等非文本文件时,使用的是字节流。

InputStream的常见子类有:

  • FileInputStream:从文件中读取信息。
  • ByteArrayInputStream: 字节数组输入流,
  • ObjectInputStream序列化时使用。一般和ObjectOutputStream一起使用
  • FilterInputStream:过滤输入流,为基础的输入流提供一些额外的操作。

OutputStream的常见子类有:

  • FileOutPutStream: 文件输出流对文件进行操作
  • ByteArrayOutputStream: 字节数组输出流
  • ObjectOutputStream: 序列化时使用。一般和OjbectInputStream一起使用
  • FilterOutputStream:过滤输出流,为基础的输出流提供一些额外的操作。

其中,创建OutputStream的子类时,若指定的文件在磁盘中不存在,则会创建对应的文件;对于InputStream,若文件不存在则抛出异常。

2.3.1 FileInputStream

FileInputStream、FileOutPutStream是针对文件的字节输入、输出流,可以通过指定文件的路径(String)或指定一个File对象来创建

一个复制文件的简单示例:

public class CopyDemo {
	public static void main(String[] args) throws IOException {
      //创建输入流,输出流,实现文件的复制
      FileInputStream input = new FileInputStream("E:\\source.java");
      FileOutputStream output = new FileOutputStream("E:\\copy.txt");
        
      byte[] buf = new byte[32];
      int hasRead = 0;
      //将读取的输入流数据写入输出流
      while((hasRead = input.read(buf)) > 0) {
          output.write(buf);
      }
      //输出复制后的文件
      FileInputStream file = new FileInputStream("E:\\copy.txt");
      while((hasRead = file.read(buf)) > 0) {
          System.out.print(new String(buf, 0 ,hasRead));
      }
      //关闭资源
      input.close();
      output.close();
      file.close();
	}
}

2.3.2 ByteArrayInputStream

流的来源或目的地并不一定是文件,也可以是内存中的一块空间,例如一个字节数组。ByteArrayInputStream、ByteArrayOutputStream就是将字节数组当作流的输入来源、输出目的地的类

创建ByteArrayInputStream需要传入一个byte[];创建ByteArrayOutputStream不是传入一个byte数组(ByteArrayOutputStream中封装了一个byte数组作为输出目的地,通过toByteArray()方法可以获取到该数组),而是指定ByteArrayOutputStream中byte数组的长度,也可以不指定,默认为32。


关于这两个流的应用场景(来自参考链接):

这两个类对于要创建临时性文件的程序以及网络数据的传输、数据压缩后的传输等可以提高运行的的效率,可以不用访问磁盘。同样有StringReader与StringWriter类以字符IO流的方式处理字符串。

两个流的用法比较简单,直接看示例:

public class ByteArrayOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        demo1();
        demo2();
        //输出:
        //hello
		//hello, world!
    }

    private static void demo1() throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ByteArrayInputStream inputStream;

        byte[] bytes = new byte[]{'h', 'e', 'l', 'l', 'o'};
        byte[] bytes1 = new byte[bytes.length];

        outputStream.write(bytes);
        inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        inputStream.read(bytes1);

        System.out.println(new String(bytes1));
    }

    private static void demo2(){
        String str = "hello, world!";
        byte[] bytes = str.getBytes();

        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
        int hasRead = 0;
        while ((hasRead = inputStream.read())>0){
            System.out.print((char)hasRead);
        }
    }
}

2.3.3 ObjectInputStream

ObjectInputStream和ObjectOutpuStream一般用于反序列化/序列化Java对象。被操作的对象对应的类必须实现Serializable接口

  • ObjectInputStream能够从输入流中读取Java对象,而不需要每次读取一个字节(反序列化)
  • ObjectOutputStream能够把对象写入到输出流中,而不需要每次写入一个字节(序列化)

创建ObjectInputStream和ObjectOutpuStream流需要传入一个节点流

示例:

一个要注意的点是:若是要从流中读取多个对象,则读取顺序要与写入的顺序一致

public void demo1() throws Exception {
    Data w=new Data(2);
    ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("E:\\worm.out"));
    //序列化对象,把对象写到worm.out里面
    out.writeObject("Worm storage\n");
    //序列化对象,把对象写到worm.out里面
    out.writeObject(w);

    //从worm.out里面读取对象
    ObjectInputStream in=new ObjectInputStream(new FileInputStream("E:\\worm.out"));
    //读取String对象
    String s=(String)in.readObject();
    //读取Data对象
    Data d=(Data)in.readObject();
    System.out.println(s+"Data = "+d);
}

class Data implements Serializable {
    private int n;
    public Data(int n){
        this.n=n;
    }
    @Override
    public String toString(){
        return Integer.toString(n);
    }
}


ObjectInputStream和ObjectOutpuStream也提供了系列readXxx()writeXxx()方法,用于写入/读取int、char或者字符串等类型的值。与读写对象一样,使用readXxx()读取数据时,要与写入时的顺序一致。如下示例:

public void demo2() throws Exception {
    ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("E:\\test.out"));
    ObjectInputStream in;

    out.writeInt(2);
    out.writeChar('H');
    out.writeDouble(3.14);
    out.writeUTF("hello, world!");//写入字符串
    out.writeObject("I am a String Object");

    out.close();

    in =new ObjectInputStream(new FileInputStream("E:\\test.out"));
    //从输入流中读取数据的顺序要与写入时的顺序一致,否则抛出异常
    int val = in.readInt();
    char c = in.readChar();
    double val1 = in.readDouble();
    String str = in.readUTF();
    String strObj = (String) in.readObject();

    System.out.println(val);
    System.out.println(c);
    System.out.println(val1);
    System.out.println(str);
    System.out.println(strObj);
}

2.3.4 过滤输入/输出流

这里的过滤输入/输出流是指FilterInputStream和FilteOutputStream,他们的作用是为基础流提供一些额外的功能

常用子类

  • FilterInputStream常用子类

    • DataInputStream
    • BufferedInputStream:可以从缓冲区中读取数据,不用每次和文件的操作都进行实际操作了。
  • FilterOutputStream常用子类

    • DataOutputStream
    • PrintStream:用于产生格式化的输出
    • BufferedOutputStream:通过缓冲区像文件中写入数据。

DataInputStream

DataInputStream是数据字节输入流,用来装饰其他的输入流,允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型;DataOutputStream可以向文件中写入基本类型的数据,与DataInputStream配合使用。

创建DataInputStream和DataOutputStream都要基于一个输入/输出节点流。


示例:

其中的writeUTF()方法,是以UTF-8编码写入指定字符串;相应的,readUTF()是以UTF-8编码读取字符串

public class DataInOutStreamDemo {
    public static void main(String[] args) throws Exception{
        File file = new File("E:\\data.txt");
        FileInputStream in;
        FileOutputStream out;
        DataInputStream dataIn;
        DataOutputStream dataOut;

        out = new FileOutputStream(file);
        dataOut = new DataOutputStream(out);

        //写入数据
        dataOut.writeInt(2020);
        dataOut.writeBoolean(true);
        dataOut.writeChar('H');
        dataOut.writeUTF("hello,world");

        in = new FileInputStream(file);
        dataIn = new DataInputStream(in);

        //读出数据(注意顺序)
        int readInt = dataIn.readInt();
        boolean readBoolean = dataIn.readBoolean();
        char readChar = dataIn.readChar();
        String readStr = dataIn.readUTF();

        System.out.println("readInt = " + readInt);
        System.out.println("readBoolean = " + readBoolean);
        System.out.println("readChar = " + readChar);
        System.out.println("readStr = " + readStr);
    }
}

BufferedInputStream

BufferedInputStream和BufferedOutputStream是两个缓冲流。对于没有缓冲的节点流,如FileInputStream,读取一个字节就要与磁盘交互一次,与磁盘的交互过于频繁,读取速度较低

缓冲流中内置了一个缓冲区,

  • 对于BufferedOutputStream,使用write()方法写入数据时,写入的数据暂时被放在缓冲区,当调用flush()方法或者缓冲区满时才把数据写入输出流,从而减少了与磁盘的交互,提高效率。
  • 对于BufferedInputStream,在新建某输入流对应的BufferedInputStream后,当通过read()读取输入流的数据时,BufferedInputStream会将该输入流的数据分批的填入到缓冲区中。每当缓冲区中的数据被读完之后,输入流会再次填充数据缓冲区;如此反复,直到读完输入流数据位置。从内存中读取数据的速度比从硬盘读取数据的速度快得多,且分批读取数据减少了与磁盘的交互,所以BufferedInputStream的效率比普通的输入节点流要高很多。

一个复制图片的示例:

public class BufferedInOutStreamDemo {
    public static void main(String[] args) throws Exception{
        copyDemo();
    }

    private static void copyDemo() throws Exception{
        File srcImg = new File("E:\\xiaogailun.jpg");
        File copyImg = new File("E:\\copy.jpg");
        FileInputStream in = new FileInputStream(srcImg);
        FileOutputStream out = new FileOutputStream(copyImg);
        BufferedInputStream bufIn = new BufferedInputStream(in);
        BufferedOutputStream bufOut = new BufferedOutputStream(out);
        byte[] buf = new byte[64];
        int hasRead = 0;

        while ((hasRead = bufIn.read(buf)) > 0){
            bufOut.write(buf, 0, hasRead);
        }
        bufOut.flush();

        bufIn.close();
        bufOut.close();
    }
}

最后关闭流时,只需关闭外层流(处理流)即可,在外层流中会把包装的内层流(节点流)关闭

PrintStream

PrintStream 是用来装饰其它输出流,其中提供了系列print()方法,让其他输出流能够方便地输出各种形式的数据。另外,PrintStream提供了自动flush(写入数据时自动调用flush())和字符集设置功能。

PrintStream的构造器:(其中的boolean值是指定是否自动flush;String是指定文件名或字符集名

image-20201028233241230

简单示例:

public class PrintStreamDemo {
    public static void main(String[] args) throws Exception{
        File file = new File("E:\\print.txt");
        PrintStream printStream = new PrintStream(new FileOutputStream(file));

        printStream.println("hello,world!啊哈哈哈");//结尾加回车符
        printStream.print(12138);//结尾无回车符
        printStream.println();
        printStream.print('H');
        
        printStream.close();
    }
}

执行后的print.txt文件内容:

hello,world!啊哈哈哈
12138
H

更多关于PrintStream的内容推荐看(贼详细):java io系列16之 PrintStream(打印输出流)详解

2.4 字符流

字符流操作的数据单位是字符,在处理文本文件的时候更多使用的是字符流。

字符流的两个抽象基类是ReaderWriter。其常见子类如下:

Reader类常见子类有:

  • InputStreamReader:字节流到字符流的桥接器。

  • FileReader:文件字符输入流

  • BufferedReader: 带缓冲区的字符输入流

  • StringReader:以字符串为源的字符输入流。(StringReader和StringWriter的使用较简单,不作总结)

Writer类常见子类有:

  • OutputStreamWriter:字节流到字符流的桥接器。

  • FileWriter:文件字符输出流

  • BufferedWriter:带缓冲区的字符输出流

  • StringWriter:内部封装了一个StringBuffer,作为该输出流的源。提供了一个getBuffer()方法获取其中的StringBuffer。


要注意的点

  • Writer抽象类中定义了一个char[]缓冲区(即Writer的系列子类都有一个缓冲区),该缓冲区的默认大小是1024,且不可自行指定大小。在执行写操作时,只有缓冲区满或调用flush()或调用close()方法时才会将内容写入磁盘中的文件
  • 创建Writer子类时,与OutputStream一样,若指定的文件在磁盘中不存在,则会创建对应的文件;对于Reader,若文件不存在则同样抛出异常。

2.4.1 InputStreamReader

InputStreamReader和OutputStreamWriter是字节流到字符流的桥接器,它们可以根据指定的字符集来进行读写内容,没有指定字符集时使用的是java虚拟机的字符集

每次调用InputStreamReader的read()都将从底层读取一个或多个字节;每次调用OutputStreamWriter的write()方法都会将指定内容写入底层文件。为了提高效率,一般用BufferedReader和BufferedWriter来包装它们。

创建InputStreamReader和OutputStreamWriter时传入的是字节流,它们会根据指定的字符集进行字节和字符间的转换

InputStreamReader的构造器:

image-20201031105204555

OutputStreamWriter的构造器:

image-20201031105018986

2.4.2 FileReader

FileWriter和FileReader是InputStreamWriter和OutputStreamReader的子类,在FileWriter和FileReader中,字符集使用的是Java虚拟机的字符集。

FileWriter和FileReader的使用比较简单。它们都是节点流,可以直接传入一个FIle或文件路径来创建流。

其中,创建FileWriter时可以传入一个boolean值,若为true,表示写入的内容是追加在原文件结尾之后,该值默认为false。对应的两个构造器:

FileWriter(File file, boolean append)
FileWriter(String fileName, boolean append)

源码中关于append参数的解释:

boolean if true, then data will be written to the end of the file rather than the beginning.

示例:

public class FileReaderAndWriterDemo {

    public static void main(String[] args) throws Exception{
        write();
        read();
        System.out.println("\n\n---------------------------------------------------------------------\n");
        appendWrite();
        read();
    }

    private static void write() throws Exception{
        File file = new File("E:\\file.txt");
        FileWriter writer = new FileWriter(file);
        writer.write("hello,world!你好,世界!\n");
        writer.write("12345678999+*-\n");
        //写入时会把int数值转换为对应的char
        writer.write(67);
        writer.flush();//可以省略,因为调用close时会调用flush
        writer.close();
    }

    private static void read()throws Exception{
        File file = new File("E:\\file.txt");
        FileReader reader = new FileReader(file);
        char[] cbuf = new char[16];
        int hasRead = 0;
        while ((hasRead=reader.read(cbuf)) > 0){
            System.out.print(new String(cbuf, 0, hasRead));
        }

        reader.close();
    }

    /**
     * 在文件结尾写入内容
     */
    private static void appendWrite() throws Exception{
        File file = new File("E:\\file.txt");
        FileWriter writer = new FileWriter(file, true);
        writer.write("\n------以下内容并没有覆盖原文件内容------\n");
        writer.write("啊哈哈哈哈哈哈哈\n");
        writer.write("弹指间、数据库灰飞烟灭");
        writer.flush();

        writer.close();
    }
}
/**
输出:
hello,world!你好,世界!
12345678999+*-
C

---------------------------------------------------------------------

hello,world!你好,世界!
12345678999+*-
C
------以下内容并没有覆盖原文件内容------
啊哈哈哈哈哈哈哈
弹指间、数据库灰飞烟灭
*/

2.4.3 BufferedReader

BufferedReader和BufferedWriter的功能是为其他字符输入/输出流提供缓存的功能。关于缓存功能的说明在上文的BufferedInputStream和BufferedOutputStream中已有描述,此处不再赘述。

一个问题是,上文中提到Writer抽象基类中已经有了一个缓冲区,为何还要使用BufferedWriter?

原因:Writer中定义的缓冲区大小是不能自定义的,且大小只有1024;BufferedWriter中的缓冲区默认大小是8192(1024的8倍),且大小可以自定义,可以指定更大的缓冲区,这个差别使得在操作大量数据时,BufferedWriter的效率将会比普通的字符流,如FileWriter要快得多(BufferedWriter与磁盘的交互次数更少)

使用BufferedReader和BufferedWriter来处理文本时为了保证字符集的统一,内层流可以使用InputStreamReader和OutputStreamWriter,它们可以指定字符集


示例:

public class BufferedWriterReaderDemo {
    public static void main(String[] args) throws Exception {
        File file = new File("E:\\file.txt");
        write(file);
        readLine(file);
        System.out.println("\n---------------------------\n");
        readAll(file);
    }

    private static void write(File file)throws Exception{

        FileOutputStream outStream = new FileOutputStream(file);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outStream, 										StandardCharsets.UTF_8));

        writer.write("hello,world!你好,世界!\n");
        writer.write("123456789", 0, 5);//写入前5个字符
        writer.flush();
        writer.close();
    }

    /**
     * 使用readLine()按行读取
     */
    private static void readLine(File file)throws Exception{
        FileInputStream inStream = new FileInputStream(file);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, StandardCharsets.UTF_8));

        String line1 = reader.readLine();
        System.out.println("读取文件的第一行:" + line1);
        String line2 = reader.readLine();
        System.out.println("读取文件的第二行:" + line2);

        reader.close();
    }

    private static void readAll(File file)throws Exception{
        FileInputStream inStream = new FileInputStream(file);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, StandardCharsets.UTF_8));
        char[] cbuf = new char[(int) file.length()];

        reader.read(cbuf);
        System.out.println(new String(cbuf));

        reader.close();
    }
}
/**
输出:
读取文件的第一行:hello,world!你好,世界!
读取文件的第二行:12345

---------------------------

hello,world!你好,世界!
12345    
*/

2.5 移动输入流中的记录指针

在对输入流进行读操作(read())时,流中的记录指针随之向后移动。InputStream和Reader中提高了下列方法来移动输入流中的记录指针,从而可以重复读取内容:

  • mark(int readlimit):在此输入流中的当前位置做一个标记。readlimit参数告诉输入流允许在标记位置之后读取多个字节(注意是字节,不管是对于InputStream还是Reader),若是reset()之后读取的字节数超过该参数,则抛出异常:java.io.IOException: Mark invalid
  • markSupported():判断是否支持标记,即是否支持mark方法。
  • reset():将此流的记录指针重新定位到上一次在此输入流上调用 mark()方法时的位置。调用reset()之前必须有进行标记,否则抛出异常:java.io.IOException: Stream not marked
  • skip(long n):跳过输入流中的 n个字节或字符


示例:

public class MarkDemo {
    public static void main(String[] args) throws Exception {
        File file = new File("E:\\file.txt");
        write(file);
        read(file);
    }

    private static void write(File file)throws Exception{
        FileOutputStream outStream = new FileOutputStream(file);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outStream, StandardCharsets.UTF_8));

        writer.write("hello,world!你好,世界!\n");
        writer.write("123456789");
        writer.flush();
        writer.close();
    }

    private static void read(File file)throws Exception{
        FileInputStream inStream = new FileInputStream(file);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, StandardCharsets.UTF_8));
        char[] chars = new char[128];

        //在开头进行mark。输出文件全部内容后进行reset
        reader.mark(1024);
        reader.read(chars);
        System.out.println("文件的全部内容是:");
        System.out.println(new String(chars) + "\n");
        reader.reset();

        reader.read(chars, 0, 12);
        System.out.println("读取前12个字符:" + new String(chars, 0, 12));
        System.out.println("BufferedReader是否支持标记:" + reader.markSupported());

        System.out.println("\n在当前位置进行mark()");
        reader.mark(32);

        //跳过7个字符,即跳过了 你好,世界!\n
        reader.skip(7);
        System.out.println("\n跳过7个字符,并读取整行:");
        String line = reader.readLine();
        System.out.println(line);

        //reset,再读取,读取的开始位置是上一次mark时的位置
        System.out.println("\n--调用了reset()--\n");
        reader.reset();
        String line2 = reader.readLine();
        System.out.println("reset之后再读取:");
        System.out.println(line2);

        reader.close();
    }

}

三、参考

《疯狂Java讲义 第4版》

关于:File.separator ( 详解 )

一文带你看懂JAVA IO流 - 知乎

ByteArrayInputStream的作用,和BufferedOutputStream 的区别

Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream

java io系列16之 PrintStream(打印输出流)详解

OutputStreamWriter InputStreamReader

另外推荐一个博主的Java io系列文章:java io系列01之 "目录"

----------------------------------------------------------------

posted @ 2020-10-31 15:08  bxxiao  阅读(140)  评论(0)    收藏  举报