詳解Java如何實現(xiàn)一個像String一樣不可變的類
如果問你在日常開發(fā)中用到的最多的一個 Java
類是什么,阿粉敢打賭絕對是 String.class
。說到 String
大家都知道 String
是一個不可變的類;雖然用的很多,那不知道小伙伴們有沒有想過怎么樣創(chuàng)建一個自己的不可變的類呢?這篇文章阿粉就帶大家來實踐一下,創(chuàng)建一個自己的不可變的類。
特性
在手動編寫代碼之前,我們先了解一下不可變類都有哪些特性,
- 定義類的時候需要使用
final
關鍵字進行修飾:之所以使用final
進行修飾是因為這樣可以避免被其他類繼承,一旦有了子類繼承就會破壞父類的不可變性機制; - 成員變量需要使用
fina
l 關鍵詞修飾,并且需要是private
的:避免屬性被外部修改; - 成員變量不可提供
setter
方法,只能提供getter
方法:避免被外部修改,并且避免返回成員變量本身; - 提供所有字段的構造函數(shù);
實操
知道了不可變類的一些基本特性之后,我們來實際寫代碼操作一下,以及我們會驗證一下,如果不按照上面的要求來編寫的話,會出現(xiàn)什么樣的問題。
這里我們定義一個 Teacher
類來測試一下,按照我們上面提到的幾點,我們給類和屬性的定義都加上 final
代碼如下所示。
package?com.example.demo.immutable; import?java.util.List; import?java.util.Map; public?final?class?Teacher?{ ??private?final?String?name; ??private?final?List<String>?students; ??private?final?Address?address; ??private?final?Map<String,?String>?metadata; ??public?Teacher(String?name,?List<String>?students,?Address?address,?Map<String,?String>?metadata)?{ ????this.name?=?name; ????this.students?=?students; ????this.address?=?address; ????this.metadata?=?metadata; ??} ??public?String?getName()?{ ????return?name; ??} ??public?List<String>?getStudents()?{ ????return?students; ??} ??public?Address?getAddress()?{ ????return?address; ??} ??public?Map<String,?String>?getMetadata()?{ ????return?metadata; ??} }
package?com.example.demo.immutable; public?class?Address?{ ??private?String?country; ??private?String?city; ??public?String?getCountry()?{ ????return?country; ??} ??public?void?setCountry(String?country)?{ ????this.country?=?country; ??} ??public?String?getCity()?{ ????return?city; ??} ??public?void?setCity(String?city)?{ ????this.city?=?city; ??} }
我們思考一下,上面的代碼是否真正的做到了不可變,好,我們思考三秒鐘,心里默默的數(shù)三下。為了回答這個問題,我們看下下面的測試代碼。
package?com.example.demo; import?com.example.demo.immutable.Address; import?com.example.demo.immutable.Teacher; import?java.util.ArrayList; import?java.util.HashMap; import?java.util.List; import?java.util.Map; /** ?*?<br> ?*?<b>Function:</b><br> ?*?<b>Author:</b>@author?Silence<br> ?*?<b>Date:</b>2022-11-22 21:17<br> ?*?<b>Desc:</b>無<br> ?*/ public?class?ImmutableDemo?{ ??public?static?void?main(String[]?args)?{ ????List<String>?students?=?new?ArrayList<>(); ????students.add("鴨血粉絲?1"); ????students.add("鴨血粉絲?2"); ????students.add("鴨血粉絲?3"); ????Address?address?=?new?Address(); ????address.setCountry("中國"); ????address.setCity("深圳"); ????Map<String,?String>?metadata?=?new?HashMap<>(); ????metadata.put("hobby",?"籃球"); ????metadata.put("age",?"29"); ????Teacher?teacher?=?new?Teacher("Java極客技術",?students,?address,?metadata); ????System.out.println(teacher.getStudents().size()); ????System.out.println(teacher.getMetadata().size()); ????System.out.println(teacher.getAddress().getCity()); ????//?修改屬性 ????teacher.getStudents().add("小明"); ????teacher.getMetadata().put("weight",?"120"); ????teacher.getAddress().setCity("廣州"); ????System.out.println(teacher.getStudents().size()); ????System.out.println(teacher.getMetadata().size()); ????System.out.println(teacher.getAddress().getCity()); ??} }
運行的結果如下截圖所示,通過測試我們可以發(fā)現(xiàn),簡單的只添加 final
關鍵字是不能解決不可變性的,我們當前的 teacher
實例已經被外層修改掉了成員變量。
為了解決這個問題,我們還需要對我們的 Teacher
類進行改造,首先我們可以想到的就是需要將 students
和 metadata
兩個成員變量不能直接返回給外層,否則外層的修改會直接影響到我們的不可變類,那么我們就可以修改 getter
方法,拷貝一下成員變量進行返回,而不是直接返回,修改代碼如下
??public?List<String>?getStudents()?{ ????return?new?ArrayList<>(students); ????//return?students; ??} ????public?Map<String,?String>?getMetadata()?{ ????return?new?HashMap<>(metadata); ??//return?metadata; ??}
我們再次運行上面的測試代碼,可以看到這次的返回數(shù)據(jù)如下,這次我們的 students
和 metadate
成員變量并沒有被外層修改掉了。但是我們的 address
成員變量還是有問題,沒關系,我們接著往下看。
很自然的為了解決 address
的問題,我們想到了也是進行一個拷貝,再調用 getter
方法的時候返回一個拷貝對象,而不是直接返回成員變量。那我們就需要改造 Address
類,將其變成 Cloneable
的即可,我們實現(xiàn) 接口,然后覆蓋一個 clone
方法,代碼如下
package?com.example.demo.immutable; public?class?Address?implements?Cloneable{ ??...//?省略 ??@Override ??public?Address?clone()?{ ????try?{ ??????return?(Address)?super.clone(); ????}?catch?(CloneNotSupportedException?e)?{ ??????throw?new?AssertionError(); ????} ??} }
再修改 Teacher
的 getAddress
方法
??public?Address?getAddress()?{ ??//return?address; ????return?address.clone(); ??}
接下來我們再運行一下測試代碼,結果如下,可以看到這次我們的 teacher
實例的成員變量并沒有被修改掉了,至此我們完成了一個不可變對象的創(chuàng)建!
String 的實現(xiàn)
前面我們看的是自定義實現(xiàn)不可變類的操作,接下來我們簡單看一下 String
類是如何實現(xiàn)不可變的,通過源碼我們可以看到 String
也使用了關鍵字 final
來避免被子類繼承,以及對應存放具體值的成員變量也使用了 final
關鍵字。
并且對外提供的方法 substring
也是通過復制的形式對外提供的新的 String
對象。
到此這篇關于詳解Java如何實現(xiàn)一個像String一樣不可變的類的文章就介紹到這了,更多相關Java不可變的類內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot+Kotlin中使用GRPC實現(xiàn)服務通信的示例代碼
本文主要介紹了SpringBoot+Kotlin中使用GRPC實現(xiàn)服務通信的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-07-07Java實現(xiàn)Map遍歷key-value的四種方法
本文主要介紹了Java實現(xiàn)Map遍歷key-value的四種方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07關于Java中try finally return語句的執(zhí)行順序淺析
這篇文章主要介紹了關于Java中try finally return語句的執(zhí)行順序淺析,需要的朋友可以參考下2017-08-08kafka 重新分配partition和調整replica的數(shù)量實現(xiàn)
當需要提升Kafka集群的性能和負載均衡時,可通過kafka-reassign-partitions.sh命令手動重新分配Partition,增加節(jié)點后,可以將Topic的Partition的Leader節(jié)點均勻分布,以提高寫入和消費速度,感興趣的可以了解一下2022-03-03