2014-04-24 12 views
5

Tôi có một tình huống mà tôi phải thay đổi hằng số java.Thay đổi biến tĩnh hoạt động Primitive Wrapper nhưng không phải với kiểu nguyên thủy

tôi có dưới đây làm việc mã

import java.lang.reflect.Field; 
import java.lang.reflect.Modifier; 

public class Main { 
    public static final Integer FLAG = 44; 

    static void setFinalStatic(Class<?> clazz, String fieldName, Object newValue) throws NoSuchFieldException, IllegalAccessException { 
     Field field = clazz.getDeclaredField(fieldName); 
     field.setAccessible(true); 
     Field modifiers = field.getClass().getDeclaredField("modifiers"); 
     modifiers.setAccessible(true); 
     modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
     field.set(null, newValue); 
    } 

    public static void main(String... args) throws Exception { 
     System.out.printf("Everything is %s%n", FLAG); 
     setFinalStatic(Main.class, "FLAG", 33); 
     System.out.printf("Everything is %s%n", FLAG); 
    } 
} 

Nếu tôi chạy ở trên, tôi nhận được sau đầu ra:

Everything is 44 
Everything is 33 

Nhưng nếu tôi thay đổi FLAG biến int tức

public static final int FLAG = 44; 

Nó không hoạt động. Đầu ra là:

Everything is 44 
Everything is 44 

Có cách nào khác để làm cho nó làm việc với Primitive Loại int.

+1

Tại sao bạn khai báo biến cuối cùng nếu bạn muốn thay đổi? Xóa cuối cùng. – mvmn

+0

+1 vào nhận xét ở trên. Câu trả lời dễ dàng là int là không thể thay đổi, nhưng tôi không biết đủ java sâu để trả lời với sự tự tin –

+0

hằng số thời gian biên dịch được nội tuyến. Nếu giá trị không được biết cho đến khi chạy, nó có thể được thay đổi. Lưu ý: thay đổi biến 'final' chắc chắn gây nhầm lẫn, do đó bạn sẽ không thực sự làm điều này trong sản xuất. –

Trả lời

6

Từ jls-4.12.4

Một biến kiểu nguyên thủy hoặc kiểu String, đó là cuối cùng và khởi tạo với một thời gian biên dịch thường xuyên biểu (§15.28), được gọi là một constant variable.

Cũng phần 13.1 nói (tôi nhấn mạnh)

3 .. Tài liệu tham khảo để các lĩnh vực mà là các biến đổi (§4.12.4) đều được giải quyết tại thời gian biên dịch với giá trị hằng số được ký hiệu . Không có tham chiếu đến trường như vậy nên có mặt trong mã trong một tệp nhị phân (ngoại trừ trong lớp hoặc giao diện chứa trường, sẽ có mã để khởi tạo nó). Trường như vậy phải luôn luôn xuất hiện để được khởi tạo (§12.4.2); giá trị ban đầu mặc định cho loại trường như vậy không bao giờ được quan sát.

Nó có nghĩa là thời gian biên dịch thường xuyên biểu từ biến liên tục sẽ được đưa trực tiếp trong mã bởi trình biên dịch (nó sẽ được inlined) không đọc từ tài liệu tham khảo chính thức trong thời gian chạy.

Ví dụ nếu bạn thực hiện phương pháp main từ Bar lớp

class Foo{ 
    static{ 
     System.out.println("test if class will be loaded"); 
    } 
    public static final int x = 42; 
} 

class Bar{ 
    public static void main(String [] args){ 
     System.out.println(Foo.x); 
    } 
} 

bạn sẽ thấy không có đầu ra từ khối tĩnh của Foo lớp có nghĩa là Foo lớp chưa được nạp, có nghĩa là giá trị của Foo.x didn' t đến từ lớp này. Trong thực tế Bar đã được biên soạn theo cách này

class Bar{ 
    public static void main(String [] args){ 
     System.out.println(42); // reference Foo.x will be removed by compiler 
           // and replaced with actual value because 
           // compiler assumes that value can't/shouldn't 
           // change at runtime 
    } 
} 

Vì vậy, thậm chí thay đổi giá trị của Foo.x khi chạy sẽ không ảnh hưởng đến giá trị in bằng main phương pháp trong Bar lớp.

Bạn không thể thay đổi cơ chế đó.


cách thể xung quanh sẽ được khởi lĩnh vực cuối cùng của bạn với các giá trị được tạo ra lúc runtime (họ sẽ không tồn tại thời gian biên dịch, mà sẽ ngăn cản họ là thời gian biên dịch biểu thức hằng số). Vì vậy, thay vì

public static final String x = "foo"; 

thử

public static final String x = new String("foo"); 

hoặc trong trường hợp của các loại nguyên thủy sử dụng Unboxing như thay vì

public static final int x = 42; 

sử dụng

public static final int x = new Integer(42); 
+0

Từ khóa cần tra cứu là các biểu thức hằng số và các biến không đổi. –

+0

@SotiriosDelimanolis Bạn có thể xem phiên bản đã chỉnh sửa hoặc câu trả lời của tôi không? Tôi chắc chắn không phải là chuyên gia Java vì vậy tôi có thể làm cho một số sai lầm đơn giản nhưng rất quan trọng mà tôi không biết:/ – Pshemo

+0

Tôi không nghĩ rằng có điều gì sai trái. Đây là câu trả lời đúng. –

0

Điều này xảy ra bởi vì các trường thức tĩnh của nguyên thủy hoặc St loại vòng được inlined trong thời gian biên dịch. Phương pháp chính sẽ trông giống như sau khi biên soạn và hủy biên dịch

public static void main(String... args) throws Exception { 
     System.out.printf("Everything is %s%n", 44); 
     setFinalStatic(Main.class, "FLAG", 33); 
     System.out.printf("Everything is %s%n", 44); 
    } 

vì FLAG được thay thế bằng giá trị thực trong thời gian biên dịch.

1
  1. Loại nguyên thủy được gạch chân.

  2. Thực tế ngay cả các hằng số không nguyên thủy, khi được nhập vào các lớp khác, sẽ được sao chép và quá trình nhập bị lãng quên. Vì vậy, nó sẽ không hoạt động. Chỉ dành cho bộ nhớ cache liên tục, như nhóm chuỗi và bộ đệm Integer (Integer.valueOf (13)) bạn có thể ghi đè lên các giá trị của chúng.

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