1.缓冲流
在java中,缓x冲流(Buffered Streams)是非常有用的,它们属于处理流的一种,用于提高原始数据流(如文件流,网络流)的读取和写入效率。
Java 的缓冲流是对字节流和字符流的一种封装,通过在内存中开辟缓冲区来提高 I/O 操作的效率。Java 通过 BufferedInputStream 和 BufferedOutputStream 来实现字节流的缓冲,通过 BufferedReader 和 BufferedWriter 来实现字符流的缓冲。
它们内部维护了一个字节数组缓冲区,当进行读写操作时,先将数据读取到缓冲区或者从缓冲区写入数据,然后再与底层设备进行交互。这种方式减少了与底层设备的直接交互次数,提高了读写的效率。
缓冲流的基本原理:
- 使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内
- 通过缓冲区的read()方法从缓冲区获取具体的字符数据,这样子就提高了效率
- 如果用read方法读取字符数据,并存储到另一个容器当中,直到读取到了换行符,将另一个容器临时存储到数据转成字符串返回,就形成了readLine()功能
字节缓冲流的使用场景
字节缓冲流主要用于提高字节流的读写效率,适用于以下场景:
- 文件读写:通过字节缓冲流可以提高文件读写的速度,尤其是对于大文件而言。
- 网络通信:通过字节缓冲流可以提高网络数据的读写效率,减少交互次数。
- 图像处理:通过字节缓冲流可以加速图像的读取和写入操作。
- 音频处理:通过字节缓冲流可以提高音频数据的读写效率。
2.字节缓冲流
构造方法:
- public BufferedInputStream(InputStream in):创建一个新的缓冲输入流,注意参数类型为InputStream
- 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);
- 这里的format是一个格式字符串,它定义了输出格式
- 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类的常用构造方法有以下几种:
- PrintWriter(Writer out): 创建一个PrintWriter对象,指定输出目标为指定的字符输出流。
- PrintWriter(Writer out, boolean autoFlush): 创建一个PrintWriter对象,指定输出目标为指定的字符输出流,并设置是否在写入操作后自动刷新输出缓冲区。
- PrintWriter(File file): 创建一个PrintWriter对象,指定输出目标为指定的文件。
- 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();
}
}
}