Effective java筆記(一),創建與銷毀對象

来源:http://www.cnblogs.com/wangyingli/archive/2016/09/24/5903697.html
-Advertisement-
Play Games

1、考慮用靜態工廠方法代替構造器 類的一個實例,通常使用類的公有的構造方法獲取。也可以為類提供一個公有的靜態工廠方法(不是設計模式中的工廠模式)來返回類的一個實例。例如: 使用靜態工廠方法代替構造器的優勢: 靜態工廠方法有名稱,更易讀。靜態工廠方法能夠使用方法名稱進行自註釋,來描述被返回的對象。例如 ...


1、考慮用靜態工廠方法代替構造器

類的一個實例,通常使用類的公有的構造方法獲取。也可以為類提供一個公有的靜態工廠方法(不是設計模式中的工廠模式)來返回類的一個實例。例如:

    
    //將boolean類型轉換為Boolean類型
    public static valueOf(boolean b) {
        return b ? Boolean.TRUE : Boolean.FALSE;
    }

使用靜態工廠方法代替構造器的優勢:

  • 靜態工廠方法有名稱,更易讀。靜態工廠方法能夠使用方法名稱進行自註釋,來描述被返回的對象。例如:BigInteger.probablePrime的靜態方法表示返回的BigInteger為素數。當一個類需要多個帶有相同簽名(參數列表僅在參數類型的順序上有所不同)的構造器時,應使用靜態工廠方法並且慎重的選擇名稱以便突出它們的區別。

  • 靜態工廠方法能夠為重覆的調用返回相同的對象。將創建好的對象緩存起來,重覆利用。

  • 靜態工廠方法可以返回原返回類型的任何子類型的對象。例如:在基於介面的框架(通過介面來引用對象)中,為了隱藏類的實現,通常會使用API返回對象的實例。由於介面中不能有靜態方法,通常把靜態工廠方法放在實現了介面的不可實例化類(private構造函數)中。

  • 創建參數化類型實例時,靜態工廠方法使代碼變的更簡潔。(java 8加入泛型的推導後,優勢不再)


    Map<String, List<String>> map = new HashMap<String, List<String>>(); // java 1.7之前必須這樣寫,前後兩次都要寫泛型列表
    Map<String, List<String>> map = new HashMap<>(); // java 1.8可以這樣寫

    //靜態工廠方法
    //HashMap類中加入
    public static <K, V> HashMap<K, V> newInstance() {
        return new HashMap<K, V>();
    }
    //使用
    Map<String, List<String>> map = HashMap.newInstance();

缺點:

  • 類如果不含有public或protected型的構造器,則不能被繼承。

2、遇到多個構造器參數時考慮用構建器

當有多個構造器參數(其中一些為可選參數)時,代碼的編寫通常有幾種方式:

  • 使用重疊的構造器,根據可選參數提供不同的構造器。

  • 使用JavaBean模式:先調用一個無參的構造器來創建對象,然後使用setter方法來設置必要的參數。

當參數很多時,使用重疊的構造器代碼的編寫將很繁瑣,難以閱讀,同時調用時容易出錯。而是用JavaBean模式,因為對象的構造過程被分到了幾個調用中,在構造過程中JavaBean處於不一致狀態。試圖使用(特別是在多線程中)不一致狀態的對象,將導致錯誤並且很難調試。並且JaveBean模式阻止了將類做成不可變的可能。

這時可以考慮使用構建器(Builder模式),不直接生成想要的對象,而是通過Builder對象來構造對象。

代碼:


    public class AlertDialog {

        private final String title;

        private  final String message;

        private final boolean cancelable;

        private AlertDialog(Builder builder) {
            this.title = builder.title;
            this.message = builder.message;
            this.cancelable = builder.cancelable;
        }

        public static class Builder {

            private final String title;

            private String message;

            private boolean cancelable;

            public Builder(String title) {
                this.title = title;
            }

            public Builder setMessage(String message) {
                this.message = message;
                return this;
            }

            public Builder setCancelable(boolean cancelable) {
                this.cancelable = cancelable;
                return this;
            }

            public AlertDialog create() {
                return new AlertDialog(this);
            }
        }

        public String toString() {
            return "title: " + title + ", message: " + message + ", cancelable: " + cancelable;
        }

        public static void main(String[] args) {
            AlertDialog dialog = new AlertDialog.Builder("地點")
                .setMessage("知識的荒漠")
                .setCancelable(true).create();
            System.out.println(dialog);
        }
    }

