JAVA IO


前言

在Java编程中,输入和输出(Input/Output,简称I/O)是非常重要的概念。Java提供了丰富和强大的I/O类库,用于处理各种输入和输出操作。本文将介绍Java中的I/O概念以及常用的I/O类的使用方法。


1、I/O概念

1.1 基本概念

这里的I和O指的是输入与输出

  • 输入:Input 用来读取数据的
    是指从外部资源(如键盘、文件、网络等)读取数据到程序中。
  • 输出:Output 用来写出数据的
    是指将程序中的数据写入到外部资源中。

1.2 流的概念

java将输入与输出比喻为"流",英文:Stream.
就像生活中的"电流",“水流"一样,它是以同一个方向顺序移动的过程.只不过这里流动的是字节(2进制数据).所以在IO中有输入流和输出流之分,我们理解他们是连接程序与另一端的"管道”,用于获取或发送数据到另一端.
在这里插入图片描述

1.3 超类

  • java.io.InputStream是所有字节输入流的超类,里面定义了所有字节输入流都必须具备的读取字节的方法
    int read()
    读取一个字节,以int形式返回,该int值的”低八位”有效,若返回值为-1则表示EOF:end of file 文件末尾
    int read(byte[] data)
    尝试最多读取给定数组的length个字节并存入该数组,返回值为实际读取到的字节量.

  • java.io.OutputStream是所有字节输出流的超类,里面定义了所有字节输出流都必须具备的写出字节的方法
    void write(int d)
    写出一个字节,写的是给定的int的”低八位”
    void write(byte[] data)
    将给定的字节数组中的所有字节全部写出
    void write(byte[] data,int offset,int len)
    将给定的字节数组中从offset处开始的连续len个字节写出

2. 常用I/O

2.1 文件流

文件流是用来链接我们的程序与文件之间的"管道",用来读写文件数据的流。

  • 件输入流java.io.FileInputStream:读取文件数据的流
  • 文件输出流java.io.FileOutputStream:写入文件数据的流
  • 文件流是继承自InputStream和OutputStream
    文件输出流
    java.io.FileOutputStream使用文件输出流向文件中写入数据
    构造器
FileOutputStream(String path)
创建文件输出流对指定的path路径表示的文件进行写操作,如果该文件不存在则将其创建出来

FileOutputStream(File file)
创建文件输出流对指定的file对象表示的文件进行写操作,如果该文件不存在则将其创建出来

package io;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FOSDemo {
    public static void main(String[] args) throws IOException {
        /*
            FileOutputStream(String path)
            FileOutputStream(File file)
         */
        //向当前项目目录下的文件fos.dat中写入数据
        FileOutputStream fos = new FileOutputStream("./fos.dat");
//        File file = new File("./fos.dat");
//        FileOutputStream fos = new FileOutputStream(file);
        /*
            void write(int d)
            用来向文件中写入1个字节
         write方法会将给定的int值对应的2进制的"低八位"写出
         fos.write(1)
         int型1的2进制:
         00000000 00000000 00000000 00000001
                                    ^^^^^^^^
                                    写出的数据
         fos.dat文件中内容:
         00000001
         write(2)
         2的2进制
         00000000 00000000 00000000 00000010
                                    ^^^^^^^^
                                    写出的数据
         fos.dat文件中内容:
         00000001 00000010
         */
        fos.write(1);
        fos.write(2);
        //当IO操作完毕后要关闭
        fos.close();
    }
}

文件输入流
java.io.FileInputStream使用文件输入流从文件中读取数据,是一对低级流.
构造器

FileInputStream(String path)
基于给定的路径对应的文件创建文件输入流
    
FileInputStream(File file)
基于给定的File对象所表示的文件创建文件输入流

package io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
 * 文件输入流
 * 用于从文件中读取字节的流
 */
