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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...