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();
      }

    }
}
相关推荐
Re.不晚16 分钟前
Java入门15——抽象类
java·开发语言·学习·算法·intellij-idea
老秦包你会18 分钟前
Qt第三课 ----------容器类控件
开发语言·qt
凤枭香21 分钟前
Python OpenCV 傅里叶变换
开发语言·图像处理·python·opencv
雷神乐乐22 分钟前
Maven学习——创建Maven的Java和Web工程,并运行在Tomcat上
java·maven
ULTRA??25 分钟前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
码农派大星。25 分钟前
Spring Boot 配置文件
java·spring boot·后端
顾北川_野32 分钟前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
江深竹静,一苇以航35 分钟前
springboot3项目整合Mybatis-plus启动项目报错:Invalid bean definition with name ‘xxxMapper‘
java·spring boot
远望清一色41 分钟前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself1 小时前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言