创建和销毁对象


1.考虑用静态工厂发方法代替构造器

  提供静态工厂方法较之提供公有构造器,具有如下几大优势:

  • 静态工厂方法与构造器不同的第一大优势在于,他们有名称。当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并且慎重地选择名称以便突出他们之间的区别。
  • 静态工厂方法与构造器不同的第二大优势在于,不必每次调用它们的时候都创建一个新对象。可以将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象。静态工厂方法能够为重复的调用返回相同的对象,这样有助于类总能严格控制在某个时刻那些实例应该存在。这种类被称作实例受控的类。
  • 静态工厂方法与构造器不同的第三大优势在于,他们可以返回原返回类型的任何子类型的对象。公有的静态工厂方法所返回的对象的类可以是非公有的(默认访问级别的类只能被同一个包中的其他类访问),而且可以返回任何实现了已声明接口类型的类的实例。
  •  静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,他们是代码变得更加简洁。

   静态工厂方法的不足:

  • 类如果不含公有的或者受保护的构造器,就不能被子类化。
  • 静态工厂方法的第二个缺点在于,他们与其他的静态方法实际上没有任何区别。他们在API文档中没有像构造器那样明确标识出来。   

 

    注意:静态工厂方法与设计模式中能够的工厂方法模式不同,只是一个返回类的实例的静态方法。


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

静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。

对于拥有大量参数的类(少数必选、多数可选),可采用:

 重叠构造器模式(telescoping constructor)

该模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含所有可选参数。

重叠构造器模式可行,但是当有许多参数的时候,客户端代码会很难编写,并且仍然较难阅读。


 JavaBeans模式

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

JavaBeans模式弥补了构造器模式的不足,其实就是创建实例容易。不过由于构造过程被分到了几个调用中,在构造过程中JavaBean可能出于不一致的状态,类无法仅仅通过检验构造器参数的有效性来保证一致性。此外JavaBean模式阻止了把类做成不可变的可能。


  Builder模式

既能保证像重叠构造器模式那样的安全性,也能保证像JavaBeans模式那么好的可读性,

Builder模式不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象,然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变的对象,这个Builder是他构建的类的静态成员类。


builder像个构造器一样,可以对其参数强加约束条件。将参数从builder拷贝到对象中之后,并在对象域而不是builder域中对他们进行检验。

Builder模式为了创建对象,必须先创建他的构建器,在对性能十分注重的情况下,会造成问题。

简而言之,如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择。特别是当大多数参数都是可选的时候。如果将来会对类添加参数,使用Buider模式也很合适。

 

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



Singleton通常被用来代表那些本质上唯一的系统组件,在JDK1.5之前,实现Singleton有两种方法。这两种方法都要把构造器保持为私有的,并导出公有的静态成员,以便允许客户端能够访问该类的唯一实现。


第一种方式:1.构造器私有,2,有一个公共的静态成员,并且是final的。 

public class TestSingleton { 
    private TestSingleton(){
        //在此将构造函数设置为private,防止该类在外部被实例化
    }
     
    public static final TestSingleton Instance = new TestSingleton(); 
}
ps:构造方法私有化的作用:外面调用只能调用类A的静态函数A_Func,而不能A   a   =   new   A();私有构造函数只能在函数内部调用,外部不能实例化,所以私有构造函数可以防止该类在外部被实例化。

不过上述方式是不能保证绝对的只有一个实例,通过反射机制可以突破这一点,所以该方法不推荐。传说中的 饿汉式的单例模式。


第二种方式:1,构造器私有,2,有一个私有的静态长远,但是有一个公共的静态工厂方法 

public class TestSingleton1 { 
    private static final TestSingleton1 INSTANCE = new TestSingleton1(); 
    private TestSingleton1(){}
     
    public static TestSingleton1 getInstance(){
        return INSTANCE;
    } 
}


这就是传说中的 懒汉式的单例模式,一般情况下,为了防止多线程的时候创建多个实例,都会加上synchronized 关键字。

对于静态方法getInstance()方法的所有调用,都会返回同一个对象的应用。这种写法的好处是很清楚地表明了该类是一个Singleton,但是这种写法有另一个好处是,可以添加其他的静态工厂方法返回另一个新的实例,这样就可以将该类变为非Signleton。

为了使利用上述方法实现Singleton类变成是可序列化的,仅仅在声明时加上“implements Serializable”是不够的。为了保证singleton必须声明所有实例域都是瞬时的(transient),并且还需要提供一个readResolve方法。否则每次反序列化都会创建一个实例。

注意:

为了使利用这其中一种方法实现的Singleton类变成是可序列化的(Serializable),仅仅在声明中加上“implements Serializable”是不够的。为了维护并保证Singleton,必须声明所有实例域都是瞬时(transient)的,并提供一个readResolve方法。

package chapter2.singleton;

// Serializable singleton with public final field - Page 18
public class serializable {
    public static final serializable INSTANCE = new serializable();
    private serializable() { }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    private Object readResolve() {
        // Return the one true Elvis and let the garbage collector
        // take care of the Elvis impersonator.
        return INSTANCE;
    }
}


