十月读书笔记:Effective Java(二)--注意栈内过期的对象引用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/l00149133/article/details/52787747

一. 避免创建重复的对象
一个我们所熟知的,经常被考来考去的例子是:

String s = new String("helloWorld");   

上面这句创建了两个对象,一个是对象s,一个是字符串实例helloWorld,而当这句被放到一个for循环中,则可能创建成百上千个实例。同样不可取的还有下面这句(不可放入到循环中):

String s= null;
s = s + "a";  

为了避免创建重复对象,我们可以使用单例,或者静态域等

二. 消除过期的对象引用
我们来先看一个例子,这个例子里面,我们创建了一个数组,并赋给它两个行为:出栈和入栈。注意,当不停入栈的时候,数组元素可能超过原数组的大小,这时候我们需要扩容数组。

public class Stack{
    private Object[] elements;
    private int size = 0;

    public Stack(int count){
        this.elements = new Object[count];
    }

    public void push(Object o){
        ensureCapacity();
        elements[size++] = o;
    }

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

    private void ensureCapacity(){
        if(elements.length==size){
            Object[] oldElements = elements;
            elements = new Object[2*elements.length + 1];
            System.arraycopy(oldElements, 0, elements, size);
        }
    }
} 

这段代码中没有明显的错误,但是实际上它存在一个代码泄露:
如果一个栈先增长,再收缩,那么,从栈中弹出来的对象将不会被垃圾回收。
这里,增长指的是ensureCapacity()扩容后,push入栈,而收缩指的是pop出栈。由于存在–size,出栈后,其实原栈顶元素永远不会再被使用,而且再push后,你也找不到这个元素了,因为它在数组中的位置已经被新的元素替换了。这个“活动区域”之外的引用,我们称之为“过期引用”,却不会被回收,因为垃圾回收机制不知道这个引用是否过期,而上面的例子里,如果Object换成一个复杂的类实例,里面再加上诸多引用,那这个类实例相关的引用都不会被释放

PS:补充一下堆栈的基础知识。
栈(stack),有时候我们也称为堆栈(这一点可能会让很多小伙伴迷茫)。是由操作系统自动分配和释放的,用来存放局部变量,基本类型的值(比如int,char,boolean等),因为它是操作系统自动分配和释放的,所以通常我们看不到栈的操作。另外,栈是先进先出的。
堆(heap)。由程序员自己来分配和释放。用来存放用new创建的对象和数组。注意,前面我们说了“由程序员自己来分配和释放”,实际上在JAVA里面,是由程序员自己来分配的(new),但是不是由程序员自己来释放的,而是通过GC(垃圾回收器)来完成释放的,程序员完全感知不到。
方法区(method)。又叫静态区,用来存放在整个程序中都是唯一的元素,比如所有的class,以及static变量

猜你喜欢

转载自blog.csdn.net/l00149133/article/details/52787747