Effective Java:创建和销毁对象

前言:

读这本书第1条规则的时候就感觉到这是一本很好的书,可以把我们的Java功底提升一个档次,我还是比较推荐的。本博客是针对《Effective Java》这本书第2章所写的一篇读书笔记。博客中也有会一些个人对某个模块的理解和深入探究,希望与大家一起进步。

概述:

本章的主题是创建和销毁对象:何时以及如何创建对象,何时以及如何避免创建对象,如何确保它们能够适时地销毁,以及如何管理对象销毁之前必须进行的各种清理动作。

相关内容:

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

思路解析:

如果有人问你,如何去初始化一个类的对象实例时,你的第一反应可能就是去实现类的构造器。那如果我告诉你,这样的做法,其实并不是最好的方法,你可能会感觉意想不到,不会啊,老师和书上都是这样写的啊!怎么可能不去实现类的构造器就可以初始化一个类的对象实例呢?

办法还真有!如果你学习过一些设计模式(这里是废话了,如果你学习过设计模式,那你肯定早就知道是怎么回事了),比如单件模式。单件模式会涉及到一个这样的问题:如何让我们类的对象实现只被初始化一次呢?你可以使用类的构造器小小地实践一下,不管是打日志还是Debug,我想答案是一样的,不可能完成!这时,你就需要了解和学习用静态工厂方法来代替构造器了,你可以模仿以下实现。

public static ClassA getInstance() {
    return new ClassA;
}

看到以上代码,可能你会有所抱怨,不对啊,上面的方法没有达成我们需求中的效果啊,你一定是在耍我!

先不要着急,上面的方法的确只是一个思路过程,如果你想让你的对象只对被实例化一次,你可以模仿以下代码:

public static ClassA getInstance() {
    if(mInstance == null) {
        mInstance = new ClassA();
    }
    return mInstance;
}

我想实现了以上代码的你可能会很开心,这样的确可以只实例化一个对象了。如果你的好奇心足够强大的话,我想你还应该尝试一下,使用构造器来重新实例一次或N次。还是可以实例化很多个对象实例,对不对?别担心,我没有骗你,是要利用以上的静态工厂方法,不过你还有一件小事没有去完成,那就是屏蔽默认的构造方法。像下面这样的:

private ClassA() {
    // do something!!!
}

没错,就是这样,你可以在私有的构造器中添加一些你想添加的,这都没有关系,但是请保证它的私有性。可能看到这里,你没有明白一个静态的工厂方法会给我们带来什么样的好处。下面就是对静态工厂方法的优点介绍。

静态工厂方法的优点:

a.它们有名称:你可以使用对方法的合理命名来提示用户,这一次被创建出来的是什么样的对象。

b.不必在每次调用它们的时候都创建一个新的对象:这一点上面的单件模式也有提到,这一优点的优势在于单件模式本身实在的需求和节约资源开支。

c.可以返回任何子类型的对象:这种灵活性的一种应用是,API可以返回对象,同时又不会使对象的类变成公有的。以这种方式隐藏实现类会使API变得非常简洁。

d.轻松创建相同参数的不同对象:想像一下,如果我们想要创建两个不同的对象,而这两个对象在创建的过程中都接受一个int型的参数,请问要如何创建?静态工厂方法就是答案!

静态工厂方法的缺点:

a.类如果不含有仅有的或是受保护的构造器,就不能被子类化:这里你可能会困惑,为什么这样会构成一个缺点?仔细一想,你肯定会原来如此。

b.它们与其他的静态方法实际上没有任何区别。

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

对于这一点我还是保留对大家的信任,大家都已经熟练掌握了它的使用和好处。它会使用在这样一个必要的环境下:如果你想构造一个类的对象实例,而这个类在构造器上需要传递很多参数,这里我们假设有10个。是不是想想就觉得很可怕,它充斥着我们的大脑,并抗争着说这样很麻烦,我们不要这样来做吧。而看起来麻烦还是小事,只要你耐心一点,总能渡过,不过如果你要把两个或N个参数给弄混了,那我想后果一定很“精彩”。

在这种情境下,我们可以考虑一下使用构建器。别被构建器的名称给吓到了,如果我换一个说法,相信你就会明白了。那就是把合适的参数封装成一个类,在类中使用setter和getter来实现需求。

