JAVA IO中的缓冲流,PrintStream类和PrintWriter以及System.in获取用户输入流详解

1.缓冲流

在java中,缓x冲流(Buffered Streams)是非常有用的,它们属于处理流的一种,用于提高原始数据流(如文件流,网络流)的读取和写入效率。

Java 的缓冲流是对字节流和字符流的一种封装,通过在内存中开辟缓冲区来提高 I/O 操作的效率。Java 通过 BufferedInputStream 和 BufferedOutputStream 来实现字节流的缓冲,通过 BufferedReader 和 BufferedWriter 来实现字符流的缓冲。

它们内部维护了一个字节数组缓冲区,当进行读写操作时,先将数据读取到缓冲区或者从缓冲区写入数据,然后再与底层设备进行交互。这种方式减少了与底层设备的直接交互次数,提高了读写的效率。

缓冲流的基本原理:

  1. 使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内
  2. 通过缓冲区的read()方法从缓冲区获取具体的字符数据,这样子就提高了效率
  3. 如果用read方法读取字符数据,并存储到另一个容器当中,直到读取到了换行符,将另一个容器临时存储到数据转成字符串返回,就形成了readLine()功能

字节缓冲流的使用场景

字节缓冲流主要用于提高字节流的读写效率,适用于以下场景:

  1. 文件读写:通过字节缓冲流可以提高文件读写的速度,尤其是对于大文件而言。
  2. 网络通信:通过字节缓冲流可以提高网络数据的读写效率,减少交互次数。
  3. 图像处理:通过字节缓冲流可以加速图像的读取和写入操作。
  4. 音频处理:通过字节缓冲流可以提高音频数据的读写效率。

2.字节缓冲流

构造方法:

  1. public BufferedInputStream(InputStream in):创建一个新的缓冲输入流,注意参数类型为InputStream
  2. public BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,注意参数类型为OutputStream
java 复制代码
// 创建字节缓冲输入流,先声明字节流
FileInputStream fps = new FileInputStream(b.txt);
BufferedInputStream bis = new BufferedInputStream(fps)

// 创建字节缓冲输入流(一步到位)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("b.txt"));

// 创建字节缓冲输出流(一步到位)
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));

缓冲流的高效

我们通过复制一个 370M+ 的大文件,来测试缓冲流的效率。为了做对比,我们先用基本流来实现一下,代码如下:

java 复制代码
// 记录开始时间
long start = System.currentTimeMillis();
// 创建流对象
try (FileInputStream fis = new FileInputStream("py.mp4");//exe文件够大
        FileOutputStream fos = new FileOutputStream("copyPy.mp4")){
    // 读写数据
    int b;
    while ((b = fis.read()) != -1) {
        fos.write(b);
    }
}
// 记录结束时间
long end = System.currentTimeMillis();
System.out.println("普通流复制时间:"+(end - start)+" 毫秒");

这个是十多分钟过去了还没写入完。

java 复制代码
// 记录开始时间
long start = System.currentTimeMillis();
// 创建流对象
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("py.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copyPy.mp4"));){
    // 读写数据
    int b;
    while ((b = bis.read()) != -1) {
        bos.write(b);
    }
}
// 记录结束时间
long end = System.currentTimeMillis();
System.out.println("缓冲流复制时间:"+(end - start)+" 毫秒");

这个只需要 8016 毫秒,如何更快呢?

可以换数组的方式来读写,因为这里定义了一个8kb的缓冲区,这意味着每次调用read方法时,它会尝试读取更多8kb的数据。这就减少了读取操作的次数,每次操作可以处理更多的数据。同样,写入操作也是一次性写入一大块数据,这比单字节写入的效率要高的多。

java 复制代码
// 记录开始时间
long start = System.currentTimeMillis();
// 创建流对象
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("py.mp4"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copyPy.mp4"));){
    // 读写数据
    int len;
    byte[] bytes = new byte[8*1024];
    while ((len = bis.read(bytes)) != -1) {
        bos.write(bytes, 0 , len);
    }
}
// 记录结束时间
long end = System.currentTimeMillis();
System.out.println("缓冲流使用数组复制时间:"+(end - start)+" 毫秒");

