另一篇:Java异常简单介绍
资源不能被垃圾回收
除了堆,元数据区也可以被垃圾回收
为了正常关闭程序中打开的物理资源,应该使用finally块来保证回收。
错误写法1:资源可能初始化失败,关闭资源时未判空
public class IODemo {
public static void main(String[] args) throws Exception {
PersonVO personVO = new PersonVO();
ObjectInputStream objectInputStream = null;
try {
// 定义输入流,将资源加载到内存中
// 资源初始化可能会失败
objectInputStream = new ObjectInputStream(new FileInputStream("a.bin"));
// 反序列化将加载到内存的资源形成对象
personVO = (PersonVO)objectInputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
// 处理异常...
} finally {
// 若资源初始化失败,这里会造成空指针异常
objectInputStream.close();
}
}
}
错误写法2:关闭多个资源时,有资源关闭不正常时,影响剩下资源的关闭
public class IODemo {
public static void main(String[] args) throws Exception {
PersonVO personVO = new PersonVO();
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
// 定义输入流,将资源加载到内存中
// 资源初始化可能会失败
objectInputStream = new ObjectInputStream(new FileInputStream("a.bin"));
objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.bin"));
// 反序列化将加载到内存的资源形成对象
personVO = (PersonVO)objectInputStream.readObject();
// 序列化输出对象到资源中
objectOutputStream.writeObject(personVO);
// 调用flush()方法的目的是强制将缓冲区中的所有数据立即发送到目标设备,并清空缓冲区。这对于实时性要求较高的场景尤其重要
// flush在计算机编程中,特别是在输入/输出(I/O)操作中,是一个非常重要的概念。当程序向输出流(如文件流、标准输出流或网络流)写入数据时,
// 这些数据通常会被缓冲在内存中,而不是立即写入到最终的目的地(如硬盘、屏幕或远程服务器)。这样做可以提高性能,因为批量写入比逐字节写入效率更高。
objectOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
// 处理异常...
} finally {
if (objectInputStream != null) {
objectInputStream.close();
}
if (objectOutputStream != null) {
objectOutputStream.close();
}
}
}
}
另:flush()的作用
正确写法
public class IODemo {
public static void main(String[] args) throws Exception {
PersonVO personVO = new PersonVO();
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
// 定义输入流,将资源加载到内存中
// 资源初始化可能会失败
objectInputStream = new ObjectInputStream(new FileInputStream("a.bin"));
objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.bin"));
// 反序列化将加载到内存的资源形成对象
personVO = (PersonVO)objectInputStream.readObject();
// 序列化输出对象到资源中
objectOutputStream.writeObject(personVO);
// 调用flush()方法的目的是强制将缓冲区中的所有数据立即发送到目标设备,并清空缓冲区。这对于实时性要求较高的场景尤其重要
// flush在计算机编程中,特别是在输入/输出(I/O)操作中,是一个非常重要的概念。当程序向输出流(如文件流、标准输出流或网络流)写入数据时,
// 这些数据通常会被缓冲在内存中,而不是立即写入到最终的目的地(如硬盘、屏幕或远程服务器)。这样做可以提高性能,因为批量写入比逐字节写入效率更高。
objectOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
// 处理异常...
} finally {
if (objectInputStream != null) {
try {
objectInputStream.close();
} catch (Exception e) {
e.printStackTrace();
// 处理close()方法抛出的异常...
// 可以选择重试或者不处理
}
}
if (objectOutputStream != null) {
try {
objectOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
// 处理close()方法抛出的异常...
// 可以选择重试或者不处理
}
}
}
}
}
try-with-resources语句是从Java 7版本开始引入的一种资源管理机制。它旨在自动关闭那些实现了AutoCloseable接口的资源对象(例如文件流、数据库连接等),以避免资源泄露。
传统的手动资源管理通常需要在finally块中关闭资源,如下所示:
InputStream is = null;
try {
is = new FileInputStream("file.txt");
// 使用is...
} catch (IOException e) {
// 处理异常...
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// 处理close()方法抛出的异常...
}
}
}
而使用try-with-resources语句后,可以简化代码并确保资源始终会被正确关闭:
try (InputStream is = new FileInputStream("file.txt")) {
// 使用is...
} catch (IOException e) {
// 处理异常...
}
在这个例子中,当离开try块时,无论是正常结束还是因为异常退出,Java都会自动调用InputStream对象的close()方法来关闭文件流。如果close()方法抛出了异常,这个异常会与原始的try块中可能发生的任何其他异常进行合并或者替代(取决于具体实现和异常处理策略)。
将之前的finally块中关闭资源改造成 try-with-resources语句如下:
public class IODemo1 {
public static void main(String[] args) throws Exception {
PersonVO personVO = new PersonVO();
try (
// 定义输入流,将资源加载到内存中
// 资源初始化可能会失败
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("a.bin"));
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.bin"));
) {
// 反序列化将加载到内存的资源形成对象
personVO = (PersonVO) objectInputStream.readObject();
// 序列化输出对象到资源中
objectOutputStream.writeObject(personVO);
// 调用flush()方法的目的是强制将缓冲区中的所有数据立即发送到目标设备,并清空缓冲区。这对于实时性要求较高的场景尤其重要
// flush在计算机编程中,特别是在输入/输出(I/O)操作中,是一个非常重要的概念。当程序向输出流(如文件流、标准输出流或网络流)写入数据时,
// 这些数据通常会被缓冲在内存中,而不是立即写入到最终的目的地(如硬盘、屏幕或远程服务器)。这样做可以提高性能,因为批量写入比逐字节写入效率更高。
objectOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
// 处理异常...
}
}
}
public class IODemo1 {
public IODemo1() {
}
public static void main(String[] args) throws Exception {
new PersonVO();
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("a.bin"));
Throwable var3 = null;
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("a.bin"));
Throwable var5 = null;
try {
PersonVO personVO = (PersonVO)objectInputStream.readObject();
objectOutputStream.writeObject(personVO);
objectOutputStream.flush();
} catch (Throwable var30) {
var5 = var30;
throw var30;
} finally {
if (objectOutputStream != null) {
if (var5 != null) {
try {
objectOutputStream.close();
} catch (Throwable var29) {
var5.addSuppressed(var29);
}
} else {
objectOutputStream.close();
}
}
}
} catch (Throwable var32) {
var3 = var32;
throw var32;
} finally {
if (objectInputStream != null) {
if (var3 != null) {
try {
objectInputStream.close();
} catch (Throwable var28) {
var3.addSuppressed(var28);
}
} else {
objectInputStream.close();
}
}
}
} catch (Exception var34) {
var34.printStackTrace();
}
}
}
– 不过两者还是有区别的
我们写的代码若未使用try-with-resources语句,在finally中关闭资源,我们可以单独为每个资源关闭不成功后单独定制处理策略,不过一般也就是trycatch不做其他任何处理,也不会重试,这样的话,其实也是没区别的
– 我们日常开发中使用try-with-resources语句完全ok,建议使用
在catch块中递归调用
public class TryCatchDemo4 {
public static void main(String[] args) {
test();
}
public static void test() {
try {
Class.forName("com.haha.Stu");
} catch (Exception e) {
test();
}
}
}
打印
在finally块中递归调用也一样
public class TryCatchDemo4 {
private static final int RETRY_MAX = 3;
public static void main(String[] args) {
LongAdder longAdder = new LongAdder();
test(longAdder);
}
public static void test(LongAdder longAdder) {
try {
longAdder.increment();
if (longAdder.intValue() > RETRY_MAX) {
System.out.println("已经超过最大重试次数" + RETRY_MAX + "了,不能再重试了");
return;
}
System.out.println("第" + longAdder.intValue() + "次重试");
Class.forName("com.haha.Stu");
} catch (ClassNotFoundException e) {
test(longAdder);
}
}
}
执行打印