public class FISDemo {
    public static void main(String[] args) throws IOException {
        /*
            FileInputStream(String path)
            FileInputStream(File file)
         */
        FileInputStream fis = new FileInputStream("fos.dat");
        /*
            int read()
            读取1个字节,以int形式返回该字节内容。int值只有"低八位"有数据,高24位
            全部补0.
            有一个特殊情况:如果返回的int值为整数-1,则表示EOF。
            EOF:end of file 文件末尾
            fos.dat文件数据
            00000001 00000010
            第一次调用:
            int d = fis.read();
            00000001 00000010
            ^^^^^^^^
            读取的字节
            返回值d的二进制样子:
            00000000 00000000 00000000 00000001
            |-----自动补充的24个0------| 读取的字节
            第二次调用:
            d = fis.read();
            00000001 00000010
                     ^^^^^^^^
                     读取的字节
            返回值d的二进制样子:
            00000000 00000000 00000000 00000010
            |-----自动补充的24个0------| 读取的字节                     
            第三次调用:
            d = fis.read();
            00000001 00000010
                              ^^^^^^^^
                              文件末尾了
            返回值d的二进制样子:
            11111111 11111111 11111111 11111111
            |----------32位2进制都是1-----------|
         */
        int d = fis.read();
        System.out.println(d);//1
        d = fis.read();
        System.out.println(d);//2
        d = fis.read();//文件只有2个字节,因此第三次读取已经是文件末尾EOF
        System.out.println(d);//-1
        fis.close();
    }
}

文件复制
复制文件的原理就是使用文件输入流从原文件中陆续读取出每一个字节,然后再使用文件输出流将字节陆续写入到另一个文件中完成的。

package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * 文件的复制
 */
public class CopyDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("image.png");
        FileOutputStream fos = new FileOutputStream("image_cp.png");
        int d;
        while((d = fis.read())!= -1){
            fos.write(d);
        }
        System.out.println("复制完毕!");
        fis.close();
        fos.close();
    }
}

效率问题
上述案例在复制文件时的读写效率是很低的。因为硬盘的特性,决定着硬盘的读写效率差,而单字节读写就是在频繁调用硬盘的读写,从而产生了"木桶效应"。
为了解决这个问题,我们可以采取使用块读写的方式来复制文件,减少硬盘的实际读写的次数,从而提高效率。
块读写

  • 块读:一次性读取一组字节的方式
    InputStream中定义了块读的方法
int read(byte[] data)
一次性读取给定字节数组总长度的字节量并存入到该数组中。
返回值为实际读取到的字节数。如果返回值为-1表示本次没有读取到任何字节已经是流的末尾了
  • 块写:一次性写出一组字节的方式
    OutputStream中定义了块写的方法
void write(byte[] data)
一次性将给定数组中所有字节写出    
void write(byte[] data,int offset,int len)
一次性将data数组中从下标offset处开始的连续len个字节一次性写出


改为块读写形式后,复制效率得到了明显的提升

package io;

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

public class CopyDemo2 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("image.png");
        FileOutputStream fos = new FileOutputStream("image_cp2.png");     
        byte[] data = new byte[1024*10];//10kb
        int d;//记录每次实际读取的数据量
        long start = System.currentTimeMillis();
        while( (d = fis.read(data)) !=-1){
            fos.write(data);
        }
        long end = System.currentTimeMillis();
        System.out.println("复制完毕!耗时:"+(end-start)+"ms");
        fis.close();
        fos.close();
    }
}
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyDemo2 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("image.png");
        FileOutputStream fos = new FileOutputStream("image_cp2.png");     
        byte[] data = new byte[1024*10];//10kb
        int d;//记录每次实际读取的数据量
        long start = System.currentTimeMillis();
        while( (d = fis.read(data)) !=-1){
            fos.write(data,0,d);
        }
        long end = System.currentTimeMillis();
        System.out.println("复制完毕!耗时:"+(end-start)+"ms");
        fis.close();
        fos.close();
    }
}

写出文本数据
文件中只能保存2进制信息,因此我们要想写出文本数据,需要先将字符串转换为2进制。
文字编码
String提供了将字符串转换为一组字节的方法

byte[] getBytes(Charset cs)
将当前字符串按照指定的字符集转换为一组字节
    
例如:
String line = "在小小的花园里面";
byte[] data = line.getBytes(StandardCharsets.UTF_8);//将字符串按照UTF-8编码转换为一组字节

写出文本文件

package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
 * 写出文本数据
 */
public class WriteStringDemo {
    public static void main(String[] args) throws IOException {
         //向文件fos1.txt中写入一行中文
        FileOutputStream fos = new FileOutputStream("fos1.txt");
        String line = "在小小的花园里面";
        byte[] data = line.getBytes(StandardCharsets.UTF_8);
        fos.write(data);
        fos.write(",挖呀挖呀挖~".getBytes(StandardCharsets.UTF_8));
        System.out.println("写出完毕!");
        fos.close();
    }
}

