2012-11-08 14 views
5

Có cách giải quyết nào có thể khôi phục mã trong Java không?Java: Mã Rollback

Ví dụ tôi muốn mã dưới đây sẽ in ra i = 1 nhưng không 2: (Khi một ngoại lệ xảy ra)

int i = 1; 
try { 
    i = 2; 
    int j = 10/0; 
} catch (Exception ex) {} 
System.out.print("i = " + i); 

Tôi không nghĩ logic này là thực tế vô dụng cho lập trình viên. Và tôi không nghĩ rằng điều này là rất khó để xử lý cho trình biên dịch. Ví dụ:

Ví dụ: có thể lưu tạm thời i = 1 trong bộ nhớ và sau khi xảy ra trường hợp ngoại lệ, quay lại giá trị của nó là 1 thay vì 2. (Sẽ tốt hơn nếu chúng tôi có rollback/catch chẳng hạn)

+0

Ngoài ra, bạn sẽ không muốn bắt tất cả ngoại lệ, nhưng chỉ là những trường hợp quan tâm, chia cho số không trong trường hợp này. Ngoại lệ khác không nên bị bắt vì vậy người gọi có cơ hội để xử lý chúng (hoặc để cho họ đi qua vì vậy các ứng dụng có cơ hội để chấm dứt một cách tiêu chuẩn hóa). – TheBlastOne

+0

Đó là một ví dụ rất đơn giản mà tôi đã viết. Trong những tình huống phức tạp, chúng tôi không thể dễ dàng xử lý nó theo cách bạn nói. – Natasha

+0

Tại sao không? * Đặc biệt * trong các hệ thống phân cấp cuộc gọi phức tạp, tôi khuyên bạn nên giữ một hình ảnh rõ ràng về người bắt và bỏ qua (hoặc thông qua) cái gì. – TheBlastOne

Trả lời

1

Chắc chắn, bạn có thể làm điều đó trong khối catch của mình. bạn đã thử nó chưa Ngoài ra, không bao giờ giữ khối catch của bạn trống rỗng. Nó không bao giờ là một ý tưởng hay. Bằng cách đó, bạn thậm chí sẽ không biết liệu một ngoại lệ có xảy ra hay không.

Thay đổi catch khối của bạn như thế này: -

int i = 1; 
int old = 0; 
try { 
    old = i; 
    i = 2; 
    int j = 10/0; 
} 
catch (Exception ex) { 
    i = old; 
    e.printStackTrace(); 
} 
System.out.print("i = " + i); 

Ngoài ra, nó là một ý tưởng tốt để bắt ngoại lệ cụ thể hơn có khối catch duy nhất mà bắt tất cả các trường hợp ngoại lệ.

Trong trường hợp của bạn, bạn nên bắt ArithmeticException thay vì Exception.

Ngoài ra, việc theo dõi tất cả các thay đổi không có trong khối thử trước khi ngoại lệ của bạn là không thực tế. Nếu bạn có một số cố định của các biến, sau đó nó sẽ không được lớn đối phó. Nhưng hãy xem xét trường hợp bạn không biết khối try của bạn sẽ thao túng các biến của bạn như thế nào. Trong trường hợp đó, bạn không thể đơn giản, và sẽ không dễ dàng để khôi phục lại trạng thái trước trạng thái của bạn trước khối thử của bạn.

+3

Tôi nghĩ rằng OP muốn khôi phục trạng thái biến không tới một trạng thái cụ thể nào đó, nhưng với trạng thái trước khi ngoại lệ được ném ra. – svz

+0

@Natasha .. Bạn có muốn quay lại giá trị ban đầu trước khối 'try' không? –

+0

@svz Thậm chí sau đó sẽ tốt hơn cho một nhà phát triển để theo dõi những gì anh ta muốn quay trở lại. Nó là không thực tế - theo ý kiến ​​của tôi - cho thời gian chạy để theo dõi tất cả các thay đổi và quyết định những gì cần phải được cuộn lại và những gì không nên. Tôi đồng ý với Rohit rằng tự mình làm là giải pháp hợp lý duy nhất ở đây. –

1

Tôi đồng ý ... Đây là tính năng cơ bản của cơ sở dữ liệu, tương ứng với giao dịch. Và tất nhiên đây không phải là hành vi mặc định, nhưng được đánh dấu là giao dịch. Tuy nhiên:

hành vi có thể đạt được bằng cách sử dụng các chức năng ... và có các khối mã sử dụng các biến cục bộ để tính toán, không trả lại kết quả trong trường hợp xảy ra lỗi. sửa đổi các biến toàn cầu từ một hàm là lập trình kém. Các cấu trúc bạn muốn lưu, bạn nên tự lưu chúng

