java的io("万字" Java I/O 详解)
Java I/O流讲解
每博一文案
谁让你读了这么多书 ,又知道了双水村以外还有一个大世界 ,如果从小你就在这个天地里 ,日出而作 ,日落而息 。 那你现在就会和众乡亲抱同一理想:经过几年的辛劳 ,像大哥一样娶个满意的媳妇 ,生个胖儿子 ,加上你的体魄 , 会成为一名出色的庄稼人 。不幸的是 ,你知道的太多了,思考的太多了 ,因此才有了 ,这种不能为周围人所理解的苦恼 。 —————— 《平凡的世界》 人生是这样的不可预测,没有永恒的痛苦 ,也没有永恒的幸福 ,生活就像流水一般, 有时是那么平展 ,有时又是那么曲折 。 世界上有些人因为忙而感到生活的沉重 ,也有些人因为闲而活得压抑 ,人啊 ,都有自己一本难念的经; 可是不同处境的人又很难理解别人的苦处 。 细想过来 ,每个人的生活也同样是一个世界 ,即使是最平方的人 ,也要为他那个世界的存在而战斗 。 —————— 《平凡的世界》@
1. File 类
java.io.File 类:文件和文件目录路径的抽象表示形式 ,与平台无关 。
File 能新建 ,删除,重命名文件和目录 ,但File 不能访问文件内容本身 。如果需要访问文件内容本身 ,则需要使用 输入/输出 流 。
想要在Java 程序中表示一个真实存在的文件或目录,那么必须有一个 File 对象 ,但是 Java 程序中的一个 File 对象 ,可能没有一个真实存在的文件或目录。
File 对象可以作为参数传递给流的构造器 。
1.1 File 类中:构造器
public File(String pathname); // 过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例 。如果给定字符串是空字符串,那么结果是空抽象路径名 public File(String parent,String child); // 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。以parent为父路径 ,child为子路径创建File对象 public File(File parent, String child); // 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例 。根据一个父File对象和子文件路径创建File对象 // 路径可以是绝对路径 ,也可以是相对路径 绝对路径: 是一个固定的路径,从盘符开始 。 相对路径: 是相对于某个位置开始。IDEA中默认相对路径是从**Project**项目(路径)下和同级的 **src** 的路径开始的 ,注意不是模块开始的**Module**的 。如下图所示:src 和 Module 模块是同级的 。1.2 File 类中:路径分隔符
路径中的每级目录之间用一个路径分隔符隔开 。
路径分隔符和系统有关:
windows和DOS系统默认使用“\ ”来表示 ,需要注意的是在 java 中 **"\"** 具有转义的意思 ,所以想要表示真正的 “\ ” 需要两个 **"\\"** 来转义回来表示一个斜杆 。 UNIX和URL使用“/ ”来表示 。Java程序支持跨平台运行 ,因此路径分隔符要慎用 。
为了解决这个隐患 ,File类提供了一个常量:
public static final String separator 。// 根据操作系统 ,动态的提供分隔符 。 File file1 = new File("E:\\Test\\info.txt"); File file2 = new File("E:" + File.separator + "Test" + File.separator + "info.txt"); File file3 = new File("E:/Test"); // 这三者表示的路径是一样的 。只是表示方式不同而已。举例:
package blogs.blog9; import java.io.File; public class FileTest { /** * File 构造器的使用 */ public static void main(String[] args) { // 构造器一: // 绝对路径: 带盘符 File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9"); // 双右斜杆表示一个 \ (转义) // 相对路径: IDEA默认是Project的根目录 ,不是模块Module的根目录, // 也可以使用:左斜杆表示路径分隔符 System.out.println(file); File file2 = new File("src/blogs/blog9"); // 这里是在src的包下(src 和 Module 模块是同级的) System.out.println(file2); // 构造器二: // 第一个参数是第二个参数的父路径 ,第二个参数是子路径 File file3 = new File("E:\\Java\\JavaRebuilt\\src\\blogs","blog9"); System.out.println(file3); // 构造器三: // 第一个参数是 File 类对象(这里是第二个参数的父路径的File对象) ,第二个参数是子路径 File file4 = new File(file3,"blog9"); System.out.println(file4); } }1.3 File 类中:常用方法
获取文件属性的信息的方法:
getAbsoluteFile() : 返回此File对象中路径的绝对路径 。返回的是 File 对象 public File getAbsoluteFile(); // 返回此抽象路径名的绝对路径名形式 。 getAbsolutePath() : 返回此抽象路径名的绝对路径名字符串 public String getAbsolutePath(); // 返回此抽象路径名的绝对路径名字符串 getPath() : 返回此抽象路径名以字符串的形式。 public String getPath(); // 返回此抽象路径名 getName() : 获取名称 public String getName(); // 返回由此抽象路径名表示的文件或目录的名称 。该名称是路径名名称序列中的最后一个名称 。如果路径名名称序列为空,则返回空字符串 getParent() : 获取上层文件目录路径(也就是父路径)。若无 ,返回null public String getParent(); // 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录 ,则返回 null 。 length() : 返回文件长度即(字节数) public long length(); // 获取文件长度(即:字节数) 。不能获取目录的长度 lastModified() : 获取最后一次的修改时间,毫秒值 。 public long lastModified(); // 获取最后一次的修改时间 ,毫秒值 list() : 获取指定目录下的所有文件或者文件目录的名称数组 public String[] list(); // 返回一个字符串数组 ,这些字符串指定此抽象路径名表示的目录中的文件和目录 。 listFiles() : 获取指定目录下的所有文件或者文件目录的 File 数组 public File[] listFiles(); // 返回一个抽象路径名数组 ,这些路径名表示此抽象路径名表示的目录中的文件 。举例:
import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; public class FileTest { public static void main(String[] args) { File file = new File("src\\blog9\\hello.txt"); // 注意转义以及文件后缀 File file2 = new File("src/blog9/hello3.txt"); // 左斜杆也是可以的 String absolutePath = file.getAbsolutePath(); // 返回绝对路径,以String的形式返回 System.out.println(absolutePath); File absoluteFile = file.getAbsoluteFile(); // 返回绝对路径,以File 对象的形式返回 System.out.println(); System.out.println(file.getPath()); // 返回此路径名/目录名 System.out.println(file.getName()); // 返回该文件名/目录名 System.out.println(file.getParent()); // 返回该上层文件/目录名称 System.out.println(file.length()); // 返回文件长度即文件的大小(字节) long l = file.lastModified(); // 返回该文件的最后一次修改的时间值(毫秒值)时间戳 // 将时间戳转换为Date ,再转换为 指定格式的字符串 Date date = new Date(l); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:ss:mm SSS"); String format = simpleDateFormat.format(date); System.out.println(format); } }举例:
import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; public class FileTest { /** * File 文件目录 */ public static void main(String[] args) { File file = new File("src/blogs/blog9"); // 也可以使用 左斜杆 String[] list = file.list(); // 返回获取指定目录下的所有文件或者文件目录的名称数组 for (String s : list) { System.out.println(s); } File[] files = file.listFiles(); for(File f : files) { System.out.println(f); } } }File类的重命名功能
renameTo(File dest) : 把文件重命名为指定的文件路径 。换句话说:就是剪切附加对文件的重命名 的意思 。 public boolean renameTo(File dest); // 重新命名此抽象路径名表示的文件 。注意: 这里的剪切效果 ,有一定的要求:就是比如:file.renameTo(dest) 想要将 file 剪切到 dest 位置路径上 。要保证 file 剪切的文件实际在硬盘中存在 ,并且 dest 不能在硬盘文件中存在(仅仅当一个路径)。如果不满足会失败 ,返回 false
举例:
import java.io.File; public class FileTest { public static void main(String[] args) { File file = new File("src\\blogs\\blog9\\hello.txt"); File dest = new File("E:\\临时文件\\temp\\test.txt"); boolean b = file.renameTo(dest); // 将file文件剪切到 dest 中并重命名 System.out.println(b); } }失败:原因是:dest 中的 test.txt 是在硬盘中实际存在的 。
将test.txt去了就没事了 ,再重新剪切 ,就可以了 。
File 类的判断功能
isDirectory() : 判断是否是文件目录 public boolean isDirectory(); // 测试此抽象路径名表示的文件是否是一个目录。 isFile() :判断是否是文件 public boolean isFile(); // 当且仅当此抽象路径名表示的文件存在且 是一个标准文件时,返回 true;否则返回 false exists() : 判断该文件/目录是否存在 public boolean exists(); // 测试此抽象路径名表示的文件或目录是否存在 canRead() : 判断该文件是否可读的 public boolean canRead(); // 当且仅当此抽象路径名指定的文件存在且 可被应用程序读取时 ,返回 true;否则返回 false canWrite() : 判断该文件是否可写的 public boolean canWrite(); // 当且仅当文件系统实际包含此抽象路径名表示的文件且 允许应用程序对该文件进行写入时 ,返回 true;否则返回 false. isHidden() : 判断该文件是否隐藏的 public boolean isHidden(); // 当且仅当此抽象路径名表示的文件根据底层平台约定是隐藏文件时,返回 true举例:
import java.io.File; public class FileTest { public static void main(String[] args) { File file = new File("src\\blogs\\blog9\\hello.txt"); // 注意转义以及文件后缀(该文件实际存在的) File file2 = new File("src/blogs/blog9/hello3.txt"); // 左斜杆也是可以的 (该文件不存在的) System.out.println(file.isDirectory()); // 判断是否是文件目录 System.out.println(file.isFile()); // 判断是否为文件 System.out.println(file.exists()); // 判断该文件/目录是否实际存在 System.out.println(file.canRead()); // 判断该我呢见是否可读的 System.out.println(file.canWrite()); // 判断该文件是否是可写的 System.out.println(file.isHidden()); // 判断该文件是否隐藏的 System.out.println("*************************** file2 *************************"); System.out.println(file2.isDirectory()); // 判断是否是文件目录 System.out.println(file2.isFile()); // 判断是否为文件 System.out.println(file2.exists()); // 判断该文件/目录是否实际存在 System.out.println(file2.canRead()); // 判断该我呢见是否可读的 System.out.println(file2.canWrite()); // 判断该文件是否是可写的 System.out.println(file2.isHidden()); // 判断该文件是否隐藏的 } }File 类的创建功能
**createNewFile() ** : 创建文件 。若文件存在 ,则不创建 ,返回false public boolean createNewFile() throws IOException // 如果指定的文件不存在并成功地创建,则返回 true;如果指定的文件已经存在 ,则返回 false mkdirs() : 创建文件目录 。创建文件目录。如果上层文件目录不存在 ,一并创建 public boolean mkdirs(); // 创建此抽象路径名指定的目录 ,包括所有必需但不存在的父目录 。注意 ,此操作失败时也可能已经成功地创建了一部分必需的父目录 。 mkdir() : 创建文件目录 。如果此文件目录存在 ,就不创建了 。如果此文件目录的上层目录不存在 ,也不创建 。 public boolean mkdir(); // 创建此抽象路径名指定的目录 。注意事项:如果你创建文件或者文件目录没有写盘符路径 ,那么 ,默认在项目路径下 。
举例:
import java.io.File; import java.io.IOException; public class FileTest { public static void main(String[] args) { File file = new File("src/blogs/blog9/hello.txt"); boolean b = false; try { b = file.createNewFile(); // 文件存在不创建 ,不存在文件创建 } catch (IOException e) { e.printStackTrace(); } System.out.println(b); } }创建目录: 使用 mkdir
import java.io.File; public class FileTest { public static void main(String[] args) { File file = new File("src/blogs/blog9/test/test2/"); boolean b = file.mkdir(); // 如果对应的 test2目录的上级目录test不存在,则目录都不创建 System.out.println(b); } }使用 mkdirs()
import java.io.File; import java.io.IOException; public class FileTest { public static void main(String[] args) { File file = new File("src/blogs/blog9/test/test2/"); boolean b = file.mkdirs(); // 如果对应的 test2目录的上级目录test不存在 ,则目录一并都创建 System.out.println(b); } }File类的删除功能:
delete() : 删除文件或者文件夹 public boolean delete(); // 删除此抽象路径名表示的文件或目录 。如果此路径名表示一个目录 ,则该目录必须为空才能删除 。删除注意事项:Java中的删除不走回收站。要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录 。如果含有无法删除的 。
举例:
import java.io.File; public class FileTest { public static void main(String[] args) { File file = new File("src/blogs/blog9/test"); boolean b = file.delete(); // test 目录下不能有文件/目录 ,有的话无法删除 System.out.println(b); } }小结 :
1.4 实用案例:
判断指定目录下是否有后缀名为.jpg的文件 ,如果有,就输出该文件名称
package com.atguigu.exer2; import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter; import org.junit.Test; /** 判断指定目录下是否有后缀名为.jpg的文件 ,如果有 ,就输出该文件名称 */ public class FindJPGFileTest { @Test public void test1(){ File srcFile = new File("d:\\code"); String[] fileNames = srcFile.list(); for(String fileName : fileNames){ if(fileName.endsWith(".jpg")){ System.out.println(fileName); } } } @Test public void test2(){ File srcFile = new File("d:\\code"); File[] listFiles = srcFile.listFiles(); for(File file : listFiles){ if(file.getName().endsWith(".jpg")){ System.out.println(file.getAbsolutePath()); } } } /* * File类提供了两个文件过滤器方法 * public String[] list(FilenameFilter filter) * public File[] listFiles(FileFilter filter) */ @Test public void test3(){ File srcFile = new File("d:\\code"); File[] subFiles = srcFile.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jpg"); } }); for(File file : subFiles){ System.out.println(file.getAbsolutePath()); } } }遍历指定目录所有文件名称 ,包括子文件目录中的文件。 拓展1:并计算指定目录占用空间的大小 拓展2:删除指定文件目录及其下的所有文件
package com.atguigu.exer2; import java.io.File; /** * 3. 遍历指定目录所有文件名称 ,包括子文件目录中的文件 。 拓展1:并计算指定目录占用空间的大小 拓展2:删除指定文件目录及其下的所有文件 * @author shkstart 邮箱:shkstart@126.com * @version 创建时间:2019年2月23日 上午1:55:31 * */ public class ListFilesTest { public static void main(String[] args) { // 递归:文件目录 /** 打印出指定目录所有文件名称 ,包括子文件目录中的文件 */ // 1.创建目录对象 File dir = new File("E:\\teach\\01_javaSE\\_尚硅谷Java编程语言\\3_软件"); // 2.打印目录的子文件 printSubFile(dir); } public static void printSubFile(File dir) { // 打印目录的子文件 File[] subfiles = dir.listFiles(); for (File f : subfiles) { if (f.isDirectory()) {// 文件目录 printSubFile(f); } else {// 文件 System.out.println(f.getAbsolutePath()); } } } // 方式二:循环实现 // 列出file目录的下级内容 ,仅列出一级的话 // 使用File类的String[] list()比较简单 public void listSubFiles(File file) { if (file.isDirectory()) { String[] all = file.list(); for (String s : all) { System.out.println(s); } } else { System.out.println(file + "是文件!"); } } // 列出file目录的下级 ,如果它的下级还是目录 ,接着列出下级的下级 ,依次类推 // 建议使用File类的File[] listFiles() public void listAllSubFiles(File file) { if (file.isFile()) { System.out.println(file); } else { File[] all = file.listFiles(); // 如果all[i]是文件,直接打印 // 如果all[i]是目录 ,接着再获取它的下一级 for (File f : all) { listAllSubFiles(f);// 递归调用:自己调用自己就叫递归 } } } // 拓展1:求指定目录所在空间的大小 // 求任意一个目录的总大小 public long getDirectorySize(File file) { // file是文件 ,那么直接返回file.length() // file是目录,把它的下一级的所有大小加起来就是它的总大小 long size = 0; if (file.isFile()) { size += file.length(); } else { File[] all = file.listFiles();// 获取file的下一级 // 累加all[i]的大小 for (File f : all) { size += getDirectorySize(f);// f的大小; } } return size; } // 拓展2:删除指定的目录 public void deleteDirectory(File file) { // 如果file是文件 ,直接delete // 如果file是目录 ,先把它的下一级干掉,然后删除自己 if (file.isDirectory()) { File[] all = file.listFiles(); // 循环删除的是file的下一级 for (File f : all) {// f代表file的每一个下级 deleteDirectory(f); } } // 删除自己 file.delete(); } }2. I/O 流的概述
一个 I / O流 代表输入源或输出目的地 。流可以表示许多不同种类的源和目的地 ,包括磁盘文件 ,设备 ,其他程序和存储器阵列。
流支持许多不同类型的数据 ,包括简单字节 ,原始数据类型 ,本地化字符和对象 。一些流简单地传递数据; 其他人以有用的方式操纵和转换数据 。
I/O 其中的 I 是 Input 的缩写 ,O 是 Output 的缩写 。I/O 技术是非常实用的技术 ,用于处理设备之间的数据传输 。如读/写文件 ,网络通讯等 。
Java 程序中,对于数据的输入/输出操作以 流(stream) 的方式进行 。
java.io 包下提供了各种 “流 ”类和接口 ,用以获取不同种类的数据 ,并通过方法输入或输出数据 。
无论内部工作如何,所有流都会使用与使用它们的程序相同的简单模型:
流是一系列数据 。程序使用 输入流 从源中读取数据:**input** 输入流以内存为参考对象(将文件中的数据内容写入到内存当中) 以及 **Read** (以文件为参考对象 ,将读取文件中的数据到内存当中) 。这两个都是将意思都是一样的将文件中的数据读取出来写入到内存当中。
程序使用 输出流 将数据写入目的地 。**Output** 输出流以内存为参考对象(将内存中的数据内容输出到硬盘文件当中) 以及 **Write** (以文件为参考对象 ,将内存中的数据到写入到硬盘文件当中) 。这两个都是将意思都是一样的:将内存中的数据输出到硬盘文件当中。
2.1 I/O的分类和体系结构
按照不同的分类方式, 可以将流分为不同的类型 。
2.1.1 输入流 和 输出流按照流的流向来分 , 可以分为输入流和输出流:
输入流:
只能从中读取数据 , 而不能向其写入数据 。
输出流:只能向其写入数据 , 而不能从中读取数据。
此处的输入 、 输出涉及一个方向问题 , 对于如图 1 所示的数据流向 , 数据从内存到硬盘 , 通常称为输出流——也就是说 , 这里的输入 、 输出都是从程序运行所在内存的角度来划分的 。对于如图 2 所示的数据流向 , 数据从服务器通过网络流向客户端 , 在这种情况下, Server 端的内存负责将数据输出到网络里 , 因此 Server 端的程序使用输出流; Client 端的内存负责从网络里读取数据 , 因此 Client 端的程序应该使用输入流 。
2.1.2 字节流 和 字符流按操作数据单位不同分为:字节流(8 bit),字符流(16 bit) 。
字节流和字符流的用法几乎完全一样 , 区别在于字节流和字符流操作的数据单元的不同:字节流是 8 位的字节 , 而字符流操作的数据单元是 16 位的字符 。其中还有一点不同的就是:
字符流:只能读取操作文本文件,因为字符流读取的是文件中的 char 字符信息 。 .c ,.java ,.c++ ,.txt 等等这些都是文本文件不仅仅只是 txt文件 ,而特别注意的是 :.wrod 不是文本文件 ,wrod中的文字是经过特殊处理的存在一定的规范格式 ,不是纯文本文件 。 字节流:可以操作任何的文件 ,因为字节流读取的是二进制信息 ,读取1个字节byte,等同于一次读取8个二进制 ,这种流是万能的,什么类型的文件都可以读取到 ,因为文件都是有二进制组成的 。包括: 文本文件 ,图片,声音文件 。 2.1.3 节点流 和 处理流(包装流)按流的角色的不同分为:节点流 ,处理流 。
节点流: 所谓的节点流:就是最基本的一个流到底目的的流向 ,其中的流没有被其它的流所包含住。直接从数据源或目的地读写数据 。如下图所示 处理流: 处理流又称为包装流 ,处理流对一个己存在的流进行连接或封装/包装 , 通过封装后的流来实现数据读/写功能 。不直接连接到数据源或目的地 ,而是“连接 ”在已存在的流(节点流或处理流)之上 ,通过对数据的处理为程序提 供更为强大的读写功能。 如下图所示:注意: 节点流和包装流是相对的 ,有时候 ,相对于不同的流的 ,一个节点流变成了是另一个流的包装流 。一个包装流变成了另一个流的节点流 。如下图所示:
区分一个流是节点流还是包装流:大家可以:以谁包装谁作为参考 ,被包装的流就是节点流 ,包装了其它的流的就是包装流 ,当然注意这是相对的。
2.1.4 流的概念模型Java 把所有设备里的有序数据抽象成流模型, 简化了输入/输出处理 , 理解了流的概念模型也就了解了Java IO 。
通过使用处理流 , Java 程序无须理会输入/输出节点是磁盘、 网络还是其他的输入/输出设备, 程序只要将这些节点流包装成处理流 , 就可以使用相同的输入/输出代码来读写不同的输入/输出设备的数据 。
2.1.5 I/O 的体系结构 Java的IO流共涉及40多个类 ,实际上非常规则,都是从如下 Java Io 流四大家族 个 抽象基类派生的 。 由以下这四个类派生出来的子类 。其名称都是以其父类名作为子类名后缀 。这样用于我们辨认 。 Java IO流四大家族: java.io.InputStream 字节输入流 ,类名是以 "stream" 结尾的 。 public abstract class InputStream implements Closeable {} java.io.OutputStream 字节输出流 ,类名是以 "stream" 结尾的 。 public abstract class OutputStream implements Closeable, Flushable {} java.io.Reader 字符输入流 ,类名是以 "Reader/Writer"结尾的 。 public abstract class Reader implements Readable, Closeable {} java.io.Writer 字符输出流 ,类名是以以 "Reader/Writer"结尾的。 public abstract class Writer implements Appendable, Closeable, Flushable {} 上述四大家族的首领都是抽象类 abstract class 。这四个类都实现了 java.io.Closeable 接口 ,都是可以关闭的 ,都有 close() 方法 。流毕竟是一个管道 ,这个内存和硬盘之间的通道 ,用完之后一定要关闭。不然会耗费很多资源(因为Java打开的资源是有限的 ,当你打开过多的资源超过限制时就无法打开其它的资源了) 。养成好习惯,用完之后一定要关闭(必须关闭) 。 这四个类都实现了java.io.Flushable 接口 ,都是可刷新 的 ,都是有 flush() 方法的,养成一个好习惯 ,输出流(将内存当中的数据输出到硬盘文件当中)在最终(输出完)之后 ,一定要记得 flush() ,刷新一下 ,这个刷新的作用就是清空管道(强制将内存当中的数据输出到文件中)。为什么要清空管道呢:因为:如果没有 flush() 可以会导致内存中一部分的数据并没有全部输出到硬盘当中 ,从而导致一部分的数据丢失 。 注意:在Java中只要 类名是以 **"stream"** 结尾的都是字节流 ,以 **"Reader/Writer"**结尾的都是字符流 。 java.io包下需要掌握的流有 16个 文件专属: java.io.FileInputStream java.io.FileOutputStream 字节流无法读取到: 文件中的空格的 java.io.FileReader java.io.FileWriter 字符流可以读取到:文件中的空格的 转换流: (将字节流转换字符流) java.io.InputStreamReader java.io.OutputStreamWriter 缓冲流专属: java.io.BufferedReader java.io.BufferedWriter java.io.BufferedInputStream java.io.BufferedOutputStream java.io.BufferedOutputStream 数据流专属: java.io.DataInputStream java.io.DataOutputStream 标准输出流: java.io.PrintWtiter java.io.PrinStream 对象专属流: java.io.ObjectInputStream java.io.ObjectOutputStream2.2 字符流
2.2.1 java.io.FileReader 字符输入流关于字符输入流的类都是继承了:java.io.Writer(字符输出流)/ java.io.Reader (字符输入流) 来使用的 ,但是这个两个类是抽象类 ,是无法 new 对象来使用的 。所以我们就需要使用其实现的子类:对于文件字符输入流比较常用的就是: java.io.FileReader 这个子类了 。
字符流: 只能读取文本文件 ,不能读取其它格式的文件 ,文本文件不仅仅是 .txt 后缀的文件 ,.c ,.java,.c++ 都是文本文件 ,注意 : word 不是文本文件 ,因为 word 中的文本字符是有一个规范格式设置的 。不是纯的文本文件 。字符流操作字符,只能操作普通文本文件 。最常见的文本文件:.txt ,.java ,.c,.cpp 等语言的源代码 。尤其注意.doc,excel,ppt这些不是文本文件 。
在读取文件时 ,必须保证该文件已存在 ,否则报异常。
其中继承的 InputStreamReader 是个转换流 ,继承了 Reader 抽象类的 。
FileReader的构造器
public FileReader(File file) throws FileNotFoundException; // 在给定从中读取数据的 File 的情况下创建一个新 FileReader对象 public FileReader(String fileName) throws FileNotFoundException; // 根据文件的相对路径名/绝对路径创建一个新 FileReader对象举例:
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; public class FileReaderTest { public static void main(String[] args) { File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\hello.txt"); // 绝对路径 try { FileReader fileReader = new FileReader(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { FileReader fileReader2 = new FileReader("src/blogs/blog9/hello.txt"); // 相对路径 } catch (FileNotFoundException e) { e.printStackTrace(); } } }如下是 FileReader 继承 java.io.InputStreamReader** 继承的方法**
read() : 读取单个字符 。作为整数读取的字符 ,范围在 0 到 65535 之间 (0x00-0xffff)(2个字节的Unicode码) ,如果已到达流的末尾 ,则返回 -1。 public int read() throws IOException; // 读取单个字符 。 int read(char[] cbuf) : 将字符读入数组 。如果已到达流的末尾 ,则返回 -1。否则返回本次读取的字符数 。 public int read(char[] cbuf) throws IOException; // 将文件中的数据读取到char[] cbuf的字符数组当中 ,返回读取到的个数 。 int read(char[] cbuf,int off,int len) : 将字符读入数组的某一部分 。存到数组cbuf中 ,从off处开始存储,最多读len个字 符 。如果已到达流的末尾 ,则返回 -1 。否则返回本次读取的字符数 。 public int read(char[] cbuf,int offset, int length) throws IOException; // 将字符读入数组中的某一部分. public void close() throws IOException :关闭此输入流并释放与该流关联的所有系统资源 。 public void close() throws IOException; // 关闭此输入流并释放与该流关联的所有系统资源 。需要明白:对于 read() 读取文件内容的方式是 ,一个一个字符的读取的,每调用一次 read()对于的文件中的光标就会往后移动一下 。对于特殊的 read(char[ ] cbuf) 读取的个数是 char[] 数组的长度 ,往后移动光标的位置也是 char[] 数组的长度。以及返回的是对于字符的编码值 。
设文件 file1.txt ,采用字符流的话是这样读的: a中国bo张三 第一次读: ‘a’字符 第二次读: ‘中’字符举例:
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) { FileReader fileReader = null; // 相对路径 try { fileReader = new FileReader("src/blogs/blog9/hello.txt"); int read = fileReader.read(); // 返回的是编码值 System.out.println(read); read = fileReader.read(); System.out.println(read); read = fileReader.read(); System.out.println(read); read = fileReader.read(); System.out.println(read); read = fileReader.read(); System.out.println(read); read = fileReader.read(); System.out.println(read); read = fileReader.read(); System.out.println(read); } catch (IOException e) { e.printStackTrace(); } finally { // fileReader 防止 null引用 if(fileReader != null) { try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }举例: 使用 while() 循环处理
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) { FileReader fileReader = null; // 相对路径 try { fileReader = new FileReader("src/blogs/blog9/hello.txt"); int len = 0; // 当read()读取到 文件末尾返回 -1,跳出循环 while((len = fileReader.read()) != -1) { System.out.println(len); } } catch (IOException e) { e.printStackTrace(); } finally { // fileReader 防止 null引用 if(fileReader != null) { try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }举例: 使用 int read(char[] cbuf) : 将字符读入数组 。如果已到达流的末尾 ,则返回 -1。否则返回本次读取的字符数
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) { FileReader fileReader = null; // 相对路径 try { fileReader = new FileReader("src/blogs/blog9/hello.txt"); int len = 0; char [] chars = new char[4]; // read(chars) 一次性读取数组长度个字符 ,返回读取到的字符个数 。到达文件末尾返回-1 while((len = fileReader.read(chars)) != -1) { // 将char[] 数组转换为字符串 System.out.println(new String(chars)); } } catch (IOException e) { e.printStackTrace(); } finally { // fileReader 防止 null引用 if(fileReader != null) { try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }read(char[]) 读取数据时的覆盖效果的讲解
举例: 去除 read(char[] cduf) 的覆盖效果 ,我们读取多到了多少个字符 ,就 new String 转换多少个字符
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) { FileReader fileReader = null; // 相对路径 try { fileReader = new FileReader("src/blogs/blog9/hello.txt"); int len = 0; char [] chars = new char[4]; // read(chars) 一次性读取数组长度个字符 ,返回读取到的字符个数 。到达文件末尾返回-1 while((len = fileReader.read(chars)) != -1) { // 将char[] 数组转换为字符串 // 这里我们 读取到了多少个字符 ,就将 chars数组中的前多少个转换为字符串 System.out.println(new String(chars,0,len)); } } catch (IOException e) { e.printStackTrace(); } finally { // fileReader 防止 null引用 if(fileReader != null) { try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 2.2.2 java.io.FileWriter 字符输出流于字符流输出的类都是继承了:java.io.Writer(字符输出流)/ java.io.Reader (字符输入流) 来使用的 ,但是这个两个类是抽象类 ,是无法 new 对象来使用的。所以我们就需要使用其实现的子类:对于文件字符输出流比较常用的就是: java.io.FileWriter 这个子类了 。
其中继承的 OutputStreamWriter 是个转换流 ,继承了 Writer 抽象类的 。
OutputStreamWriter 的构造器:
public FileWriter(File file) throws IOException; // 根据给定的 File 对象构造一个 FileWriter 对象 public FileWriter(String fileName) throws IOException; // 根据给定的文件名构造一个 FileWriter 对象 。 public FileWriter(File file,boolean append) throws IOException; // 根据给定的 File 对象构造一个 FileWriter 对象 。如果第二个参数为 true,则将字节写入文件末尾处 ,而不是写入文件开始处 。 // file - 要写入数据的 File 对象 // append - 如果为 true ,则将字节写入文件末尾处,而不是写入文件开始处 ,默认是 false,不写的话也是 false举例:
package blogs.blog9; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class FileWriterTest { public static void main(String[] args) { File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\hello.txt"); // 绝对路径 try { FileWriter fileWriter = new FileWriter(file); } catch (IOException e) { e.printStackTrace(); } try { FileWriter fileWriter2 = new FileWriter("src/blogs/blog9/hello.txt"); // 相对路径 } catch (IOException e) { e.printStackTrace(); } try { FileWriter fileWriter3 = new FileWriter("src/blogs/blog9/hello.txt",true); } catch (IOException e) { e.printStackTrace(); } } }如下是 FileWriter 继承 java.io.OutputStreamWriter继承的方法
void write(int c) : 写入单个字符 。要写入的字符包含在给定整数值的 16 个低位中 ,16 高位被忽略 。 即写入0 到 65535 之间的Unicode码 。 public void write(int c) throws IOException; // 写入单个字符 。 void write(char[] cbuf) : 将字符数组的内容 ,写入到对应的硬盘文件中去 public void write(char[] cbuf) throws IOException; // 将字符数组的内容,写到文件中 void write(char[] cbuf,int off,int len) : 写入字符数组的某一部分。从off开始 ,写入len个字符 。 public void write(char[] cbuf,int off,int len) throws IOException // 写入字符数组的某一部分 。 void write(String str) :将字符串的内容 ,写入到对应的硬盘文件中去 public void write(Stirng str) throws IOException void write(String str,int off,int len) :写入字符串的某一部分。从off开始 ,写入len个结束 public void write(String str,int off,int len) throws IOException void flush() :刷新该流的缓冲 ,立即内存中的数据写入预期目标硬盘文件中去 。 public void flush() throws IOException; // 刷新该流的缓冲 。 public void close() :关闭此输出流并释放与该流关联的所有系统资源 public void close() throws IOException; // 关闭此流 ,但要先刷新它。在关闭该流之后 ,再调用 write() 或 flush() 将导致抛出 IOException 。关闭以前关闭的流无效 。注意: 如果写入到的文件不存在 ,是会自动创建的 。如果文件已经存在了 ,在创建FileWriter 对象时没有设置为 true 的话 ,是会将原本文件中已经存在的内容覆盖的,写入新的内容 。
举例: 文件不存在 ,自动创建 。
import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class FileReaderTest { public static void main(String[] args) { FileReader fileReader = null; // 相对路径 try { fileReader = new FileReader("src/blogs/blog9/hello.txt"); int len = 0; char [] chars = new char[4]; // read(chars) 一次性读取数组长度个字符 ,返回读取到的字符个数 。到达文件末尾返回-1 while((len = fileReader.read(chars)) != -1) { // 将char[] 数组转换为字符串 // 这里我们 读取到了多少个字符,就将 chars数组中的前多少个转换为字符串 System.out.println(new String(chars,0,len)); } } catch (IOException e) { e.printStackTrace(); } finally { // fileReader 防止 null引用 if(fileReader != null) { try { fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } } }举例: 文件已经存在 ,写入的信息覆盖原本文件的全部内容
import java.io.File; import java.io.FileWriter; import java.io.IOException; public class FileWriterTest { public static void main(String[] args) { FileWriter fileWriter = null; // 相对路径 try { // 1. 创建输出流对象: FileWriter fileWriter = new FileWriter("src/blogs/blog9/hello2.txt"); // 2. 将内存当中的内容写入到文件中 fileWriter.write("你好世界"); // 3. 刷新:将内存中没有输出到文件中的内容 ,强制全部写入到文件中 fileWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { // 防止 null 引用 if(fileWriter != null) { // 4. 关闭IO资源 try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } } }举例: 创建 **FileWriter ** 对象时,设置 true ,将内存当中的信息写入到文件的末尾去 ,不会覆盖原本文件中的内容
import java.io.File; import java.io.FileWriter; import java.io.IOException; public class FileWriterTest { public static void main(String[] args) { FileWriter fileWriter = null; // 相对路径 try { // 1. 创建输出流对象: FileWriter,并设置 true 将写入的内容追加到文件的末尾中去 fileWriter = new FileWriter("src/blogs/blog9/hello2.txt",true); // 2. 将内存当中的内容写入到文件中 fileWriter.write("\n"); // 换行 fileWriter.write("Hello World"); // 3. 刷新:将内存中没有输出到文件中的内容 ,强制全部写入到文件中 fileWriter.flush(); } catch (IOException e) { e.printStackTrace(); } finally { // 防止 null 引用 if(fileWriter != null) { // 4. 关闭IO资源 try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } } } 2.2.3 实例文本文件的拷贝将同目录中的 hello.txt 文件的内容拷贝到 同目录中的 hello2.txt 中去
思路:
import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class FileWriterTest { public static void main(String[] args) { FileWriter descFile = null; FileReader srcFile = null; // 注意文件后缀 try { // 1. 创建hello.txt 文件的字符输入流对象 ,以及 hello2.txt文件的字符输出流对象 descFile = new FileWriter("src/blogs/blog9/hello2.txt"); srcFile = new FileReader("src/blogs/blog9/hello.txt"); // 2. 一边读 ,一边写 int len = 0; char[] chars = new char[10]; // 读取hello.txt的数据信息 while((len = srcFile.read(chars)) != -1) { // 将读取到的内容写入到hello2.txt文件中 descFile.write(chars,0,len); } // 3. 刷新:将内存中遗留没有写入到文件中的信息 ,全部强制写入到文件中去 descFile.flush(); } catch (IOException e) { e.printStackTrace(); } finally { // 5. 关闭IO资源 // 分开 try,如果两个一起try的话其中一个出现异常了 ,后面的一个IO就无法关闭了 if(srcFile != null) { // 防止 null引用 try { srcFile.close(); } catch (IOException e) { e.printStackTrace(); } } if(descFile != null) { // 防止 null引用 try { descFile.close(); } catch (IOException e) { e.printStackTrace(); } } } } }2.3 字节流
2.3.1 FileInputStream 字节输入流关于字节输入流的类都是继承了:InputStream(字节输入流) 来使用的 ,但是个类是抽象类 ,是无法 new 对象来使用的 。所以我们就需要使用其实现的子类:对于文件字符输入流比较常用的就是: **java.io.FileInputStream ** 这个子类了 。
字节流: 可以操作任何的文件,因为字节流读取的是二进制信息 ,读取1个字节byte,等同于一次读取8个二进制 ,这种流是万能的,什么类型的文件都可以读取到 ,因为文件都是有二进制组成的 。包括: 文本文件 ,图片,声音文件。再比如:比如:.mp3 ,.avi ,.rmvb ,mp4 ,.jpg ,.doc ,.ppt等文件 。
FileInputStream的构造器
public FileInputStream(File file) throws FileNotFoundException; // 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的 File 对象 file 指定 。 public FileInputStream(String name) throws FileNotFoundException; // 通过打开一个到实际文件的连接来创建一个 FileInputStream ,该文件通过文件系统中的路径名 name 指定。和 字符流中的 FileReader 基本上是一样的 。
举例:
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; public class FileInputStreamTest { public static void main(String[] args) { File file = new File("E:\\Java\\JavaRebuilt\\src\\blogs\\blog9\\test.png"); // 绝对路径 try { FileInputStream fileInputStream = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { FileInputStream fileInputStream2 = new FileInputStream("src/blogs/blog9/test.png"); // 相对路径 } catch (FileNotFoundException e) { e.printStackTrace(); } } }如下是 FileInputStream 继承 java.io.InputStream 继承的方法
字节流和字符流的用法几乎完全一样 , 区别在于字节流和字符流操作的数据单元的不同:字节流是 8 位的字节, 而字符流操作的数据单元是 16 位的字符 。方法是上的使用也是一样的。
read() :读取文件的一个字节 ,返回值是: 读取到"字节"本身 ,到达文件末尾返回 -1 。 public int read() throws IOException ; // 从此输入流中读取一个数据字节 。如果没有输入可用,则此方法将阻塞 。 read(byte[] b) : 读取文件中 byte[] 数组长度的字节个数 ,返回读取的的字节个数 ,到位文件末尾返回 -1 。 public int read(byte[] b) throws IOException; // 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中 。在某些输入可用之前,此方法将阻塞 。 read(byte[] b int off, int len) : 将字节读入数组的某一部分 。存到数组b中 ,从off处开始存储 ,最多读len个字 符 。如果已到达流的末尾 ,则返回 -1 。否则返回本次读取的字符数。 public int read(byte[] b, int off, int len) throws IOException; // 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中 。如果 len 不为 0 ,则在输入可用之前 ,该方法将阻塞;否则 ,不读取任何字节并返回 0 。 public void close() throws IOException :关闭此输入流并释放与该流关联的所有系统资源。 public void close() throws IOException; // 关闭此输入流并释放与该流关联的所有系统资源 。举例:
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!