读取文本数据
先将文件中的字节读取出来,然后再将这些字节按照对应的字符集转换为字符串即可
文本解码
String的构造器提供了对字节解码为字符串的操作

String(byte[] data,Charset cn)
将data数组中的所有字节按照指定的字符集转换为字符串

package io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 读取文本数据
 */
public class ReadStringDemo {
    public static void main(String[] args) throws IOException {
        /*
            1:先从fos.txt中读取所有的字节
            2:再将这些字节转换为字符串
         */
        //1
        File file = new File("fos.txt");
        long len = file.length();//文件名长度

        FileInputStream fis = new FileInputStream(file);
        byte[] data = new byte[(int)len];//创建一个与文件长度等长的字节数组
        fis.read(data);//一口气将文件所有字节读入到data数组中(块读)

        //2将data数组中所有字节按照UTF-8编码还原为字符串
        String line = new String(data, StandardCharsets.UTF_8);
        System.out.println(line);

        fis.close();
    }
}

高级流
java将IO分为了两类

  • 节点流:又称为"低级流"
    特点:直接链接程序与另一端的"管道",是真实读写数据的流
    IO一定是建立在节点流的基础上进行的。
    文件流就是典型的节点流(低级流)

  • 处理流:又称为"高级流"
    特点:不能独立存在,必须链接在其他流上
    目的:当数据经过当前高级流时可以对数据进行某种加工操作,来简化我们的同等操作
    实际开发中我们经常"串联"一组高级流最终到某个低级流上,使读写数据以流水线式的加工处理完成。这一操作也被称为使"流的链接"。流链接也是JAVA IO的精髓所在。
    在这里插入图片描述

2.2 缓冲流

java.io.BufferedInputStream和BufferedOutputStream
功能
在流链接中的作用:加快读写效率
通常缓冲是最终链接在低级流上的流
构造器

  • 缓冲字节输出流
BufferedOutputStream(OutputStream out)
实例化一个缓冲字节输出流并链接在指定的字节输出流上。默认缓冲区大小8kb(内部维护的byte[] buf数组长度8192)

BufferedOutputStream(OutputStream out,int size)
实例化一个指定缓冲区大小的缓冲字节输出流并链接在指定的字节输出流上。
  • 缓冲字节输入流
BufferedInputStream(InputStream in)
实例化一个缓冲字节输入流并链接在指定的字节输入流上。默认缓冲区大小8kb(内部维护的byte[] buf数组长度8192)

BufferedInputStream(InputStream in,int size)
实例化一个指定缓冲区大小的缓冲字节输入流并链接在指定的字节输入流上。

在这里插入图片描述

package io;
import java.io.*;
/**
 * 使用缓冲流完成文件复制操作
 */
public class CopyDemo3 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("image.png");
        BufferedInputStream bis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream("image_cp3.png");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        int d;
        long start = System.currentTimeMillis();
        while((d=bis.read())!= -1){
            bos.write(d);
        }
        long end = System.currentTimeMillis();
        System.out.println("复制完毕!耗时:"+(end-start)+"ms");
        bis.close();
        bos.close();
    }
}

原理
内部定义了一个属性byte buf[]。它等同于前面复制案例时的块写操作。
并且默认创建时,该buf数组的长度为8192(8kb)长度。
缓冲流在读写数据时总是以块读写数据(默认是8kb)来保证读写效率的
缓冲流提供了多种构造器,可以自行指定缓冲区大小。
在这里插入图片描述
写缓冲问题

由于缓冲输出流会将写出的数据装满内部缓冲区(默认8kb的字节数组)后才会进行一次真实的写出操作。当我们的数据不足时,如果想要及时写出数据,可以调用缓冲流的flush()方法,强制将缓冲区中已经缓存的数据写出一次。

package io;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 缓冲输出流的写缓冲问题
 */
public class BosFlushDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("bos.txt");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        String line = "super idol的笑容都没你的甜~";
        byte[] data = line.getBytes(StandardCharsets.UTF_8);
        bos.write(data);
        /*
            void flush()
            强制将缓冲流的缓冲取(内部维护的字节数组)中已经缓存的字节一次性写出
         */
