2010-02-18 41 views
34

Hết sức tò mò,Tại sao phương thức finalize() trong java.lang.Object được bảo vệ?

Tại sao sửa đổi truy cập của phương thức finalize() được thực hiện là protected. Tại sao nó không thể là public? Ai đó có thể giải thích cho tôi bất kỳ lý do cụ thể nào đằng sau điều này?

Ngoài ra, tôi đã biết rằng phương thức finalize() chỉ được gọi một lần. Nếu tôi gọi nó hai lần trong chương trình của tôi trong nội bộ, điều gì đang xảy ra? Bộ thu gom rác sẽ gọi lại lần nữa?

private void dummyCall() { 
    try { 
     finalize(); 
     finalize(); 
    } catch (Throwable e) { 
     e.printStackTrace();//NOT REACHES EXCEPTION 
    } 
} 
+2

tại sao bạn sẽ viết một phương thức để gọi finalize(). finalize() được gọi tại sự phá hủy dụ bởi JVM. bạn không nên gọi nó. Bạn có thể ghi đè lên nó mặc dù, trong trường hợp bạn muốn hành vi đặc biệt tại thời điểm phá hủy ... – harschware

+0

Có. Cá nhân tôi sẽ không bao giờ làm điều đó. Nhưng tôi hỏi nó ra khỏi tò mò vì tôi đọc GC sẽ không gọi nó nếu nó đã được gọi trên một đối tượng. Ai đang theo dõi liệu phương thức finalize() này có được gọi hay không là những gì tôi nghi ngờ. Tuy nhiên, tôi chưa có một lời giải thích thuyết phục nào cho câu hỏi thứ hai này. – bragboy

+1

Câu hỏi thực sự thú vị là: tại sao * hoàn thành() * xuất hiện ở phần trên cùng của hệ thống phân cấp Java OO? Dự án 200K LOC ở đây và chúng tôi đã không ghi đè * hoàn thành() * một lần duy nhất. Một số thậm chí sẽ nói rằng đó là một mùi mã để ghi đè * finalize() * và một số dị giáo sẽ đi xa như nói rằng * finalize() * không tồn tại ở cấp OOA/OOD và rằng nó hiện diện ở đầu Java phân cấp là một (tách) Java idiosynchrasy (và hoàn toàn không liên quan đến không gian vấn đề của bạn).Khá một số câu trả lời upvoted ở đây btw xem xét * finalize() * để bị hỏng/thiếu sót trong cách này hay cách khác :) – SyntaxT3rr0r

Trả lời

23

tôi trả lời câu hỏi của bạn có một câu hỏi khác:

Tại sao phương pháp finalize không cần được bảo vệ?

Nói chung, bạn nên cố gắng giữ mọi thứ càng riêng tư càng tốt. Đó là những gì đóng gói là tất cả về. Nếu không, bạn có thể tạo mọi thứpublic. finalize không thể là private (vì các lớp dẫn xuất sẽ có thể truy cập để có thể ghi đè), vì vậy ít nhất phải là protected nhưng tại sao lại cấp quyền truy cập nhiều hơn khi không mong muốn?


Sau khi đọc nhận xét của bạn cẩn thận hơn, tôi đoán tôi sẽ thấy điểm chính của bạn ngay bây giờ. Tôi nghĩ điểm của bạn là vì mọi thứ xuất phát từ java.lang.Object và do đó truy cập vào các thành viên protected của mình, nó sẽ không tạo ra bất kỳ sự khác biệt nào (hoặc bất kỳ phương pháp nào trong số java.lang.Object cho vấn đề đó) là public thay vì protected. Cá nhân, tôi sẽ đếm điều này như là một lỗ hổng thiết kế trong Java. Điều này thực sự cố định trong C#. Vấn đề không phải là lý do tại sao finalize được bảo vệ. Vậy là được rồi. Vấn đề thực sự là bạn không nên gọi các phương thức được bảo vệ trong lớp cơ sở thông qua một tham chiếu đối tượng của kiểu lớp cơ sở. Eric Lippert có một số blog entry thảo luận về lý do tại sao việc cho phép quyền truy cập như vậy vào các thành viên được bảo vệ là một ý tưởng tồi, là further elaborated on Stack Overflow in this question.

+2

"Ẩn" càng nhiều thông tin càng tốt là bản chất của sự trừu tượng hóa. – jldupont

+0

Tại sao không? Tôi có thể gọi phương thức finalize() của một cá thể từ một cá thể khác phải không? Cả hai trường hợp, ý định của tôi là như nhau. Vì vậy, tại sao tôi không thể làm điều đó? – bragboy

+0

@ Bragaadeesh: nó là một trong những trường hợp mà nền tảng có thể bảo vệ lập trình viên của mình ;-) – jldupont

3

Khám phá this link thảo luận về điều đó.

Về cơ bản, nó có ý nghĩa nhất đối với nó là private, vì nó chỉ nên được gọi bởi JVM (bộ thu gom rác). Nhưng để cho phép một lớp con gọi phương thức cha mẹ là finalize() là một phần của số finalize(), nó phải là protected.

