Ý nghĩa của bất biến là gì?


400

Đây có thể là câu hỏi ngớ ngẩn nhất từng được hỏi nhưng tôi nghĩ nó khá khó hiểu đối với một người mới sử dụng Java.

  1. Ai đó có thể làm rõ những gì có nghĩa là bất biến ?
  2. Tại sao là một Stringbất biến?
  3. Những lợi thế / bất lợi của các đối tượng bất biến là gì?
  4. Tại sao một đối tượng có thể thay đổi như StringBuilderđược ưu tiên hơn Chuỗi và ngược lại?

Một ví dụ đẹp (trong Java) sẽ thực sự được đánh giá cao.


73
Hãy xem, đó không phải là một câu hỏi ngớ ngẩn. Vui mừng bạn hỏi!
DOK

2
Nhân tiện, tôi không nghĩ đó là câu hỏi ngớ ngẩn nhất từng có :) Tôi nghĩ đó là một khái niệm khá quan trọng để hiểu
Jason Coco

1
Khi bạn nói StringBuilder, ý bạn không phải là lớp StringBuffer có thể thay đổi? String và StringBuffer có chức năng tương tự nhiều hơn String và StringBuilder. StringBuffer thực sự là một chuỗi có thể thay đổi.
Derek Mahar

3
Tôi có thể đề nghị chúng ta thêm thẻ "người mới bắt đầu" câu hỏi này để các lập trình viên mới biết về Java có thể tìm thấy nó trong tìm kiếm cho các câu hỏi giới thiệu khác không?
Derek Mahar

Câu trả lời:


268

Bất biến có nghĩa là một khi hàm tạo cho một đối tượng đã hoàn thành thực thi thì thể hiện đó không thể bị thay đổi.

Điều này rất hữu ích vì nó có nghĩa là bạn có thể chuyển các tham chiếu đến đối tượng xung quanh mà không phải lo lắng rằng ai đó sẽ thay đổi nội dung của nó. Đặc biệt là khi xử lý đồng thời, không có vấn đề khóa với các đối tượng không bao giờ thay đổi

ví dụ

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Fookhông phải lo lắng rằng người gọi getValue()có thể thay đổi văn bản trong chuỗi.

Nếu bạn tưởng tượng một lớp học tương tự Foo, nhưng với một StringBuilderchứ không phải là một Stringnhư một thành viên, bạn có thể thấy rằng một người gọi đến getValue()sẽ có thể thay đổi các StringBuilderthuộc tính của một Fooví dụ.

Cũng hãy cẩn thận với các loại bất biến khác nhau mà bạn có thể tìm thấy: Eric Lippert đã viết một bài viết trên blog về điều này. Về cơ bản, bạn có thể có các đối tượng có giao diện là bất biến nhưng đằng sau hậu trường trạng thái riêng tư có thể thay đổi thực tế (và do đó không thể được chia sẻ an toàn giữa các luồng).


3
Tôi nghĩ bạn nên thêm một hàm tạo một arg để gán giá trị ít nhất một lần. Điểm của mã hiện tại không rõ ràng vì không có giá trị để thay đổi thực sự :).
Georgy Bolyuba

4
Bạn nên làm cho trường chỉ đọc. Nó làm cho nó hoàn toàn rõ ràng rằng lĩnh vực này là bất biến. Ngay bây giờ, nó không thay đổi theo quy ước
JaredPar

7
Thành viên myVar nên là người cuối cùng để điều này thực sự bất biến.
laz

13
Bạn đúng là myVar không thể truy cập bên ngoài Foo. Tuy nhiên, sự hiện diện của trận chung kết cho bất kỳ ai có thể sửa đổi lớp trong tương lai rằng giá trị của nó không có nghĩa là thay đổi. Tôi có xu hướng ủng hộ càng rõ ràng càng tốt trong những trường hợp như vậy.
laz

2
Làm thế nào về "Các loại tham chiếu không thể được thực hiện bất biến chỉ bằng cách sử dụng từ khóa cuối cùng. Cuối cùng chỉ ngăn chặn việc gán lại." từ en.wikipedia.org/wiki/Immutable_object
Yousha Aleayoub

81

Một đối tượng bất biến là một đối tượng trong đó các trường bên trong (hoặc ít nhất, tất cả các trường bên trong ảnh hưởng đến hành vi bên ngoài của nó) không thể thay đổi.