//        bos.flush();
        System.out.println("写出完毕!");
        bos.close();//缓冲输出流的close()方法内部会自动调用一次flush()方法确保数据写出
    }
}

flush的传递
flush()方法是被定义在java.io.Flushable中。而字节输出流的超类java.io.OutputStream实现了该接口,这意味着所有的字节输出流都有flush方法。而除了缓冲流之外的高级流的flush方法作用就是调用它链接的流的flush方法将该动作传递下去。最终传递给缓冲流来清空缓冲区。
在这里插入图片描述

2.3 对象流

java.io.ObjectInputStream和ObjectOutputStream
作用

  • 对象输出流:将我们的java对象进行序列化
  • 对象输入流:将java对象进行反序列化
    序列化
    将一个对象转换为一组可被传输或保存的字节。这组字节中除了包含对象本身的数据外,还会包含结构信息。
    序列化的意义
    实际开发中,我们通常会将对象
  • 写入磁盘,进行长久保存
  • 在网络间两台计算机中的java间进行传输
    对象输出流的序列化操作
void writeObject(Object obj)
将给定的对象转换为一组可保存或传输的字节然后通过其链接的流将字节写出

在这里插入图片描述

package io;
import java.io.*;
/**
 * 对象流
 * 使用对象输出流完成对象序列化操作并最终保存到文件person.obj中
 */
public class OOSDemo {
    public static void main(String[] args) throws IOException {
        String name = "张三";
        int age = 18;
        String gender = "女";
        String[] otherInfo = {"黑","嗓门大","技术好","大家的启蒙老师","来自廊坊佳木斯"};
        Person p = new Person(name,age,gender,otherInfo);
        FileOutputStream fos = new FileOutputStream("person.obj");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        /*
            对象输出流提供的序列化对象方法:
            void writeObject(Object obj)
            将给定的对象转换为一组可保存或传输的字节然后通过其链接的流将字节写出
            序列化对象时要求该对象对应的类必须实现接口:java.io.Serializable
            如果写出的对象对应的类没有实现该接口,那么writeObject会抛出下面异常
            java.io.NotSerializableException
         */
        oos.writeObject(p);
        System.out.println("对象写出完毕");
        oos.close();
    }
}

序列化要求
对象输出流要求写出的对象必须实现接口:java.io.Serializable
上述案例中我们看到,如果写出的对象Person没有实现java.io.Serializable时会抛出异常:
java.io.NotSerializableException

对象输入流
java.io.ObjectInputStream使用对象流可以进行对象反序列化

构造器

ObjectInputStream(InputStream in)
将当前创建的对象输入流链接在指定的输入流上

方法

Object readObject()
进行对象反序列化并返回。该方法会从当前对象输入流链接的流中读取若干字节并将其还原为对象。这里要注意读取的字节必须是由ObjectOutputStream序列化一个对象所得到的字节。

package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
 * 对象输入流,用来进行对象反序列化
 */
public class OISDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //读取person.obj文件并将其中保存的数据进行反序列化
        FileInputStream fis = new FileInputStream("person.obj");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Person person = (Person)ois.readObject();
        System.out.println(person);
        ois.close();
    }
}

transient关键字
当一个属性被transient关键字修饰后,该对象在进行序列化时,转换出来的字节中是不包含该属性的。忽略不必要的属性可以达到对象"瘦身"的操作。

对象瘦身可以在对象持久化时减少磁盘开销。在进行传输时可以缩短传输速度。

如果该对象不需要序列化,那么该关键字不发挥其他任何效果

在这里插入图片描述
序列化时不包含otherInfo属性,并且反序列化时该属性值为null

2.4 字符流

  • java将流按照读写单位划分为字节流与字符流.

  • java.io.InputStream和OutputStream是所有字节流的超类

  • 而java.io.Reader和Writer则是所有字符流的超类,它们和字节流的超类是平级关系.

  • Reader和Writer是两个抽象类,里面规定了所有字符流都必须具备的读写字符的相关方法.

  • 字符流最小读写单位为字符(char),但是底层实际还是读写字节,只是字符与字节的转换工作由字符流完成.

  • 字符流都是高级流
    超类

  • java.io.Writer 所有字符输入流的超类
    常用方法

