🔥作者主页:小林同学的学习笔录
🔥mysql专栏:小林同学的专栏
目录
生活中,你肯定经历过这样的场景。当你编辑一个文本文件,忘记了ctrl+s ,可能文件就白白编辑了。当你电脑上插入一个U盘,可以把一个视频,拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢?键盘、内存、硬盘、外接设备等等。
我们把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input 和输出output ,即流向内存是输入流,流出内存的输出流。
根据数据的流向分为:输入流和输出流。
输入流 :把数据从其他设备上读取到内存中的流。
输出流 :把数据从内存 中写出到其他设备上的流。
根据数据的类型分为:字节流和字符流。
字节流 :以字节为单位,读写数据的流。
字符流 :以字符为单位,读写数据的流。

一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。
public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
public abstract void write(int b) :将指定的字节输出流。
需要注意的是完成流的操作时,需要close()方法释放资源
流的关闭原则:先开后关,后开先关。
public FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(String name): 创建文件输出流以指定的名称写入文件。
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。
经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?
public FileOutputStream(File file, boolean append): 创建文件输出流以写入由指定的 File对象表示的文件。
public FileOutputStream(String name, boolean append): 创建文件输出流以指定的名称写入文件。
这两个构造方法,参数中都需要传入一个boolean类型的值,true 表示追加数据,false 为默认值,表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,代码使用演示:
java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。
public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
public abstract int read(): 从输入流读取数据的下一个字节。
public int read(byte[] b): 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
需要注意的是完成流的操作时,需要close()方法释放资源,
流的关闭原则:先开后关,后开先关。
FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException 。
代码演示
- public class InputStream {
- public static void main(String[] args) throws IOException {
- String srcFile = "D:\\img\\R.jpg";
- String descFile = "R.jpg";
-
- byte[] bytes = new byte[1024];
- int len = 0;
- FileInputStream fileInputStream = new FileInputStream(srcFile);
- FileOutputStream fileOutputStream = new FileOutputStream(descFile);
- //循环读取
- while ((len = fileInputStream.read(bytes)) != -1){
- //边读边写
- fileOutputStream.write(bytes,0,len);
- }
- //流的关闭原则:先开后关,后开先关。
- fileInputStream.close();
- fileOutputStream.close();
- }
- }
java.io.Reader抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。
public void close() :关闭此流并释放与此流相关联的任何系统资源。
public int read(): 从输入流读取一个字符。
public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。
java.io.FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象。
FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称。
当你创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。
java.io.Writer抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。
void write(int c) 写入单个字符。
void write(char[] cbuf)写入字符数组。
abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
void write(String str)写入字符串。
void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
void flush()刷新该流的缓冲。
void close() 关闭此流,但要先刷新它。
FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象。
FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称。
当你创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。
write(int b) 方法,每次可以写出一个字符数据,代码使用演示:
- class testClass{
- @Test
- public void test01() throws IOException {
- FileWriter fileWriter = new FileWriter("lhx.txt");
- fileWriter.write(98);
- fileWriter.write('b');
-
- /*
- 【注意】关闭资源时,与FileOutputStream不同。
- 如果不关闭,数据只是保存到缓冲区,并未保存到文件。
- */
- fileWriter.close();
- }
- }
注意:与fileOutputStream 不同,fileWriter 如果未调用close方法,数据只是保存到了缓冲区,并未写出到文件中。
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush 方法了。
flush :刷新缓冲区,流对象可以继续使用。
close:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
代码演示:
- class testClass{
- @Test
- public void test02() throws IOException {
- FileWriter fileWriter = new FileWriter("lhx.txt");
- fileWriter.write("你好");
- //刷新缓存,下面可以继续写入数据
- fileWriter.flush();
-
- fileWriter.write("帅");
- //刷新缓存并关闭资源
- fileWriter.close();
- }
- }
-
- 输出结果:
-
- 你好帅
之前的入门练习,我们一直把异常抛出,而实际开发中并不能这样处理,建议使用try...catch...finally 代码块,处理异常部分,因为有可能close()上面的代码出异常,然后导致程序结束,导致close()方法并没有被执行
- public class HandleException {
- public static void main(String[] args) {
- // 声明变量(要初始化)
- FileWriter fw = null;
- try {
- //创建流对象
- fw = new FileWriter("lhx.txt");
- // 写出数据
- fw.write("你好");
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- //做非null判断是因为有可能文件不存在,流对象没有被创建
- if (fw != null) {
- fw.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
如果流对象比较多的时候,关闭流的代码量还是会比较多,因此下面有解决方案:

具体分为两个过程:
缓存过程:当使用缓冲流进行读写操作时,数据首先被读入或写入缓存区,这是一个字节数组,其大小由缓冲流的构造函数参数指定。在读取数据时,缓冲流会将磁盘上的数据分块读取到缓存区中,然后逐个字节地读取缓存区中的数据。当缓存区中的数据被读取完毕后,缓冲流会再次读取磁盘上的数据到缓存区中,直到读取到所需的数据。在写入数据时,缓冲流会将数据写入缓存区中,然后再将缓存区中的数据一次性地写入磁盘。当缓存区被写满时,缓冲流也会将缓存区中的数据一次性地写入磁盘。
刷新过程:缓冲流会在缓存区被写满时自动将缓存区中的数据写入磁盘。此外,缓冲流还提供了手动刷新缓存区的方法flush(),调用该方法会强制缓冲流将缓存区中的数据写入磁盘。在正常情况下,缓冲流会在关闭时自动刷新缓存区,将缓存区中的数据写入磁盘。
总的来说,缓冲流通过在内存中建立一个缓冲区来减少与磁盘或网络的IO次数,从而提高读写的效率。因为磁盘或网络IO操作是相对较慢的,而内存中的读写操作是相对较快的,所以通过缓冲区可以减少对磁盘或网络的IO操作,从而提高读写的速度。
缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:
字节缓冲流:BufferedInputStream,BufferedOutputStream
字符缓冲流:BufferedReader,BufferedWriter
public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流。
①.基本流,代码如下:
- public class BufferStream {
- public static void main(String[] args) {
-
- FileInputStream fileInputStream = null;
- FileOutputStream fileOutputStream = null;
- try {
- long begin = System.currentTimeMillis();
- String filePath = "D:\\java_project_source\\mysql\\资料-MySQL数据库\\进阶篇\\相关SQL脚本\\load_user_100w_sort.sql";
- String descFile = "d:\\lhx.sql";
- int len = 0;
- byte[] bytes = new byte[1024];
- fileInputStream = new FileInputStream(filePath);
- fileOutputStream = new FileOutputStream(descFile);
- while((len = fileInputStream.read(bytes)) != -1){
- fileOutputStream.write(bytes,0,len);
- }
-
- long end = System.currentTimeMillis();
- System.out.println("花费的时间(ms):" + (end - begin));
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if(fileInputStream != null){
- fileInputStream.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- if(fileOutputStream != null){
- fileOutputStream.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- }
-
- 输出结果:
-
- 花费的时间(ms):650
②.缓冲流,代码如下:
- public class BufferStream02 {
- public static void main(String[] args) throws IOException {
- BufferedInputStream bis = null;
- BufferedOutputStream bos = null;
-
- try {
- long begin = System.currentTimeMillis();
- String filePath = "D:\\java_project_source\\mysql\\资料-MySQL数据库\\进阶篇\\相关SQL脚本\\load_user_100w_sort.sql";
- String descFile = "d:\\lhx.sql";
- int len = 0;
- byte[] bytes = new byte[1024];
- bis = new BufferedInputStream(new FileInputStream(filePath));
- bos = new BufferedOutputStream(new FileOutputStream(descFile));
- while((len = bis.read(bytes)) != -1){
- bos.write(bytes,0,len);
- }
- long end = System.currentTimeMillis();
- System.out.println("花费的时间(ms):" + (end - begin));
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- try {
- if(bis != null){
- bis.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- if(bos != null){
- bos.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- }
-
- 输出结果:
-
- 花费的时间(ms):142
public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
public BufferedWriter(Writer out): 创建一个新的缓冲输出流。
BufferedReader:public String readLine(): 读一行文字。
BufferedWriter:public void newLine(): 写一行行分隔符,由系统属性定义符号。
readLine方法演示,代码如下:
- public class BufferStreamDemo {
- public static void main(String[] args) {
- BufferedReader bis = null;
- try {
- byte[] bytes = new byte[1024];
- String line = null;
- int len = 0;
- bis = new BufferedReader(new FileReader("lhx.txt"));
- while ((line = bis.readLine()) != null){
- System.out.println((++len) + " " + line);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- try {
- if(bis != null){
- bis.close();
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- 输出结果:
-
- 1 package inputstream;
- 2
- 3 import org.junit.jupiter.api.Test;
newLine方法演示,代码如下:
- public class BufferStreamDemo2 {
- public static void main(String[] args) {
- BufferedWriter br = null;
- try {
- br = new BufferedWriter(new FileWriter("lhx2.txt"));
- br.write("哥哥好帅");
- br.newLine();
- br.write("消失的他");
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- try {
- if(br != null){
- br.close();
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- 输出结果:
-
- 在lhx2.txt看到内容:
-
- 哥哥好帅
- 消失的他
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
编码:字符(能看懂的)--字节(看不懂的)
解码:字节(看不懂的)-->字符(能看懂的)
Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。

字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。
ASCII(美国标准信息交换码):
Unicode:
UTF-8(Unicode转换格式-8位):
在IDEA中,使用FileReader 读取项目中的文本文件。由于IDEA的设置,都是默认的UTF-8编码,所以没有任何问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码。
代码演示:
- public class ReaderDemo {
- public static void main(String[] args) throws IOException {
- FileReader fileReader = new FileReader("D:\\File_GBK.txt");
- int read;
- while ((read = fileReader.read()) != -1) {
- System.out.print((char)read);
- }
- fileReader.close();
- }
- }
-
- 输出结果:
- ���
为什么会出现乱码?
如何不产生乱码呢?
转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。
- public class ReaderDemo2 {
- public static void main(String[] args) throws IOException {
- // 定义文件路径,文件为gbk编码
- String FileName = "D:\\file_gbk.txt";
- // 创建流对象,默认UTF8编码
- InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
- // 创建流对象,指定GBK编码
- InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
- // 定义变量,保存字符
- int read;
- // 使用默认编码字符流读取,乱码
- while ((read = isr.read()) != -1) {
- System.out.print((char)read); // ��Һ�
- }
- isr.close();
-
- // 使用指定编码字符流读取,正常解析
- while ((read = isr2.read()) != -1) {
- System.out.print((char)read);// 大家好
- }
- isr2.close();
- }
- }
转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。
- public class OutputDemo {
- public static void main(String[] args) throws IOException {
- // 定义文件路径
- String FileName = "D:\\out.txt";
- // 创建流对象,默认UTF8编码
- OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
- // 写出数据
- osw.write("你好"); // 保存为6个字节
- osw.close();
-
- // 定义文件路径
- String FileName2 = "D:\\out2.txt";
- // 创建流对象,指定GBK编码
- OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
- // 写出数据
- osw2.write("你好");// 保存为4个字节
- osw2.close();
- }
- }

未完待续
未完待续