【Java从入门到大牛】File和IO流上篇 末蓝、 2024-04-26 02:51 74阅读 0赞 > ? 本文由 [程序喵正在路上][Link 1] 原创,CSDN首发! > ? 系列专栏:[Java从入门到大牛][Java] > ? 首发时间:2023年8月9日 > ? 欢迎关注?点赞?收藏?留言? > ? 一以贯之的努力 不得懈怠的人生 #### 目录 #### * 存储数据的方案 * File * * 创建对象 * 常用方法1:判断文件类型、获取文件信息 * 常用方法2:创建文件、删除文件 * 常用方法3:遍历文件夹 * 前置知识:方法递归 * * 认识递归的形式 * 应用、执行流程、算法思想 * 案例:文件搜索 * 案例:删除非空文件夹 * 案例:啤酒问题 * 前置知识:字符集 * * 常见字符集介绍 * 总结 * 字符集的编码、解码操作 * IO流 * IO流-字节流 * * 文件字节输入流:每次读取一个字节 * 文件字节输入流:每次读取多个字节 * 文件字节输入流:一次读取完全部字节 * 文件字节输出流:写字节出去 * 案例:文件复制 * 释放资源的方式 * * try-catch-finally * try-with-resource ## 存储数据的方案 ## double money = 9999.5; // 变量 int[] age = new int[100]; // 数组 Student s = new Student(); // 对象 List<Student> students = new ArrayList<>(); // 集合 上面这些都是内存中的数据容器,它们记住的数据,在断点或者程序终止时会丢失 **有些数据想长久保存起来,该怎么办 ?** * 文件是非常重要的存储方式,在计算机硬盘中 * 即便断电或者程序终止了,存储在硬盘文件中的数据也不会丢失 **File** File 是 java.io 包下的类,File 类的对象,用于代表当前操作系统的文件(可以是文件或者文件夹) 注意:File 类只能对文件本身进行操作,不能读写文件里面存储的数据 **IO流** 用于读写数据的,可以读写文件或者网络中的数据… ## File ## ### 创建对象 ### **创建File类的对象** ![在这里插入图片描述][66427c161bec432996fbed06066cc3de.png] * File 对象既可以代表文件,也可以代表文件夹 * File 封装的对象仅仅是一个路径名,这个路径可以是存在的,也允许是不存在的 **绝对路径、相对路径** * 绝对路径:从盘符开始 File f1 = new File("D:\\soft\\QQ"); * 相对路径:不带盘符,默认直接到当前工程下的目录寻找文件 File f2 = new File("模块名\\a.txt"); **具体应用** import java.io.File; /** * 目标:掌握File创建对象,代表具体文件的方案 */ public class FileTest1 { public static void main(String[] args) { // 创建一个File对象,指代某个具体的文件 File f1 = new File("D:/resource/a.txt"); // 方式一:最常用 // File f1 = new File("D:\\resource\\ab.txt"); // 方式二:第一个反斜杠用来转义 // File f1 = new File("D:" + File.separator +"resource" + File.separator + "ab.txt"); // 方式三:File.separator表示分隔符 "\" System.out.println(f1.length()); // 文件大小 // 创建一个File对象,指代某个文件夹 File f2 = new File("D:/resource"); System.out.println(f2.length()); // 文件夹的大小,不是计算其包括的所有文件的大小 // 注意:File对象可以指代一个不存在的文件路径 File f3 = new File("D:/resource/b.txt"); System.out.println(f3.length()); System.out.println(f3.exists()); // false // 我现在要定位的文件是在模块中,应该怎么定位呢? // 绝对路径:带盘符的,不推荐使用 // File f4 = new File("D:\\code\\javasepromax\\file-io-app\\src\\c.txt"); // 相对路径(重点):不带盘符,默认是直接去工程下寻找文件的 File f4 = new File("file-io-app\\src\\itheima.txt"); System.out.println(f4.length()); } } ### 常用方法1:判断文件类型、获取文件信息 ### **File提供的判断文件类型、获取文件信息功能** ![在这里插入图片描述][29dcc003161c4a7384efdd5354b0ceb5.png] **具体应用** import java.io.File; import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; /** 目标:掌握File提供的判断文件类型、获取文件信息功能 */ public class FileTest2 { public static void main(String[] args) throws UnsupportedEncodingException { // 1.创建文件对象,指代某个文件 File f1 = new File("D:/resource/a.txt"); // 2、public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true System.out.println("f1.exists: " + f1.exists()); // 3、public boolean isFile() : 判断当前文件对象指代的是否是文件,是文件返回true,反之 System.out.println("f1.isFile: " + f1.isFile()); // 4、public boolean isDirectory(): 判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之 System.out.println("f1.isDirectory: " + f1.isDirectory()); // 5.public String getName():获取文件的名称(包含后缀) System.out.println("f1.getName: " + f1.getName()); // 6.public long length():获取文件的大小,返回字节个数 System.out.println("f1.length: " + f1.length()); // 7.public long lastModified():获取文件的最后修改时间 long time = f1.lastModified(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); System.out.println("f1.lastModified: " + sdf.format(time)); // 8.public String getPath():获取创建文件对象时,使用的路径 File f2 = new File("D:\\resource\\ab.txt"); File f3 = new File("file-io-app\\src\\itheima.txt"); System.out.println("f2.getPath: " + f2.getPath()); System.out.println("f3.getPath: " + f3.getPath()); // 9.public String getAbsolutePath():获取绝对路径 System.out.println("f2.getAbsolutePath: " + f2.getAbsolutePath()); System.out.println("f3.getAbsolutePath: " + f3.getAbsolutePath()); } } ![在这里插入图片描述][534a4bdc87eb4b38862126aa204b1114.png] ### 常用方法2:创建文件、删除文件 ### **File类创建文件的功能** ![在这里插入图片描述][ef1d8a6be22143dc8600ef5c83cc366b.png] **File类删除文件的功能** ![在这里插入图片描述][4686132d9f9d45b7bc91c64a85cf2cb5.png] 注意:delete 方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站 **具体应用** import java.io.File; /** * 目标:掌握File创建和删除文件相关的方法 */ public class FileTest3 { public static void main(String[] args) throws Exception { // 1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之 File f1 = new File("D:/code/aa.txt"); System.out.println(f1.createNewFile()); // 2、public boolean mkdir():用于创建文件夹,注意:只能创建一级文件夹 File f2 = new File("D:/code/aaa"); System.out.println(f2.mkdir()); // 3、public boolean mkdirs():用于创建文件夹,注意:可以创建多级文件夹 File f3 = new File("D:/code/bbb/ccc/ddd/eee/fff/ggg"); System.out.println(f3.mkdirs()); // 3、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹 System.out.println(f1.delete()); System.out.println(f2.delete()); File f4 = new File("D:/code"); System.out.println(f4.delete()); } } ### 常用方法3:遍历文件夹 ### **File类提供的遍历文件夹的功能** ![在这里插入图片描述][7517300acf8d4e5eb388eddd88d84ad3.png] **使用listFiles方法时的注意事项:** * 当主调是文件,或者路径不存在时,返回 null * 当主调是空文件夹时,返回一个长度为 0 的数组 * 当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在 File 数组中返回 * 当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在 File 数组中返回,包含隐藏文件 * 当主调是一个文件夹,但是没有权限访问该文件夹时,返回 null **具体应用** import java.io.File; import java.io.IOException; import java.util.Arrays; /** * 目标:掌握File提供的遍历文件夹的方法 */ public class FileTest4 { public static void main(String[] args) throws IOException { File f1 = new File("D:/code/aaa"); f1.mkdir(); for (int i = 1; i < 4; i++) { File f2 = new File("D:/code/aaa/a" + i + ".txt"); if(!f2.createNewFile()) System.out.println("Failed to createNewFile!"); } // 1、public String[] list():获取当前目录下所有的 "一级文件名称"到一个字符串数组中去返回 String[] names = f1.list(); for (String name : names) { System.out.println(name); } // 2、public File[] listFiles():(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点) File[] files = f1.listFiles(); for (File file : files) { System.out.println(file.getAbsolutePath()); } File f3 = new File("D:/code/aaa"); File[] files1 = f3.listFiles(); System.out.println(Arrays.toString(files1)); } } ![在这里插入图片描述][5614faaaf24145259c3e38332ae1b96e.png] ## 前置知识:方法递归 ## ### 认识递归的形式 ### **什么是方法递归 ?** * 递归是一种算法,在程序设计语言中广泛应用 * 从形式上看,方法调用自身的形式称为方法递归(recursion) **递归的形式** * 直接递归:方法自己调用自己 * 间接递归:方法调用其他方法,其他方法又回调方法自己 **使用方法递归时需要注意的问题:** 递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误 ### 应用、执行流程、算法思想 ### **案例导学-计算 n 的阶乘** 需求:计算 n 的阶乘,例如 5 的阶乘 = 1 \* 2 \* 3 \* 4 \* 5 分析 1. 假如我们认为存在一个公式是 f(n) = 1 \* 2 \* 3 \* … \* (n-1) \* n 2. 那么公式等价形式就是:f(n) = f(n-1) \* n 3. 如果求的是 1-5 的阶乘,我们如何手算出它的结果:f(5) = f(4) \* 5,f(4) = f(3) \* 4,…,f(2) = f(1) \* 2,f(1) = 1,然后依次递归回来算出结果 **具体实现** /** * 目标:掌握递归的应用,执行流程和算法思想。 */ public class RecursionTest1 { public static void main(String[] args) { System.out.println("5的阶乘是:" + f(5)); } public static int f(int n){ // 终结点 if(n == 1){ return 1; }else { return f(n - 1) * n; } } } ![在这里插入图片描述][8ca2dc450f954c68af0c5cb44b3187e7.png] **递归算法三要素:** 1. 递归的公式:`f(n) = f(n-1) * n` 2. 递归的终结点:f(1) 3. 递归的方向必须走向终结点 **案例-猴子吃桃问题** 猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个;第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个;以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个;等到第10天的时候发现桃子只有1个了。 **需求**:请问猴子第一天摘了多少个桃子? **分析**: 整体来看,每一天都是做同一个事件,典型的规律化问题,考虑递归三要素: * 递归公式: `f(n) = (f(n + 1) + 1) * 2` * 递归终结点:f(10) = 1 * 递归方向:从第一天到第十天 /* 猴子吃桃问题 */ public class RecursionTest2 { public static void main(String[] args) { System.out.println("猴子第一天摘了" + f(1) + "个桃子"); // 验证结果 double num = f(1); for (int i = 1; i < 11; i++) { System.out.print("第" + i + "天一开始有" + num + "个桃子"); num = num / 2 - 1; if (num >= 0) System.out.println(", 吃完还剩" + num + "个桃子"); } } public static double f(double n) { if (n == 10) { return 1; } else { return (f(n + 1) + 1) * 2; } } } /* 思路分析: f(2) = f(1) / 2 - 1; f(3) = f(2) / 2 - 1; f(4) = f(3) / 2 - 1; ... f(9) = f(8) / 2 - 1; f(10) = f(9) / 2 - 1 = 1; */ ![在这里插入图片描述][05a3eb01259a4905b45686190bc42a5a.png] ### 案例:文件搜索 ### **案例:文件搜索** **需求**:从 D 盘中,搜索 “QQ.exe” 这个文件,找到后直接输出其位置 **分析** 1. 先找出 D 盘下的所有一级文件对象 2. 遍历全部一级文件对象,判断是否是文件 3. 如果是文件,判断是否是自己想要的 4. 如果是文件夹,需要继续进入到该文件夹,重复上述过程 **代码实现** import java.io.File; /** * 目标:掌握文件搜索的实现 */ public class RecursionTest3 { public static void main(String[] args) throws Exception { searchFile(new File("D:/") , "QQ.exe"); } /** * 去目录下搜索某个文件 * @param dir 目录 * @param fileName 要搜索的文件名称 */ public static void searchFile(File dir, String fileName) throws Exception { // 1、把非法的情况都拦截住 if(dir == null || !dir.exists() || dir.isFile()){ return; // 代表无法搜索 } // 2、dir不是null,存在,一定是目录对象 // 获取当前目录下的全部一级文件对象 File[] files = dir.listFiles(); // 3、判断当前目录下是否存在一级文件对象,以及是否可以拿到一级文件对象 if(files != null && files.length > 0){ // 4、遍历全部一级文件对象。 for (File f : files) { // 5、判断文件是否是文件,还是文件夹 if(f.isFile()){ // 是文件,判断这个文件名是否是我们要找的 if(f.getName().contains(fileName)){ System.out.println("找到了:" + f.getAbsolutePath()); // 拓展:启动QQ Runtime runtime = Runtime.getRuntime(); runtime.exec(f.getAbsolutePath()); } }else { // 是文件夹,继续重复这个过程(递归) searchFile(f, fileName); } } } } } ![在这里插入图片描述][41079d27f6004f0b8b264c91269105b4.png] ### 案例:删除非空文件夹 ### **需求** 删除非空文件夹 **分析** 1. File 默认不可以直接删除非空文件夹 2. 所有我们需要遍历文件夹,先删除里面的内容,再删除自己 **代码实现** import java.io.File; /** * 删除非空文件夹 */ public class RecursionTest4 { public static void main(String[] args) throws Exception { createTestFile(); File dir = new File("D:/test007"); deleteDir(dir); // 可以先注释这一行,到 D盘查看非空文件夹是否创建成功 } // 创建用来删除的非空文件夹 public static void createTestFile() throws Exception { // 创建多级文件夹 File f1 = new File("D:/test007/aaa/bbb/ccc/ddd/eee"); if (!f1.mkdirs()) return; // 创建一级文件夹 File f2 = new File("D:/test007/fff"); if (!f2.mkdir()) return; // 再随便添加一些文件 for (int i = 1; i < 4; i++) { File f = new File("D:/test007/t00" + i + ".txt"); if (!f.createNewFile()) return; } File f3 = new File("D:/test007/aaa/a.jpg"); if (!f3.createNewFile()) return; System.out.println("非空文件夹创建成功!"); } // 删除非空文件夹 public static void deleteDir(File dir) { if (dir == null || !dir.exists()) { return; } // 是一个文件 if (dir.isFile()) { if(dir.delete()) System.out.println("文件" + dir.getName() + "删除成功!"); return; } // 是文件夹 File[] files = dir.listFiles(); // 尝试取出所有一级文件对象 // 取出一级文件对象失败 if (files == null) { return; } // 取出成功,但是空文件夹 if (files.length == 0) { if(dir.delete()) System.out.println("文件" + dir.getName() + "删除成功!"); return; } // 是非空文件夹 for (File file : files) { if (file.isFile()) { // 是文件,直接删除 if (file.delete()) System.out.println("文件" + file.getName() + "删除成功!"); } else { deleteDir(file); // 是文件夹,递归删除 } } // 最后还剩下一个空文件夹,记得删除 if(dir.delete()) System.out.println("文件" + dir.getName() + "删除成功!"); } } **执行结果** ![在这里插入图片描述][2da49236755f499eb04bc686cc5180da.png] ### 案例:啤酒问题 ### **需求** 啤酒 2 元 1 瓶,4 个盖子可以换一瓶,2 个空瓶可以换一瓶,请问 10 元钱可以喝多少瓶酒,剩余多少空瓶和盖子 **代码实现** public class RecursionTest5 { public static int totalNumber; // 总酒数 public static int lastBottleNumber; // 每轮剩余瓶子数 public static int lastCoverNumber; // 每轮剩余盖子数 public static void main(String[] args) { buy(10); System.out.println("总酒数: " + totalNumber); System.out.println("剩余瓶子数: " + lastBottleNumber); System.out.println("剩余盖子数: " + lastCoverNumber); } // 买酒 public static void buy(int money) { // 有多少钱先买酒 int buyNumber = money / 2; // 可买酒数 totalNumber += buyNumber; // 瓶子和盖子还可以换酒 int allBottleNumber = buyNumber + lastBottleNumber; // 当前总瓶子数 int allCoverNumber = buyNumber + lastCoverNumber; // 当前总盖子数 int allMoney = 0; // 记录下次买酒还有多少钱 // 瓶子换酒 if (allBottleNumber >= 2) { allMoney += (allBottleNumber / 2) * 2; } lastBottleNumber = allBottleNumber % 2; // 盖子换酒 if (allCoverNumber >= 4) { allMoney += (allCoverNumber / 4) * 2; } lastCoverNumber = allCoverNumber % 4; // 如果钱还够买酒, 继续买 if (allMoney >= 2) { buy(allMoney); } } } **执行结果** ![在这里插入图片描述][49c1ec6b769b4a3bb2de6b0ed4380699.png] ## 前置知识:字符集 ## ### 常见字符集介绍 ### ![在这里插入图片描述][e4f1152d51ed4bc9973ce84f9a40b150.png] **标准ASCII字符集** * ASCII(American Standard Code for Information Interchange):美国信息交换标准代码,包括了英文、符号等 * 标准 ASCII 使用 1 个字节存储一个字符,首尾是 0,总共可表示 128 个字符 ![在这里插入图片描述][f046c81d04094b2c9030d8431c730be1.png] **GBK字符集(汉字内码扩展规范,国标)** * 汉字编码字符集,包含了 2 万多个汉字等字符,GBK 中一个中文字符编码成两个字节的形式存储 * 注意:GBK 兼容了 ASCII 字符集 * GBK 规定:汉字的第一个字节的第一位必须是 1 **Unicode字符集(统一码,也叫万国码)** Unicode 是国际组织规定的,可以容纳世界上所有文字、符号的字符集 **UTF-8字符集** * 是 Unicode 字符集的一种编码方案,采取可变长编码方案,共分为四个长度区:1 个字节、2 个字节、3 个字节、4 个字节 * 英文字符、数字等只占 1 个字节(兼容标准 ASCII 编码),汉字字符占用 3 个字节 * 注意:技术人员在开发时都应该使用 UTF-8 编码! ![在这里插入图片描述][7a8a8c3e61464cc6a5858ee4276bf2d8.png] ### 总结 ### * ASCII 字符集:只有英文、数字、符号等,占 1 个字节 * GBK 字符集:汉字占 2 个字节,英文、数字占 1 个字节 * UTF-8 字符集:汉字占 3 个字节,英文、数字占 1 个字节 * 字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码 * 英文、数字一般不会乱码,因为很多字符集都兼容了 ASCII 编码 ### 字符集的编码、解码操作 ### **什么是编码 ?** 把字符按照指定的字符集编码成字节 **什么是解码 ?** 把字节按照指定的字符集编码解码成字符 **Java 代码完成对字符的编码** ![在这里插入图片描述][9ee83df452ef4a44bbf9327bed38f732.png] **Java 代码完成对字符的解码** ![在这里插入图片描述][ea51527c8ec24f4b8ef637d4c59af0f7.png] **具体应用** import java.util.Arrays; /** * 目标:掌握如何使用Java代码完成对字符的编码和解码 */ public class Test { public static void main(String[] args) throws Exception { // 1、编码 String data = "a我b"; byte[] bytes = data.getBytes(); // 默认是按照平台字符集(UTF-8)进行编码的。 System.out.println(Arrays.toString(bytes)); // 按照指定字符集进行编码 byte[] bytes1 = data.getBytes("GBK"); System.out.println(Arrays.toString(bytes1)); // 2、解码 String s1 = new String(bytes); // 按照平台默认编码(UTF-8)解码 System.out.println(s1); String s2 = new String(bytes1, "GBK"); System.out.println(s2); } } ![在这里插入图片描述][4efd1cdccc2d454abb979d39673ad7b4.png] ## IO流 ## **什么是IO流 ?** 输入输出流,用来读写数据的 ![在这里插入图片描述][6b4db0f5902b4501a6842e79deb4c399.png] **如何学习IO流 ?** 1. 先搞清楚 IO 流的分类、体系 2. 再挨个学习每个 IO 流的作用、用法 **IO流的分类** ![在这里插入图片描述][e11e178f3a76410db67f2156cc134d77.png] IO 流总体来看就有四大流:字节输入流、字节输出流、字符输入流、字符输出流 * **字节输入流 InputStream**:以内存为基准,来自磁盘文件 / 网络中的数据以字节的形式读入到内存中去的流 * **字节输出流 OutputStream**:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流 * **字符输入流 Reader**:以内存为基准,来自磁盘文件 / 网络中的数据以字符的形式读入到内存中去的流 * **字符输出流 Writer**:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络中去的流 ## IO流-字节流 ## ### 文件字节输入流:每次读取一个字节 ### **IO流的体系** ![在这里插入图片描述][8a600872221e4da38b7eb5deee59360f.png] **FileInputStream(文件字节输入流)** 作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去 ![在这里插入图片描述][5765a3eab0aa4ef1b9d8318105967437.png] **注意事项** 使用 FileInputStream 每次读取一个字节,读取性能较差,并且读取汉字输出会乱码 **具体应用** 准备一个测试文本文件: ![在这里插入图片描述][baa56c0c249d4f3ca8b261fa52059e8b.png] import java.io.*; /** * 目标:掌握文件字节输入流,每次读取一个字节 */ public class FileInputStreamTest1 { public static void main(String[] args) throws Exception { // 1、创建文件字节输入流管道,与源文件接通 // InputStream is = new FileInputStream(new File("file-io-app/src/test1.txt")); // 简化写法:推荐使用 InputStream is = new FileInputStream(("file-io-app/src/test1.txt")); // 2、开始读取文件的字节数据 // public int read():每次读取一个字节返回,如果没有数据了,返回-1 int b; // 用于记住读取的字节 while ((b = is.read()) != -1){ System.out.print((char) b); } // 读取数据的性能很差! // 读取汉字输出会乱码!!无法避免的!! // 流使用完毕之后,必须关闭!释放系统资源! is.close(); } } ![在这里插入图片描述][58ebe3b054324a5a8df5a054f4694eb5.png] ### 文件字节输入流:每次读取多个字节 ### **FileInputStream(文件字节输入流)** 作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去 ![在这里插入图片描述][6a69843dedf5485380374f23ff221168.png] **注意事项** 使用 FileInputStream 每次读取多个字节,读取性能得到了提升,但读取汉字输出还是会乱码 **具体应用** import java.io.FileInputStream; import java.io.InputStream; /** * 目标:掌握使用FileInputStream每次读取多个字节 */ public class FileInputStreamTest2 { public static void main(String[] args) throws Exception { // 1、创建一个字节输入流对象代表字节输入流管道与源文件接通 InputStream is = new FileInputStream("file-io-app/src/test1.txt"); // 2、开始读取文件中的字节数据:每次读取多个字节 // public int read(byte b[]) throws IOException // 每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1 byte[] buffer = new byte[3]; int len; // 记住每次读取了多少个字节 while ((len = is.read(buffer)) != -1){ // 注意:读取多少,倒出多少。 String rs = new String(buffer, 0 , len); System.out.print(rs); } // 性能得到了明显的提升!! // 但是这种方案也不能避免读取汉字输出乱码的问题!! is.close(); // 关闭流 } } ![在这里插入图片描述][dd38e4a721074055b49ab3c0ff75a222.png] ### 文件字节输入流:一次读取完全部字节 ### 方式一:自己定义一个字节数组与被读取的文件大小一样大,然后使用该字节数组,一次读完文件的全部字节 ![在这里插入图片描述][9f772979811b425bbd87997faa34c411.png] 方式二:Java 官方为 InputStream 提供了如下方法,可以直接把文件的全部字节读取到一个字节数组中返回 ![在这里插入图片描述][c039b9eb6c8345698edac02b950319ee.png] **直接把文件数据全部读取到一个字节数组可以避免乱码,是否还存在问题 ?** * 如果文件过大,创建的字节数组也会过大,就可能引起内存溢出的问题 读写文本内容更适合用字符流,字节流适合做数据的转移,如:文件复制等 **具体应用** ![在这里插入图片描述][06ec447a1c39498cba2999b7c2fae982.png] import java.io.*; /** * 目标:使用文件字节输入流一次读取完文件的全部字节 */ public class FileInputStreamTest3 { public static void main(String[] args) throws Exception { // 1、一次性读取完文件的全部字节到一个字节数组中去 // 创建一个字节输入流管道与源文件接通 InputStream is1 = new FileInputStream("file-io-app/src/test2.txt"); // 2、方式一:准备一个字节数组,大小与文件的大小正好一样大 File f = new File("file-io-app/src/test2.txt"); long size = f.length(); byte[] buffer1 = new byte[(int) size]; int len = is1.read(buffer1); System.out.println(new String(buffer1)); System.out.println("---------------------------------"); // 方式二:使用Java提供的方法 InputStream is2 = new FileInputStream("file-io-app/src/test2.txt"); byte[] buffer2 = is2.readAllBytes(); System.out.println(new String(buffer2)); // 关闭流 is1.close(); is2.close(); } } ![在这里插入图片描述][d2eaaf7bf17a4cbd9c4f6fbe44d5dea2.png] ### 文件字节输出流:写字节出去 ### **FileOutputStream(文件字节输出流)** 作用:以内存为基准,把内存中的数据以字节的形式写出到文件中 ![在这里插入图片描述][16ec58112a094211a3cc511d7d3ab192.png] **具体应用** import java.io.*; /** * 目标:掌握文件字节输出流FileOutputStream的使用 */ public class FileOutputStreamTest4 { public static void main(String[] args) throws Exception { // 1、创建一个字节输出流管道与目标文件接通 // 覆盖管道:覆盖之前的数据 // OutputStream os = // new FileOutputStream("file-io-app/src/testOut.txt"); // 追加数据的管道 OutputStream os = new FileOutputStream("file-io-app/src/testOut.txt", true); // 2、开始写字节数据出去 os.write(97); // 97就是一个字节,代表a os.write('b'); // 'b'也是一个字节 byte[] bytes = "我爱你中国abc".getBytes(); os.write(bytes); os.write("\n".getBytes()); // 换行符 os.write(bytes, 0, 15); // 一个汉字占3个字节 os.close(); // 关闭流 } } ![在这里插入图片描述][a2d8e723a3d7450c9da3270ee3dbe3e0.png] ### 案例:文件复制 ### ![在这里插入图片描述][4c3116db81eb4cebb5e913310c20a1fb.png] 字节流非常适合做一切文件的复制操作。任何文件的底层都是字节,字节流做复制,是一字不漏地转移完全部字节,只要复制后的文件格式一致就没问题 **具体应用** import java.io.*; /** * 目标:使用字节流完成对文件的复制操作 */ public class CopyTest5 { public static void main(String[] args) throws Exception { // 需求:复制照片 // 1、创建一个字节输入流管道与源文件接通 InputStream is = new FileInputStream("file-io-app/src/1.png"); // 2、创建一个字节输出流管道与目标文件接通 OutputStream os = new FileOutputStream("file-io-app/src/copy.png"); // 3、创建一个字节数组,负责转移字节数据 byte[] buffer = new byte[1024]; // 4、从字节输入流中读取字节数据,写出去到字节输出流中。读多少写出去多少 int len; // 记住每次读取了多少个字节 while ((len = is.read(buffer)) != -1){ os.write(buffer, 0, len); } os.close(); is.close(); System.out.println("复制完成!!"); } } ![在这里插入图片描述][0103d62da9b249c8857f9ecd000605fb.png] ## 释放资源的方式 ## ### try-catch-finally ### try { ... } catch(IOException e) { e.printStackTrace(); } finally { ... } finally 代码区的特点:无论 try 中的程序是正常执行了,还是出现了异常,最后都一定会执行 finally 区,除非 JVM 终止 作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法) **具体应用** /** * 目标:认识try-catch-finally */ public class Test1 { public static void main(String[] args) { try { System.out.println(10 / 2); // return; // 跳出方法的执行 // System.exit(0); // 虚拟机 }catch (Exception e){ e.printStackTrace(); } finally { System.out.println("===finally执行了一次==="); } System.out.println(div(10, 2)); } public static int div(int a, int b){ try { return a / b; }catch (Exception e){ e.printStackTrace(); return -1; // 代表的是出现异常 }finally { // 千万不要在finally中返回数据! return 111; } } } **手动释放资源** /** * 目标:掌握释放资源的方式 */ public class Test2 { public static void main(String[] args) { InputStream is = null; OutputStream os = null; try { // 1、创建一个字节输入流管道与源文件接通 is = new FileInputStream("file-io-app/src/1.png"); // 2、创建一个字节输出流管道与目标文件接通 os = new FileOutputStream("file-io-app/src/copy.png"); // 3、创建一个字节数组,负责转移字节数据 byte[] buffer = new byte[1024]; // 4、从字节输入流中读取字节数据,写出去到字节输出流中 int len; // 记住每次读取了多少个字节 while ((len = is.read(buffer)) != -1){ os.write(buffer, 0, len); } System.out.println("复制完成!!"); } catch (IOException e) { e.printStackTrace(); } finally { // 释放资源的操作 try { if(os != null) os.close(); } catch (IOException e) { e.printStackTrace(); } try { if(is != null) is.close(); } catch (IOException e) { e.printStackTrace(); } } } } ### try-with-resource ### JDK 7 开始提供了更简单的资源释放方案:try-with-resource try(定义资源1; 定义资源2; ...) { 可能出现异常的代码; } catch(异常类名 变量名) { 异常的处理代码; } 上述资源使用完毕后,会自动调用其 close() 方法,完成对资源的释放 * () 中只能放置资源,否则报错 * 什么是资源呢? * 资源一般指的是最终实现了 AutoCloseable 接口 public abstract class InputStream implements Closeable { } public abstract class OutputStream implements Closeable, Flushable { } public interface Closeable extends AutoCloseable { } **具体应用** public class MyConnection implements AutoCloseable{ @Override public void close() throws Exception { System.out.println("释放了与某个硬件的链接资源~~~~"); } } import java.io.*; /** * 目标:掌握释放资源的方式:try-with-resource */ public class Test3 { public static void main(String[] args) { try ( // 注意:这里只能放置资源对象(流对象) // 1、创建一个字节输入流管道与源文件接通 InputStream is = new FileInputStream("file-io-app/src/1.png"); // 2、创建一个字节输出流管道与目标文件接通 OutputStream os = new FileOutputStream("file-io-app/src/copy.png"); MyConnection conn = new MyConnection(); ){ // 3、创建一个字节数组,负责转移字节数据 byte[] buffer = new byte[1024]; // 4、从字节输入流中读取字节数据,写出去到字节输出流中 int len; // 记住每次读取了多少个字节 while ((len = is.read(buffer)) != -1){ os.write(buffer, 0, len); } System.out.println("复制完成!!"); } catch (Exception e) { e.printStackTrace(); } } } ![在这里插入图片描述][8a795089251342a9b0881b81eec94b63.png] [Link 1]: https://blog.csdn.net/weixin_62511863?spm=1011.2421.3001.5343 [Java]: https://blog.csdn.net/weixin_62511863/category_12365679.html?spm=1001.2014.3001.5482 [66427c161bec432996fbed06066cc3de.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/be76c1bea9f249a1be242015f64b0b68.png [29dcc003161c4a7384efdd5354b0ceb5.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/82d1fe8079e449ddb353e67dcaa549ac.png [534a4bdc87eb4b38862126aa204b1114.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/7a42650044f94abf9bfa24fc3feb1046.png [ef1d8a6be22143dc8600ef5c83cc366b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/52ab6198852d4319aa60641f074c435e.png [4686132d9f9d45b7bc91c64a85cf2cb5.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/69f356167300407d808d663936d1c1cb.png [7517300acf8d4e5eb388eddd88d84ad3.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/88c1cde13e1f44f8af7ec29bcf5ddbeb.png [5614faaaf24145259c3e38332ae1b96e.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/8057784fdc154e158bafbf9a9ce1f1bb.png [8ca2dc450f954c68af0c5cb44b3187e7.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/578caef91010459a9876bad53afc458d.png [05a3eb01259a4905b45686190bc42a5a.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/6b768696f7e1403898b2cfbab25889ee.png [41079d27f6004f0b8b264c91269105b4.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/6f2153917ec24caf88ba699bab79993a.png [2da49236755f499eb04bc686cc5180da.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/5d700220106c46489a84854083895522.png [49c1ec6b769b4a3bb2de6b0ed4380699.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/1e481f97239244128d63b460129990bf.png [e4f1152d51ed4bc9973ce84f9a40b150.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/bb812b9b75ea45a8b394f2c7f55efccc.png [f046c81d04094b2c9030d8431c730be1.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/f292488d96bf495597c775f4f2103f9f.png [7a8a8c3e61464cc6a5858ee4276bf2d8.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/44372e8db46f467aad3af3a5a317f8e5.png [9ee83df452ef4a44bbf9327bed38f732.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/09b789fb2895425caf0112d4ebb38b85.png [ea51527c8ec24f4b8ef637d4c59af0f7.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/d7a3690291db4fb98fe7f769052c1203.png [4efd1cdccc2d454abb979d39673ad7b4.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/8cfe0e4a15834c009c4f592732f52f9f.png [6b4db0f5902b4501a6842e79deb4c399.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/5aee563bb67b40bf9dacd9041398eb04.png [e11e178f3a76410db67f2156cc134d77.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/b5ece752261942d7a599fc6d53c76534.png [8a600872221e4da38b7eb5deee59360f.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/8cd502c2fbdb47dbb5ec863b421e62a1.png [5765a3eab0aa4ef1b9d8318105967437.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/160b3f176ebc48d2964ca35dcaa8de27.png [baa56c0c249d4f3ca8b261fa52059e8b.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/1dc079bbf9e940e9bfe44a2c9dd75b0a.png [58ebe3b054324a5a8df5a054f4694eb5.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/4213f621b843494aa0a309e63ff40ae8.png [6a69843dedf5485380374f23ff221168.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/896b4a7bae6e4160a32e6b629c76daa8.png [dd38e4a721074055b49ab3c0ff75a222.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/d247c8f787cf4b058324baa4f7b7cd0f.png [9f772979811b425bbd87997faa34c411.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/547de930510a4062a80142c7b2d6dceb.png [c039b9eb6c8345698edac02b950319ee.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/193f748c5c044fd08f68c48f57a436ae.png [06ec447a1c39498cba2999b7c2fae982.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/33b0dfaf3dc043948cd69c107f2cc6c2.png [d2eaaf7bf17a4cbd9c4f6fbe44d5dea2.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/df9389dcc6154a9892d3ce9055352497.png [16ec58112a094211a3cc511d7d3ab192.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/0b9cb761b1024f9a85597e5dde2e5e73.png [a2d8e723a3d7450c9da3270ee3dbe3e0.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/8e2e5e41a1fb4b0391c1bc49c64b7af7.png [4c3116db81eb4cebb5e913310c20a1fb.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/0c7df743569a47e6b249c615d2499628.png [0103d62da9b249c8857f9ecd000605fb.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/95632668bd494e6db85bdd515c96088c.png [8a795089251342a9b0881b81eec94b63.png]: https://image.dandelioncloud.cn/pgy_files/images/2024/04/26/d04fa1cc1bd14fd09ad83b9720cb7f99.png
相关 【Java从入门到大牛】网络编程 可以让设备中的程序与网络上的其他设备中的程序进行数据交互,也就是实现网络通信Java的 java.net.* 包下提供了网络编程的解决方案。 Myth丶恋晨/ 2024年04月26日 02:56/ 0 赞/ 109 阅读
相关 【Java从入门到大牛】String和ArrayList 本文主要介绍了String类和ArrayList类的基本概念及其使用方法。 淩亂°似流年/ 2024年04月26日 02:42/ 0 赞/ 83 阅读
相关 【Java|基础篇】File类和IO流 文章目录 1.File类 2.流的概念 3.InputStream 4.OutputStream 5.Reader 6.Writer ゝ一纸荒年。/ 2023年10月14日 09:32/ 0 赞/ 52 阅读
还没有评论,来说两句吧...