void write(int c):写出一个字符,写出给定int值”低16”位表示的字符。
void write(char[] chs):将给定字符数组中所有字符写出。
void write(String str):将给定的字符串写出
void write(char[] chs,int offset,int len):将给定的字符数组中从offset处开始连续的len个字符写出
  • java.io.Reader 所有字符输出流的超类
    常用方法
int read():读取一个字符,返回的int值“低16”位有效。当返回值为-1时表达流读取到了末尾。
int read(char[] chs):从该流中读取一个字符数组的length个字符并存入该数组,返回值为实际读取到的字符量。当返回值为-1时表达流读取到了末尾。

转换流
java.io.InputStreamReader和OutputStreamWriter是常用的字符流的实现类。
实际开发中我们不会直接操作他们,但是他们在流连接中是必不可少的一环。
流连接中的作用

  • 衔接字节流与其他字符流
  • 将字符与字节相互转换
    意义
    实际开发中我们还有功能更好用的字符高级流.但是其他的字符高级流都有一个共通点:不能直接连接在字节流上.而实际操作设备的流都是低级流同时也都是字节流.因此不能直接在流连接中串联起来.转换流是一对可以连接在字节流上的字符流,其他的高级字符流可以连接在转换流上.在流连接中起到"转换器"的作用(负责字符与字节的实际转换)
    输出流
    构造器
OutputStreamWriter(OutputStream out,Charset cs)
基于给定的字节输出流以及字符编码创建OSW
    
OutputStreamWriter(OutputStream out)
该构造方法会根据系统默认字符集创建OSW

在这里插入图片描述

package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
/**
 * 转换流写出文本数据
 */
public class OSWDemo {
    public static void main(String[] args) throws IOException {
        //向文件osw.txt中写出文本数据
        FileOutputStream fos = new FileOutputStream("osw.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
        osw.write("夜空中最亮的星,能否听清,");
        osw.write("那仰望的人心底的孤独和叹息。");
        System.out.println("写出完毕");
        osw.close();
    }
}

输入流
构造器

InputStreamWriter(InputStream in,Charset cs)
基于给定的字节输入流以及字符编码创建当前转换流
    
InputStreamWriter(InputStream in)
该构造方法会根据系统默认字符集创建当前转换流

package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
 * 使用转换流读取文本数据
 */
public class ISRDemo {
    public static void main(String[] args) throws IOException {
        //将osw.txt文件中的文本信息读取回来
        FileInputStream fis = new FileInputStream("osw.txt");
        InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
        //00000000 00000000 10011101 01110010
        int d;
        while(( d = isr.read()) != -1) {
            System.out.print((char) d);
        }
        isr.close();
    }
}

缓冲字符流
缓冲字符输出流-java.io.PrintWriter

  • java.io.BufferedWriter和BufferedReader
  • 缓冲字符流内部也有一个缓冲区,读写文本数据以块读写形式加快效率.并且缓冲流有一个特别的功能:可以按行读写文本数据.缓冲流内部维护一个char数组,默认长度8192.以块读写方式读写字符数据保证效率
  • java.io.PrintWriter具有自动行刷新的缓冲字符输出流,实际开发中更常用.它内部总是会自动连BufferedWriter作为块写加速使用.
    在这里插入图片描述
    特点
  • 可以按行写出字符串
  • 具有自动行刷新功能

对文件写操作的构造器

PrintWriter(File file)
PrintWriter(String path)
    
还支持指定字符集
PrintWriter(File file,String csn)
PrintWriter(String path,String csn)  
    
上述构造器看似PW可以直接对文件进行操作,但是它是一个高级流,实际内部会进行流连接:
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),false);
如上面工作原理图

package io;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
public class PWDemo {
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        //向文件中写入文本数据
        /*
            PrintWriter(File file)
            PrintWriter(String path)
         */
        PrintWriter pw = new PrintWriter("pw.txt");
//        PrintWriter pw = new PrintWriter("pw.txt", "UTF-8");
        pw.println("我祈祷拥有一颗透明的心灵,和会流泪的眼睛。");
        pw.println("给我再去相信的勇气,oh越过黄昏去拥抱你。");
        System.out.println("写出完毕");
        pw.close();
    }
}

其他构造器

PritWriter(Writer writer)
将当前实例化的PrintWriter链接在指定的字符输出流上    
    