从JDK1.5开始,实现Singleton还有第三种方法,只需编写一个包含单个元素的枚举类型。推荐的写法: 

public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

单元素的枚举类型已经成为实现Singleton的最佳方法。

登记式模式(holder)

<span style="font-weight: normal;">public class Singleton {
    private Singleton() {} //构造方法是私有的,从而避免外界利用构造方法直接创建任意多实例。
    public static Singleton getInstance() {
        return Holder.SINGLETON;
    }
    private static class Holder {
       private static final Singleton SINGLETON = new Singleton();
    }
}</span>

内部类只有在外部类被调用才加载,产生SINGLETON实例;又不用加锁。此模式有上述前两个模式的优点,屏蔽了它们的缺点,是最好的单例模式。


4.通过私有构造器强化不可实例化的能力

对于一些不需要被实例化的工具类,需要将其构造器私有化。因为在缺少显示构造器的情况下,编译器会自动提供一个公有的、无参的缺省构造器。
这样做也有副作用,它使得一个类不能被子类化。因为所有的构造器都必须显式或隐式地调用超类构造器,而这种情况下,子类就没有可访问的超类构造器可调用了。

5.避免创建不必要的对象

一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。
demo1:
String s = new String("hello world");
需要改成
String s = "hello world";
因为第一种写法,每一次调用的时候都会创建一个新的String实例。但是第二种写法,在同一台虚拟机中运行的代码,只要他们包含相同的字符串字面常量,就会被重用。

对于同时提供了静态工厂方法(见第1条)和构造器的不可变类,通常可以使用静态工厂方法而不是构造器,以避免创建不必要的对象。

6.消除过期的对象引用

下面这段程序存在一个“内存泄漏”。在极端的情况下,会导致磁盘交换(Disk Paging),甚至导致程序失败(OutOfMemoryError)。
那么哪里引起的内存泄漏呢?
如果一个栈先是增长,然后再收缩,那么从栈中弹出来的对象讲不会被当做垃圾回收,即使使用的栈程序不再引用这些对象。
这是因为,栈内部维护着对这些对象的过期引用(obsolete reference)。过期引用是指,永远也不会再被接触的引用。

修复的方法:

一旦对象引用已经过期,只需清空这些引用即可。
当然也没必要谨慎的将所有不用的东西设置为null。
清空对象引用应该是一种例外,而不是一种规范行为。
消除过期引用最好的方法是让包含该引用的变量结束其生命周期。

那么,合适应该清空引用呢?

问题在于,Stack类自己管理内存。存储池包含了elements数组的元素。数组活动区域中的元素是已分配的,而数组其余部分的元素则是自由的。
但是对于垃圾回收器而言,elements数组中的所有对象引用都同等有效。
一旦数组元素变成了非活动部分的一部分,程序员就收工清空这些数组元素。
只要类是自己管理内存,程序员就应该警惕内存泄漏问题。

缓存

LinkedHashMap类的removeEldestEntry(Map.Entry<K,V> eldest) 此方法非常有用:它允许映射通过删除旧条目来减少内存损耗。 

监听器和其他回调

如果你实现了一个API,客户端在这个API中注册回调,却没有显示的取消注册,那么除非你采取某些动作,否则他们就会积聚。
最佳方法是只保存他们的弱引用(weak reference),例如,只将他们保存为WeakHashMap中键。

可借助于Heap剖析工具(Heap Profiler)发现内存泄漏问题。

7.避免使用终结方法

终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的。
如果程序依赖于终结方法被执行的时间点,那么这个程序的行为在不同的JVM实现中会大相径庭。

Java语言规范不仅不保证终结方法会被及时的执行,而且根本不保证他们会被执行。

不应该依赖终结方法来更新重要的持久状态。

不要被System.gc和System.runFinalization这两个方法所诱惑,他们确实增加了终结方法被执行的机会,但是他们不保证终结方法一定被执行。唯一声称保证终结方法被执行的方法是System.runFinalizersOnExit,以及他臭名昭著的孪生兄弟Runtime.runFinalizersOnExit。这两个方法都有致命的缺陷,都被废弃了。

使用终结方法有一个非常严重的(Severe)性能损失。
提供一个显示终止的方法来终止一些必要的资源。

显示终止方法的典型例子是InputStream、OutputStream和java.sql.Connection上的close方法。
另一个例子是java.util.Timer上的cancel方法,它执行必要的状态改变,使得与Timer实例相关联的该线程温和的终止自己。
java.awt中的例子还包括Graphics.dispose和Window.dispose。
Image.flush,他会释放所有与Image实例相关联的资源,但是该实例仍然处于可用的状态,如果有必要的话,会重新分配资源。

总结:除非作为安全网,或者是为了终止非关键的本地资源,否则请不要使用终结方法。在这些很少见的情况下,既然使用了终结方法,就要记住调用super.finalize。如果用终结方法作为安全网,要记得记录终结方法的非法用法。最后,如果需要吧终结方法与公有的非final类关联起来,请考虑使用终结方法守护者,以确保即使子类的终结方法未能调用super.finalize,该终结方法也会被执行。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值