Builder模式的優點:

  • 比JavaBean更安全,比重疊的構造器更易於閱讀和編寫

  • 適用於參數較多,且大多數參數可選的情況

3、用私有構造器或枚舉類型強化Singleton屬性

實現Singleton的幾種方式:

方式一:餓漢方式,線程安全

    
    public class Singleton {
        private static final Singleton singleton = new Singleton();

        private Singleton() {}

        public static Singleton getInstance() {
            return singleton;
        }
    }

這種方式基於classloader機制避免了線程同步的問題。但singleton在類被裝載時立即實例化,沒有實現類的延時載入(lazy loading)。另外這種方式可以通過反射機制(藉助Class對象的setAccessible方法)來生成對象。

方式二:懶漢方式,線程不安全

    
    public class Singleton {
        private static final Singleton singleton = null;

        private Singleton() {}

        public static Singleton getInstance() {
            if(singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    }

這種方式實現了類的延時載入,但致命的是多線程下不安全。

方式三:雙重校驗鎖,線程安全


    public class Singleton {
        private volatile static Singleton singleton = null;
        private Singleton() {}
        public static Singleton getInstance() {
            if (singleton == null) {
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

方式四:枚舉,線程安全,可避免反序列化時重新創建對象,推薦使用


    public enum Singleton {
        INSTANCE;

        private Singleton() {}
        .....
    }

    //使用
    Singleton single = Singleton.INSTANCE;

這種方式可自由序列化,可有效避免反序列化時創建多個對象。保證只有一個實例(即使反射機制也無法多次實例化一個枚舉量),線程安全。缺點是使用的人較少(java1.5後才有enum類型),失去了類的一些特性。單元素的枚舉型是實現Singleton的最佳方式

4、通過私有構造器強化不可實例化的能力

編寫一些只包含靜態方法或靜態域的類(工具類)時,可以使用private構造器來使類不能被實例化。這些類不能被繼承。(因為子類的構造器都要顯示或隱式調用父類的構造器)。自己編寫的工具類要使用這種方法

    
    public class UtilityClass {
        private UtilityClass() {
            throw new AssertionError(); //斷言,確保不會在類內部被調用
        }
        .....
    }

5、避免創建不必要的對象

重用對象而不是每次需要時創建一個相同的新對象,這可以顯著程式提高性能。對於不可變的對象應始終重用,對於可變的對象應儘量重用。

例如:

    
    String str1 = "abc";
    String str2 = new String("abc");
    String str3 = new String("abc");

記憶體模型:

new String("abc")每次執行都會創建一個新的String實例,在堆中分配一個新的空間。而使用String str1 = "abc"每次調用都將指向常量池中同一個常量,不會產生新的實例。

對於可變對象,那些一旦計算出來就不再變化的子對象或常量可單獨提取到靜態域中。


    class Person {
        private final Date birthDate;
        private static final Date BOOM_STATE; //第一次初始化後將不改變,所有對象共用
        private static final Date BOOM_END;

        static {
            Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
            gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
            BOOM_STATE = gmtCal.getTime();
            gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
            BOOM_END = gmtCal.getTime();
        }

        public boolean isBabyBoomer() {
            return birthDate.compareTo(BOOM_STATE) >= 0 && birthDate.compareTo(BOOM_END) < 0;
        }
    }

當心無意識的自動裝箱,要優先使用基本類型而不是包裝類

例如:計算所有int正值的和


    Long sum = 0L; //應使用基本類型long
    for(long i=0; i<Integer.MAX_VALUE; i++ ) {
        sum += i; //自動裝箱,將創造2^32個多餘的Long實例,降低性能
    }
    System.out.println(sum);

6、消除過期的對象引用

過期引用是指永遠也不會再被解除的引用。在java中記憶體泄漏是隱藏的(無意識的對象保持)。如果一個對象的引用被無意識的保留下來,那麼垃圾回收機制不會回收這個對象及這個對象所持有的所有對象。

消除過期引用最好的方法是讓包含該引用的變數結束其生命周期。只要類自己管理記憶體,就應該小心記憶體泄漏問題。

    public class Stack {
        private Object[] elements;
        private int size = 0;
        private static final int DEFAULT_INITIAL_CAPACITY = 16;

        public Stack() {
            elements = new Object[DEFAULT_INITIAL_CAPACITY];
        }

        public void push(Object o) {
            ensureCapacity();
            elements[size++] = o;
        }

        public Object pop() {
            if(size == 0) 
                throw new EmptyStackException();
            Object result = elements[--size];
            elements[size] = null; //【避免記憶體泄漏】
            return result;
        }

        private void ensureCapacity() {
            if(elements.length == size) {
                elements = Arrays.copyOf(elements, 2*size + 1);
            }
        }
    }

對於垃圾回收器而言,elements數組中的所有對象的引用都同等有效,它無法區分哪些可以回收哪些不能回收。

記憶體泄漏的常見來源:

  • 數組

  • 緩存。使用WeakHashMap代表緩存,對於複雜的緩存必須使用java.lang.ref

  • 監聽器及其回調。要及時取消註冊,可以使用弱引用

7、避免使用終結方法

終結方法finalizer()為類Object中的方法,當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。

  • 終結方法是不可預測的,通常不要直接調用。

  • Java語言規範不保證終結方法會被立即執行,所以不應該依賴終結方法來更新重要的持久狀態。

  • 終結方法可用作安全網或終止非關鍵的本地資源(記住調用super.finalize())


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • httpModules是往當前應用程式添加HttpModule(http模塊)的標簽。配置節如下 提起httpModule不得不提一下Http請求處理流程 ASP.NET對請求處理的過程: 當請求一個*.aspx文件的時候,這個請求會被inetinfo.exe進程截獲,它判斷文件的尾碼(aspx)之 ...
  • Response對象:響應請求 Response.Write("<script>alert('添加成功!')</script>"); Response.Redirect("Default.aspx"); Request對象:獲取請求 Request["key"]來獲取傳遞過來的值 QueryStri ...
  • 佈局:有2個屬性: Anchor:鎖定位置Dock:填充位置一般Dock是與容器控制項配合使用 容器控制項:Panel:就是一個區域,類似於DIV,可以獨立佈局,還可以讓其它控制項及容器在它的內部再次佈局 FlowLayoutPanel:流式佈局容器,內容會預設從左向右排列,如果寬度不夠了,那麼自動換行 ...
  • The alias cmd list your current aliases. For example : alias Use alias to shorten a long cmd in current shell session: alias [name=['command']] Use un... ...
  • 學習過Spring框架的人一定都會聽過Spring的IoC(控制反轉) 、DI(依賴註入)這兩個概念,對於初學Spring的人來說,總覺得IoC 、DI這兩個概念是模糊不清的,是很難理解的,今天和大家分享網上的一些技術大牛們對Spring框架的IOC的理解以及談談我對Spring Ioc的理解。 一 ...
  • 2、下載地址:https://github.com/MSOpenTech/redis/releases 3、最新的安裝包: 4、點擊msi文件開始安裝。 5、勾選同意,下一步。 6、使用預設的安裝地址地址,並且將redis安裝路徑添加到系統環境變數。 7、修改埠號為“46379”,添加防火牆例外。 ...
  • import java.util.*;import java.math.*;public class CaculatorLnN { public static void main(String[] args) { // TODO 自動生成的方法存根 int N; System.out.println ...
  • 網上目前還找不到完整的mac下golang環境配置支持,本人配置成功,現在整理分享出來。 mac最好裝下xcode,好像有依賴關係安裝Homebrew打開終端視窗, 粘貼腳本執行/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent. ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...