疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java创建和销毁对象


 

   

  考虑使用静态工厂方法代替构造器

  类可以提供一个公有的静态工厂方法(public static factory method)来返回一个类的实例。

  例如,Boolean类的valueOf()方法:

  public static Boolean valueOf(boolean b) {

  return (b ? TRUE : FALSE);

  }

  提供public静态工厂方法而不是public的构造器的优势如下:

  静态工厂方法有名称,不过要慎重地选择名称

  例如,构造器BigIntger(int, int, Random)返回的BigInteger可能为素数,如果用名为BigInteger.probablePrime()的静态工厂方法显然更清晰。

  示例: BigIntegerTest

  不必在每次调用静态工厂方法的时候都创建一个新对象

  如果程序经常经常请求创建相同的对象,并且创建对象的代码很高,则这项技术可以极大地提升性能。

  示例:BooleanTest

  当且仅当a==b的时候才有a.equals(b)为true, 如果类保证了这一点, 它的客户端就可以使用==操作符来代替equals(Object)方法,这样可以提升性能。

  静态工厂方法可以返回子类类型的对象

  API返回的对象对应的子类可以不是公有的,这种方式可以隐藏具体的实现类。

  这项技术适合于基于接口的框架,接口为静态工厂提供了自然返回类型。

  接口不能有静态方法,因此按照惯例,返回接口的静态工厂方法是放在一个不可实例化(non-instantiability)的类中。

  示例: InterfaceCannotStaticTest

  示例: java.util.Collections是一个不可实例化的类,它提供了不可修改的集合、同步集合的静态工厂方法。我们模仿其写了一个简单例子: MyListTest

  通过使用这种静态工厂方法,甚至要求客户端通过接口来引用被返回的对象,而不是通过它的实现类来引用被返回的对象,这是一种良好的习惯。 为了提升软件的可维护性和性能,返回对象的(子)类也可能随着发行版本的不同而不同。

  示例:EnumSetTest 发行版本1.5中引入java.util.EnumSet没有公有的构造器,只有静态工厂方法,返回两种实现类之一,具体则取决于底层枚举类型的大小。

  示例:服务提供者框架

  静态工厂方法在创建参数化类型实例的时候,使得代码更加简洁

  例如,创建一个Map

  Map

  随着类型参数变得越来越长,越来越复杂,这一冗长的说明也很快变得痛苦起来。

  但是有了静态方法,编译器可以替你找到类型参数,这被称作类型推导(type inference):

  Map

  假设HashMap提供了这个静态工厂方法:

  public static

  总有一天,Java将能够在构造器调用以及方法调用中执行这些类型推导,但到发行版本1.6为止暂时还无法这么做。

  遗憾的是,到发行版本1.6为止,标准的集合实现如HashMap并没有工厂方法,但是可以把这些方法放在你自己的工具类中。

  公有的静态工厂方法的缺点:

  类如果不含有公有的或者受保护的构造器,静态工厂方法返回的实例不能被子类化。

  同样地,对于非公有类(nonpublic classes),静态工厂方法返回的实例也不能被子类化。 示例: MyListTest

  静态工厂方法与其它的静态方法无法被明确区分,不像构造器那样在api文档中被明确的标识出来。 所以,在类或接口注释中关注静态工厂,使用一些约定俗成的命名

  惯用名称 说明

  valueOf Returns an instance that has, loosely speaking, the same value as its parameters. Such static factories are effectively type-conversion methods.

  of A concise alternative to valueOf , popularized by EnumSet.

  getInstance Returns an instance that is described by the parameters but cannot be said to have the same value. In the case of a singleton, getInstance takes no parameters and returns the sole instance.

  newInstance Like getInstance , except that newInstance guarantees that each instance returned is distinct from all others.

  getType Like getInstance , but used when the factory method is in a different class. Type indicates the type of object returned by the factory method.

  newType Like newInstance , but used when the factory method is in a different class. Type indicates the type of object returned by the factory method.

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

  回到顶部

  遇到多个构造器参数时要考虑用构建器

  静态工厂和构造器有一个共同的局限性:它们都不能很好地扩展到大量的可选参数。考虑用一个类表示一个食品的营养成分表。对于这样的类,应该用哪个构造器或者静态方法来编写呢?

  重叠构造器(telescoping constructor)

  示例: telescoping_constructor/NutritionFacts

  ① 当有许多参数的时候,客户端代码会很难编写,并且仍然较难以阅读。

  ② 如果读者想知道那些值是什么意思,必须很仔细地数着这些参数来探个究竟。如果客户端不小心颠倒了其中两个参数的顺序,编译器也不会出错,但是在程序运行时会出现错误行为。

  JavaBeans模式:调用一个无参构造器来创建对象,然后调用setter()方法来设置每个必要的参数,以及每个相关的可选参数。

  示例: javabeans/NutritionFacts

  ① 无法保证一致性:JavaBean模式自身有着很严重的缺点,因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处在不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。试图使用处于不一致状态的对象,将会导致失败,这种失败调试起来十分困难。

  ② 阻止了把类做成不可变,这就需要程序员付出额外的努力来确保它的线程安全。

  Builder模式

  示例:builder/NutritionFacts

  这样的客户端代码很容易编写,而且易读。Builder模式模拟了具名的可选参数,就像Python一样。

  对参数加强约束条件: build()方法检验或者多个setter()方法检验这些约束条件,如果该约束条件没有得到满足,就抛出IllegalStateException或IllegalArgumentException。

  设置了参数的Builder生成了一个很好的抽象工厂(Abstract Factory)。Java中的Class对象就是一个抽象工厂,用newInstance()方法充当build()方法的一部分。

  Builder模式也有它自身不足,为了创建对象,必须先创建它的构建器,它还会比重叠构造器模式更加冗长,因此只有在很多参数(4个以上)的时候才考虑使用Builder模式。但是要记住,将来你可能需要添加参数,如果一开始就使用构造器或者静态工厂,等到类需要多个参数时才添加构建器,就无法控制,那些过时的构造器或者静态工厂显得十分不协调。因此通常最好一开始就使用构建器。

  回到顶部

  用私有构造器或者枚举类型强化Singleton属性

  Singleton指仅仅被实例化一次的类。Singleton通常被用来代表那些本质上唯一的系统组件。

  在Java 1.5发行版本之前,实现Singleton有两种方法。这两种方法都要把构造器保持为私有的,并导出公有的静态成员,以便允许客户端能够访问该类的唯一实例。

  第一种方法,公有静态成员是一个final域。示例: field/Elvis.java

  私有构造器仅被调用一次,用来实例化公有的静态域Elvis.INSTANCE。

  但要提醒一点:享有特权的客户端可以借助AccessibleObject.setAccessible()方法,通过反射机制调用私有构造器,示例:ModifyingSingleton 。如果需要抵御这种攻击,可以修改构造器,让它在被要求创建第二个实例的时候抛出异常。