Chapter2 Creating and Destroying Objects

ITEM 1: Consider static factory methods instead of constructors

Advanteges

  • One advantage of static factory methods is that, unlike constructors, they have names.
  • A second advantage of static factory methods is that, unlike constructors, they are not required to create a new object each time they’re invoked.
  • A third advantage of static factory methods is that, unlike constructors, they can return an object of any subtype of their return type.
  • A fourth advantage of static factories is that the class of the returned object can vary from call to call as a function of the input parameters.
  • A fifth advantage of static factories is that the class of the returned object need not exist when the class containing the method is written

Disadvantages

  • The main limitation of providing only static factory methods is that classes without public or protected constructors cannot be subclassed.
  • A second shortcoming of static factory methods is that they are hard for programmers to find.

ITEM 2: Consider a builder when faced with many constructor parameters

the telescoping constructor pattern works, but it is hard to write client code when there are many parameters, and harder still to read it.

//Telescoping constructor pattern - does not scale well!
public class NutritionFacts {
    private final int servingSize; // (mL) required
    private final int servings; // (per container) required
    private final int calories; // (per serving) optional
    private final int fat; // (g/serving) optional
    private final int sodium; // (mg/serving) optional
    private final int carbohydrate; // (g/serving) optional

    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories) {
        this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat) {
        this(servingSize, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat,
            int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }

    public NutritionFacts(int servingSize, int servings, int calories, int fat,
            int sodium, int carbohydrate) {
        this.servingSize = servingSize;
        this.servings = servings;
        this.calories = calories;
        this.fat = fat;
        this.sodium = sodium;
        this.carbohydrate = carbohydrate;
    }
}

JavaBeans pattern

//JavaBeans Pattern - allows inconsistency, mandates mutability
public class NutritionFacts {
    // Parameters initialized to default values (if any)
    private int servingSize = -1; // Required; no default value
    private int servings = -1; // Required; no default value
    private int calories = 0;
    private int fat = 0;
    private int sodium = 0;
    private int carbohydrate = 0;

    public NutritionFacts() {
    }

    // Setters
    public void setServingSize(int val) {
        servingSize = val;
    }

    public void setServings(int val) {
        servings = val;
    }

    public void setCalories(int val) {
        calories = val;
    }

    public void setFat(int val) {
        fat = val;
    }

    public void setSodium(int val) {
        sodium = val;
    }

    public void setCarbohydrate(int val) {
        carbohydrate = val;
    }
}

This pattern has none of the disadvantages of the telescoping constructor pattern. It is easy, if a bit wordy, to create instances, and easy to read the resulting code:

NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27);

Disadvantages

  • JavaBean may be in an inconsistent state partway through its construction
  • the JavaBeans pattern precludes the possibility of making a class immutable

Builder Pattern

the Builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters

//Builder Pattern
public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;
        // Optional parameters - initialized to default values
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
        private int carbohydrate = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings = servings;
        }

        public Builder calories(int val) {
            calories = val;
            return this;
        }

        public Builder fat(int val) {
            fat = val;
            return this;
        }

        public Builder sodium(int val) {
            sodium = val;
            return this;
        }

        public Builder carbohydrate(int val) {
            carbohydrate = val;
            return this;
        }

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

    private NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}
  • This client code is easy to write and, more importantly, easy to read
NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
.calories(100).sodium(35).carbohydrate(27).build();

The Builder pattern simulates named optional parameters as found in Python and Scala

  • The Builder pattern is well suited to class hierarchies.

ITEM 3: Enforce the singleton property with a private constructor or an enum type

Making a class a singleton can make it difficult to test its clients

There are two common ways to implement singletons. Both are based on keeping the constructor private and exporting a public static member to provide access to the sole instance. In one approach, the member is a final field:

  • Singleton with public final field
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() { ... }

    public void leaveTheBuilding() { ... }
}
  • Singleton with static factory
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();

    private Elvis() { ... }

    public static Elvis getInstance() {
        return INSTANCE;
    }

    public void leaveTheBuilding() { ... }
}