Có rất nhiều lợi thế cho chuỗi bất biến:

Hiệu suất: Thực hiện các thao tác sau:

String substring = fullstring.substring(x,y);

C cơ bản cho phương thức chuỗi con () có lẽ giống như thế này:

// Assume string is stored like this:
struct String { char* characters; unsigned int length; };

// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
    struct String* out = malloc(sizeof(struct String));
    out->characters = in->characters + begin;
    out->length = end - begin;
    return out;
}

Lưu ý rằng không ai trong số các nhân vật phải được sao chép! Nếu đối tượng Chuỗi có thể thay đổi (các ký tự có thể thay đổi sau này) thì bạn sẽ phải sao chép tất cả các ký tự, nếu không, các thay đổi thành các ký tự trong chuỗi con sẽ được phản ánh trong chuỗi khác sau đó.

Đồng thời: Nếu cấu trúc bên trong của một đối tượng bất biến là hợp lệ, nó sẽ luôn có hiệu lực. Không có khả năng các luồng khác nhau có thể tạo trạng thái không hợp lệ trong đối tượng đó. Do đó, các đối tượng bất biến là Thread Safe .

Thu gom rác thải: Người thu gom rác dễ dàng đưa ra quyết định hợp lý về các đối tượng bất biến.

Tuy nhiên, cũng có những nhược điểm đối với sự bất biến:

Hiệu suất: Đợi đã, tôi nghĩ bạn nói hiệu suất là một mặt trái của sự bất biến! Vâng, đôi khi, nhưng không phải luôn luôn. Lấy mã sau:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

Cả hai dòng đều thay thế ký tự thứ tư bằng chữ "a". Không chỉ là đoạn mã thứ hai dễ đọc hơn, nó còn nhanh hơn. Nhìn vào cách bạn sẽ phải làm mã cơ bản cho foo. Các chuỗi con rất dễ dàng, nhưng bây giờ vì đã có một nhân vật trong không gian năm và một cái gì đó khác có thể đang tham chiếu foo, bạn không thể thay đổi nó; bạn phải sao chép toàn bộ chuỗi (tất nhiên một số chức năng này được trừu tượng hóa thành các hàm trong C bên dưới thực sự, nhưng vấn đề ở đây là hiển thị mã được thực thi tất cả ở một nơi).

struct String* concatenate(struct String* first, struct String* second)
{
    struct String* new = malloc(sizeof(struct String));
    new->length = first->length + second->length;

    new->characters = malloc(new->length);

    int i;

    for(i = 0; i < first->length; i++)
        new->characters[i] = first->characters[i];

    for(; i - first->length < second->length; i++)
        new->characters[i] = second->characters[i - first->length];

    return new;
}

// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));

Lưu ý rằng concatenate được gọi hai lần có nghĩa là toàn bộ chuỗi phải được lặp qua! So sánh mã này với mã C cho barhoạt động:

bar->characters[4] = 'a';

Các hoạt động chuỗi đột biến rõ ràng là nhanh hơn nhiều.

Trong kết luận: Trong hầu hết các trường hợp, bạn muốn một chuỗi bất biến. Nhưng nếu bạn cần thực hiện nhiều thao tác nối và chèn vào một chuỗi, bạn cần khả năng biến đổi cho tốc độ. Nếu bạn muốn các lợi ích an toàn và thu gom rác đồng thời với nó, điều quan trọng là giữ các đối tượng có thể thay đổi của bạn cục bộ thành một phương thức:

// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
    StringBuilder mutable;
    boolean first = true;

    for(int i = 0; i < strings.length; i++)
    {
        if(!first) first = false;
        else mutable.append(separator);

        mutable.append(strings[i]);
    }

    return mutable.toString();
}

mutableđối tượng là một tham chiếu cục bộ, bạn không phải lo lắng về an toàn đồng thời (chỉ có một luồng từng chạm vào nó). Và vì nó không được tham chiếu ở bất kỳ nơi nào khác, nên nó chỉ được phân bổ trên ngăn xếp, vì vậy nó được giải quyết ngay khi cuộc gọi chức năng kết thúc (bạn không phải lo lắng về việc thu gom rác). Và bạn nhận được tất cả các lợi ích hiệu suất của cả khả năng biến đổi và bất biến.