(Chỉnh sửa - Và chỉ cần thận trọng - sử dụng phương thức finalize() thường không được khuyến khích vì không có cách nào đảm bảo rằng nó sẽ được gọi. dịp để sử dụng nó - nó chỉ là hiếm.)

20

Tại sao truy cập của phương thức finalize() công cụ sửa đổi được thực hiện như được bảo vệ. Tại sao không thể công khai?

Nó không công khai vì không được gọi bởi bất kỳ ai ngoài JVM. Tuy nhiên, nó phải được bảo vệ để nó có thể bị ghi đè bởi các lớp con cần xác định hành vi cho nó.

Nếu tôi gọi nó hai lần trong chương trình của mình, nội bộ những gì đang diễn ra?

Bạn có thể gọi nó là tất cả những gì bạn muốn, chỉ sau một phương pháp. Tuy nhiên, giống như public static void main(String [] args), nó có ý nghĩa đặc biệt đối với JVM

Trình thu gom rác có gọi lại số này nữa không?

+0

Nhưng tôi vẫn gọi đúng (nghĩa là tôi có thể gọi phương thức finalize() và khi tôi muốn bên trong đối tượng của mình mặc dù nó không tạo ra bất kỳ logic nào)? Trong trường hợp đó, họ nên thiết kế nó theo cách như vậy, tôi không nên gọi nó (từ quan điểm của lập trình viên). – bragboy

+0

Vấn đề là, bạn không nên gọi tự hoàn thiện. Không bao giờ. Đó là có cho jvm để làm sạch (nếu cần thiết) tại thời gian thu gom rác thải trong trường hợp có nguồn tài nguyên bên ngoài/bản địa mà cần phải được phát hành trong trường hợp ứng dụng không làm như vậy. Nó được bảo vệ để các lớp con của bạn có thể cung cấp một triển khai thực hiện nếu cần. .finalize() không phải là sự thay thế cho .close/.dispose hoặc các quy ước phát hành tài nguyên chung khác – nos

+0

@nos: Bạn nên gọi 'super.finalize()' khi bạn ghi đè nó. –

2

Nó không phải public (hoặc truy cập mặc định), vì nó có nghĩa là để được gọi bằng JVM trong nội bộ khi đối tượng được thu gom rác thải - đó là không nghĩa là để được gọi bằng bất cứ điều gì khác. Và nó không phải là private bởi vì nó có nghĩa là để được ghi đè và bạn không thể ghi đè lên phương pháp riêng tư.

Nếu tôi gọi nó hai lần trong chương trình của mình, nội bộ những gì đang diễn ra? Bộ thu gom rác sẽ gọi lại số này nữa không?

Có thể có, nhưng thật khó để tưởng tượng một trường hợp điều này có ý nghĩa gì - điểm finalize() là để dọn dẹp khi đối tượng bị thu gom rác. Và nó thậm chí không làm điều đó tốt, vì vậy nó thực sự là một cái gì đó bạn nên tránh hoàn toàn hơn là thử nghiệm với.

1

finalize() chỉ được JVM sử dụng để dọn sạch tài nguyên khi đối tượng được thu thập. Đó là hợp lý cho một lớp học để xác định những hành động nên được thực hiện trên bộ sưu tập, mà nó có thể cần phải truy cập super.finalize(). Nó không thực sự có ý nghĩa đối với một quá trình bên ngoài để gọi finalize(), vì một quá trình bên ngoài không có quyền kiểm soát khi đối tượng được thu thập.

+0

Điều đó nói rằng, điều gì sẽ là câu trả lời cho câu hỏi thứ hai của tôi. Nếu tôi gọi finalize() hai lần, điều gì sẽ xảy ra? – bragboy

+0

Nếu phương thức hoàn thành được triển khai đúng cách, nó sẽ không gây hại gì khi gọi nó hai lần. Object.finalize không có gì, nada, zip theo mặc định trừ khi bạn đã ghi đè nó hoặc lấy được từ một lớp không ghi đè lên nó. Nếu bạn gọi finalize trên một cái gì đó mà không ghi đè lên nó, bạn có thể giải phóng tài nguyên của nó trước khi bạn nên, và các hoạt động tiếp theo trên đối tượng đó có thể thất bại theo nhiều cách khác nhau. – nos

+0

'Nếu tôi gọi finalize() hai lần, điều gì sẽ xảy ra?' Tại sao bạn làm điều đó? Bạn không được phép gọi nó ngay cả một lần. Câu hỏi không nảy sinh. – EJP

1

Ngoài ra, tôi đã biết rằng phương thức hoàn thiện() chỉ được gọi một lần. Nếu tôi gọi nó hai lần trong chương trình của tôi, nội bộ điều gì đang xảy ra?

Bạn có thể hỏi điều này dưới dạng hiển thị của C++ ~ destructors. Trong java finalize() phương pháp không làm bất kỳ phép thuật (như thanh toán bù trừ bộ nhớ). Nó được gọi là thu gom rác. Nhưng không phải ngược lại.

