Tôi không thể hiểu được các tình huống mà chúng ta cần một lớp bất biến là gì.
Bạn đã bao giờ phải đối mặt với bất kỳ yêu cầu nào như vậy chưa? hoặc bạn có thể vui lòng cho chúng tôi bất kỳ ví dụ thực tế nào mà chúng tôi nên sử dụng mẫu này không.
Tôi không thể hiểu được các tình huống mà chúng ta cần một lớp bất biến là gì.
Bạn đã bao giờ phải đối mặt với bất kỳ yêu cầu nào như vậy chưa? hoặc bạn có thể vui lòng cho chúng tôi bất kỳ ví dụ thực tế nào mà chúng tôi nên sử dụng mẫu này không.
Câu trả lời:
Các câu trả lời khác dường như quá tập trung vào việc giải thích tại sao tính bất biến là tốt. Nó rất tốt và tôi sử dụng nó bất cứ khi nào có thể. Tuy nhiên, đó không phải là câu hỏi của bạn . Tôi sẽ xem xét từng điểm câu hỏi của bạn để cố gắng đảm bảo rằng bạn nhận được câu trả lời và ví dụ mà bạn cần.
Tôi không thể hiểu được các tình huống mà chúng ta cần một lớp bất biến là gì.
"Cần" ở đây là một thuật ngữ tương đối. Các lớp bất biến là một mẫu thiết kế, giống như bất kỳ mô hình / mẫu / công cụ nào, ở đó để làm cho phần mềm xây dựng dễ dàng hơn. Tương tự, rất nhiều mã đã được viết trước khi mô hình OO xuất hiện, nhưng hãy đếm tôi trong số các lập trình viên "cần" OO. Các lớp bất biến, như OO, không hoàn toàn cần thiết , nhưng tôi sẽ hành động như thể tôi cần chúng.
Bạn đã bao giờ phải đối mặt với bất kỳ yêu cầu nào như vậy chưa?
Nếu bạn không nhìn các đối tượng trong miền vấn đề với góc nhìn phù hợp, bạn có thể không thấy yêu cầu đối với một đối tượng bất biến. Có thể dễ dàng nghĩ rằng miền có vấn đề không yêu cầu bất kỳ lớp bất biến nào nếu bạn không biết khi nào sử dụng chúng một cách thuận lợi.
Tôi thường sử dụng các lớp không thay đổi trong đó tôi coi một đối tượng nhất định trong miền vấn đề của mình như một giá trị hoặc trường hợp cố định . Khái niệm này đôi khi phụ thuộc vào quan điểm hoặc góc nhìn, nhưng lý tưởng nhất, nó sẽ dễ dàng chuyển sang quan điểm đúng để xác định các đối tượng ứng viên tốt.
Bạn có thể hiểu rõ hơn về nơi mà các đối tượng bất biến thực sự hữu ích (nếu không thực sự cần thiết) bằng cách đảm bảo rằng bạn đã đọc nhiều sách / bài báo trực tuyến khác nhau để phát triển ý thức tốt về cách suy nghĩ về các lớp bất biến. Một bài viết hay để giúp bạn bắt đầu là lý thuyết và thực hành Java: Đột biến hay không đột biến?
Tôi sẽ cố gắng đưa ra một vài ví dụ dưới đây về cách người ta có thể nhìn thấy các đối tượng ở các góc độ khác nhau (có thể thay đổi và không thể thay đổi) để làm rõ ý tôi theo quan điểm.
... bạn có thể vui lòng cho chúng tôi bất kỳ ví dụ thực tế nào mà chúng tôi nên sử dụng mẫu này không.
Vì bạn đã yêu cầu các ví dụ thực tế, tôi sẽ cung cấp cho bạn một số, nhưng trước tiên, hãy bắt đầu với một số ví dụ cổ điển.
Đối tượng giá trị cổ điển
Chuỗi và số nguyên thường được coi là giá trị. Do đó, không ngạc nhiên khi thấy rằng lớp String và lớp Integer wrapper (cũng như các lớp wrapper khác) là bất biến trong Java. Một màu thường được coi là một giá trị, do đó, lớp Màu bất biến.
Counterexample
Ngược lại, một chiếc xe hơi thường không được coi là một vật có giá trị. Tạo mẫu xe hơi thường có nghĩa là tạo ra một lớp có trạng thái thay đổi (đồng hồ đo đường, tốc độ, mức nhiên liệu, v.v.). Tuy nhiên, có một số miền mà nó có thể là một đối tượng giá trị. Ví dụ: một chiếc ô tô (hoặc cụ thể là một mẫu ô tô) có thể được coi là một đối tượng giá trị trong một ứng dụng để tra cứu loại dầu động cơ phù hợp cho một loại xe nhất định.
Đang chơi bài
Bao giờ viết một chương trình chơi bài? Tôi đã làm. Tôi có thể đã đại diện cho một thẻ chơi như một vật thể có thể thay đổi với một bộ đồ và thứ hạng có thể thay đổi. Một ván bài poker rút thăm có thể là 5 trường hợp cố định trong đó việc thay thế lá bài thứ 5 trong tay tôi có nghĩa là biến đổi bài chơi thứ 5 thành một lá bài mới bằng cách thay đổi bộ đồ và xếp hạng của nó.
Tuy nhiên, tôi có xu hướng nghĩ về một thẻ chơi như một vật thể bất biến có một bộ đồ và thứ hạng cố định không thay đổi sau khi được tạo ra. Ván bài poker rút của tôi sẽ có 5 phiên bản và việc thay thế một lá bài trong tay tôi sẽ liên quan đến việc loại bỏ một trong những phiên bản đó và thêm một phiên bản ngẫu nhiên mới vào ván bài của tôi.
Phép chiếu bản đồ
Một ví dụ cuối cùng là khi tôi làm việc trên một số mã bản đồ nơi bản đồ có thể tự hiển thị trong các phép chiếu khác nhau . Mã ban đầu có bản đồ sử dụng một phiên bản phép chiếu cố định nhưng có thể thay đổi (giống như thẻ chơi có thể thay đổi ở trên). Thay đổi phép chiếu bản đồ có nghĩa là làm thay đổi các hình thức chiếu của bản đồ (kiểu chiếu, điểm trung tâm, thu phóng, v.v.).
Tuy nhiên, tôi cảm thấy thiết kế đơn giản hơn nếu tôi coi một phép chiếu như một giá trị bất biến hoặc trường hợp cố định. Thay đổi phép chiếu bản đồ có nghĩa là bản đồ tham chiếu đến một thể hiện phép chiếu khác chứ không phải thay đổi thể hiện phép chiếu cố định của bản đồ. Điều này cũng làm cho việc chụp các phép chiếu có tên như MERCATOR_WORLD_VIEW
.
Các lớp bất biến nói chung đơn giản hơn nhiều để thiết kế, triển khai và sử dụng đúng cách . Một ví dụ là String: việc triển khai java.lang.String
đơn giản hơn đáng kể so với std::string
trong C ++, chủ yếu là do tính bất biến của nó.
Một lĩnh vực cụ thể mà tính không thay đổi tạo ra sự khác biệt đặc biệt lớn là tính đồng thời: các đối tượng bất biến có thể được chia sẻ một cách an toàn giữa nhiều luồng , trong khi các đối tượng có thể thay đổi phải được tạo ra an toàn cho luồng thông qua thiết kế và triển khai cẩn thận - thường thì điều này không phải là một nhiệm vụ tầm thường.
Cập nhật: Phiên bản Java thứ 2 hiệu quả giải quyết vấn đề này một cách chi tiết - xem Mục 15: Giảm thiểu khả năng thay đổi .
Xem thêm các bài viết liên quan này:
Java hiệu quả của Joshua Bloch nêu ra một số lý do để viết các lớp bất biến:
Nói chung, bạn nên làm cho một đối tượng không thể thay đổi được trừ khi có vấn đề nghiêm trọng về hiệu suất. Trong những trường hợp như vậy, các đối tượng trình tạo có thể thay đổi có thể được sử dụng để xây dựng các đối tượng bất biến, ví dụ: StringBuilder
Hashmap là một ví dụ cổ điển. Điều bắt buộc là chìa khóa của bản đồ là bất biến. Nếu khóa không phải là bất biến và bạn thay đổi một giá trị trên khóa sao cho hashCode () sẽ dẫn đến một giá trị mới, thì bản đồ hiện đã bị hỏng (một khóa hiện nằm sai vị trí trong bảng băm.).
Java thực tế là một và tất cả các tham chiếu. Đôi khi một thể hiện được tham chiếu nhiều lần. Nếu bạn thay đổi một trường hợp như vậy, nó sẽ được phản ánh trong tất cả các tham chiếu của nó. Đôi khi bạn chỉ đơn giản là không muốn có điều này để cải thiện tính mạnh mẽ và an toàn của chuỗi. Sau đó, một lớp không thay đổi sẽ hữu ích để người ta buộc phải tạo một thể hiện mới và gán lại nó cho tham chiếu hiện tại. Bằng cách này, phiên bản gốc của các tham chiếu khác vẫn không bị ảnh hưởng.
Hãy tưởng tượng Java sẽ trông như thế nào nếu String
có thể thay đổi được.
Date
và Calendar
có thể thay đổi. Ồ, chờ đã, họ rồi, OH SH
String
có thể thay đổi! (gợi ý: một số phiên bản JRockit cũ hơn). Gọi string.trim () kết quả trong chuỗi ban đầu được tỉa
Chúng ta không cần các lớp bất biến, nhưng chắc chắn chúng có thể làm cho một số tác vụ lập trình dễ dàng hơn, đặc biệt khi có nhiều luồng tham gia. Bạn không phải thực hiện bất kỳ khóa nào để truy cập vào một đối tượng không thể thay đổi và mọi dữ kiện mà bạn đã thiết lập về một đối tượng như vậy sẽ tiếp tục đúng trong tương lai.
Hãy lấy một trường hợp cực đoan: hằng số nguyên. Nếu tôi viết một tuyên bố như "x = x + 1", tôi muốn bộc bạch 100% rằng số "1" bằng cách nào đó sẽ không trở thành 2, bất kể điều gì xảy ra ở bất kỳ nơi nào khác trong chương trình.
Bây giờ không sao, hằng số nguyên không phải là một lớp, nhưng khái niệm thì giống nhau. Giả sử tôi viết:
String customerId=getCustomerId();
String customerName=getCustomerName(customerId);
String customerBalance=getCustomerBalance(customerid);
Trông đơn giản thôi. Nhưng nếu Chuỗi không phải là bất biến, thì tôi sẽ phải xem xét khả năng getCustomerName có thể thay đổi customerId, để khi tôi gọi getCustomerBalance, tôi sẽ nhận được số dư cho một khách hàng khác. Bây giờ bạn có thể nói, "Tại sao ai đó viết hàm getCustomerName lại khiến nó thay đổi id? Điều đó chẳng có nghĩa lý gì." Nhưng đó chính xác là nơi bạn có thể gặp rắc rối. Người viết đoạn mã trên có thể hiểu rằng các hàm sẽ không thay đổi tham số. Sau đó, ai đó đi cùng phải sửa đổi cách sử dụng khác của chức năng đó để xử lý trường hợp khách hàng có nhiều tài khoản dưới cùng một tên. Và anh ấy nói, "Ồ, đây là chức năng tên getCustomer tiện dụng này đã tìm kiếm tên. Tôi '
Tính bất biến đơn giản có nghĩa là một lớp đối tượng nào đó là hằng số, và chúng ta có thể coi chúng là hằng số.
(Tất nhiên người dùng có thể gán một "đối tượng hằng số" khác cho một biến. Ai đó có thể viết String s = "hello"; và sau đó viết s = "tạm biệt"; Trừ khi tôi tạo biến cuối cùng, tôi không thể chắc chắn rằng nó không bị thay đổi trong khối mã của riêng tôi. Giống như các hằng số nguyên đảm bảo với tôi rằng "1" luôn là một số giống nhau, nhưng không phải "x = 1" sẽ không bao giờ bị thay đổi bằng cách viết "x = 2". Nhưng tôi có thể tự tin rằng nếu tôi có một xử lý đối với một đối tượng bất biến, thì không hàm nào tôi chuyển nó vào có thể thay đổi nó đối với tôi, hoặc nếu tôi tạo hai bản sao của nó, thì thay đổi đối với biến giữ một bản sao sẽ không thay đổi khác. v.v.
Có nhiều lý do cho tính bất biến:
String
lớp học.Vì vậy, nếu bạn muốn gửi dữ liệu thông qua một dịch vụ mạng và bạn muốn cảm giác đảm bảo rằng bạn sẽ có kết quả giống hệt như những gì bạn đã gửi, hãy đặt nó là bất biến.
final
trong Java đều không thay đổi và không phải tất cả các lớp không thay đổi đều được gắn cờ final
.
Tôi sẽ tấn công điều này từ một góc độ khác. Tôi thấy các đối tượng bất biến giúp cuộc sống của tôi dễ dàng hơn khi đọc mã.
Nếu tôi có một đối tượng có thể thay đổi, tôi không bao giờ chắc chắn giá trị của nó là gì nếu nó từng được sử dụng bên ngoài phạm vi trước mắt của tôi. Giả sử tôi tạo MyMutableObject
các biến cục bộ của một phương thức, điền vào nó các giá trị, sau đó chuyển nó cho năm phương thức khác. BẤT KỲ MỘT trong những phương thức đó có thể thay đổi trạng thái của đối tượng của tôi, vì vậy một trong hai điều phải xảy ra:
Điều đầu tiên làm cho việc lập luận về mã của tôi trở nên khó khăn. Điều thứ hai làm cho mã của tôi kém hiệu suất - về cơ bản tôi đang bắt chước một đối tượng bất biến với ngữ nghĩa copy-on-write, nhưng làm điều đó mọi lúc cho dù các phương thức được gọi có thực sự sửa đổi trạng thái đối tượng của tôi hay không.
Nếu tôi sử dụng thay vào đó MyImmutableObject
, tôi có thể yên tâm rằng những gì tôi đặt chính là giá trị sẽ có trong vòng đời của phương pháp của tôi. Không có "hành động ma quái nào ở khoảng cách xa" sẽ thay đổi nó so với tôi và tôi không cần thiết phải tạo các bản sao phòng thủ của đối tượng của mình trước khi sử dụng năm phương pháp khác. Nếu các phương thức khác muốn thay đổi mọi thứ vì mục đích của chúng, chúng phải tạo bản sao - nhưng chúng chỉ làm điều này nếu chúng thực sự phải tạo một bản sao (trái ngược với việc tôi làm trước mỗi lần gọi phương thức bên ngoài). Tôi dành cho mình nguồn lực tinh thần để theo dõi các phương pháp thậm chí có thể không có trong tệp nguồn hiện tại của tôi và tôi dự phòng hệ thống không ngừng tạo ra các bản sao phòng thủ không cần thiết đề phòng.
(Nếu tôi ra ngoài thế giới Java và vào, chẳng hạn như thế giới C ++, trong số những thứ khác, tôi có thể gặp khó khăn hơn. Tôi có thể làm cho các đối tượng xuất hiện như thể chúng có thể thay đổi được, nhưng đằng sau hậu trường làm cho chúng sao chép một cách rõ ràng trên bất kỳ kiểu thay đổi trạng thái — đó là copy-on-write — không ai khôn ngoan hơn.)
2 xu của tôi cho những vị khách trong tương lai:
2 tình huống trong đó các đối tượng không thay đổi là lựa chọn tốt là:
Các vấn đề đồng thời trong môi trường đa luồng rất có thể được giải quyết bằng cách đồng bộ hóa nhưng đồng bộ hóa là một vấn đề tốn kém (sẽ không tìm hiểu ở đây về "lý do"), vì vậy nếu bạn đang sử dụng các đối tượng không thay đổi thì không có đồng bộ hóa để giải quyết vấn đề đồng thời vì trạng thái Các đối tượng không thể thay đổi không thể thay đổi và nếu trạng thái không thể thay đổi thì tất cả các luồng có thể truy cập liền mạch đối tượng. Vì vậy, các đối tượng không thay đổi là một lựa chọn tuyệt vời cho các đối tượng được chia sẻ trong môi trường đa luồng.
Một trong những điều quan trọng nhất cần lưu ý khi làm việc với bộ sưu tập dựa trên băm là khóa phải hashCode()
luôn trả về cùng một giá trị cho thời gian tồn tại của đối tượng, bởi vì nếu giá trị đó bị thay đổi thì mục nhập cũ sẽ được thực hiện trong bộ sưu tập dựa trên băm sử dụng đối tượng đó không thể được truy xuất, do đó nó sẽ gây ra rò rỉ bộ nhớ. Vì trạng thái của các đối tượng không thể thay đổi không thể thay đổi nên chúng là một lựa chọn tuyệt vời làm chìa khóa trong bộ sưu tập dựa trên băm. Vì vậy, nếu bạn đang sử dụng đối tượng không thay đổi làm khóa cho bộ sưu tập dựa trên băm thì bạn có thể chắc chắn rằng sẽ không có bất kỳ rò rỉ bộ nhớ nào vì điều đó (tất nhiên vẫn có thể bị rò rỉ bộ nhớ khi đối tượng được sử dụng làm khóa không được tham chiếu từ bất kỳ đâu khác, nhưng đó không phải là vấn đề ở đây).
Các đối tượng bất biến là các thể hiện mà trạng thái của nó không thay đổi khi được khởi tạo. Việc sử dụng các đối tượng như vậy là yêu cầu cụ thể.
Lớp bất biến tốt cho mục đích lưu vào bộ nhớ đệm và nó an toàn cho luồng.
Nhờ tính bất biến, bạn có thể chắc chắn rằng hành vi / trạng thái của đối tượng bất biến bên dưới không thay đổi, nhờ đó bạn có thêm lợi thế khi thực hiện các thao tác bổ sung:
Bạn có thể sử dụng nhiều lõi / xử lý (xử lý đồng thời / song song ) một cách dễ dàng (vì trình tự hoạt động sẽ không còn quan trọng nữa.)
Có thể làm bộ nhớ đệm cho các hoạt động tốn kém (vì bạn chắc chắn về cùng một
kết quả).
Có thể gỡ lỗi một cách dễ dàng (vì lịch sử chạy sẽ không còn là mối quan tâm
nữa)
Sử dụng từ khóa cuối cùng không nhất thiết phải làm cho một cái gì đó bất biến:
public class Scratchpad {
public static void main(String[] args) throws Exception {
SomeData sd = new SomeData("foo");
System.out.println(sd.data); //prints "foo"
voodoo(sd, "data", "bar");
System.out.println(sd.data); //prints "bar"
}
private static void voodoo(Object obj, String fieldName, Object value) throws Exception {
Field f = SomeData.class.getDeclaredField("data");
f.setAccessible(true);
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.set(obj, "bar");
}
}
class SomeData {
final String data;
SomeData(String data) {
this.data = data;
}
}
Chỉ là một ví dụ để chứng minh rằng từ khóa "cuối cùng" ở đó để ngăn lỗi của lập trình viên và không nhiều hơn thế. Trong khi việc chỉ định lại giá trị thiếu từ khóa cuối cùng có thể dễ dàng xảy ra một cách tình cờ, việc thay đổi giá trị đến độ dài này sẽ phải được thực hiện có chủ ý. Nó ở đó để cung cấp tài liệu và ngăn lỗi của lập trình viên.
Cấu trúc dữ liệu bất biến cũng có thể giúp ích khi viết mã các thuật toán đệ quy. Ví dụ: giả sử bạn đang cố gắng giải quyết vấn đề 3SAT . Một cách là làm như sau:
Nếu bạn có cấu trúc có thể thay đổi để đại diện cho vấn đề, thì khi bạn đơn giản hóa trường hợp trong nhánh TRUE, bạn sẽ phải:
Tuy nhiên, nếu bạn viết mã nó một cách thông minh, bạn có thể có một cấu trúc bất biến, trong đó bất kỳ thao tác nào đều trả về phiên bản cập nhật (nhưng vẫn không thay đổi) của vấn đề (tương tự như String.replace
- nó không thay thế chuỗi, chỉ cung cấp cho bạn một phiên bản mới ). Cách đơn giản để thực hiện điều này là có cấu trúc "bất biến" chỉ cần sao chép và tạo một cấu trúc mới trên bất kỳ sửa đổi nào, giảm nó thành giải pháp thứ 2 khi có một cấu trúc có thể thay đổi, với tất cả chi phí đó, nhưng bạn có thể làm điều đó bằng nhiều cách khác cách hiệu quả.
Một trong những lý do giải thích cho "nhu cầu" đối với các lớp không thay đổi là sự kết hợp của việc truyền mọi thứ bằng tham chiếu và không hỗ trợ các chế độ xem chỉ đọc của một đối tượng (tức là của C ++ const
).
Hãy xem xét trường hợp đơn giản của một lớp có hỗ trợ cho mẫu người quan sát:
class Person {
public string getName() { ... }
public void registerForNameChange(NameChangedObserver o) { ... }
}
Nếu string
không phải là bất biến, lớp sẽ không thể Person
triển khai registerForNameChange()
chính xác, bởi vì ai đó có thể viết như sau, sửa đổi hiệu quả tên của người đó mà không kích hoạt bất kỳ thông báo nào.
void foo(Person p) {
p.getName().prepend("Mr. ");
}
Trong C ++, getName()
trả về a const std::string&
có tác dụng trả về bằng tham chiếu và ngăn truy cập vào các trình đột biến, có nghĩa là các lớp bất biến không cần thiết trong ngữ cảnh đó.
Họ cũng cung cấp cho chúng tôi một sự đảm bảo. Đảm bảo tính bất biến có nghĩa là chúng ta có thể mở rộng chúng và tạo ra những người sáng chế mới để đạt được hiệu quả mà nếu không thì không thể.
Một tính năng của các lớp bất biến vẫn chưa được gọi ra: lưu trữ một tham chiếu đến một đối tượng lớp không thay đổi sâu là một phương tiện hiệu quả để lưu trữ tất cả trạng thái có trong đó. Giả sử tôi có một đối tượng có thể thay đổi sử dụng một đối tượng không thể thay đổi sâu để chứa 50K thông tin trạng thái. Ngoài ra, giả sử rằng tôi muốn trong 25 lần tạo một "bản sao" của đối tượng ban đầu (có thể thay đổi) của tôi (ví dụ: đối với bộ đệm "hoàn tác"); trạng thái có thể thay đổi giữa các hoạt động sao chép, nhưng thường thì không. Tạo một "bản sao" của đối tượng có thể thay đổi sẽ chỉ yêu cầu sao chép một tham chiếu đến trạng thái bất biến của nó, vì vậy 20 bản sao chỉ đơn giản là 20 tham chiếu. Ngược lại, nếu trạng thái được giữ trong các đối tượng có thể thay đổi trị giá 50K, thì mỗi trong số 25 hoạt động sao chép sẽ phải tạo ra bản sao dữ liệu trị giá 50K của chính nó; giữ tất cả 25 bản sao sẽ yêu cầu giữ hơn một meg dữ liệu chủ yếu được sao chép. Mặc dù thao tác sao chép đầu tiên sẽ tạo ra một bản sao dữ liệu sẽ không bao giờ thay đổi và 24 thao tác khác về lý thuyết có thể chỉ đơn giản là tham chiếu lại điều đó, trong hầu hết các lần triển khai sẽ không có cách nào để đối tượng thứ hai yêu cầu bản sao của thông tin để biết rằng một bản sao bất biến đã tồn tại (*).
(*) Một mẫu đôi khi có thể hữu ích là các đối tượng có thể thay đổi phải có hai trường để giữ trạng thái của chúng - một ở dạng có thể thay đổi và một ở dạng bất biến. Các đối tượng có thể được sao chép dưới dạng có thể thay đổi hoặc bất biến và sẽ bắt đầu hoạt động với một hoặc tập tham chiếu khác. Ngay khi đối tượng muốn thay đổi trạng thái của nó, nó sẽ sao chép tham chiếu bất biến tới tham chiếu có thể thay đổi (nếu nó chưa được thực hiện) và vô hiệu hóa tham chiếu bất biến. Khi đối tượng được sao chép dưới dạng bất biến, nếu tham chiếu không thay đổi của nó không được đặt, một bản sao không thay đổi sẽ được tạo và tham chiếu không thay đổi được trỏ đến đó. Cách tiếp cận này sẽ yêu cầu một vài thao tác sao chép nhiều hơn so với "bản sao chính thức khi ghi" (ví dụ: yêu cầu sao chép một đối tượng đã bị đột biến vì bản sao cuối cùng sẽ yêu cầu thao tác sao chép,
Tại sao lớp Immutable?
Khi một đối tượng được khởi tạo thì trạng thái đó không thể thay đổi trong suốt thời gian tồn tại. Điều này cũng làm cho nó an toàn.
Ví dụ:
Rõ ràng là String, Integer và BigDecimal, v.v. Một khi những giá trị này được tạo ra thì không thể thay đổi trong suốt cuộc đời.
Ca sử dụng: Khi đối tượng kết nối Cơ sở dữ liệu được tạo với các giá trị cấu hình của nó, bạn có thể không cần thay đổi trạng thái của nó, nơi bạn có thể sử dụng một lớp không thay đổi
từ Java hiệu quả; Một lớp bất biến chỉ đơn giản là một lớp mà các thể hiện của nó không thể sửa đổi được. Tất cả thông tin có trong mỗi cá thể được cung cấp khi nó được tạo và được cố định trong suốt thời gian tồn tại của đối tượng. Các thư viện nền tảng Java chứa nhiều lớp bất biến, bao gồm String, các lớp nguyên thủy đóng hộp, BigInte- ger và BigDecimal. Có nhiều lý do chính đáng cho điều này: Các lớp bất biến dễ thiết kế, triển khai và sử dụng hơn các lớp có thể thay đổi. Chúng ít bị lỗi hơn và an toàn hơn.