4
Tuyệt vời đọc! chỉ có một điều tôi nghĩ nó nên là nếu (đầu tiên) chứ không phải nếu (! đầu tiên)
Siddhartha

Điều cần thiết không phải là các trường là bất biến, mà là trạng thái quan sát được xác định của đối tượng là bất biến; một đối tượng chứa tham chiếu đến một đối tượng khác như một phương tiện đóng gói trạng thái chứa trong đó chỉ có thể là bất biến nếu tất cả các khía cạnh được đóng gói của trạng thái mà nó tiếp xúc với thế giới bên ngoài cũng bất biến. Lưu ý rằng các trường không phải là không cần thiết cũng không đủ. Điều quan trọng là trạng thái hữu hình.
supercat

7
Passing pointers because Java is pass-by-referenceKhông phải java "pass-by-value?"
Cristian Gutu

@CristianGutu vâng, bạn đúng JAVA là "Vượt qua giá trị" chứ không phải "Vượt qua TÀI LIỆU THAM KHẢO"
Arsh Kaushal

Tham chiếu được thông qua như giá trị !!
devv

31

Thực tế String không phải là bất biến nếu bạn sử dụng định nghĩa wikipedia được đề xuất ở trên.

Chuỗi trạng thái không thay đổi xây dựng bài. Hãy xem phương thức hashcode (). Chuỗi lưu trữ giá trị mã băm trong trường cục bộ nhưng không tính toán cho đến lần gọi đầu tiên của mã băm (). Sự đánh giá lười biếng này của mã băm đặt Chuỗi ở một vị trí thú vị như một đối tượng bất biến có trạng thái thay đổi, nhưng không thể quan sát thấy nó đã thay đổi mà không sử dụng sự phản chiếu.

Vì vậy, có lẽ định nghĩa bất biến nên là một đối tượng không thể quan sát được đã thay đổi.

Nếu trạng thái thay đổi trong một đối tượng bất biến sau khi nó được tạo ra nhưng không ai có thể nhìn thấy nó (không có sự phản chiếu) thì đối tượng vẫn bất biến?


1
Ý tưởng tốt - một đối tượng không thể quan sát được đã thay đổi, cũng như, không có cách nào để thay đổi nó từ bên ngoài. Trường riêng cho hashCode () là một thay đổi bên trong không phải là vật chất đối với trạng thái hiển thị bên ngoài của đối tượng.
mparaz

2
Trên thực tế nó có thể được quan sát là đã thay đổi nếu bạn sử dụng sự phản chiếu. Xem thêm tại Chuỗi của Sedgewick có thể thay đổi nếu bạn cho phép phản chiếu .
Miguel

24

Đối tượng bất biến là những đối tượng không thể thay đổi theo chương trình. Chúng đặc biệt tốt cho các môi trường đa luồng hoặc các môi trường khác, nơi có nhiều hơn một quá trình có thể thay đổi (biến đổi) các giá trị trong một đối tượng.

Tuy nhiên, chỉ cần làm rõ, StringBuilder thực sự là một đối tượng có thể thay đổi, không phải là một đối tượng bất biến. Chuỗi java thông thường là bất biến (có nghĩa là một khi nó được tạo, bạn không thể thay đổi chuỗi bên dưới mà không thay đổi đối tượng).

Ví dụ: giả sử tôi có một lớp có tên là ColorString có giá trị Chuỗi và màu Chuỗi:

public class ColoredString {

    private String color;
    private String string;

    public ColoredString(String color, String string) {
        this.color  = color;
        this.string = string;
    }

    public String getColor()  { return this.color;  }
    public String getString() { return this.string; }

    public void setColor(String newColor) {
        this.color = newColor;
    }

}

Trong ví dụ này, ColoringString được cho là có thể thay đổi vì bạn có thể thay đổi (biến đổi) một trong các thuộc tính quan trọng của nó mà không cần tạo lớp ColourString mới. Lý do tại sao điều này có thể xấu là, ví dụ, giả sử bạn có một ứng dụng GUI có nhiều luồng và bạn đang sử dụng ColourStrings để in dữ liệu ra cửa sổ. Nếu bạn có một thể hiện của ColourString được tạo như

new ColoredString("Blue", "This is a blue string!");

Sau đó, bạn sẽ mong đợi chuỗi luôn luôn là "Màu xanh". Nếu một chủ đề khác, tuy nhiên, đã hiểu về trường hợp này và được gọi

