为跳槽面试做准备,今天开始进入 Java 基础的复习。希望基础不好的同学看完这篇文章,能掌握泛型,而基础好的同学权当复习,希望看完这篇文章能够起一点你的青涩记忆。
想象一个场景:我们在电脑上编辑文件,可以保存到硬盘上,也可以拷贝到 U 盘中。那这个看似简单的过程,背后其实是数据的传输。
数据的传输,也就是数据的流动。既然是流动也就会有方向,有入方向和出方向。举个上传文件的栗子,现在有三个对象,文件、应用程序、上传的目标地址(服务器)。简化的上传文件有两步:
注意这个入和出是相对的,它相对于应用程序而言。如果相对于服务器而言,这个上传文件操作就是入方向,从应用程序读入。Java中I/O操作主要是指使用 java.io
包下的内容,进行输入、输出操作。 输入 也叫做 读取 数据, 输出 也叫做作 写出 数据。
我不认同网络上很多 IO 流的图,他们只是简单的把 io 流分成字节流和字符流。这样的分类也不是说不好,只是太臃肿、难记。
先上一张我自己总结的 IO 留的思维导图,我先把它分成了 节点流 和 处理流 , 节点流是直接接触数据源的,而处理流是出于各种目的在节点流的基础上再套一层的 IO 流 。再按照 操作类型 ,分成 8 个小类,然后才是 字节、字符 分类,最后才是 输入、输出 的分类。具体可以看以下思维导图(可能不清晰,有需要的在后台回复 IO流 获取原思维导图)
根据数据的流向分为: 输入流 和 输出流 。
其他设备
上读取到 内存
中的流。 内存
中写出到 其他设备
上的流。 根据数据的类型分为: 字节流 和 字符流 。
IO 流要说明白需要好几篇才行,今天我们先复习文件流。
所有的文件(包括图片、音乐、视频),都是字节。所以字节流可以传输任意文件数据。在操作流的时时,无论使用什么样的流对象,底层传输的始终为二进制数据。
文件流也就是直接操作文件的流,文件流又分为字节流 ( FileInputStream
和 FileOutputStream
)和字符流( FileReader
和 FileWriter
)。其中 字节流可用于操作一切文件,而字符流只能用于操作文本文件。
java.io.FileOutputStream
类继承于 OutputStream
是文件输出流,用于将数据写出到文件。
构造方法:可用文件路径构造,也可创建 File 对象之后构造。
/** * Project Name:review_java <br/> * Package Name:com.nasus.io.filestream <br/> * Date:2020/1/5 19:24 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class FOSWriterStream { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象,构造函数中的 true 表示在原有数据末尾追加续写 FileOutputStream fos = new FileOutputStream("fos.txt", true); // 1、逐个字节写出 fos.write(97); // 97 的 ascll 码是 a fos.write(98); // 98 的 ascll 码是 b fos.write(99); // 99 的 ascll 码是 c // 2、写出一个换行, 换行符号转成数组写出 fos.write("/r/n".getBytes()); // 字符串转换为字节数组 byte[] b = "一个优秀的废人".getBytes(); // 3、写出字节数组数据 fos.write(b); // 4、写出指定长度字节数组数据(不可超过 b 的长度,否则数组越界) fos.write(b, 0, b.length); // 关闭资源 fos.close(); } }
java.io.FileInputStream
类继承于 InputStream
是文件输入流,用于将数据从文件读出。
构造方法:可用文件路径构造,也可创建 File 对象之后构造。
/** * Project Name:review_java <br/> * Package Name:com.nasus.io.filestream <br/> * Date:2020/1/5 19:31 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class FISReadStream { public static void main(String[] args) throws IOException { // 1、逐个读取字节 int b; FileInputStream fis1 = new FileInputStream("fis.txt"); // 循环读取 while ((b = fis1.read())!=-1) { System.out.println((char)b); } // 关闭资源 fis1.close(); System.out.println("----华丽丽的分割线----"); // 2、定义字节数组读取 int length; FileInputStream fis2 = new FileInputStream("fis.txt"); // 定义字节数组,作为装字节数据的容器 byte[] bytes = new byte[1024]; // 循环读取 while ((length = fis2.read(bytes))!=-1) { // 每次读取后,把数组变成字符串打印 System.out.println(new String(bytes, 0, length)); } // 关闭资源 fis2.close(); } }
/** * Project Name:review_java <br/> * Package Name:com.nasus.io.filestream <br/> * Date:2020/1/5 19:43 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class FileCopyStream { public static void main(String[] args) throws IOException { // 指定数据源 FileInputStream fis = new FileInputStream("Java IO 流.png"); // 指定目的地 FileOutputStream fos = new FileOutputStream("流.png"); // 定义数组 byte[] b = new byte[1024]; // 定义长度 int len; // 循环读取 while ((len = fis.read(b))!=-1) { // 写出数据 fos.write(b, 0 , len); } // 关闭资源,后开先关,后开先关 fos.close(); fis.close(); } }
首先明确一点: 一个英文字母占一个字节,一个汉字占两个字节 ,所以当字节流读取字符流就会出现乱码或者显示不全。所以用字节流操作含有中文字符的文件时,要转换成字符流并指定编码格式才能防止乱码。( 这点,后面转换流会复习到 )
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以 Java 提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
java.io.FileReader
类继承于 Reader 类,是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
构造方法:可用文件路径构造,也可创建 File 对象之后构造。
PS:有时候出现乱码,多考虑下是不是编码的原因:字节与字符的规则对不上。
/** * Project Name:review_java <br/> * Package Name:com.nasus.io.filereadwrite <br/> * Date:2020/1/5 20:19 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class FileRead { public static void main(String[] args) throws IOException { // 1、逐个字符读取 int b = 0; FileReader fileReader1 = new FileReader("read.txt"); // 循环读取 while ((b = fileReader1.read())!=-1) { // 自动提升类型提升为 int 类型,所以用 char 强转 System.out.println((char)b); } // 关闭流 fileReader1.close(); System.out.println("----华丽丽的分割线----"); // 2、利用字符数组,每次读取两个字符 int length = 0; FileReader fileReader2 = new FileReader("read.txt"); char[] charArray = new char[2]; // 读取数据 while ((length = fileReader2.read(charArray)) != -1) { System.out.println(new String(charArray, 0, length)); } // 关闭流 fileReader2.close(); } }
java.io.FileWriter
类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
构造方法:可用文件路径构造,也可创建 File 对象之后构造。
public static void main(String[] args) throws IOException { // 使用文件名称创建流对象,true 表示在原有数据末尾追加续写 FileWriter fileWriter = new FileWriter("fw.txt", true); // 1、逐个写出字符 fileWriter.write(97); fileWriter.write('C'); fileWriter.write('Z'); fileWriter.write('Y'); // 中文编码表中30000对应一个汉字。 fileWriter.write(30000); // 2、写出字符串 fileWriter.write("是一个"); // 3、写出 Windows 换行 fileWriter.write("/r/n"); // 4、写出字符串数组 // 字符串转换为字节数组 char[] chars = "优秀的废人".toCharArray(); fileWriter.write(chars, 0, chars.length); // 关闭资源,close方法调用之前,数据只是保存到了缓冲区,并未写出到文件中。 fileWriter.close(); }
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要 flush
方法了。
flush close
/** * Project Name:review_java <br/> * Package Name:com.nasus.io.filereadwrite <br/> * Date:2020/1/5 22:25 <br/> * * @author <a href="turodog@foxmail.com">chenzy</a><br/> */ public class FileFlushClose { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象 FileWriter fw = new FileWriter("fw.txt"); // 1、通过 flush 写出数据 // 写出第 1 个字符 fw.write('刷'); fw.flush(); // 继续写出第 2 个字符,写出成功 fw.write('新'); fw.flush(); // 2、通过 close 写出数据,流关闭后不可用 // 写出第 1 个字符 fw.write('关'); fw.close(); // 继续写出第 2 个字符,【报错】java.io.IOException: Stream closed fw.write('闭'); fw.close(); } }
如果看到这里,喜欢这篇文章的话,帮忙 " 转发 "或者点个" 在看 ",行吗?祝你们 2020 暴富。微信搜索「 一个优秀的废人 」,欢迎关注。
回复「 1024 」送你一套完整的 java、python、c++、go、前端、linux、算法、大数据、人工智能、小程序以及英语教程。
回复「 电子书 」送你 50+ 本 java 电子书。