引言
本人之前总结过一篇《Java 文件操作》博客,那篇博客更加详细。如果小伙伴遇到本篇博客不懂的地方,可以点击下方链接,去看看之前的那篇博客。
本篇博客主要围绕着【字节流读写文件】 与 【字符流读写文件】进行展开讨论。
我们知道 Java 的读文件与写文件,都是依靠流对象来进行的,主要有【字节流】与【字符流】两类可以供我们选择,然而它们其实就是我们日常开发中,对于文件操作,最常用的选择。
一、字符流读写文件
准备工作
创建一个 TestFile2 类,里面放两个方法,一个用来读文件,一个用来写文件。
并在准备好的 " test.txt " 文件中,准备好数据。
字符流读文件
public class TestFile2 { /** * 将文件中的数据 " 以字符流的形式 " 读出来,再用一个字符串接收 */ public static String readFile(String filePath) { StringBuilder result = new StringBuilder(); try (FileReader fileReader = new FileReader(filePath)) { while (true) { int ch = fileReader.read(); if (ch == -1) { break; } result.append((char)ch); // 一次拼接一个字符 } return result.toString(); } catch (IOException e) { e.printStackTrace(); } return null; } public static void main(String[] args) { String filePath = "D:/文件/test.txt"; String data = TestFile2.readFile(filePath); System.out.println(data); } }
在上面的代码中,我们需要注意几个点:
1. 我们将读到的数据,通过一个字符串接收,但是为什么不直接用 String 类型接受呢?
① 在当前场景中," fileReader.read() " 这个方法一次只读一个字符,这里 " read " 方法的无参形式也是用的最多的形式。因为你拿一个字符数组接收,你压根不知道要 new 多大的内存。但是无参形式的 " read " 方法,只要你读到末尾了,就返回 -1.
② 利用 StringBuilder 类进行拼接更合理,而不是用 String 类的加号拼接。 因为 String 类 的实例是一个不可变对象,每次利用加号拼接,就会 new 一个新的对象,这样会让代码效率变低。而 StringBuilder 类就是不断地将字符拼接到同一个对象中。
2. 本次使用流对象后,并没有使用 " close " 方法来关闭,因为我采用了 " try with resources " 语法,不理解的小伙伴,可以点击文章引言处的链接看一下。
3. 实际上 " read " 方法中可以支持很多参数,我们可以根据不同场景,根据参数读取需要的数据。但是,可想而知,字符流读文件,里面的参数要么和字符有关,要么就跟字符串有关。
展示结果:
字符流写文件
public class TestFile2 { /** * 将数据 " 以字符流的形式 " 写入文件 */ public static void writeFile(String data, String filePath) { // 使用 try with resource 这样的语法 try (FileWriter fileWriter = new FileWriter(filePath)) { fileWriter.write(data); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { String filePath = "D:/文件/test.txt"; String data = "Welcome to the world !"; TestFile2.writeFile(data, filePath); } }
展示结果:
在上面的代码中,我们需要注意的事项:
1. 和前面的思想一样," write " 方法支持的传入参数有很多,我们可以根据自己的需求来设定,上面的例子,是直接将字符串写入文件,这很方便。
2. 可以看到,上面的代码直接写入了文件,但也有一个问题,它直接就将原先的文本进行了覆盖。如果不覆盖,需要拼接改怎么办呢?
使用下面代码:
try (FileWriter fileWriter = new FileWriter(filePath, true))
将上面 new 对象的第二个参数置为 true,表示可以拼接原先的文本,如果不设置,那么默认就为 false.
假设我们依照刚刚的 main 方法,再对文件进行一次写入,看看效果:
二、字节流读写文件
public class TestFile1 { /** * 将文件中的数据 " 以字节流的形式 " 读出来,再用一个字符串接收 */ public static String readFile(String filePath) { StringBuilder result = new StringBuilder(); try (InputStream inputStream = new FileInputStream(filePath)) { while (true) { int ch = inputStream.read(); if (ch == -1) { break; } result.append((char) ch); } return result.toString(); } catch (IOException e) { e.printStackTrace(); } return null; } /** * 将数据 " 以字节流的形式 " 写入文件 */ public static void writeFile(String data, String filePath) { try (OutputStream outputStream = new FileOutputStream(filePath, true)) { outputStream.write(data.getBytes()); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { String filePath = "D:/文件/test.txt"; // String data = TestFile1.readFile(filePath); // System.out.println(data); String data = "hello world"; TestFile1.writeFile(data, filePath); } }
字节流和字符流的用法相似,这里就不再展示说明了。
针对上面的代码,我们需要注意的事项:
1. 字节流读写文件和字符流读写文件的思路是一样的,只是一个单位是字节,一个单位是字符而已,所以我们使用的时候,需要明确场景需要的单位。
2. 上面代码中,字节流读写文件用到的类是 InputStream 和 OutputStream,当然,也有其他的类可供使用,这里就不展开了,只要自己会用即可。
总结
1. 在上面的代码中,可以看到【字符流】与【字节流】的用法是很相似的,这就是 Java 为我们提供流思想,只是两类流对象,使用的类不同。
2. 【字符流】和【字节流】虽然有共同之处,但是在对于读写文件上的策略还是很不相同的,因为这就是两个单位不同所引发的不同之处。虽然,我们可以利用 ( byte / char ) 进行强制转换,但是我们最好不要这么做,因为这可能会造成数据丢失的情况。
3. 【字符流】常用于读写文本文件,因为文本文件,不同的操作系统可能有不同的编码,你压根不知道多少个字节对应着一个字符,所以使用对于文本文件,我们应该使用【字符流】,简单又方便。
4. 【字节流】常用于读取 HTTP 请求中的正文,对于 HTTP 协议中的 " header " 中 Content - Length,它表示正文的 " 字节数 ",我们就可以利用这一点,一次性读取所有字节的数据。
综上所述,【字符流】与【字节流】读写文件并无好坏之分,哪种场景下方便,就使用哪种方案。另外,多熟悉它们的类以及类提供的方法,也利于我们日常开发。