🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏: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类的对象
- 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提供的判断文件类型、获取文件信息功能
具体应用
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():判断当前文件对象,对应的文件路径是否存在,存在返回trueSystem.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());}
}
常用方法2:创建文件、删除文件
File类创建文件的功能
File类删除文件的功能
注意: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类提供的遍历文件夹的功能
使用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));}
}
前置知识:方法递归
认识递归的形式
什么是方法递归 ?
- 递归是一种算法,在程序设计语言中广泛应用
- 从形式上看,方法调用自身的形式称为方法递归(recursion)
递归的形式
- 直接递归:方法自己调用自己
- 间接递归:方法调用其他方法,其他方法又回调方法自己
使用方法递归时需要注意的问题:
递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误
应用、执行流程、算法思想
案例导学-计算 n 的阶乘
需求:计算 n 的阶乘,例如 5 的阶乘 = 1 * 2 * 3 * 4 * 5
分析
- 假如我们认为存在一个公式是 f(n) = 1 * 2 * 3 * … * (n-1) * n
- 那么公式等价形式就是:f(n) = f(n-1) * n
- 如果求的是 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;}}
}
递归算法三要素:
- 递归的公式:
f(n) = f(n-1) * n
- 递归的终结点:f(1)
- 递归的方向必须走向终结点
案例-猴子吃桃问题
猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个;第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个;以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个;等到第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;*/
案例:文件搜索
案例:文件搜索
需求:从 D 盘中,搜索 “QQ.exe” 这个文件,找到后直接输出其位置
分析
- 先找出 D 盘下的所有一级文件对象
- 遍历全部一级文件对象,判断是否是文件
- 如果是文件,判断是否是自己想要的
- 如果是文件夹,需要继续进入到该文件夹,重复上述过程
代码实现
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());// 拓展:启动QQRuntime runtime = Runtime.getRuntime();runtime.exec(f.getAbsolutePath());}}else {// 是文件夹,继续重复这个过程(递归)searchFile(f, fileName);}}}}
}
案例:删除非空文件夹
需求
删除非空文件夹
分析
- File 默认不可以直接删除非空文件夹
- 所有我们需要遍历文件夹,先删除里面的内容,再删除自己
代码实现
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() + "删除成功!");}
}
执行结果
案例:啤酒问题
需求
啤酒 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);}}
}
执行结果
前置知识:字符集
常见字符集介绍
标准ASCII字符集
- ASCII(American Standard Code for Information Interchange):美国信息交换标准代码,包括了英文、符号等
- 标准 ASCII 使用 1 个字节存储一个字符,首尾是 0,总共可表示 128 个字符
GBK字符集(汉字内码扩展规范,国标)
- 汉字编码字符集,包含了 2 万多个汉字等字符,GBK 中一个中文字符编码成两个字节的形式存储
- 注意:GBK 兼容了 ASCII 字符集
- GBK 规定:汉字的第一个字节的第一位必须是 1
Unicode字符集(统一码,也叫万国码)
Unicode 是国际组织规定的,可以容纳世界上所有文字、符号的字符集
UTF-8字符集
-
是 Unicode 字符集的一种编码方案,采取可变长编码方案,共分为四个长度区:1 个字节、2 个字节、3 个字节、4 个字节
-
英文字符、数字等只占 1 个字节(兼容标准 ASCII 编码),汉字字符占用 3 个字节
-
注意:技术人员在开发时都应该使用 UTF-8 编码!
总结
- ASCII 字符集:只有英文、数字、符号等,占 1 个字节
- GBK 字符集:汉字占 2 个字节,英文、数字占 1 个字节
- UTF-8 字符集:汉字占 3 个字节,英文、数字占 1 个字节
- 字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码
- 英文、数字一般不会乱码,因为很多字符集都兼容了 ASCII 编码
字符集的编码、解码操作
什么是编码 ?
把字符按照指定的字符集编码成字节
什么是解码 ?
把字节按照指定的字符集编码解码成字符
Java 代码完成对字符的编码
Java 代码完成对字符的解码
具体应用
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);}
}
IO流
什么是IO流 ?
输入输出流,用来读写数据的
如何学习IO流 ?
- 先搞清楚 IO 流的分类、体系
- 再挨个学习每个 IO 流的作用、用法
IO流的分类
IO 流总体来看就有四大流:字节输入流、字节输出流、字符输入流、字符输出流
- 字节输入流 InputStream:以内存为基准,来自磁盘文件 / 网络中的数据以字节的形式读入到内存中去的流
- 字节输出流 OutputStream:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流
- 字符输入流 Reader:以内存为基准,来自磁盘文件 / 网络中的数据以字符的形式读入到内存中去的流
- 字符输出流 Writer:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络中去的流
IO流-字节流
文件字节输入流:每次读取一个字节
IO流的体系
FileInputStream(文件字节输入流)
作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去
注意事项
使用 FileInputStream 每次读取一个字节,读取性能较差,并且读取汉字输出会乱码
具体应用
准备一个测试文本文件:
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():每次读取一个字节返回,如果没有数据了,返回-1int b; // 用于记住读取的字节while ((b = is.read()) != -1){System.out.print((char) b);}// 读取数据的性能很差!// 读取汉字输出会乱码!!无法避免的!!// 流使用完毕之后,必须关闭!释放系统资源!is.close();}
}
文件字节输入流:每次读取多个字节
FileInputStream(文件字节输入流)
作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去
注意事项
使用 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// 每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1byte[] 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(); // 关闭流}
}
文件字节输入流:一次读取完全部字节
方式一:自己定义一个字节数组与被读取的文件大小一样大,然后使用该字节数组,一次读完文件的全部字节
方式二:Java 官方为 InputStream 提供了如下方法,可以直接把文件的全部字节读取到一个字节数组中返回
直接把文件数据全部读取到一个字节数组可以避免乱码,是否还存在问题 ?
- 如果文件过大,创建的字节数组也会过大,就可能引起内存溢出的问题
读写文本内容更适合用字符流,字节流适合做数据的转移,如:文件复制等
具体应用
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();}
}
文件字节输出流:写字节出去
FileOutputStream(文件字节输出流)
作用:以内存为基准,把内存中的数据以字节的形式写出到文件中
具体应用
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就是一个字节,代表aos.write('b'); // 'b'也是一个字节byte[] bytes = "我爱你中国abc".getBytes();os.write(bytes);os.write("\n".getBytes()); // 换行符os.write(bytes, 0, 15); // 一个汉字占3个字节os.close(); // 关闭流}
}
案例:文件复制
字节流非常适合做一切文件的复制操作。任何文件的底层都是字节,字节流做复制,是一字不漏地转移完全部字节,只要复制后的文件格式一致就没问题
具体应用
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("复制完成!!");}
}
释放资源的方式
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{@Overridepublic 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();}}
}