Tôi khuyên bạn nên đọc chương tương ứng trong "Java hiệu quả" của Joshua Bloch. Nó nói rằng sử dụng finalizers là một thực hành xấu và có thể gây ra hiệu suất và các vấn đề khác, và chỉ có một số trường hợp khi chúng nên được sử dụng. Chương bắt đầu bằng các từ tiếp theo:

Các trình hoàn thành không thể dự đoán được, thường là nguy hiểm và thường không cần thiết.

12
  • Finalize có nghĩa là để được gọi bằng các gc chỉ và như vậy không yêu cầu truy cập công cộng
  • Finalize được đảm bảo để được gọi là một lần duy nhất bởi gc, gọi đó là mình sẽ phá vỡ đảm bảo này, như gc wont biết về nó.
  • Bất kỳ lớp ghi đè nào có thể kết thúc công khai, điều mà tôi cho là không tốt vì các lý do trên đây
  • hoàn thành không được chứa nhiều mã, vì bất kỳ trường hợp ngoại lệ nào được hoàn thành đều có thể hủy chuỗi kết thúc của gc.

Rant chống Finalize()

  • Quản lý tài nguyên tự nhiên hoặc bất kỳ tài nguyên mà đòi hỏi phải xử lý() hoặc close() được gọi là có thể gây ra khó có thể tìm thấy lỗi vì chúng sẽ chỉ được phát hành khi JVM hết bộ nhớ, bạn nên giải phóng tài nguyên theo cách thủ công. Việc hoàn thiện chỉ nên được sử dụng để gỡ lỗi rò rỉ tài nguyên hoặc trong trường hợp quản lý tài nguyên theo cách thủ công là quá nhiều công việc.
  • hoàn thành sẽ được gọi trong một chuỗi bổ sung của gc và có thể gây ra sự cố với khóa tài nguyên, v.v.
  • các lớp tham chiếu như WeakReference và ReferenceQueue là một cách thay thế (khá phức tạp) để xử lý việc dọn dẹp và có thể có cùng các vấn đề như hoàn thành() cho tài nguyên gốc.

Cảnh giác với những sai sót trong báo cáo trên, tôi là một chút mệt mỏi :-)

+1

Tôi nghi ngờ rằng các nhà thiết kế Java đã quá lạc quan về tiện ích của 'Finalize'; họ đã không được, một cái gì đó tương tự như AutoCloseable sẽ là một phần của Java 1.0. Nếu ngôn ngữ cung cấp phương tiện phân biệt quyền sở hữu tài nguyên và tài nguyên chỉ xác định tài nguyên thuộc sở hữu của người khác, tôi cho rằng 99% dọn dẹp tài nguyên có thể tự động xác định, vì 99% thực thể cần dọn dẹp, mọi lúc trong cuộc sống của họ, có chính xác một chủ sở hữu. Không phải lúc nào cũng là chủ nhân, nhưng không bao giờ bằng không và không bao giờ là hai. – supercat

+0

Trong khi có một vài trường hợp có ý nghĩa đối với các thực thể yêu cầu dọn dẹp nhưng không có chủ sở hữu rõ ràng, hầu hết các đối tượng đóng gói trạng thái có thể thay đổi phải có quyền sở hữu rõ ràng (có yêu cầu dọn dẹp) hay không và hầu hết các đối tượng yêu cầu dọn dẹp gói gọn trạng thái có thể thay đổi. Biết khi nào tham chiếu cuối cùng tới một đối tượng nằm ngoài phạm vi là khó; biết khi nào một chủ sở hữu duy nhất của một đối tượng được thực hiện với nó thật dễ dàng. Điểm sau là khi đối tượng cần được dọn dẹp, * bất kể những tham chiếu khác có thể tồn tại *. – supercat

3

Phần về finalize() được gọi là một lần duy nhất chỉ áp dụng cho các cuộc gọi từ các GC. Bạn có thể tưởng tượng đối tượng là có một cờ ẩn "finalize() được gọi bởi GC", và GC kiểm tra cờ đó để biết phải làm gì với đối tượng. Cờ không bị ảnh hưởng theo bất kỳ cách nào bằng các cuộc gọi thủ công của riêng bạn tới finalize().

Khi hoàn tất, hãy đọc this article từ Hans Boehm (người nổi tiếng với công việc của mình về thu gom rác). Đây là một mở mắt về việc hoàn thiện; đặc biệt, Boehm giải thích lý do tại sao việc hoàn tất nhất thiết là không đồng bộ. Một hệ quả là trong khi hoàn thành là một công cụ mạnh mẽ, nó rất hiếm khi là công cụ thích hợp cho một công việc nhất định.

1

Tôi nghĩ lý do tại sao finalize được bảo vệ sẽ có thể bị ghi đè bởi một số lớp trong JDK và các phương thức ghi đè này được JVM gọi.

Các vấn đề liên quan