blueString.setColor("Red");

Bạn sẽ đột nhiên, và có thể bất ngờ, bây giờ có một chuỗi "Đỏ" khi bạn muốn một chuỗi "Màu xanh". Bởi vì điều này, các đối tượng bất biến hầu như luôn được ưa thích khi chuyển các thể hiện của các đối tượng xung quanh. Khi bạn gặp trường hợp các đối tượng có thể thay đổi là thực sự cần thiết, thì bạn thường sẽ bảo vệ objet bằng cách chỉ gửi các bản sao ra khỏi lĩnh vực kiểm soát cụ thể của bạn.

Tóm lại, trong Java, java.lang.String là một đối tượng không thay đổi ( không thể thay đổi một khi nó được tạo) và java.lang.StringBuilder là một đối tượng có thể thay đổi vì nó có thể được thay đổi mà không cần tạo một thể hiện mới.


Bạn nên làm cho các lĩnh vực chỉ đọc. Ngay bây giờ lớp học của bạn là bất biến theo quy ước. Không có dấu hiệu cho các nhà phát triển trong tương lai tha thứ bất biến là cố ý. Làm cho các trường chỉ đọc sẽ giúp làm rõ ý định của bạn với một nhà phát triển trong tương lai
JaredPar

@JaredPar - Trên thực tế, lớp học không phải là bất biến ... đó là một ví dụ về một lớp có thể thay đổi để chứng minh tại sao nó có thể là một vấn đề.
Jason Coco

1
@JaredPar - Ồ, điều đó hoàn toàn ổn :) Tôi sẽ viết lại một chút để rõ ràng hơn, nhưng Douglas đã được viết tốt và dường như là yêu thích, vì vậy tôi sẽ chỉ để tôi làm ví dụ khác; nhưng ai đó thực sự đã chỉnh sửa nó để làm cho các thuộc tính cuối cùng mà tôi nghĩ là thú vị :)
Jason Coco

24
  1. Trong các ứng dụng lớn, thông thường các chuỗi ký tự sẽ chiếm các bit lớn của bộ nhớ. Vì vậy, để xử lý một cách hiệu quả bộ nhớ, JVM phân bổ một khu vực gọi là "Chuỗi liên tục hồ bơi". ( Lưu ý rằng trong bộ nhớ ngay cả một chuỗi unreferenced mang xung quanh một char [], int cho chiều dài của nó, và một cho hashCode của nó. Đối với một số , ngược lại, cần tối đa tám byte ngay lập tức )
  2. Khi trình biên dịch bắt gặp một chuỗi ký tự, nó sẽ kiểm tra nhóm để xem liệu đã có một nghĩa đen giống hệt nhau chưa. Và nếu một cái được tìm thấy, tham chiếu đến nghĩa đen mới được hướng đến Chuỗi hiện có và không có 'Đối tượng chuỗi ký tự' mới nào được tạo ra (Chuỗi hiện tại chỉ cần có một tham chiếu bổ sung).
  3. Do đó: Khả năng biến đổi chuỗi giúp tiết kiệm bộ nhớ ...
  4. Nhưng khi bất kỳ biến nào thay đổi giá trị, Trên thực tế - chỉ có tham chiếu của chúng thay đổi, không phải giá trị trong bộ nhớ (do đó nó sẽ không ảnh hưởng đến các biến khác tham chiếu đến nó) như được thấy dưới đây ....

Chuỗi s1 = "Chuỗi cũ";

//s1 variable, refers to string in memory
        reference                 |     MEMORY       |
        variables                 |                  |

           [s1]   --------------->|   "Old String"   |

Chuỗi s2 = s1;

//s2 refers to same string as s1
                                  |                  |
           [s1]   --------------->|   "Old String"   |
           [s2]   ------------------------^

s1 = "Chuỗi mới";

//s1 deletes reference to old string and points to the newly created one
           [s1]   -----|--------->|   "New String"   |
                       |          |                  |
                       |~~~~~~~~~X|   "Old String"   |
           [s2]   ------------------------^

Chuỗi ban đầu 'trong bộ nhớ' không thay đổi, nhưng biến tham chiếu đã được thay đổi để nó tham chiếu đến chuỗi mới. Và nếu chúng ta không có s2, "Chuỗi cũ" sẽ vẫn còn trong bộ nhớ nhưng chúng ta sẽ không thể truy cập nó ...