ITEM 4: Enforce noninstantiability with a private constructor

a class can be made noninstantiable by including a private constructor

  • Noninstantiable utility class
public class UtilityClass {
    // Suppress default constructor for noninstantiability
    private UtilityClass() {
        throw new AssertionError();
    }
    ... // Remainder omitted
}

ITEM 5: Prefer dependency injection to hardwiring resources

  • Dependency injection provides flexibility and testability
public class SpellChecker {
    private final Lexicon dictionary;

    public SpellChecker(Lexicon dictionary) {
        this.dictionary = Objects.requireNonNull(dictionary);
    }

    public boolean isValid(String word) { ... }

    public List<String> suggestions(String typo) { ... }
}

In summary, do not use a singleton or static utility class to implement a class that depends on one or more underlying resources whose behavior affects that of the class, and do not have the class create these resources directly. Instead, pass the resources, or factories to create them, into the constructor (or static factory or builder). This practice, known as dependency injection, will greatly enhance the flexibility, reusability, and testability of a class.

ITEM 6: Avoid creating unnecessary objects

  • While String.matches is the easiest way to check if a string matches a regular expression, it’s not suitable for repeated use in performance-critical situations.
  • Performance can be greatly improved!
static boolean isRomanNumeral(String s) {
    return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
            + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}
  • Reusing expensive object for improved performance
public class RomanNumerals {
    private static final Pattern ROMAN = Pattern
            .compile("^(?=.)M*(C[MD]|D?C{0,3})"
                    + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");

    static boolean isRomanNumeral(String s) {
        return ROMAN.matcher(s).matches();
    }
}
  • Autoboxing blurs but does not erase the distinction between primitive and boxed primitive types.
  • prefer primitives to boxed primitives, and watch out for unintentional autoboxing.

ITEM 7: Eliminate obsolete object references

  • Can you spot the "memory leak"?
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 e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        return elements[--size];
    }

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

The fix for this sort of problem is simple: null out references once they become obsolete. In the case of our Stack class, the reference to an item becomes obsolete as soon as it’s popped off the stack. The corrected version of the pop method looks like this:

    public Object pop() {
        if (size == 0)
            throw new EmptyStackException();
        Object result = elements[--size];
        elements[size] = null; // Eliminate obsolete reference
        return result;
    }
  • Nulling out object references should be the exception rather than the norm.
  • Another common source of memory leaks is caches.
  • A third common source of memory leaks is listeners and other callbacks.

ITEM 8: Avoid finalizers and cleaners

  • Finalizers are unpredictable, often dangerous, and generally unnecessary.
  • .Cleaners are less dangerous than finalizers, but still unpredictable, slow, and generally unnecessary.
  • never do anything time-critical in a
    finalizer or cleaner.
  • never depend on a finalizer or cleaner to update persistent state.
  • Just have your class
    implement AutoCloseable , and require its clients to invoke the close method on each instance when it is no longer needed, typically using try -with-resources to ensure termination even in the face of exceptions

ITEM 9: Prefer try-with-resource to try-finally

The lesson is clear: Always use try -with-resources in preference to try-finally when working with resources that must be closed. The resulting code is shorter and clearer, and the exceptions that it generates are more useful. The try- with-resources statement makes it easy to write correct code using resources that must be closed, which was practically impossible using try - finally.

\

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,918评论 0 10
  • 因家境贫寒,年轻时的你就怀有“大济于苍生”的壮志,二十九岁时走上仕途,历任江州祭酒、镇军参军、彭泽令等下级...
    陈程云阅读 295评论 0 0
  • 1.美颜瘦身习惯: 2.定的目标体重:55KG 抽的人像卡,感觉有些艰难 3.抽一张人像卡,自觉一下它认为你的体重...
    黄宇婷a阅读 209评论 0 1
  • 生活对于那些活得精彩的人就是人生,对于没有追求的痞子就是没有了希望,正如我一样,有着各式各样的想法,却没有一点...
    向国跃阅读 215评论 0 0

友情链接更多精彩内容