PrintWriter(OutputStream out)    
将当前实例化的PrintWriter链接在指定的字节输出流上
由于除了转换流外的其他字符流都不能直接连在字节流上,因此这个构造器内部会自动链接在BufferedWriter上
并且让BufferedWriter链接在转换流OutputStream上,最后再让转换流链接再指定的字节输出流上

package io;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 *  自行完成流连接向文件写出字符串
 */
public class PWDemo2 {
    public static void main(String[] args) throws FileNotFoundException {
        //负责:将写出的字节写入到文件中
        FileOutputStream fos = new FileOutputStream("pw2.txt");
        //负责:将写出的字符全部转换为字节(可以按照指定的字符集转换)
        OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
        //负责:块写文本数据(攒够8192个字符一次性写出)
        BufferedWriter bw = new BufferedWriter(osw);
        //负责:按行写出字符串
        PrintWriter pw = new PrintWriter(bw);

        pw.println("你停在了这条我们熟悉的街,");
        pw.println("把你准备好的台词全念一遍。");

        System.out.println("写出完毕");
        pw.close();
    }
}

自动行刷新
PrintWriter支持自动行刷新,每当我们调用println方法写出一行内容后自动flush一次。

对应的构造器
PritWriter(Writer writer,boolean autoflush)
如果第二个参数为true则开启自动行刷新

package io;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * 实现一个简易的记事本工具
 * 利用流连接
 * 在文件输出流上最终链接到PrintWriter上。
 * 然后将用户在控制台上输入的每一行字符串都可以按行写入到对应的文件中。
 * 当用户在控制台上单独输入"exit"时程序退出。
 */
public class AutoFlushDemo {
    public static void main(String[] args) throws FileNotFoundException {
        //负责:将写出的字节写入到文件中
        FileOutputStream fos = new FileOutputStream("note.txt");
        //负责:将写出的字符全部转换为字节(可以按照指定的字符集转换)
        OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
        //负责:块写文本数据(攒够8192个字符一次性写出)
        BufferedWriter bw = new BufferedWriter(osw);
        //负责:按行写出字符串
        PrintWriter pw = new PrintWriter(bw,true);//开启自动行刷新

        Scanner scanner = new Scanner(System.in);
        System.out.println("请开始输入内容,单独输入exit退出");
        while(true){
            String line = scanner.nextLine();
            //String可以忽略大小写比较字符串内容:equalsIgnoreCase
            if("exit".equalsIgnoreCase(line)){
                break;
            }
            pw.println(line);//每当println后自动flush。注意:print方法并不会自动flush
        }
        System.out.println("再见!");
        pw.close();
    }
}

缓冲字符输入流-java.io.BufferedReader
缓冲字符输入流内部维护一个默认8192长度的char数组,总是以块读取文本数据保证读取效率。
缓冲输入流提供了一个按行读取文本数据的方法

String readLine()
返回一行字符串。方法内部会连续扫描若干个字符,直到遇到换行符为止,将换行符之前的内容以一个字符串形式返回。
返回的字符串中不含有最后的换行符。
返回值有三种情况:
1:正常一行内容
2:空字符串。当读取了一个空行时(这一行只有一个换行符)3:null。当流读取到了末尾时。  
    
当我们第一次调用readLine()时,流并不是只读取了一行字符串,而是先进行块读操作(一次性读取8192个字符并转入到内部的char数组中),然后扫描内部的char数组,然后将第一行字符串返回。第二次调用后再继续扫描后去的内容以此类推。

package io;

import java.io.*;

/**
 * 使用缓冲字符输入流读取文本数据
 */
public class BRDemo {
    public static void main(String[] args) throws IOException {
        //将当前源代码输出到控制台上
        /*
            1:创建文件输入流读取当前源代码文件
            2:进行流连接最终链接到BufferedReader上
            3:读取每一行字符串并输出到控制台上
         */
        FileInputStream fis = new FileInputStream(
                "./src/main/java/io/BRDemo.java"
        );
        InputStreamReader isr = new InputStreamReader(fis);
        BufferedReader br = new BufferedReader(isr);

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

总结

以上是关于Java I/O的简要介绍和常用类的示例。Java的I/O类库提供了丰富的功能和灵活的操作方式,可以满足各种输入和输出需求。希望本文对小伙伴们理解和使用Java I/O类库有所帮助!

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值