2013-04-12 19 views
6

Tôi muốn thiết kế một lớp học để chứng minh tính bất biến, từng bước.Bảo vệ các biến số cuối cùng khỏi sự phản ánh

Sau đây là một lớp đơn giản

public class ImmutableWithoutMutator { 
    private final String firstName; 
    private final String lastName; 
    private final int age; 

    public ImmutableWithoutMutator(final String firstName, final String lastName, final int age) { 
     this.firstName = firstName; 
     this.lastName = lastName; 
     this.age = age; 
    } 

    @Override 
    public String toString() { 
     return String.format(
       "ImmutableWithoutMutator [age=%s, firstName=%s, lastName=%s]", 
       age, firstName, lastName); 
    } 
} 

Bây giờ tôi vẫn có thể vi phạm trong việc sử dụng phản chiếu, bằng cách sử dụng đoạn mã sau.

import java.lang.reflect.Field; 

public class BreakImmutableUsingReflection { 
    public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 
     ImmutableWithoutMutator immMutator = new ImmutableWithoutMutator(
       "firstName", "lastName", 2400); 
     System.out.println(immMutator); 

     // now lets try changing value using reflection 
     Field f = ImmutableWithoutMutator.class.getDeclaredField("age"); 
     f.setAccessible(true); 
     f.set(immMutator, 2000); 

     System.out.println(immMutator); 
    } 
} 

Câu hỏi của tôi là, tôi chưa sửa đổi Modifiers cho các trường sử dụng API phản chiếu. Sau đó, làm thế nào mã vẫn có thể đột biến các lĩnh vực cuối cùng?

+0

Bản sao có thể có của http://stackoverflow.com/questions/1615163/modifying-final-fields-in-java?rq=1 – Patashu

Trả lời

10

Đây là hành vi được mong đợi - đó chỉ là điều bạn không nên làm.

Từ tài liệu cho Field.set:

Nếu trường cơ bản là cuối cùng, phương pháp ném một IllegalAccessException trừ setAccessible (true) đã thành công cho đối tượng Dòng này và lĩnh vực này là không tĩnh. Việc thiết lập một trường cuối cùng theo cách này chỉ có ý nghĩa trong quá trình deserialization hoặc xây dựng lại các cá thể của các lớp với các trường cuối cùng trống, trước khi chúng được tạo sẵn để truy cập bởi các phần khác của chương trình. Sử dụng trong bất kỳ ngữ cảnh nào khác có thể có các hiệu ứng không thể đoán trước, bao gồm các trường hợp trong đó các phần khác của chương trình tiếp tục sử dụng giá trị ban đầu của trường này.

Bây giờ nếu bạn muốn tranh luận vi phạm tất cả các loại bảo mật, bạn cần phải tự hỏi tại sao bạn có mã mà bạn không tin tưởng là chạy lành mạnh với đủ quyền gọi field.setAccessible(true). Về cơ bản, bảo vệ được cung cấp bởi trình quản lý bảo mật - nhưng nếu bạn đang chạy mà không có trình quản lý bảo mật ngăn bạn thực hiện việc này, bạn có thể tự chụp.

+4

'if (author.getName(). Bằng (" Jon Skeet ")) { this.upvote(); } else {this.readAnswer(); } ' –

+0

@ Jon-skeet Cảm ơn bạn đã giải thích chi tiết. – Vivek

1

Không, bạn có thể làm điều đó. Tôi đề nghị bạn đọc The Security ManagerJava SE 7 Security Documentation nếu bạn muốn hạn chế một số hoạt động nhất định.

+2

ok Tôi không thấy câu trả lời của Jon Skeet trước khi đăng bài. Giờ tôi sẽ xuống địa ngục. – dierre

2

Bạn có thể bảo vệ khỏi thiết lậpTruy cập bằng cách sử dụng SecurityManager. Bạn có thể chỉ định nó trên dòng lệnh hoặc tạo nó động, giống như họ làm điều đó here.

Nhưng ngay cả khi bạn bảo vệ khỏi phản ánh, mã của bạn sẽ vẫn không được bảo vệ khỏi các thư viện sửa đổi bytecode như ASM.

Vì vậy, bạn không nên dựa vào các trường của mình là cuối cùng.

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