sẽ chỉ có một số trường hợp giới hạn khả thi ... hãy tưởng tượng tệp viết ... hoặc thậm chí tệ hơn, chuyển mạng ... để quay lại

Thậm chí nhiều hơn, trường hợp của bạn rất đơn giản ... nhưng chỉnh sửa hình ảnh hình ảnh hoặc mảng lớn trong vòng lặp ... bạn có thực sự muốn biên dịch sao chép "chỉ trong trường hợp" không?

0

Không, điều này là không thể. Nếu bạn có một trường hợp sử dụng cho điều này, bạn chắc chắn có thể viết một lớp tùy chỉnh. Đây là một ví dụ tầm thường (không xử lý nhiều trường hợp cạnh).

public class MemoryInt { 
    private int current; 
    private int last; 

    public MemoryInt(int value) { 
     this.current = value; 
     this.last = null; 
    } 

    public int get() { 
     return current; 
    } 

    public int set(int value) { 
     this.last = current; 
     this.current = value; 
    } 

    public void rollback() { 
     this.current = last; 
     this.last = null; 
    } 
} 
2

Làm thế nào về điều này?

int i = 1; 
    int oldValue = 0; 
    try { 
     oldValue = i; 
     i = 2; 
     int j = 10/0; 
    } catch (Exception ex) { 
     i = oldValue; 
    } 
    System.out.print("i = " + i); 
0

Alternative để rollback là không "cam kết" sự thay đổi trạng thái cho đến khi sau khi mã mà có thể ném ngoại lệ:

int i = 1; 
try { 
    // initialize temporary state with current state 
    int newState = i; 

    // have actual operation using the temporary state 
    newState = 2; 
    int j = 10/0; 

    // commit temporary state with code which can not throw exceptions 
    i = newState; 

} catch (ArithmeticException ex) { // catch only the expected exception! 
    /* Add possible error reporting or whatever needs to be done */ 
} 

System.out.print("i = " + i); 
2

Về cơ bản, bạn cần phải phiên bản bộ nhớ của bạn. Điều này đã được khám phá trong một số nghiên cứu, chẳng hạn như bộ nhớ giao dịch, phiên bản đồ thị đối tượng, cơ sở dữ liệu đối tượng.

Vấn đề là khó thực hiện chính xác hơn có vẻ như. Các vấn đề không tầm thường để xử lý là:

  • Bạn sử dụng phiên bản bao nhiêu? Toàn bộ đồ thị đối tượng có thể truy cập? Một tập hợp con quan tâm?
  • Điều gì về tương tác giữa dữ liệu được phiên bản và không được phiên bản có cấu trúc?
  • Còn các hoạt động khác có thể xảy ra trong "khối có thể phục hồi" như chủ đề sinh sản và đọc/ghi IO. Bạn có muốn hoàn tác tác vụ đó không? Bạn có thể?

Bộ nhớ giao dịch chậm trở thành cơ chế chính thống, với ngôn ngữ như Clojure. Nó requries tuy nhiên một loại đặc biệt của thiết kế để đối phó với các vấn đề trên. Clojure đạt được một sự cân bằng tốt đẹp vì nó là cơ bản chức năng, và dữ liệu phiên bản được xác định rõ ràng. Thêm các tính năng tương tự vào một ngôn ngữ mệnh lệnh hướng đối tượng như Java không đến miễn phí.


Bạn có thể quan tâm trong các liên kết/ấn phẩm sau:

+0

Bạn có liên kết có thể sử dụng không? tức là không đề cập đến một paywall? –

+0

@StephanEggermont Liên kết sẽ hoạt động ngay bây giờ. Tôi vừa mới tìm được bản PDF đầu tiên tôi tìm thấy. – ewernli

0

Tôi không biết tại sao ai không gợi ý nó. Nếu bạn muốn rollback sau đó chỉ cần sử dụng một hàm

public int performOperation(int i) throws CustomException { 
    try { 
     i = 2; 
     int j = 10/0; 
    } catch (Exception ex) { 
     // Handle exception here. 
     throw new CustomException(); 
    } 
    return i; 
} 

public void calculate() { 
    int i = 1; 
    try { 
     i = performOperation(i);//I will not be assigned if exception occurs 
    } catch (CustomException e) { 
     // Handle exception here. 
    } 
    System.out.println(i);//i will be always 1 here 
} 

Vì java là pass-by-value các i bên trong phương pháp sẽ không bao giờ thay đổi gọi. Không cần quay trở lại.

+0

Cảm ơn bạn. Nhưng tôi không muốn tập trung vào ví dụ của mình. Đó chỉ là một điều đơn giản. Điều gì về các tình huống mà ** thedayofcondor ** nói hoặc ** ewernli ** giải thích ... – Natasha