2010-03-12 22 views
60

Tôi đã xem qua đoạn mã sau vào một cơ sở mã tôi đang làm việc trên:Tại sao đối tượng cuối cùng có thể được sửa đổi?

public final class ConfigurationService { 
    private static final ConfigurationService INSTANCE = new ConfigurationService(); 
    private List providers; 

    private ConfigurationService() { 
     providers = new ArrayList(); 
    } 

    public static void addProvider(ConfigurationProvider provider) { 
     INSTANCE.providers.add(provider); 
    } 

    ... 

dụ được khai báo là final. Tại sao các đối tượng có thể được thêm vào INSTANCE? Không nên làm mất hiệu lực việc sử dụng cuối cùng. (Nó không).

Tôi giả định câu trả lời phải làm điều gì đó với con trỏ và bộ nhớ nhưng muốn biết chắc chắn.

+0

Nhận thức sai này xuất hiện khá thường xuyên, không nhất thiết phải là câu hỏi. Thường là câu trả lời hoặc bình luận. – Robin

+4

Giải thích đơn giản từ JLS: ** "Nếu biến cuối cùng chứa tham chiếu đến đối tượng, thì trạng thái của đối tượng có thể bị thay đổi bởi thao tác trên đối tượng, nhưng biến sẽ luôn tham chiếu đến cùng một đối tượng." ** [Tài liệu JLS] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4) – realPK

Trả lời

122

'cuối cùng' chỉ đơn giản là làm cho đối tượng tham chiếu không thể thay đổi. Các đối tượng nó trỏ đến là không thay đổi bằng cách làm điều này. INSTANCE không bao giờ có thể ám chỉ đến một đối tượng khác, nhưng đối tượng nó đề cập đến có thể thay đổi trạng thái.

+1

+1, Để biết thêm chi tiết, vui lòng kiểm tra http: //java.sun.com/docs/books/jls/second_edition/html/typesValues.doc.html, phần 4.5.4. –

+0

Vì vậy, hãy nói rằng tôi deserialize một đối tượng 'ConfigurationService' và tôi cố gắng làm một INSTANCE =' deserializedConfigurationService' sẽ không được phép? – diegoaguilar

+0

Bạn không bao giờ có thể gán 'INSTANCE' để chỉ một đối tượng khác. Không quan trọng đối tượng kia đến từ đâu. (NB có một 'INSTANCE' trên' ClassLoader' đã nạp lớp này. Bạn có thể trong lý thuyết tải lớp nhiều lần trong một JVM và mỗi lớp riêng biệt. Nhưng đây là một điểm kỹ thuật khác.) –

3

Cuối cùng và không thay đổi không giống nhau. Cuối cùng có nghĩa là không thể gán lại tham chiếu để bạn không thể nói

INSTANCE = ... 

Không thể sửa đổi có nghĩa là bản thân đối tượng không thể sửa đổi được. Một ví dụ về điều này là lớp java.lang.String. Bạn không thể sửa đổi giá trị của một chuỗi.

20

Chìa khóa cho sự hiểu lầm nằm trong tiêu đề câu hỏi của bạn. Đây không phải là đối tượng là cuối cùng, đó là biến số . Giá trị của biến không thể thay đổi, nhưng dữ liệu bên trong có thể thay đổi.

Luôn nhớ rằng khi bạn khai báo biến kiểu tham chiếu, giá trị của biến đó là tham chiếu chứ không phải đối tượng.

10

final chỉ có nghĩa là không thể thay đổi tham chiếu. Bạn không thể chỉ định lại INSTANCE cho một tham chiếu khác nếu nó được khai báo là cuối cùng. Trạng thái bên trong của đối tượng vẫn còn có thể thay đổi.

final ConfigurationService INSTANCE = new ConfigurationService(); 
ConfigurationService anotherInstance = new ConfigurationService(); 
INSTANCE = anotherInstance; 

sẽ ném một lỗi biên dịch

6