16

"bất biến" có nghĩa là bạn không thể thay đổi giá trị. Nếu bạn có một thể hiện của lớp String, bất kỳ phương thức nào bạn gọi có vẻ như sửa đổi giá trị, sẽ thực sự tạo ra một Chuỗi khác.

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

Để duy trì các thay đổi, bạn nên làm một cái gì đó như thế này foo = foo.sustring (3);

Bất biến vs mutable có thể buồn cười khi bạn làm việc với các bộ sưu tập. Hãy suy nghĩ về những gì sẽ xảy ra nếu bạn sử dụng đối tượng có thể thay đổi làm chìa khóa cho bản đồ và sau đó thay đổi giá trị (mẹo: nghĩ về equalshashCode).


13

java.time

Có thể hơi muộn nhưng để hiểu đối tượng bất biến là gì, hãy xem xét ví dụ sau từ API Ngày và Giờ Java 8 mới ( java.time ). Như bạn có thể biết tất cả các đối tượng ngày từ Java 8 là bất biến, vì vậy trong ví dụ sau

LocalDate date = LocalDate.of(2014, 3, 18); 
date.plusYears(2);
System.out.println(date);

Đầu ra:

2014 / 03-18

Điều này in cùng năm với ngày ban đầu vì plusYears(2) trả về một đối tượng mới nên ngày cũ vẫn không thay đổi vì đó là một đối tượng không thay đổi. Sau khi tạo, bạn không thể sửa đổi thêm và biến ngày vẫn trỏ đến nó.

Vì vậy, ví dụ mã đó sẽ nắm bắt và sử dụng đối tượng mới được khởi tạo và trả về bởi lệnh gọi đó plusYears.

LocalDate date = LocalDate.of(2014, 3, 18); 
LocalDate dateAfterTwoYears = date.plusYears(2);

date.toString () bản 2014 / 03-18

dateAfterTwoYears.toString () 2016-201-18


8

Tôi thực sự thích phần giải thích từ SCJP Sun Certified Lập trình viên cho Hướng dẫn học Java 5 .

Để làm cho Java hiệu quả hơn về bộ nhớ, JVM dành một vùng bộ nhớ đặc biệt gọi là "Nhóm hằng số chuỗi". Khi trình biên dịch gặp một chuỗi ký tự, nó sẽ kiểm tra nhóm để xem nếu một Chuỗi giống hệt đã tồn tại. Nếu một kết quả khớp được tìm thấy, tham chiếu đến nghĩa đen mới được hướng đến Chuỗi hiện có và không có đối tượng chuỗi ký tự Chuỗi mới nào được tạo.


Nó có thể làm điều này với bất kỳ đối tượng bất biến giống hệt nhau nào, nhưng tôi cho rằng sẽ mất quá nhiều thời gian chạy.
Zan Lynx

8

Các đối tượng không thay đổi không thể thay đổi trạng thái sau khi chúng được tạo.

Có ba lý do chính để sử dụng các đối tượng bất biến bất cứ khi nào bạn có thể, tất cả những điều này sẽ giúp giảm số lượng lỗi bạn giới thiệu trong mã của mình:

  • Sẽ dễ dàng hơn nhiều để lý giải về cách chương trình của bạn hoạt động khi bạn biết rằng trạng thái của một đối tượng không thể được thay đổi bằng phương pháp khác
  • Các đối tượng không thay đổi được tự động xử lý luồng an toàn (giả sử chúng được xuất bản an toàn), do đó sẽ không bao giờ là nguyên nhân gây ra các lỗi đa luồng khó xác định
  • Các đối tượng không thay đổi sẽ luôn có cùng mã Hash, vì vậy chúng có thể được sử dụng làm khóa trong HashMap (hoặc tương tự). Nếu mã băm của một phần tử trong bảng băm bị thay đổi, thì mục nhập bảng sẽ bị mất một cách hiệu quả, vì các nỗ lực tìm kiếm nó trong bảng sẽ kết thúc ở vị trí sai. Đây là lý do chính mà các đối tượng String là bất biến - chúng thường được sử dụng làm khóa HashMap.

