zoukankan      html  css  js  c++  java
  • Effective Java2读书笔记创建和销毁对象(三)

    第5条:避免创建不必要的对象

    本条主要讲的是一些反面教材,希望大家引以为鉴。

    ①无意中使用自动装箱导致多创建对象。

    public class Sum {
        public static void main(String[] args) {
            Long sum = 0L;
            for (long i = 0; i < Integer.MAX_VALUE; i++) {
                sum += i;
            }
            System.out.println(sum);
        }
    }

    sum被声明为Long而不是long,意味着每次i都要被自动装箱(创建Long实例),因此速度会慢很多。

    ②可以重用不会被修改的可变对象。

    也就是说,有些看起来是变量,但是实际上它的值一旦计算出来之后就不再变化,这种也应该视作常量,而不是每次都计算。

    下面先看一段代码。

    public class Person {
        //生日
        private final Date birthDate;
    
        public Person(Date birthDate) {
            this.birthDate = new Date(birthDate.getTime());
        }
    
        public boolean isBabyBoomer() {
            Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
            gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
            //婴儿潮开始时间
            Date boomStart = gmtCal.getTime();
            gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
            //婴儿潮结束时间
            Date boomEnd = gmtCal.getTime();
            return birthDate.compareTo(boomStart) >= 0
                    && birthDate.compareTo(boomEnd) < 0;
        }
    }

    这段代码中的isBabyBoomer方法,根据生日判断是否属于婴儿潮时间。看起来并没有什么问题。

    但实际上,每次调用这个方法时,都会新建一个Calendar,一个TimeZone和两个Date实例,这是没有必要的(所以,在平时写方法时也要注意,有很多对象是可以提取出来的)。下面看如何改进。

    public class Person {
        private final Date birthDate;
    
        public Person(Date birthDate) {
            this.birthDate = new Date(birthDate.getTime());
        }
        private static final Date BOOM_START;
        private static final Date BOOM_END;
    
        static {
            Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
            gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
            BOOM_START = gmtCal.getTime();
            gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
            BOOM_END = gmtCal.getTime();
        }
    
        public boolean isBabyBoomer() {
            return birthDate.compareTo(BOOM_START) >= 0
                    && birthDate.compareTo(BOOM_END) < 0;
        }
    }

    改进之后Person类仅在初始化时创建了实例,后面调用isBabyBoomer方法时,就没有生成对象的消耗了。

    第6条:消除过期的对象引用

    下面的例子对java中的栈进行一个简单的模拟,看起来逻辑上没什么问题,却隐藏着一个问题。

    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();
            Object result = elements[--size];
            //如果没有这一步,会产生很大的问题
            //elements[size] = null;
            return result;
        }
    
        private void ensureCapacity() {
            if (elements.length == size)
                elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }

    问题就在于,当栈先增长,然后再收缩。从栈中弹出来的对象并不会被当做垃圾回收,这是因为栈内还维护着这些对象的引用。弹出之后,使用栈的程序不再需要它,只能人为清理掉。一般而言,只要类是自己管理内存,程序员就应该警惕内存泄漏问题。

    文中还还提到在使用回调时,确保回调立即被当作垃圾回收的最佳方法时只保存它们的弱引用,例如,只将它们保存成WeakHashMap中的键(这个平时很少用到,只作为了解)。

    弱引用与强引用的区别在于,即使内存不足,强引用也不会被回收掉。而弱引用不管内存足不足,一旦被垃圾回收线程发现,就会被回收掉。

    作者: 张万帆
    欢迎任何形式的转载,但请务必注明出处。

  • 相关阅读:
    python2在安装pywin32后出现ImportError: DLL load failed 解决方法
    selenium webdriver 启动三大浏览器Firefox,Chrome,IE
    windows下创建Python虚拟环境
    个人随笔
    在Pycharm中使用jupyter笔记本
    python之socket网络编程
    如何创建一个Django项目
    在Ubuntu终端彻底删除软件
    Ubuntu16.04下安装redis
    postgresql11 查看表结构和系统视图
  • 原文地址:https://www.cnblogs.com/ZhangWanFan/p/5246422.html
Copyright © 2011-2022 走看看