Có thể xây dựng một đoạn mã trong Java để làm cho một giả thuyết java.lang.ChuckNorrisException
không thể so sánh được không?
Những suy nghĩ nảy ra trong đầu đang sử dụng ví dụ đánh chặn hoặc lập trình hướng theo khía cạnh .
finalize()
?
Có thể xây dựng một đoạn mã trong Java để làm cho một giả thuyết java.lang.ChuckNorrisException
không thể so sánh được không?
Những suy nghĩ nảy ra trong đầu đang sử dụng ví dụ đánh chặn hoặc lập trình hướng theo khía cạnh .
finalize()
?
Câu trả lời:
Tôi đã không cố gắng này, vì vậy tôi không biết nếu JVM sẽ hạn chế một cái gì đó như thế này, nhưng có lẽ bạn có thể biên dịch mã mà ném ChuckNorrisException
, nhưng trong thời gian chạy cung cấp một định nghĩa lớp ChuckNorrisException
mà không mở rộng Throwable .
CẬP NHẬT:
Nó không hoạt động. Nó tạo ra một lỗi xác minh:
Exception in thread "main" java.lang.VerifyError: (class: TestThrow, method: ma\
in signature: ([Ljava/lang/String;)V) Can only throw Throwable objects
Could not find the main class: TestThrow. Program will exit.
CẬP NHẬT 2:
Trên thực tế, bạn có thể làm cho nó hoạt động nếu bạn vô hiệu hóa trình xác minh mã byte! ( -Xverify:none
)
CẬP NHẬT 3:
Đối với những người theo dõi từ nhà, đây là kịch bản đầy đủ:
Tạo các lớp sau:
public class ChuckNorrisException
extends RuntimeException // <- Comment out this line on second compilation
{
public ChuckNorrisException() { }
}
public class TestVillain {
public static void main(String[] args) {
try {
throw new ChuckNorrisException();
}
catch(Throwable t) {
System.out.println("Gotcha!");
}
finally {
System.out.println("The end.");
}
}
}
Biên dịch các lớp:
javac -cp . TestVillain.java ChuckNorrisException.java
Chạy:
java -cp . TestVillain
Gotcha!
The end.
Nhận xét "mở rộng RuntimeException" và chỉ biên dịch lạiChuckNorrisException.java
:
javac -cp . ChuckNorrisException.java
Chạy:
java -cp . TestVillain
Exception in thread "main" java.lang.VerifyError: (class: TestVillain, method: main signature: ([Ljava/lang/String;)V) Can only throw Throwable objects
Could not find the main class: TestVillain. Program will exit.
Chạy mà không cần xác minh:
java -Xverify:none -cp . TestVillain
The end.
Exception in thread "main"
Object
thay vì Throwable
vậy thì sao? (Trình biên dịch sẽ không cho phép nhưng vì chúng tôi đã vô hiệu hóa trình xác minh, nên có thể người ta có thể hack mã byte để làm điều đó.)
Sau khi suy nghĩ về điều này, tôi đã tạo thành công một ngoại lệ không thể so sánh được. Tôi đã chọn đặt tên cho nó JulesWinnfield
, tuy nhiên, thay vì Chuck, bởi vì nó là một ngoại lệ của một đám mây hình nấm. Hơn nữa, nó có thể không chính xác như những gì bạn đã nghĩ, nhưng chắc chắn nó không thể bị bắt. Quan sát:
public static class JulesWinnfield extends Exception
{
JulesWinnfield()
{
System.err.println("Say 'What' again! I dare you! I double dare you!");
System.exit(25-17); // And you shall know I am the LORD
}
}
public static void main(String[] args)
{
try
{
throw new JulesWinnfield();
}
catch(JulesWinnfield jw)
{
System.out.println("There's a word for that Jules - a bum");
}
}
Et voila! Không ngoại lệ.
Đầu ra:
chạy:
Hãy nói điều đó lại một lần! Tao thách mày! Tôi thách bạn hai lần!
Kết quả Java: 8
BUILD SUCCESSFUL (tổng thời gian: 0 giây)
Khi tôi có thêm một chút thời gian, tôi sẽ xem liệu tôi có thể nghĩ ra điều gì khác không.
Ngoài ra, hãy kiểm tra điều này:
public static class JulesWinnfield extends Exception
{
JulesWinnfield() throws JulesWinnfield, VincentVega
{
throw new VincentVega();
}
}
public static class VincentVega extends Exception
{
VincentVega() throws JulesWinnfield, VincentVega
{
throw new JulesWinnfield();
}
}
public static void main(String[] args) throws VincentVega
{
try
{
throw new JulesWinnfield();
}
catch(JulesWinnfield jw)
{
}
catch(VincentVega vv)
{
}
}
Gây ra lỗi tràn ngăn xếp - một lần nữa, các ngoại lệ vẫn chưa được xử lý.
JulesWinfield
? Hệ thống sẽ không dừng lại trước khi nó bị ném?
throw new Whatever()
thực sự là hai phần: Whatever it = new Whatever(); throw it;
và hệ thống chết trước khi đến phần thứ hai.
class cn extends exception{private cn(){}}
Với một ngoại lệ như vậy rõ ràng sẽ bắt buộc phải sử dụng một System.exit(Integer.MIN_VALUE);
từ hàm tạo bởi vì đây là điều sẽ xảy ra nếu bạn ném một ngoại lệ như vậy;)
while(true){}
thay vì System.exit()
.
System.exit()
làm việc bằng cách cài đặt trình quản lý bảo mật không cho phép. điều đó sẽ biến hàm tạo thành một ngoại lệ khác (SecurityException), có thể bị bắt.
Bất kỳ mã nào cũng có thể bắt được throwable. Vì vậy, không, bất kỳ ngoại lệ nào bạn tạo sẽ là một lớp con của Ném được và sẽ bị bắt.
hang
ném chính nó trong một nỗ lực để bắt ChuckNorrisException: P
public class ChuckNorrisException extends Exception {
public ChuckNorrisException() {
System.exit(1);
}
}
(Cấp, về mặt kỹ thuật , ngoại lệ này không bao giờ thực sự bị ném, nhưng ChuckNorrisException
không thể ném đúng - nó sẽ ném bạn trước.)
Bất kỳ ngoại lệ nào bạn ném phải mở rộng Ném được, vì vậy nó luôn có thể bị bắt. Vì vậy, câu trả lời là không.
Nếu bạn muốn làm cho nó khó khăn để xử lý, bạn có thể ghi đè lên các phương pháp getCause(), getMessage()
, getStackTrace()
, toString()
để ném nhau java.lang.ChuckNorrisException
.
catch(Throwable t)
chỉ lưu trữ nó thành biến nên các đề xuất của tôi chỉ áp dụng trong khối tiếp theo khi người dùng muốn đối phó với ngoại lệ
Câu trả lời của tôi dựa trên ý tưởng của @ jtahlborn, nhưng đó là một chương trình Java hoạt động hoàn toàn , có thể được đóng gói thành tệp JAR và thậm chí được triển khai đến máy chủ ứng dụng yêu thích của bạn như một phần của ứng dụng web .
Trước hết, hãy định nghĩa ChuckNorrisException
lớp để nó không bị lỗi JVM ngay từ đầu (Chuck thực sự thích làm hỏng JVMs BTW :)
package chuck;
import java.io.PrintStream;
import java.io.PrintWriter;
public class ChuckNorrisException extends Exception {
public ChuckNorrisException() {
}
@Override
public Throwable getCause() {
return null;
}
@Override
public String getMessage() {
return toString();
}
@Override
public void printStackTrace(PrintWriter s) {
super.printStackTrace(s);
}
@Override
public void printStackTrace(PrintStream s) {
super.printStackTrace(s);
}
}
Bây giờ đi đến Expendables
lớp để xây dựng nó:
package chuck;
import javassist.*;
public class Expendables {
private static Class clz;
public static ChuckNorrisException getChuck() {
try {
if (clz == null) {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("chuck.ChuckNorrisException");
cc.setSuperclass(pool.get("java.lang.Object"));
clz = cc.toClass();
}
return (ChuckNorrisException)clz.newInstance();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
Và cuối cùng là Main
lớp để đá mông:
package chuck;
public class Main {
public void roundhouseKick() throws Exception {
throw Expendables.getChuck();
}
public void foo() {
try {
roundhouseKick();
} catch (Throwable ex) {
System.out.println("Caught " + ex.toString());
}
}
public static void main(String[] args) {
try {
System.out.println("before");
new Main().foo();
System.out.println("after");
} finally {
System.out.println("finally");
}
}
}
Biên dịch và chạy nó với lệnh sau:
java -Xverify:none -cp .:<path_to_javassist-3.9.0.GA.jar> chuck.Main
Bạn sẽ nhận được đầu ra sau:
before
finally
Không có gì ngạc nhiên - đó là một cú đá tròn
Trong hàm tạo, bạn có thể bắt đầu một chuỗi liên tục gọi originalThread.stop (ChuckNorisException.this)
Sợi chỉ có thể bắt ngoại lệ nhiều lần nhưng sẽ tiếp tục ném nó cho đến khi nó chết.
Không. Tất cả các ngoại lệ trong Java phải phân lớp java.lang.Throwable
và mặc dù nó có thể không phải là thông lệ tốt, bạn có thể nắm bắt mọi loại ngoại lệ như vậy:
try {
//Stuff
} catch ( Throwable T ){
//Doesn't matter what it was, I caught it.
}
Xem tài liệu java.lang.Throwable để biết thêm thông tin.
Nếu bạn đang cố gắng tránh các ngoại lệ được kiểm tra (những trường hợp phải được xử lý rõ ràng) thì bạn sẽ muốn phân lớp Lỗi hoặc RuntimeException.
Trên thực tế, câu trả lời được chấp nhận là không tốt vì Java cần được chạy mà không cần xác minh, tức là mã sẽ không hoạt động trong các trường hợp thông thường.
AspectJ để giải cứu cho giải pháp thực sự !
Lớp ngoại lệ:
package de.scrum_master.app;
public class ChuckNorrisException extends RuntimeException {
public ChuckNorrisException(String message) {
super(message);
}
}
Khía cạnh:
package de.scrum_master.aspect;
import de.scrum_master.app.ChuckNorrisException;
public aspect ChuckNorrisAspect {
before(ChuckNorrisException chuck) : handler(*) && args(chuck) {
System.out.println("Somebody is trying to catch Chuck Norris - LOL!");
throw chuck;
}
}
Ứng dụng mẫu:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
catchAllMethod();
}
private static void catchAllMethod() {
try {
exceptionThrowingMethod();
}
catch (Throwable t) {
System.out.println("Gotcha, " + t.getClass().getSimpleName() + "!");
}
}
private static void exceptionThrowingMethod() {
throw new ChuckNorrisException("Catch me if you can!");
}
}
Đầu ra:
Somebody is trying to catch Chuck Norris - LOL!
Exception in thread "main" de.scrum_master.app.ChuckNorrisException: Catch me if you can!
at de.scrum_master.app.Application.exceptionThrowingMethod(Application.java:18)
at de.scrum_master.app.Application.catchAllMethod(Application.java:10)
at de.scrum_master.app.Application.main(Application.java:5)
Một biến thể của chủ đề là một thực tế đáng ngạc nhiên rằng bạn có thể ném các ngoại lệ được kiểm tra không được khai báo từ mã Java. Vì nó không được khai báo trong chữ ký phương thức, trình biên dịch sẽ không cho phép bạn bắt ngoại lệ, mặc dù bạn có thể bắt nó dưới dạng java.lang.Exception.
Đây là một lớp trợ giúp cho phép bạn ném bất cứ thứ gì, tuyên bố hay không:
public class SneakyThrow {
public static RuntimeException sneak(Throwable t) {
throw SneakyThrow.<RuntimeException> throwGivenThrowable(t);
}
private static <T extends Throwable> RuntimeException throwGivenThrowable(Throwable t) throws T {
throw (T) t;
}
}
Bây giờ throw SneakyThrow.sneak(new ChuckNorrisException());
không ném ChuckNorrisException, nhưng trình biên dịch phàn nàn trong
try {
throw SneakyThrow.sneak(new ChuckNorrisException());
} catch (ChuckNorrisException e) {
}
về việc bắt một ngoại lệ không được ném nếu ChuckNorrisException là một ngoại lệ được kiểm tra.
Các ChuckNorrisException
s duy nhất trong Java phải là OutOfMemoryError
và StackOverflowError
.
Bạn thực sự có thể "bắt" chúng theo nghĩa là catch(OutOfMemoryError ex)
sẽ thực thi trong trường hợp ngoại lệ được ném, nhưng khối đó sẽ tự động lấy lại ngoại lệ cho người gọi.
Tôi không nghĩ đó public class ChuckNorrisError extends Error
là mánh khóe nhưng bạn có thể thử. Tôi không tìm thấy tài liệu nào về việc gia hạnError
Is it possible to construct a snippet of code in java that would make a hypothetical java.lang.ChuckNorrisException uncatchable?
Có, và đây là câu trả lời: Thiết kế java.lang.ChuckNorrisException
sao cho nó không phải là một ví dụ java.lang.Throwable
. Tại sao? Theo định nghĩa, một vật thể không thể phá hủy là không thể so sánh được bởi vì bạn không bao giờ có thể bắt được thứ gì đó không bao giờ có thể ném được.
java.lang.ChuckNorrisException
phải là một ngoại lệ, chứ đừng nói là có thể ném được
Bạn có thể giữ ChuckNorris nội bộ hoặc riêng tư và gói gọn anh ta hoặc theo dõi anh ta ...
try { doChuckAction(); } catch(ChuckNorrisException cne) { /*do something else*/ }
Hai vấn đề cơ bản với xử lý ngoại lệ trong Java là nó sử dụng loại ngoại lệ để cho biết liệu có nên thực hiện hành động dựa trên nó hay không và rằng bất kỳ hành động nào dựa trên một ngoại lệ (ví dụ: "bắt" nó đều được coi là để giải quyết điều kiện cơ bản. Sẽ rất hữu ích khi có một phương tiện để một đối tượng ngoại lệ có thể quyết định trình xử lý nào sẽ thực thi và liệu các trình xử lý đã thực hiện cho đến nay có làm sạch mọi thứ đủ để phương thức hiện tại đáp ứng các điều kiện thoát của nó hay không. Mặc dù điều này có thể được sử dụng để tạo ra các ngoại lệ "không thể so sánh được", hai cách sử dụng lớn hơn sẽ là (1) tạo ra các ngoại lệ sẽ chỉ được xem xét xử lý khi chúng bị bắt bởi mã thực sự biết cách xử lý chúng,finally
FooException
trong một finally
khối trong quá trình tháo gỡ của a BarException
, cả hai ngoại lệ sẽ truyền lên ngăn xếp cuộc gọi; cả hai đều có thể bắt được, nhưng việc tháo gỡ nên tiếp tục cho đến khi cả hai bị bắt). Thật không may, tôi không nghĩ sẽ có bất kỳ cách nào để làm cho mã xử lý ngoại lệ hiện có hoạt động theo cách đó mà không phá vỡ mọi thứ.
finally
khối đang dọn sạch từ một ngoại lệ trước đó, thì hoàn toàn có thể là ngoại lệ, nếu không có ngoại lệ khác, có thể là điều mà mã sẽ được xử lý và tiếp tục, nhưng xử lý cái này và bỏ qua cái kia sẽ là xấu. Tuy nhiên, không có cơ chế để tạo ra một ngoại lệ tổng hợp mà cả hai trình xử lý sẽ xử lý.
Foo
có thể phân biệt giữa một ngoại lệ Foo
tự ném hoặc cố tình muốn giả vờ, từ một điều Foo
không mong muốn xảy ra khi nó xảy ra gọi một số phương pháp khác. Đó là khái niệm về các ngoại lệ "được kiểm tra" nên là "về".
Có thể dễ dàng mô phỏng một ngoại lệ chưa được phát hiện trên luồng hiện tại. Điều này sẽ kích hoạt hành vi thường xuyên của một ngoại lệ chưa được bắt gặp, và do đó hoàn thành công việc về mặt ngữ nghĩa. Tuy nhiên, nó sẽ không nhất thiết dừng quá trình thực thi của luồng hiện tại, vì không có ngoại lệ nào thực sự bị ném.
Throwable exception = /* ... */;
Thread currentThread = Thread.currentThread();
Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
currentThread.getUncaughtExceptionHandler();
uncaughtExceptionHandler.uncaughtException(currentThread, exception);
// May be reachable, depending on the uncaught exception handler.
Điều này thực sự hữu ích trong các tình huống (rất hiếm), ví dụ khi Error
cần xử lý đúng cách , nhưng phương thức được gọi từ một khung bắt (và loại bỏ) bất kỳ Throwable
.