Ngoài ra còn có một số tối ưu hóa khác mà bạn có thể tạo mã khi bạn biết rằng trạng thái của một đối tượng là bất biến - ví dụ, lưu trữ hàm băm được tính toán - nhưng đây là những tối ưu hóa và do đó gần như không thú vị.


5

Một ý nghĩa liên quan đến cách lưu trữ giá trị trong máy tính, ví dụ, đối với một chuỗi .Net, điều đó có nghĩa là chuỗi trong bộ nhớ không thể thay đổi, Khi bạn nghĩ rằng bạn đang thay đổi nó, thực tế bạn đang tạo một cái mới chuỗi trong bộ nhớ và trỏ biến hiện có (chỉ là một con trỏ tới tập hợp ký tự thực tế ở một nơi khác) vào chuỗi mới.


4
String s1="Hi";
String s2=s1;
s1="Bye";

System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
System.out.println(s1); //Bye

s1="Hi": một đối tượng s1đã được tạo với giá trị "Hi" trong đó.

s2=s1 : một đối tượng s2được tạo với tham chiếu đến đối tượng s1.

s1="Bye": s1giá trị của đối tượng trước không thay đổi vìs1 có loại Chuỗi và loại Chuỗi là loại không thay đổi, thay vào đó trình biên dịch tạo một đối tượng Chuỗi mới có giá trị "Bye" và được s1tham chiếu đến nó. ở đây khi chúng tôi in s2giá trị, kết quả sẽ là "Hi" chứ không phải "Tạm biệt" vì được s2tham chiếu đến s1đối tượng trước đó có giá trị "Hi".


bạn có thể thêm một chút giải thích?
minigeek

3

Bất biến có nghĩa là một khi đối tượng được tạo ra, không phải thành viên của nó sẽ thay đổi. Stringlà bất biến vì bạn không thể thay đổi nội dung của nó. Ví dụ:

String s1 = "  abc  ";
String s2 = s1.trim();

Trong đoạn mã trên, chuỗi s1 không thay đổi, một đối tượng khác ( s2) đã được tạo bằng cách sử dụng s1.


3

Bất biến đơn giản có nghĩa là không thể thay đổi hoặc không thể thay đổi. Khi đối tượng chuỗi được tạo, dữ liệu hoặc trạng thái của nó không thể thay đổi

Xem xét ví dụ dưới đây,

class Testimmutablestring{  
  public static void main(String args[]){  
    String s="Future";  
    s.concat(" World");//concat() method appends the string at the end  
    System.out.println(s);//will print Future because strings are immutable objects  
  }  
 }  

Hãy lấy ý tưởng xem xét sơ đồ dưới đây,

nhập mô tả hình ảnh ở đây

Trong sơ đồ này, bạn có thể thấy đối tượng mới được tạo là "Thế giới tương lai". Nhưng không thay đổi "Tương lai". Because String is immutable. s, vẫn đề cập đến "Tương lai". Nếu bạn cần gọi "Thế giới tương lai",

String s="Future";  
s=s.concat(" World");  
System.out.println(s);//print Future World

Tại sao các đối tượng chuỗi bất biến trong java?

Bởi vì Java sử dụng khái niệm chuỗi ký tự. Giả sử có 5 biến tham chiếu, tất cả đều đề cập đến một đối tượng "Tương lai". Nếu một biến tham chiếu thay đổi giá trị của đối tượng, nó sẽ bị ảnh hưởng đến tất cả các biến tham chiếu. Đó là lý do tại sao các đối tượng chuỗi là bất biến trong java.


2

Một khi đã được xác định, không thể thay đổi. Hãy xem xét một lớp mà một thể hiện của có thể được sử dụng làm khóa cho hàm băm hoặc tương tự. Kiểm tra các thực tiễn tốt nhất của Java.


0

Đối tượng bất biến

Một đối tượng được coi là bất biến nếu trạng thái của nó không thể thay đổi sau khi nó được xây dựng. Sự phụ thuộc tối đa vào các đối tượng bất biến được chấp nhận rộng rãi như một chiến lược hợp lý để tạo mã đơn giản, đáng tin cậy.

Các đối tượng bất biến đặc biệt hữu ích trong các ứng dụng đồng thời. Vì chúng không thể thay đổi trạng thái, chúng không thể bị hỏng do nhiễu luồng hoặc quan sát ở trạng thái không nhất quán.