这样的做法不仅可以让代码更简洁,还不容易让参数混淆,真是一个好东西啊。

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

在第1条中我们说到了关于单件模式的一些使用场景,下面会有一些额外介绍。如果你对单件模式还有一些想要了解的,可以在Java设计模式中进行了解。

对于如何使用私有构造器优化Singleton属性我们在第1条和Java设计模式的《Java设计模式——单件模式》中都有介绍,这里不再赘述。

关于如何使用枚举来强化Singleton属性,我们可以像下面这样:

public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding(){...}
}

这种方法和公有域方法相近,但是它更加简洁,无偿地提供了序列化机制,绝对防止多次实例化。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法

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

从节省资源的角度考虑,我们应该尽量避免创建不必要的对象。看到这里,你可能会说上面提到的单件模式算不算这个避免创建不必要的对象呢?我说它是,又不全是。因为它不单是因为要避免创建不必要的对象而设计的,更多的是它要实现的是只能有一个对象。

你是否会像这样来创建一个字符串:

String s = new String("abcdefghi");

可能你会说你没有,可是你可能很少会想到这样的一种方式来初始化一个字符串对象的实例。但是我想要说的是最好不要像上面那样创建,因为它在一个多次创建中会产生很多不必要的实例。

你可以这样来改进它,使之更加合理:

String s = "abcdefghi";

在Java的机制中,上面的这个版本只用了一个String实例,对于所有在同一台虚拟机中运行的代码,只要它们包含的字符串字面常量,该对象就会被重用。

5.消除过期的对象引用

在项目优化的时候,我们经常可能要去做内存泄漏的检测。不要以为Java已经有了垃圾回收的机制,我们就可以坐享其成,不再去考虑内存管理的事情了。如果你想了解更多内容,可以去看《Android开发中,可能会导致内存泄漏的问题》这篇文章。

下面请看这个例子: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];
    }

    private void ensureCapacity() {
        if(elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}

你能看出这段代码的问题所在吗?

我们试想一下,如果我们的栈在先增加元素,然后再收缩。那么,这时从栈中pop的对象不会被当作垃圾回收,即使用栈的程序不再引用它们,它们也不会被回收。这是因为,栈内部维护着这些对象的过期引用。这么说,可能你还不清楚什么是过期引用。换句话说吧,就是说我们的栈还在,这些被pop的对象曾经是属于这个栈的,它们都还持有这个活动栈的引用,那么Java的垃圾回收机制就不会对它们怎么样了。

这个就有一点像Android中,初始化了很多持有生命周期较长的Context的对象,而这些对象由于始终持有Context的引用,所以不会被Java回收机制回收。

这类问题的解决方法也很简单:一旦对象引用已经过期,只需清空这些引用即可。根据这一点,改进的程序如下:

public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }

        Object result = elements[--size];
        elements[size] = null;
        return result;
    }

清空过期引用的另一个好处是,如果它们以后又被错误地解除引用,程序就会立即抛出一个NullPointerException的异常,而不是悄悄地错误运行下去。

时间: 2024-12-23 11:15:46

Effective Java:创建和销毁对象的相关文章

[Effective Java]创建和销毁对象

第一条:考虑用静态工厂方法代替构造器 使用静态工厂方法的优势: 静态工厂方法具有名称,具有适当名称的方法更易阅读. 具有多个构造器的类用户往往不知道该用哪个,可考虑提供多个合适命名的静态工厂方法. 相比于构造器,不必再每次调用时都创建一个新对象 不可变类可以预先创建好实例,或者将构件好的实例缓存起来,从而避免重复创建对象.此方法类似于Flyweight模式.如果程序经常请求创建相同的对象,并且创建对象的代价很高,此项技术可以极大地提升性能.  可以返回原返回类型的任意子类型 在选择返回对象的类时

【Effective Java】创建和销毁对象

一.考虑用静态工厂方法代替构造器 构造器是创建一个对象实例的最基本最常用的方法.开发者在使用某个类的时候,通常会使用new一个构造器来实现,其实也有其他方式可以实现的,如利用发射机制.这里主要说的是通过静态类工厂的方式来创建class的实例,如: public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; } 静态工厂方法和构造器不同有以下主要优势: 1.有意义的名称. 可能有多个构造器,