3.字符缓冲流

BufferedReader 类继承自 Reader 类,提供了一些便捷的方法,例如 readLine() 方法可以一次读取一行数据,而不是一个字符一个字符地读取。

BufferedWriter 类继承自 Writer 类,提供了一些便捷的方法,例如 newLine() 方法可以写入一个系统特定的行分隔符。

构造方法

  • BufferedReader(Reader in) :创建一个新的缓冲输入流,注意参数类型为Reader。
  • BufferedWriter(Writer out): 创建一个新的缓冲输出流,注意参数类型为Writer。
java 复制代码
public class Main {
    public static void main(String[] args) throws IOException {
        //创建流的对象
     BufferedWriter bw =new BufferedWriter(new FileWriter("b.txt"));

     bw.write("我");
     bw.newLine();
     bw.write("是");
     bw.newLine();
     bw.write("大帅比");
     bw.newLine();
     
     bw.close();
     //运行结果:我是大帅比

    }
}

4.PrintStream类和PrintWriter类

1.PrintStream类

PrintStream类是打印输出流,它可以直接输出各种数据类型的数据。它是OutputStream的子类,提供了一系列用于打印数据的方法,方便输出到控制台或文件。

下面是常用的构造方法:

  • PrintStream(OutputStream out): 创建一个PrintStream对象,指定输出目标为指定的字节输出流。
  • PrintStream(OutputStream out, boolean autoFlush): 创建一个PrintStream对象,指定输出目标为指定的字节输出流,并设置是否在写入操作后自动刷新输出缓冲区。
  • PrintStream(File file): 创建一个PrintStream对象,指定输出目标为指定的文件。
  • PrintStream(String fileName): 创建一个PrintStream对象,指定输出目标为指定文件名对应的文件。

这里讲解一下printf的用法:

他是属于PrintStream类的方法,基本语法是

java 复制代码
printStream.printf(format,args);
  1. 这里的format是一个格式字符串,它定义了输出格式
  2. args是一个参数列表,它包含了要格式化的数据

下面来介绍一下格式化符号:

字符串格式化

  • %s : 输出字符串
java 复制代码
System.out.printf("%s", "hello world"); // 输出 "hello world"

整数格式化

  • %d : 输出十进制整数
  • %o : 输出八进制整数
  • %x : 输出十六进制整数
  • %X : 输出大写十六进制整数
java 复制代码
System.out.printf("%d %o %x %X", 10, 10, 10, 10); // 输出 "10 12 a A"

浮点数格式化

  • %f : 输出浮点数
  • %e : 输出科学计数法表示的浮点数(小写e)
  • %E : 输出科学计数法表示的浮点数(大写E)
  • %.nf : 控制小数点后的位数,n为数字
java 复制代码
System.out.printf("%.2f %e %E", 3.1415926, 3.1415926, 3.1415926); // 输出 "3.14 3.141593e+00 3.141593E+00"

就介绍这么多,其他的用法在实际开发中可以边学习边用,反正学了你也记不住。

下面是PrintStream类常用方法代码演示:

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
      try{
          //创建PrintStream对象,并指定输出目标为文件
          PrintStream ps =new PrintStream(new FileOutputStream("b.txt"));
          //打印字符串
          ps.print("Hello, ");
          ps.println("world!");

          //打印整数
          ps.printf("The answer ifs %d\n",42);
          //打印浮点数
          ps.printf("The value of Pi is %.2f\n",Math.PI);
          //关闭PrintStream
          ps.close();
      }catch (FileNotFoundException e){
          e.printStackTrace();
      }
    }
}

打印结果为:

java 复制代码
Hello, world!
The answer ifs 42
The value of Pi is 3.14

2.PrintWriter类

