高效缓冲流,转换流基础知识详解
- 能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等等。
高效缓冲流
- 字节缓冲流: BufferedInputStream , BufferedOutputStream
- 字符缓冲流: BufferedReader , BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO 次数,从而提高读写的效率。
read()方法原理:
- 在调用read()方法时,封装的FileReader里的底层流read(char[])方法会从硬盘中的一部分数据存入到缓冲区(含有字符数组)里边。
- 进而调用BufferedReader里的read()方法一个一个的读取字符.读取完字符数组中的数据之后,会重新获取数据到字符数组中。
readLine方法原理:
在调用readLine之时,底层会调用缓冲区中的read()方法,将读取到的字符存储到另外一个容器中,当读到行终止符时,就将临时容器中存储的数据转成字符串返回。
自定义缓冲区
package oneSelf_Steam;
import java.io.IOException;
import java.io.Reader;
/**
* @author lx
* @date 2019/3/9 - 15:45
* 自定义一个字符缓冲区。
* 用于缓冲字符数据,从而提高操作效率。
* 并提供了更多操作缓冲区数据的方法。
* 需要使用具体的流对象未完成数据的获取。
*/
public class MyBufferedReader {
private Reader r;
//定义一个数组角标
private int index = 0;
//定义一个字符数组作为缓冲区
private char[] buf = new char[1024];
//定义一个计数器
private int count = 0;
//创建流构造器
public MyBufferedReader(Reader r) {
this.r = r;
}
//关流
public void close(Reader r) throws IOException {
r.close();
}
//需要先通过流对象从底层设备上获取一定数据到缓冲区数组中,使用流对象read(char[]);
public int read() throws IOException {
if (count == 0) {
// 需要从设备上获取一定数量的数据存储到缓冲区中,
// 并用count记录存储字符的个数。
count = r.read();
//每获取一次底层来的数据,角标清零。
index = 0;
}//如果读取结束了,count<0.直接返回-1
if (count < 0) {
return -1;
}
//获取缓冲区的一个字符
int ch = buf[index];
//角标自增
index++;
//计数器自减
count--;
return ch;
}
/**
* 思路:
* 从缓冲区中读取一个字符,并将这个字符存储到临时容器中
* 存储之前要进行判断,如果遇到行终止符,停止存储,
* 直接将临时容器中的数据转成字符串返回
*
* @return
*/
public String readLine() throws IOException {
//定义一个临时容器 StringBuilder是线程安全的
StringBuilder bu = new StringBuilder();
/**
* 调用本类中的read()方法,从缓冲区中读取一个字符,
* 存储到临时容器中,存储前判断是否是行终止符
*/
int ch = 0;
while ((ch = r.read()) != -1) {
if (ch == '\r') {
continue;
}
if (ch == '\n') {
return bu.toString();
}
bu.append((char) ch);
}
if (bu.length() != 0) {
return bu.toString();
}
return null;
}
}
自定义缓冲区测试类
package oneSelf_Steam;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class MybufferDemo {
public static void main(String[] args) throws IOException {
MyBufferedReader mybufer=new MyBufferedReader(new FileReader("D:\\Java代码\\Java基础加强\\Day10_Add\\面向对象到日历类5天知识小结.txt"));
String line=null;
while ((line=mybufer.readLine())!=null){
System.out.println(line);
}
mybufer.close();
}
}
效率测试
- 使用高效缓冲字节流一个字节一个字节的读取写入。
package bufferedStream;
import java.io.*;
/**
* public BufferedInputStream(InputStream in) :创建一个新的缓冲输入流。
* public BufferedOutputStream(OutputStream out) : 创建一个新的缓冲输出流
*/
public class BufferedDemo {
public static void main(String[] args) throws IOException {
//计算开始时间
long first=System.currentTimeMillis();
BufferedInputStream inputStream =null;
BufferedOutputStream outputStream=null;
// 创建字节缓冲输入流
try {
inputStream= new BufferedInputStream(new FileInputStream("D:\\Java代码\\Java基础加强\\Day10\\src\\bufferedStream\\枪.mp4"));
// 创建字节缓冲输出流
outputStream=new BufferedOutputStream(new FileOutputStream("D:\\Java代码\\Java基础加强\\Day10\\src\\copy.mp4"));
int len;
while ((len=inputStream.read())!=-1) {
outputStream.write(len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) inputStream.close();
}
long end=System.currentTimeMillis();
System.out.println("使用高校缓冲区所消耗的时间是:"+(end-first)+"ms");
//使用高校缓冲区所消耗的时间是:866ms
}
}
** 使用普通字节流一个字节一个字节的读取写入**
package bufferedStream;
import java.io.*;
public class StreamDemo {
public static void main(String[] args) {
long begin=System.currentTimeMillis();
FileInputStream in=null;
FileOutputStream out=null;
try {
in=new FileInputStream("D:\\Java代码\\Java基础加强\\Day10\\src\\bufferedStream\\枪.mp4");
out=new FileOutputStream("D:\\Java代码\\Java基础加强\\Day10\\src\\普通字节流复制.mp4");
int len;
while ((len=in.read())!=-1){
out.write(len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (out == null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (in == null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end=System.currentTimeMillis();
System.out.println("使用普通字节流复制视频所需毫秒值为"+(end-begin));
//使用普通字节流复制视频所需毫秒值为288631
// 288631/1000/60=4.8分钟
//实用高效缓冲区只需要886毫秒,也就是不到1秒的时间完成。
}
}
** 使用字节数组,普通字节流来复制视频文件**
package bufferedStream_byteArray;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 创建输入输出流对象
* 创建字节数组读取
*/
public class StreamDemo2_ByteArray {
public static void main(String[] args) {
long begin=System.currentTimeMillis();
FileInputStream in=null;
FileOutputStream out=null;
try {
in=new FileInputStream("D:\\Java代码\\Java基础加强\\Day10\\src\\bufferedStream\\枪.mp4");
out=new FileOutputStream("D:\\Java代码\\Java基础加强\\Day10\\src\\普通字节流复制.mp4");
int len;
byte [] bytes=new byte[1024];
while ((len=in.read(bytes))!=-1){
out.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (out == null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (in == null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end=System.currentTimeMillis();
System.out.println("使用普通字节流,字节数组复制视频所需时间为:"+(end-begin)+"毫秒");
// 使用普通字节流,字节数组复制视频所需时间为:507毫秒
//明显快了不少
}
}
使用高校缓冲区,字节数组测试
package bufferedStream_byteArray;
import java.io.*;
public class BufferedByteDemo {
public static void main(String[] args) {
//计算开始时间
long first=System.currentTimeMillis();
BufferedInputStream in =null;
BufferedOutputStream out=null;
// 创建字节缓冲输入流
try {
in= new BufferedInputStream(new FileInputStream("D:\\Java代码\\Java基础加强\\Day10\\src\\bufferedStream\\枪.mp4"));
// 创建字节缓冲输出流
out=new BufferedOutputStream(new FileOutputStream("D:\\Java代码\\Java基础加强\\Day10\\src\\copy.mp4"));
int len;
byte [] bytes=new byte[1024];
while ((len=in.read(bytes))!=-1) {
out.write(bytes,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long end=System.currentTimeMillis();
System.out.println("使用高校缓冲区,字节数组所消耗的时间是:"+(end-first)+"ms");
/**
* 输出结果:使用高校缓冲区,字节数组所消耗的时间是:123ms
*/
}
}
从这可以看出高效缓冲流,字节数组是最快的方式了,复制视频音乐文件。
字符缓冲流
BufferedReader
BufferedWriter
package buffered_Reader_Writer;
import java.io.*;
/**
* public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
* public BufferedWriter(Writer out) : 创建一个新的缓冲输出流。
*
* 用于纯文本文件.
* 特有方法:
* 字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。
* BufferedReader: public String readLine() : 读一行文字。
* BufferedWriter: public void newLine() : 写一行行分隔符,由系统属性定义符号
*/
public class BUfferedDemo {
public static void main(String[] args) {
//计算开始时间
long first=System.currentTimeMillis();
//创建流对象
BufferedReader reader=null;
BufferedWriter writer=null;
try {
reader=new BufferedReader(new FileReader("D:\\Java代码\\Java基础加强\\ss.txt"));
writer=new BufferedWriter(new FileWriter("D:\\Java代码\\Java基础加强\\sss.txt"));
String line=null;
while ((line=reader.readLine())!=null){
System.out.println(line);
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
//计算开始时间
long end=System.currentTimeMillis();
System.out.println("消耗时间为"+(end-first));
//比较高效读取写入,专为字符串类型的。
}
}
文本信息恢复顺序
package buffered_Reader_Writer;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* 实现排序,要先读取文本
* 排序当然要使用集合,内容当然要截取,用到字符串的截取操作splt()方法:\\.
*集合存的数据为序号和内容,以便后续操作排序,因此需要使用HashMap集合
* 遍历集合,获取集合键,使用将
*/
public class PaiXuBufferedDemo {
public static void main(String[] args) {
//计算开始时间
long first=System.currentTimeMillis();
//创建存储序号和内容的键和值的集合
Map map=new HashMap<>();
//创建流对象
try (BufferedReader reader = new BufferedReader(new FileReader("D:\\Java代码\\Java基础加强\\出师表乱序.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("D:\\Java代码\\Java基础加强\\出师表修正.txt"))) {
String line = null;
while ((line = reader.readLine()) != null) {
// 解析文本
String[] split = line.split("\\.");
//将序号和内容别放入集合的键和值中
map.put(split[0], split[1]);
}
for (int i = 1; i < map.size(); i++) {
//获取集合中的键值
String key=String.valueOf(i);
// 获取map中文本
String value=map.get(key);
// 写出拼接文本
writer.write(key+"."+value);
//换行
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
//计算开始时间
long end=System.currentTimeMillis();
System.out.println("消耗时间为"+(end-first));
//消耗时间为79 毫秒
}
}
转换流
InputStreamReader类
- java.io.InputStreamReader是Reader的子类,也是从字节流到字符流的桥梁。
- 其字符集可以由名称指定,也可以接受平台的默认字符集。
指定编码读取
package in_out_Reader_Writer;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。
* InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流。
*/
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
// 创建流对象,默认UTF8编码
InputStreamReader reader1 = new InputStreamReader(new FileInputStream("E:\\ssy.txt"));
// 创建流对象,指定GBK编码
InputStreamReader reader2 = new InputStreamReader(new FileInputStream("E:\\ssy.txt"),"gbk");
//默认编码读取
int le;
while ((le=reader1.read())!=-1){
System.out.print((char) le);
}
reader1.close();
//指定编码读取
int len;
while ((len=reader2.read())!=-1){
System.out.print((char) len);
}
reader2.close();
}
}
OutputStreamWriter类
- 转换流 java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。
- 使用指定的字符集将字符编码为字节。其字符集可以由名称指定,也可以接受平台的默认字符集。
package in_out_Reader_Writer;
import java.io.*;
/**
* OutputStreamWriter(OutputStream in) : 创建一个使用默认字符集的字符流。
* OutputStreamWriter(OutputStream in, String charsetName) : 创建一个指定字符集的字符流
*/
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
OutputStreamWriter writer1=new OutputStreamWriter(new FileOutputStream("D:\\Java代码\\Java基础加强\\ms1.txt"));
OutputStreamWriter writer2=new OutputStreamWriter(new FileOutputStream("D:\\Java代码\\Java基础加强\\ms2.txt"),"gbk");
writer1.write("你好");//默认的编码为utf-8 你好 字节数为 6
writer1.close();//你好
writer2.write("你好"); //gbk 两个字节一个字 存储字节数为 4
writer2.close();//���
}
}
转换文件编码
将GBK编码的文本文件,转换为UTF-8编码的文本文件。
package in_out_Reader_Writer;
import java.io.*;
/**
* 需求:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
*
* 分析:
* 1.首先需要解码-->使用gbk型,读取流
* 2.然后需要编码-->写入时使用utf-8 写入到文本中去
*/
public class In_out_Change {
public static void main(String[] args) {
//创建读取
InputStreamReader reader =null;
OutputStreamWriter writer=null;
try {
reader=new InputStreamReader(new FileInputStream("E:\\ssy.txt"),"gbk");
writer=new OutputStreamWriter(new FileOutputStream("E:\\copySsy.txt"),"utf-8");
//定义长度
int len;
//定义数组
char [] chars=new char[1024];
//循环读取
while ((len=reader.read(chars))!=-1){
// 循环写出
writer.write(chars,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
总结:
缓冲区的原理:
1.使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区中的数据内。
2.通过缓冲区的read方法从缓冲区来获取具体的字符数据,这样就提高了数据。
3.如果用read()方法读取字符数据,并存储到另一个容器中,直到读到了换行符时,另一个容器的临时存储的数据转成字符串返回,就形成了readLine()功能。