2013-08-08 35 views
9

Vui lòng tham khảo mã bên dưới. Khi tôi chạy mã, tôi có thể thay đổi giá trị của một biến không phải tĩnh cuối cùng. Nhưng nếu tôi cố gắng thay đổi giá trị của biến tĩnh cuối cùng thì nó sẽ ném java.lang.IllegalAccessException.thay đổi các biến cuối cùng thông qua sự phản ánh, tại sao sự khác biệt giữa biến cuối cùng tĩnh và không tĩnh

Câu hỏi của tôi là tại sao nó không ném ngoại lệ trong trường hợp biến cuối cùng không tĩnh cũng hoặc ngược lại. Tại sao sự khác biệt?

import java.lang.reflect.Field; 
import java.util.Random; 

public class FinalReflection { 

    final static int stmark = computeRandom(); 
    final int inmark = computeRandom(); 

    public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 
     FinalReflection obj = new FinalReflection(); 
     System.out.println(FinalReflection.stmark); 
     System.out.println(obj.inmark); 
     Field staticFinalField = FinalReflection.class.getDeclaredField("stmark"); 
     Field instanceFinalField = FinalReflection.class.getDeclaredField("inmark"); 
     staticFinalField.setAccessible(true); 
     instanceFinalField.setAccessible(true); 

     instanceFinalField.set(obj, 100); 
     System.out.println(obj.inmark); 

     staticFinalField.set(FinalReflection.class, 101); 
     System.out.println(FinalReflection.stmark); 

    } 

    private static int computeRandom() { 
     return new Random().nextInt(5); 
    } 
} 
+1

Tôi đã đăng mã không đưa ra ngoại lệ. Nhưng nó là một hack cho chắc chắn. –

Trả lời

10
FinalReflectionobj = new FinalReflection(); 
System.out.println(FinalReflection.stmark); 
System.out.println(obj.inmark); 
Field staticFinalField = FinalReflection.class.getDeclaredField("stmark"); 
Field instanceFinalField = FinalReflection.class.getDeclaredField("inmark"); 
staticFinalField.setAccessible(true); 
instanceFinalField.setAccessible(true); 

//EXTRA CODE 
//Modify the final using reflection 
Field modifiersField = Field.class.getDeclaredField("modifiers"); 
modifiersField.setAccessible(true); 
modifiersField.setInt(staticFinalField, staticFinalField.getModifiers() & ~Modifier.FINAL); 


instanceFinalField.set(obj, 100); 
System.out.println(obj.inmark); 
staticFinalField.set(FinalReflection.class, 101); 
System.out.println(FinalReflection.stmark); 

Giải pháp này không đến mà không có một số nhược điểm, nó có thể không hoạt động trong mọi trường hợp:

Trong trường hợp một lĩnh vực final được khởi tạo một thời gian biên dịch liên tục trong việc kê khai trường, thay đổi vào trường final có thể không hiển thị, vì việc sử dụng trường cuối cùng được thay thế tại thời gian biên dịch với hằng số biên dịch.

Một vấn đề khác là đặc điểm kỹ thuật cho phép tối ưu hóa tích cực các trường final. Trong một chủ đề, cho phép sắp xếp lại các lần đọc của trường final với những sửa đổi của trường final không diễn ra trong hàm tạo. More về điều này cũng được giải thích trong câu hỏi tương tự này.

+0

@assylias không thấy phần mã phụ –

+2

@assylias điều này cũng sẽ cho phép bạn thay đổi trường cuối cùng tĩnh nếu không có trình quản lý bảo mật. –

+0

Lưu ý rằng điều này sẽ không hoạt động với các nguyên tố tĩnh cuối cùng được khởi tạo với một biểu thức không đổi. – assylias

0

Cuối cùng, nó có thể được gán các giá trị khác nhau khi chạy khi được khởi tạo.

Class Test{  
public final int a; 
} 

Test t1 = new Test(); 
t1.a = 10; 
Test t2 = new Test(); 
t1.a = 20; 

Vì vậy, mỗi trường hợp có giá trị khác nhau của trường a.

Đối với kết quả tĩnh, tất cả các phiên bản chia sẻ cùng một giá trị và không thể thay đổi sau lần khởi tạo đầu tiên.

Class TestStatic{ 
    public static final int a; 
} 

Test t1 = new Test(); 
t1.a = 10; 
Test t2 = new Test(); 
t1.a = 20; // ERROR, CAN'T BE ALTERED AFTER THE FIRST INITIALIZATION. 
+0

+1. Có, tôi đồng ý với những gì bạn đang nói. –

+0

Nhưng ông chỉ sử dụng một trường hợp duy nhất của lớp. Và biến cuối cùng không tĩnh không thể thay đổi sau lần gán đầu tiên. Do đó giải thích này là sai. –

2

Các javadoc là rõ ràng:

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.

Từ góc độ JLS, hành vi chính xác của phản xạ nên làm việc như thế nào không được xác định, nhưng trong JLS 17.5.4:

Thông thường, một lĩnh vực mà là cuối cùng và tĩnh có thể không được sửa đổi.

Một giải pháp khác là remove the final modifier through reflection.

+1

Câu trả lời Excelent, nhưng tôi nghĩ câu hỏi đúng hơn là: tại sao các nhà thiết kế java lại quyết định điều này? – morgano

+0

@morgano nếu đó là trường hợp SO có lẽ không phải là nơi tốt nhất để hỏi! Nó chắc chắn gây ra tất cả các loại vấn đề. Ví dụ: hằng số nguyên thủy được inline tại thời gian biên dịch, vì vậy bạn không thể thay đổi chúng trong thời gian chạy trừ khi bạn thay đổi bytecode bên dưới. – assylias

+0

@assylias cảm ơn thông tin tuyệt vời. nhưng có câu hỏi của tôi là ít liên kết với những gì morgano nói. Nhưng có, tôi không thể đồng ý nhiều hơn rằng SO có thể không phải là nơi tốt nhất để hỏi điều này. Tôi sẽ nhớ nó từ lần sau. Anyways nguyên thủy được inlined đi cho cả hai biến cuối cùng tĩnh và không tĩnh. thử chạy giá trị mã đã sửa đổi 5 thay vì computeRandom(). – veritas

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