Chapter 01:创建和销毁对象

简介: 《一》考虑用静态工厂方法代替构造器 下面是Boolean类的一个简单示例: public final class Boolean implements java.io.Serializable, Comparable { public static final...

《一》考虑用静态工厂方法代替构造器

下面是Boolean类的一个简单示例:

public final class Boolean implements java.io.Serializable,
        Comparable<Boolean> {

    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
}

 Why ?平时我们用共有的构造器不爽吗?

1.静态工厂方法与构造器不同的第一大优势在于,它们有名称!!!名称对于程序多重要,毋庸置疑了,如果你没这样感觉,那就什么都别说,多敲代码就知道了。

 

public class BigInteger extends Number implements Comparable<BigInteger> {
    /**
     * Returns a positive BigInteger that is probably prime, with the
     * specified bitLength. The probability that a BigInteger returned
     * by this method is composite does not exceed 2<sup>-100</sup>.
     */

    public static BigInteger probablePrime(int bitLength, Random rnd) {
    }
}

 

2.静态工厂方法与构造器不同的第二大优势在于,不必在每次调用它们的时候创建一个新的对象。比如不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而达到提升性能的效果。

 

3.静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象。这样在选择返回对象上就有了更大的灵活性。

 

public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
    implements Cloneable, java.io.Serializable {

    EnumSet(Class<E>elementType, Enum[] universe) {
    }
    //RegularEnumSet与JumboEnumSet均为EnumSet的子类
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }
}

 

 

4.静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。

 

//常规实例化方式
Map<String, List<String>> m =
    new HashMap<String, List<String>>();

public static <K, V> HashMap<K, V> newInstance() {
    return new HashMap<K, V>();
}
//使用静态工厂方法实例化,简化的声明
Map<String, List<String>> m = HashMap.newInstance();

 

 

Not? 利弊总是同时存在,没有尽善尽美的东西

 

1.静态工厂的方法的主要缺点在于,类如果不含公有的或者受保护的构造器,就不能子类化。

 

如果我们在类中将构造函数设为private,只提供静态工厂方法来构建对象,那么我们将不能通过继承扩展该类。 
但是这也会鼓励我们使用复合而不是继承来扩展类。

 

2.静态工厂方法的第二缺点在于,它们与其他静态方法实际没有任何区别。

 

在API文档中,构建对象的静态工厂方法并没有像构造器那样明确标识出来,不能和其他静态方法很方便地区分开来。 
可以通过遵循静态工厂方法的命名规范来弥补这一劣势:

  • valueOf - 不太严格的讲,该方法返回的实例与它的参数具有相同的值,一般作为类型转换使用
  • of - valueOf的更为简洁的替代。
  • getInstance - 返回的实例通过方法的参数来描述,但不能说与参数具有同样的值。对于Singleton来说,使用无参getInstance,返回唯一的实例。
  • newInstance - 像getInstance一样,但其能够确保每次都返回新的对象。
  • getType - 像getInstance一样,但此方法返回的对象是另一个不同的类。
  • newType - 像getType一样,但每次返回一个新对象。

总而言之,静态工厂方法和公有构造器具有各自的用处,但静态工厂方法通常更加合适,因此切记第一反应就是提供公有的构造器,而先不考虑静态工厂方法。

 

《二》遇到多个构造器参数时候要考虑构建器

遇到多个构造器参数,一般采用的是重叠构造器模式,或者JavaBeans模式(线程不安全)

这里要说的是第三种,既能像重叠构造器模式那样安全性,也能保证像JavaBeans模式那么好的可读性,这就是Builder模式,构建器模式。

eg:

// Builder Pattern - Pages 14-15

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 carbohydrate  = 0;
        private int sodium        = 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 carbohydrate(int val)
            { carbohydrate = val;  return this; }
        public Builder sodium(int val)
            { sodium = 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;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).
            calories(100).sodium(35).carbohydrate(27).build();
    }
}

 

Java传统抽象工厂实现是class对象,用newInstance方法充当build方法的一部分,这种用法隐含许多问题,因为newInstance总是企图调用类的无参构造,这个构造可能根本不存在,但是也不会收到编译时期错误,相反,必须在运行时处理,换句话说,Class.newInstance破坏了编译时期的异常检查。

 

Builder模式不足之处,为了创建对象必须先创建它的构造器,虽然创建构造器的开销,实际中可以忽略,注重性能的情况除外,同时他比其他模式更加冗余。

 

因此,只有在参数比较多的时候使用它才比较合理(一般参数4个+),切记,一开始使用重叠构造或者JavaBeans,中间再切换构造器,这样会导致代码不协调,很乱,最好计划一开始就用。简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时候,Builder是不错选择,特别当大多数参数都是可选的,这样会使代码更加容易阅读和编写,同时也比JavaBeans更安全。

 

 

目录
相关文章
|
3月前
|
Java 数据库连接 数据库
|
Java 调度
【JavaEE】线程的创建及常见方法解析(Tread类)
【JavaEE】线程的创建及常见方法解析(Tread类)
|
安全 编译器 数据安全/隐私保护
对象的动态创建和销毁以及对象的复制,赋值
🐰对象的动态创建和销毁 🐰对象的复制 🐰对象的赋值
|
存储 缓存 Java
Effective Java 第二章 创建和销毁对象2
Effective Java 第二章 创建和销毁对象
121 0
|
设计模式 缓存 安全
Effective Java 第二章 创建和销毁对象1
Effective Java 第二章 创建和销毁对象
68 0
|
算法 Java 程序员
Effective Java 第二章 创建和销毁对象3
Effective Java 第二章 创建和销毁对象
64 0
|
Java 编译器
创建一个对象的时候
创建一个对象的时候
75 0
|
JavaScript 前端开发 Java
创建Class类的实例对象的三种方法
创建Class类的实例对象的三种方法
252 0
|
设计模式 存储 缓存
快速带你看完《Effective Java》—— 创建和销毁对象篇
1 静态工厂代替构造器 2 构造函数有多个参数时要考虑使用构建器 3 使用private构造器或枚举类型强化Singleton属性 4 使用privete的构造函数强化不可实例化的能力 5 引用资源时应优先考虑依赖注入 6 避免创建不必要的对象 7 消除过期的对象引用 8 避免使用终结方法和清除方法 9 try-with-resources优先于try-finally
快速带你看完《Effective Java》—— 创建和销毁对象篇
|
缓存 安全 Java
仅且仅创建一次对象
此篇算是对《voliatile,synchronized,cas》理论的一种实践
152 0