Effective Java 读书笔记(2创建和销毁对象)

第一章是引言,所以这里不做笔记,总结一下书中第一章的主要内容是向我们解释了这本书所做的事情:指导Java程序员如何编写出清晰.正确.可用.健壮.灵活和可维护的程序. 2.1考虑用静态工厂方法代替构造器 静态工厂方法与构造器相比有四大优势: (1)静态工厂方法有名称,具有适当名称的静态工厂方法易于使用.易于阅读: (2)不必每次在调用它们的时候都创建一个新的对象: (3)可以返回原返回类型的任何子类型的对象: (4)在创建参数化类型实例的时候,它们使代码变得更加简洁. 同时静态工厂方法也有两大缺点

effective java读书笔记1——创建和销毁对象

今天刚开始读effective java,中文版的读起来很拗口,但感觉收获很多. 另外,这本书的内容是针对Java 1.5和1.6的. 在这里整理一下第2章:创建和销毁对象 的内容. 第一条:考虑用静态工厂方法代替构造器 这一条针对的情景是要获得类的实例时.一般说来,想要获得类的实例,都是通过构造函数(书里叫做构造器). 最常见的构造函数是这样的,没有返回参数,名字和类名相同. public class A{ public A(int a){ //构造函数内容 ... } } 而所谓的静态工厂,

【读书笔记】《Effective Java》——创建和销毁对象

Item 1. 考虑用静态工厂方法替代构造器 获得一个类的实例时我们都会采取一个公有的构造器.Foo x = new Foo(): 同时我们应该掌握另一种方法就是静态工厂方法(static factory method). 一句话总结,静态工厂方法其实就是一个返回类的实例的静态方法. 书中给出的例子是Boolean的valueOf方法: 通过valueOf方法将boolean基本类型转换成了一个Boolean类型,返回了一个新的对象引用. 除valueOf外,像Java中的getInstance

[Effective Java]第二章 创建和销毁对象

第一章      前言 略... 第二章      创建和销毁对象 1.            考虑用静态工厂方法代替构造器 创建对象方法:一是最常用的公有构造器,二是静态工厂方法.下面是一个Boolean的简单示例: public static Boolean valueOf(boolean b) { return (b ? Boolean.TRUE : Boolean.FALSE); } l  静态工厂方法与构造器不同的第一大优势在于,它们有名称. 作用不同的公有构造器只能通过参数来区别(因

[Effective Java 读书笔记] 第二章 创建和销毁对象 第一条

第二章  创建和销毁对象 第一条 使用静态工厂方法替代构造器,原因: 静态工厂方法可以有不同的名字,也就是说,构造器只能通过参数的不同来区分不同的目的,静态工厂在名字上就能表达不同的目的 静态工厂方法不用每次调用的时候都创建新的对象(其实是因为它是static的,所以只能用static的,所以是一早就创建了,不需要重复创建吧..),比如书中 Boolean.valueOf(boolean) 1 public static final Boolean TRUE = new Boolean(true

《Effect Java》学习笔记1———创建和销毁对象

第二章 创建和销毁对象 1.考虑用静态工厂方法代替构造器 优势: 1.有名称 2.不必在每次调用它们的时候都创建一个新的对象:   3.可以返回原返回类型的任何子类型的对象: JDBC(Java Database Connectivity) 服务提供者框架(Service Provider Framework) 服务接口.提供者注册API.服务访问API.服务提供者接口(负责创建其服务实现的实例): 4.在创建参数类型实例的对象,它们使代码变得更加简洁 2.遇到多个构造器参数时,要考虑使用构造器

Java进阶 创建和销毁对象

最近准备写点Javase的东西,希望可以帮助大家写出更好的代码. 1.给不可实例化的类提供私有构造器 比如:每个项目中都有很多工具类,提供了很多static类型的方法供大家使用,谁也不希望看到下面的代码: TextUtils textUtils = new TextUtils(); if(textUtils.isDigitsOnly("123")) { //doSometing }else { //doSomething } 自己写个工具类,总有人喜欢先初始化个实例在调用方法,然后还附