Khi một biến final đã được chỉ định, nó luôn luôn chứa cùng giá trị. Nếu biến số final giữ một tham chiếu đến một đối tượng, thì trạng thái của đối tượng có thể bị thay đổi bởi các hoạt động trên đối tượng, nhưng biến sẽ luôn tham chiếu đến cùng một đối tượng. Điều này cũng áp dụng cho các mảng, vì mảng là các đối tượng; nếu biến số final giữ tham chiếu đến một mảng, thì các thành phần của mảng có thể được thay đổi bởi các hoạt động trên mảng, nhưng biến sẽ luôn tham chiếu đến cùng một mảng.

Source

Dưới đây là một hướng dẫn về making an object immutable.

30

Cuối cùng không giống như không thay đổi.

final != immutable

Từ khóa final được sử dụng để đảm bảo rằng các tài liệu tham khảo không được thay đổi (có nghĩa là, các tài liệu tham khảo nó đã không thể được thay thế bằng một cái mới)

Nhưng, nếu thuộc tính là tự có thể sửa đổi được, bạn có thể làm những gì bạn vừa mô tả.

Ví dụ

class SomeHighLevelClass { 
    public final MutableObject someFinalObject = new MutableObject(); 
} 

Nếu chúng ta nhanh chóng lớp học này, chúng tôi sẽ không thể gán giá trị khác cho các thuộc tính someFinalObject vì nó là thức.

Vì vậy, đây là không thể:

.... 
SomeHighLevelClass someObject = new SomeHighLevelClass(); 
MutableObject impostor = new MutableObject(); 
someObject.someFinal = impostor; // not allowed because someFinal is .. well final 

Nhưng nếu đối tượng tự nó là có thể thay đổi như thế này:

class MutableObject { 
    private int n = 0; 

    public void incrementNumber() { 
     n++; 
    } 
    public String toString(){ 
     return ""+n; 
    } 
} 

Sau đó, giá trị chứa bởi đối tượng có thể thay đổi có thể được thay đổi.

SomeHighLevelClass someObject = new SomeHighLevelClass(); 

someObject.someFinal.incrementNumber(); 
someObject.someFinal.incrementNumber(); 
someObject.someFinal.incrementNumber(); 

System.out.println(someObject.someFinal); // prints 3 

này có tác dụng tương tự mà bài viết của bạn:

public static void addProvider(ConfigurationProvider provider) { 
    INSTANCE.providers.add(provider); 
} 

Ở đây bạn không thay đổi giá trị của Ví dụ, bạn đang thay đổi trạng thái nội bộ của mình (thông qua, phương pháp providers.add)

nếu bạn muốn ngăn chặn rằng định nghĩa lớp học phải được thay đổi như thế này:

public final class ConfigurationService { 
    private static final ConfigurationService INSTANCE = new ConfigurationService(); 
    private List providers; 

    private ConfigurationService() { 
     providers = new ArrayList(); 
    } 
    // Avoid modifications  
    //public static void addProvider(ConfigurationProvider provider) { 
    // INSTANCE.providers.add(provider); 
    //} 
    // No mutators allowed anymore :) 
.... 

Nhưng, nó có thể không có ý nghĩa nhiều :)

Nhân tiện, bạn cũng phải synchronize access to it về cơ bản vì lý do tương tự.

2

Java không có khái niệm về tính không thay đổi được tích hợp sẵn cho ngôn ngữ. Không có cách nào để đánh dấu các phương thức như một mutator. Do đó ngôn ngữ không có cách nào để thực thi bất biến đối tượng.

1

Nếu bạn đang sử dụng từ khóa cuối cùng cho đối tượng, hashCode sẽ không bao giờ thay đổi do đó làm cho nó không thay đổi. Nó sẽ được sử dụng trong mẫu đơn.

class mysingleton1{ 

private mysingleton1(){} 
public static final mysingleton1 instance= new mysingleton1(); 

    public static mysingleton1 getInstance(){ 

     return instance; 

    } 

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