Theo kinh nghiệm của tôi, có một và chỉ một lý do để ghi đè Object.finalize()
, nhưng đó là một lý do rất chính đáng :
Để đặt mã đăng nhập lỗi trong finalize()
đó thông báo cho bạn nếu bạn quên gọi close()
.
Phân tích tĩnh chỉ có thể bắt gặp thiếu sót trong các tình huống sử dụng tầm thường và các cảnh báo của trình biên dịch được đề cập trong câu trả lời khác có một cái nhìn đơn giản như vậy về những điều mà bạn thực sự phải vô hiệu hóa chúng để thực hiện mọi thứ không tầm thường. (Tôi có nhiều cảnh báo được kích hoạt hơn bất kỳ lập trình viên nào khác mà tôi biết hoặc đã từng nghe nói, nhưng tôi không bật cảnh báo ngu ngốc.)
Hoàn thiện dường như là một cơ chế tốt để đảm bảo rằng các tài nguyên không bị ảnh hưởng, nhưng hầu hết mọi người nhìn nhận nó theo một cách hoàn toàn sai: họ nghĩ về nó như một cơ chế dự phòng thay thế, một "cơ hội thứ hai" sẽ tự động cứu ngày bằng cách xử lý các tài nguyên mà họ quên. Đây là sai lầm chết người . Chỉ có một cách để thực hiện bất kỳ điều gì đã cho: hoặc bạn luôn đóng mọi thứ hoặc hoàn thành luôn đóng mọi thứ. Nhưng vì quyết toán là không đáng tin cậy, quyết toán có thể là nó.
Vì vậy, có sơ đồ này mà tôi gọi là Xử lý bắt buộc và nó quy định rằng lập trình viên có trách nhiệm luôn luôn đóng một cách rõ ràng mọi thứ thực hiện Closeable
hoặc AutoCloseable
. (Câu lệnh try-with-resource vẫn được tính là đóng rõ ràng.) Tất nhiên, lập trình viên có thể quên, vì vậy đó là nơi hoàn thành trò chơi, nhưng không phải là một nàng tiên ma thuật cuối cùng sẽ làm mọi thứ trở nên kỳ diệu: Nếu cuối cùng phát hiện ra Điều đó close()
không được viện dẫn, nó khôngcố gắng gọi nó, chính xác bởi vì sẽ có (với sự chắc chắn về mặt toán học) sẽ có một nhóm lập trình viên n00b, những người sẽ dựa vào nó để làm công việc mà họ quá lười biếng hoặc quá lơ đãng để làm. Vì vậy, với việc xử lý bắt buộc, khi hoàn tất phát hiện ra mà close()
không được gọi, nó sẽ ghi một thông báo lỗi màu đỏ sáng, nói với lập trình viên bằng các chữ cái viết hoa lớn để sửa lỗi của mình.
Như một lợi ích bổ sung, có tin đồn rằng "JVM sẽ bỏ qua một phương thức quyết định () không quan trọng (ví dụ như phương thức trả về mà không làm gì cả, như phương thức được định nghĩa trong lớp Object)", vì vậy, với việc xử lý bắt buộc, bạn có thể tránh tất cả quyết toán chi phí trong toàn bộ hệ thống của bạn ( xem câu trả lời của alip để biết thông tin về mức chi phí khủng khiếp này) bằng cách mã hóa finalize()
phương pháp của bạn như thế này:
@Override
protected void finalize() throws Throwable
{
if( Global.DEBUG && !closed )
{
Log.Error( "FORGOT TO CLOSE THIS!" );
}
//super.finalize(); see alip's comment on why this should not be invoked.
}
Ý tưởng đằng sau đó Global.DEBUG
là một static final
biến có giá trị được biết đến tại thời điểm biên dịch, vì vậy nếu đó là false
trình biên dịch sẽ không phát ra bất kỳ mã nào cho toàn bộ if
câu lệnh, điều này sẽ biến nó thành một bộ hoàn thiện tầm thường (trống) có nghĩa là lớp của bạn sẽ được đối xử như thể nó không có bộ hoàn thiện. (Trong C #, điều này sẽ được thực hiện với một #if DEBUG
khối đẹp , nhưng chúng ta có thể làm gì, đây là java, nơi chúng ta trả tiền đơn giản rõ ràng trong mã với chi phí bổ sung trong não.)
Tìm hiểu thêm về Xử lý bắt buộc, với thảo luận bổ sung về việc xử lý tài nguyên trong dot Net, tại đây: michael.gr: Xử lý bắt buộc so với việc ghê tởm "vứt bỏ"
finalize()
hơi bị rối. Nếu bạn từng thực hiện nó, hãy đảm bảo rằng nó an toàn cho tất cả các phương thức khác trên cùng một đối tượng.