天天看點

EffectiveJava筆記(一)-建立和銷毀對象建立和銷毀對象

建立和銷毀對象

1. 考慮用靜态方法代替構造器

例如

public static Boolean valueOf(boolean b){
    return b > Boolean.TRUE : Boolean.FALSE
}
           

優勢

  • 有名稱
  • 不必每次調用他們的的時候都建立一個新對象
  • 可以傳回員傳回類型的任何子類型的對象

缺點

  • 類如果不含共有的或者搜保護的構造器沒,就不能被子類化
  • 他們于其他的靜态方法實際上沒有任何差別

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

JavaBeans

調用一個無參構造器來建立對象,然後調用 setter方法來設定每個必要的參數以及各每個相關的可選參數

缺點:

  • 因為被分到幾個調用中,在建構過程中可能處于不一緻的狀态。
  • 阻止了把類做成不可變的可能,需求程式員付出額外的努力來確定它的線程安全

Builder模式(建造者模式)

如果類的構造器或者靜态工廠中具有多個參數,設計這種類時,Builder模式是不錯的選擇。

// 例如
public class Course {
    private String courseName;
    private String coursePPT;
    private String courseVideo;
    private String courseArticle;

    /**
     * question & answer
     */
    private String courseQA;

    public Course(CourseBuilder courseBuilder) {
        this.courseName = courseBuilder.courseName;
        this.coursePPT = courseBuilder.coursePPT;
        this.courseVideo = courseBuilder.courseVideo;
        this.courseArticle = courseBuilder.courseArticle;
        this.courseQA = courseBuilder.courseQA;
    }

    @Override
    public String toString() {
        return "Course{" +
                "courseName='" + courseName + '\'' +
                ", coursePPT='" + coursePPT + '\'' +
                ", courseVideo='" + courseVideo + '\'' +
                ", courseArticle='" + courseArticle + '\'' +
                ", courseQA='" + courseQA + '\'' +
                '}';
    }

    public static class CourseBuilder {

        private String courseName;
        private String coursePPT;
        private String courseVideo;
        private String courseArticle;

        /**
         * question & answer
         */
        private String courseQA;

        public CourseBuilder buildCoureseName(String courseName) {
            this.courseName = courseName;
            return this;
        }

        public CourseBuilder buildCoursePPT(String coursePPT) {
            this.coursePPT = coursePPT;
            return this;
        }

        public CourseBuilder buildCourseVideo(String courseVideo) {
            this.courseVideo = courseVideo;
            return this;
        }

        public CourseBuilder buildCourseArticle(String courseArticle) {
            this.courseArticle = courseArticle;
            return this;
        }

        public CourseBuilder buildCourseQA(String courseQA) {
            this.courseQA = courseQA;
            return this;
        }

        public Course build() {
            return new Course(this);
        }

    }
}

// 測試
Course course = new Course.CourseBuilder()
.buildCoureseName("Java設計模式精講")
.buildCoursePPT("Java設計模式PPT")
.buildCourseVideo("Java設計模式視訊")
.build();
System.out.println(course);

           

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

Singleton是指僅僅被執行個體化一次的類。

注意:享有特權的用戶端可以借助 AccessibleObject.setAccessible方法,通過反射機制調用私有構造器

4.通過私有構造器強化不可執行個體化的能力

企圖通過将類做成抽象類來強制該類不可被執行個體化,是行不通的

5.避免建立不必要的對象

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

6.消除過期的對象引用

/**
 * @author stone
 * @des 記憶體洩露示例
 * @date 2018/12/5/005 8:40
 **/
public class Stack {

    private Object[] element;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {

    }

    public void push(Object e) {
        ensureCapacity();
        element[size++] = e;
    }

    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        // 注意下面這行代碼會引起記憶體洩露
        // 如果一個棧是先增長,然後再收縮,那麼,從棧中彈出來的對象将不會被當做垃圾回收,即使使用棧的程式不再引用這些對象,他們也不會被回收
        // 棧内部維護着這些對象的過期引用。過期引用:永遠也不會再被解除的引用。
        // 本例中 凡是element數組中的活動部分之外的任何引用都是過期的。活動部分:element中下标小于size的那些元素
        // return element[--size];

        Object result = element[--size];
        // 解決方法如下 消除過期引用
        // Eliminate obsolete reference
        element[size] = null;
        return result;


    }

    /**
     * ensure space for at least one more element,roughly
     * doubling the capacity each time the array needs to grow
     */
    private void ensureCapacity() {
        if (element.length == size) {
            element = Arrays.copyOf(element, 2 * size + 1);
        }
    }
}
           

總結 : 過期的對象引用 可能會引起記憶體洩露

隻要類是自己管理記憶體,程式員就應該警惕記憶體洩露的問題。

記憶體洩露常見來源

  • 隻要類是自己管理記憶體,程式員就應該警惕記憶體洩露的問題。
  • 緩存
  • 監聽器和其他回調

7.避免使用終結方法

終結方法(finalizer)通常是不可預測的,很危險的,也一般是不必要的。

Java語言規範不僅不保證終結方法會被及時地執行,而且根本就不保證它們會被執行。

不應該依賴終結方法來更新重要的持久狀态。

System.gc System.runFinalization這兩個方法确實增加了終結方法被執行的機會,但它們并不保證終結方法一定會被執行。

終結方法有一個非常嚴重的(Severe)性能損失。

用終結方法建立和銷毀對象比正常要慢上百倍。