在面向对象编程中,构造函数和析构函数是用来创建和销毁对象的特殊方法。在Java中,构造函数和析构函数被称为构造器和终结器,分别用于对象的初始化和销毁。
PS:构造函数在Java_2介绍过 这里就不介绍了。
构造器是一种特殊的方法,用于在对象创建时初始化该对象的状态。在Java中,构造器的名称必须与类名相同,它没有返回类型,也不需要显示声明返回类型。在创建对象时,会自动调用与该类名称相同的构造器,并在构造器中执行必要的初始化操作。如果一个类没有定义构造器,则会默认提供一个无参构造器。如果需要定义有参构造器,需要在类中显式声明。
析构函数则是一种在对象销毁时自动被调用的特殊方法。在Java中,由于Java具有自动垃圾回收机制,因此不需要显式地调用析构函数来销毁对象。当对象不再被引用时,Java虚拟机会自动回收该对象所占用的内存空间。
需要注意的是,在Java中并没有直接的析构函数。相反,Java提供了finalize()方法来实现类似于析构函数的功能。在Java中,如果定义了finalize()方法,当对象被垃圾回收时,该方法会自动被调用。
finalize()
方法是一个受保护的方法,它在Object
类中定义,子类可以重写该方法以实现自己的清理逻辑。finalize()
方法的声明如下:
需要注意的是,finalize()
方法只在垃圾回收器进行对象回收之前被调用一次,因此不能保证该方法被及时执行或者执行一次。此外,由于Java虚拟机的垃圾回收机制是不确定的,所以应该尽量避免在finalize()
方法中编写过多的代码,以免影响程序的性能。
在Java 9中,finalize()
方法已经被弃用,取而代之的是Cleaner
类,可以通过Cleaner
类中的clean()
方法实现类似于finalize()
方法的清理逻辑。
Cleaner类是Java 9引入的一个新类,用于替代Java 8中已经废弃的finalize()方法。Cleaner类的主要作用是在Java对象不再被引用时执行一些清理操作,从而避免了垃圾回收机制的不确定性。
Cleaner类中有一个clean()方法,它的作用是执行清理操作。当Java对象不再被引用时,垃圾回收器会将它放入一个待清理队列中。然后,在某个时刻,Cleaner线程会扫描这个队列,执行队列中所有对象的clean()方法。
在Cleaner类中,使用了一个“幽灵引用”(PhantomReference)来实现对象的清理。幽灵引用是一种特殊的引用,它的作用是在对象被垃圾回收器回收时得到通知,但是在这个时刻之前,无法通过幽灵引用访问到这个对象。当一个对象被垃圾回收器回收时,它的幽灵引用会被放入待清理队列中,然后在Cleaner线程中执行清理操作。
假设我们在编写一个处理文件的程序,我们需要在文件处理完毕后关闭文件句柄。但是由于一些原因(例如程序异常终止),可能会导致文件句柄没有被关闭,从而导致资源泄漏。为了解决这个问题,我们可以使用Cleaner
类来注册一个回调函数,在Cleaner
对象被回收时自动执行关闭文件句柄的操作。
举一个复杂的例子:(注释给了,看不懂没关系,就是要多看例子才会写)
import java.io.File; //引入File类,用于操作文件。
import java.io.FileInputStream; //引入FileInputStream类,用于读取文件。
import java.io.IOException; //引入IOException异常,用于处理文件读取异常。
import java.lang.ref.Cleaner; //引入Cleaner类,用于管理和执行清理操作。
public class FileHandler implements Runnable { //定义FileHandler类,实现Runnable接口,用于处理文件相关逻辑。
private final File file; //定义file变量,表示要处理的文件。
private final FileInputStream fis; //定义fis变量,表示要读取的文件流。
private final Cleaner cleaner; //定义cleaner变量,用于管理和执行清理操作。
public FileHandler(File file) throws IOException { //定义FileHandler的构造函数,传入要处理的文件,并抛出IOException异常。
this.file = file; //将传入的文件赋值给file变量。
fis = new FileInputStream(file); //创建一个新的FileInputStream对象,用于用于读取文件。
cleaner = Cleaner.create(); //创建一个Cleaner对象,用于管理和执行清理操作。
cleaner.register(this, new CloseHandler(fis));
//册一个清理操作,当FileHandler对象被回收时,会自动调用CloseHandler的run()方法,关闭fis文件流。
}
@Override //重写run()方法。
public void run() { //run()方法实现,用于处理文件相关逻辑。
// TODO: 处理文件逻辑
}
private static class CloseHandler implements Runnable {
//定义CloseHandler内部类,实现Runnable接口,用于关闭文件流。
private final FileInputStream fis; //定义fis变量,表示要关闭的文件流.
public CloseHandler(FileInputStream fis) {//定义CloseHandler的构造函数,传入要关闭的文件流。
this.fis = fis; //将传入的文件流赋值给fis变量
}
@Override //重写run()方法
public void run() { //run()方法实现,用于关闭文件流。
try { //开始try块。
fis.close(); //关闭文件流。
System.out.println("File " + fis.getFD() + " has been closed"); //输出文件流已经被关闭的消息。
} catch (IOException e) { //捕获IOException异常。
try { //开始try块。
System.err.println("Failed to close file " + fis.getFD()); //输出文件关闭失败的消息。
} catch (IOException ex) { //捕获IOException异常。
throw new RuntimeException(ex); //抛出异常
}
} //try-catch块结束
} //run()方法结束。
} //CloseHandler内部类结束。
} //FileHandler类结束。
因为Cleaner类是Java 9引入的,所以在早期版本的Java中无法使用。在早期版本中,可以使用finalize()方法来执行清理操作,但是这种方式已经被废弃,不建议使用。
总之,构造函数和析构函数是用于创建和销毁对象的特殊方法,可以在对象创建和销毁时执行必要的操作。在Java中,构造函数和析构函数被称为构造器和终结器,分别用于对象的初始化和销毁。