PrintWriter主要继承Writer,PrintWriter主要用于输出字符,它是可以指定字符编码的,这使得它在处理Unicode字符时更加的灵活。

PrintWriter类的常用构造方法有以下几种:

  1. PrintWriter(Writer out): 创建一个PrintWriter对象,指定输出目标为指定的字符输出流。
  2. PrintWriter(Writer out, boolean autoFlush): 创建一个PrintWriter对象,指定输出目标为指定的字符输出流,并设置是否在写入操作后自动刷新输出缓冲区。
  3. PrintWriter(File file): 创建一个PrintWriter对象,指定输出目标为指定的文件。
  4. PrintWriter(String fileName): 创建一个PrintWriter对象,指定输出目标为指定文件名对应的文件。

跟上面的PrintStream是差不多的,这里就不举例了

5.System.in获取用户输入

Java 的控制台输入由 System.in 完成。

为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。

这里的in是静态变量,类型是InputStream

下面是创建 BufferedReader 的基本语法:

java 复制代码
第一种方法
InputStreamReader isr =new InputStreamReader(System.in);
BufferReader br =new BufferReader(isr);
第二种方法
BufferedReader br = new BufferedReader(new 
                      InputStreamReader(System.in));

BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。

System.in 返回的是 InputStream 指向命令行输入的字节流,InputStream 的 read 方法以字节流的方式来读取命令行的输入的数据。
查看源码(InputStream.java)我们常用的有:

java 复制代码
1 int System.read()                       //以字节的方式读取输入的第一字符,返回该字符的ASCII码
2 
3 int System.read(byte b[])              //以字节的方式把输入的字符放入byte数组中
4 
5 int System.read(byte b[], int off, int len)   //以字节的方式把输入的字符放入byte数组中  off是起始位置,len是最大读入的字节数。

使用实例:

java 复制代码
//  以字节的方式读取输入的第一字符,返回该字符的ASCII码
int inputOne = System.in.read();  // 输入 abc
System.out.println(inputOne);     // 输出 97 (a 的 ascii 码)
java 复制代码
byte[] b = new byte[5];
int length = System.in.read(b);    // 输入 abc
System.out.println(length);          // 输出 4, 包含换行键
System.out.println(Arrays.toString(b));  // 输出 [97, 98, 99, 10, 0]

接下来我们来感受一下不同的使用场景,可以跟着敲一遍

1.使用InputStream读取单个字节

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
      InputStream input =System.in;
      int data;
      try{
          while ((data=input.read())!=-1){
              System.out.print((char) data);
          }
      }catch (IOException e){
          e.printStackTrace();
      }

    }
}

在这个例子中我们使用InputStream的read()方法来读取单个字节,并打印出来

2.使用BufferedReader读取输入

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
      BufferedReader reader =new BufferedReader(new InputStreamReader(System.in));
      try{
          System.out.println("请输入你的年龄:");
          int age =Integer.parseInt(reader.readLine());//读取一行数据
          System.out.println("YOu are"+age+"years old");
      }catch (IOException e){
          e.printStackTrace();
      }
    }
}

3.读取多个输入直到读取到指定的字符串

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
      BufferedReader reader =new BufferedReader(new InputStreamReader(System.in));
      try{
          String input;
          System.out.println("Enter Lines of text(type'exit'to quit)");
          while (!(input= reader.readLine()).equals("exit"))){
              System.out.println("You entered:"+input);
          }
      }catch (IOException e){
          e.printStackTrace();
      }

    }
}
相关推荐
考虑考虑4 分钟前
Jpa使用union all
java·spring boot·后端
用户37215742613526 分钟前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊1 小时前
Java学习第22天 - 云原生与容器化
java
渣哥3 小时前
原来 Java 里线程安全集合有这么多种
java
间彧3 小时前
Spring Boot集成Spring Security完整指南
java
间彧4 小时前
Spring Secutiy基本原理及工作流程
java
Java水解5 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆7 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学7 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole7 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端