不可變對象
如果一個對象的狀態在構造后不能改變,則該對象被認為是不可變的,對不可變對象的最大依賴被廣泛認為是一種創建簡單、可靠代碼的合理策略。
不可變對象在并發應用程序中特別有用,由于它們不能改變狀態,因此它們不會被線程干擾破壞或在不一致的狀態下觀察。
程序員通常不愿意使用不可變對象,因為他們擔心創建新對象的成本而不是就地更新對象的成本,對象創建的影響經常被高估,并且可以通過與不可變對象相關聯的一些效率來抵消,這些包括由于垃圾收集而減少的開銷,以及消除保護可變對象免于損壞所需的代碼。
以下小節采用其實例可變的類,并從中派生出具有不可變實例的類,通過這樣做,它們為這種轉換提供了一般規則,并演示了不可變對象的一些優點。
同步類示例SynchronizedRGB類定義了表示顏色的對象,每個對象將顏色表示為代表主要顏色值的三個整數和一個給出顏色名稱的字符串。
public class SynchronizedRGB { // Values must be between 0 and 255. private int red; private int green; private int blue; private String name; private void check(int red, int green, int blue) { if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) { throw new IllegalArgumentException(); } } public SynchronizedRGB(int red, int green, int blue, String name) { check(red, green, blue); this.red = red; this.green = green; this.blue = blue; this.name = name; } public void set(int red, int green, int blue, String name) { check(red, green, blue); synchronized (this) { this.red = red; this.green = green; this.blue = blue; this.name = name; } } public synchronized int getRGB() { return ((red << 16) | (green << 8) | blue); } public synchronized String getName() { return name; } public synchronized void invert() { red = 255 - red; green = 255 - green; blue = 255 - blue; name = "Inverse of " + name; } }
必須小心使用SynchronizedRGB以避免在不一致的狀態下被查看,例如,假設一個線程執行以下代碼:
SynchronizedRGB color = new SynchronizedRGB(0, 0, 0, "Pitch Black"); ... int myColorInt = color.getRGB(); //Statement 1 String myColorName = color.getName(); //Statement 2
如果另一個線程在語句1之后但在語句2之前調用color.set,則myColorInt的值將與myColorName的值不匹配,為了避免這種結果,必須將兩個語句綁定在一起:
synchronized (color) { int myColorInt = color.getRGB(); String myColorName = color.getName(); }
這種不一致只適用于可變對象 — 對于不可變版本的SynchronizedRGB,它不會是一個問題。
一種定義不可變對象的策略以下規則定義了用于創建不可變對象的簡單策略,并非所有記錄為“不可變”的類都遵循這些規則。這并不一定意味著這些類的創造者是草率的 — 他們可能有充分的理由相信他們類的實例在構造后永遠不會改變,但是,這種策略需要復雜的分析,不適合初學者。
不要提供“setter”方法 — 修改字段或字段引用的對象的方法。
使所有字段為final和private。
不允許子類重寫方法,最簡單的方法是將類聲明為final,更復雜的方法是使構造函數為private并在工廠方法中構造實例。
如果實例字段包含對可變對象的引用,則不允許更改這些對象:
不要提供修改可變對象的方法。
不要共享對可變對象的引用,永遠不要存儲對傳遞給構造函數的外部可變對象的引用,如有必要,創建副本并存儲對副本的引用,同樣,必要時創建內部可變對象的副本,以避免在方法中返回原始對象。
將此策略應用于SynchronizedRGB會導致以下步驟:
這個類中有兩個setter方法,第一個方法set,任意改變對象,在類的不可變版本中不存在,第二個方法invert,可以通過讓它創建一個新對象而不是修改現有對象來進行調整。
所有字段都已為private,他們進一步獲得final。
該類本身被聲明為final。
只有一個字段引用一個對象,該對象本身是不可變的,因此,不需要防止改變“包含的”可變對象的狀態的保護措施。
在這些更改之后,我們有ImmutableRGB:
final public class ImmutableRGB { // Values must be between 0 and 255. final private int red; final private int green; final private int blue; final private String name; private void check(int red, int green, int blue) { if (red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255) { throw new IllegalArgumentException(); } } public ImmutableRGB(int red, int green, int blue, String name) { check(red, green, blue); this.red = red; this.green = green; this.blue = blue; this.name = name; } public int getRGB() { return ((red << 16) | (green << 8) | blue); } public String getName() { return name; } public ImmutableRGB invert() { return new ImmutableRGB(255 - red, 255 - green, 255 - blue, "Inverse of " + name); } }上一篇:守護阻塞
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73050.html
Date-Time Java SE 8發行版中引入的Date-Time包java.time提供了全面的日期和時間模型,是在JSR 310:Date and Time API下開發的,盡管java.time基于國際標準化組織(ISO)日歷系統,但也支持常用的全球日歷。 此課程介紹了使用基于ISO的類來表示日期和時間以及操作日期和時間值的基本原理。 概述 時間似乎是一個簡單的主題,即便是便宜的手表也能...
摘要:原文鏈接已于成功發布,不過目前絕大多數人在生產環境仍舊使用的是。這篇以案例為主的教程涵蓋了從到的絕大多數重要的語法與特性。當編譯器不能正確識別出變量的數值類型時,將不被允許使用。同步請求將會阻塞當前的線程,直到返回響應消息。 showImg(https://segmentfault.com/img/remote/1460000016575203); 原文鏈接:https://wangw...
摘要:在接下來的分鐘,你將會學會如何通過同步關鍵字,鎖和信號量來同步訪問共享可變變量。所以在使用樂觀鎖時,你需要每次在訪問任何共享可變變量之后都要檢查鎖,來確保讀鎖仍然有效。 原文:Java 8 Concurrency Tutorial: Synchronization and Locks譯者:飛龍 協議:CC BY-NC-SA 4.0 歡迎閱讀我的Java8并發教程的第二部分。這份指南將...
摘要:創建字符串教程字符串長度用于獲取有關對象的信息的方法稱為訪問器方法。類在中被提出,它和之間的最大不同在于的方法不是線程安全的不能同步訪問。然而在應用程序要求線程安全的情況下,則必須使用類。 一般地,當需要使用數字的時候,我們通常使用內置數據類型,如:byte、int、long、double 等。 在實際開發過程中,我們經常會遇到需要使用對象,而不是內置數據類型的情形。為了解決這個問題,...
字符串 在Java編程中廣泛使用的字符串是一系列字符,在Java編程語言中,字符串是對象。 Java平臺提供String類來創建和操作字符串。 創建字符串 創建字符串的最直接方法是編寫: String greeting = Hello world!; 在這種情況下,Hello world!是一個字符串文字 — 代碼中的一系列字符,用雙引號括起來,每當它在代碼中遇到字符串文字時,編譯器就會創建一個帶...
閱讀 1740·2021-11-25 09:43
閱讀 1785·2021-11-24 10:41
閱讀 3105·2021-09-27 13:36
閱讀 811·2019-08-30 15:53
閱讀 3567·2019-08-30 15:44
閱讀 866·2019-08-30 14:03
閱讀 2572·2019-08-29 16:38
閱讀 996·2019-08-29 13:23