Các lập trình viên thường miễn cưỡng sử dụng các đối tượng bất biến, bởi vì họ lo lắng về chi phí tạo ra một đối tượng mới trái ngược với việc cập nhật một đối tượng tại chỗ. Tác động của việc tạo đối tượng thường được đánh giá quá cao và có thể được bù đắp bằng một số hiệu quả liên quan đến các đối tượng bất biến. Chúng bao gồm giảm chi phí do thu gom rác và loại bỏ mã cần thiết để bảo vệ các đối tượng có thể thay đổi khỏi tham nhũng.

Các phần phụ sau đây có một lớp có các thể hiện là có thể thay đổi và xuất phát một lớp với các thể hiện bất biến từ nó. Khi làm như vậy, họ đưa ra các quy tắc chung cho loại chuyển đổi này và chứng minh một số lợi thế của các đối tượng bất biến.

Nguồn


0

Vì câu trả lời được chấp nhận không trả lời tất cả các câu hỏi. Tôi buộc phải đưa ra câu trả lời sau 11 năm 6 tháng.

Ai đó có thể làm rõ những gì có nghĩa là bất biến?

Hy vọng bạn có nghĩa là đối tượng bất khả xâm phạm (bởi vì chúng ta có thể nghĩ về tài liệu tham khảo bất biến ).

Một đối tượng là bất biến : iff một khi được tạo, chúng luôn đại diện cho cùng một giá trị (không có bất kỳ phương thức nào thay đổi giá trị).

Tại sao là một Stringbất biến?

Tôn trọng định nghĩa trên có thể được kiểm tra bằng cách xem mã nguồn Sting.java .

Những lợi thế / bất lợi của các đối tượng bất biến là gì? loại bất biến là:

  • an toàn hơn từ bọ.

  • Dễ hiểu.

  • và sẵn sàng hơn để thay đổi.

Tại sao một đối tượng có thể thay đổi như StringBuilder nên được ưu tiên hơn String và ngược lại?

Thu hẹp câu hỏi Tại sao chúng ta cần StringBuilder có thể thay đổi trong lập trình? Một cách sử dụng phổ biến cho nó là nối một số lượng lớn các chuỗi với nhau, như thế này:

String s = "";
for (int i = 0; i < n; ++i) {
    s = s + n;
}

Sử dụng các chuỗi bất biến, điều này tạo ra rất nhiều bản sao tạm thời - số đầu tiên của chuỗi ("0") thực sự được sao chép n lần trong quá trình xây dựng chuỗi cuối cùng, số thứ hai được sao chép n-1 lần, và vì vậy trên. Nó thực sự tốn thời gian O (n2) chỉ để thực hiện tất cả việc sao chép đó, mặc dù chúng tôi chỉ kết hợp n phần tử.

StringBuilder được thiết kế để giảm thiểu việc sao chép này. Nó sử dụng cấu trúc dữ liệu nội bộ đơn giản nhưng thông minh để tránh thực hiện bất kỳ sao chép nào cho đến khi kết thúc, khi bạn yêu cầu Chuỗi cuối cùng với lệnh gọi toString ():

StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
  sb.append(String.valueOf(n));
}
String s = sb.toString();

Có được hiệu suất tốt là một lý do tại sao chúng tôi sử dụng các đối tượng có thể thay đổi. Một cách khác là chia sẻ thuận tiện: hai phần trong chương trình của bạn có thể giao tiếp thuận tiện hơn bằng cách chia sẻ cấu trúc dữ liệu có thể thay đổi chung.

Nhiều hơn có thể được tìm thấy ở đây: https://web.mit.edu/6.005/www/fa15/groupes/09-immutability/#usiously_immutable_types


-1

Một đối tượng bất biến là đối tượng bạn không thể sửa đổi sau khi tạo. Một ví dụ điển hình là chuỗi ký tự.

Ngôn ngữ lập trình AD, ngày càng trở nên phổ biến, có khái niệm "bất biến" thông qua từ khóa "bất biến". Kiểm tra bài viết này của Dr.Dobb về nó - http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29 . Nó giải thích vấn đề một cách hoàn hảo.


Tôi tin rằng từ D 2.020, từ khóa đã được thay đổi từ bất biến thành bất biến. Tôi không thấy một điểm, nhưng nó nói, "bất biến bây giờ được thực hiện." digitalmars.com/d/2.0/changelog.html#new2_020
he_the_great
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.