Effective Java(第三版) 学习笔记 - 第二章 创建和销毁对象 Rule5~9

Effective Java(第三版) 学习笔记 - 第二章 创建和销毁对象 Rule5~9

目录

Effective Java(第三版) 学习笔记 - 第二章 创建和销毁对象 Rule5~9

Rule5 优先考虑依赖注入来引用资源

Rule6 避免创建不必要的对象

Rule7 消除过期的对象引用

Rule8 避免使用终结方法和清除方法

Rule9 try-with-resource优先于try-finally


Rule5 优先考虑依赖注入来引用资源

对于做开发的来说,依赖注入、控制反转已经不是新鲜的名词了,Spring的依赖注入框架也早已被大家日常使用。

只需要记住,静态工具类和Singleton类不适合于需要引用底层资源的类

顺便提一下书中提到了Java8的Supplier<T>

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

这个可以理解为一个创建指定对象的工厂

/**
 * @author xuweijsnj
 */
public class SupplierTest {
    private double num = Math.random();
    public double getNum() {
        return num;
    }
    public static void main(String[] args) {
        Supplier<SupplierTest> s = SupplierTest::new;
        SupplierTest s1 = s.get();
        System.out.println(s1.getNum());
        System.out.println(s1.getNum());
        SupplierTest s2 = s.get();
        System.out.println(s2.getNum());
    }
}

---输出结果
0.308063828592685
0.308063828592685
0.15816737830758765

 

Rule6 避免创建不必要的对象

这个也是日常开发中代码review经常老生常谈的问题之一了。有兴趣的建议阅读下《重构 改善既有代码的设计》类似的书籍了解。

不变的对象:

//Reusing expensive object for improved performance (Pages 22 and 23)
public class RomanNumerals {
    // Performance can be greatly improved! (Page 22)
    static boolean isRomanNumeralSlow(String s) {
        return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
                + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
    }

    // Reusing expensive object for improved performance (Page 23)
    private static final Pattern ROMAN = Pattern.compile(
            "^(?=.)M*(C[MD]|D?C{0,3})"
                    + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");

    static boolean isRomanNumeralFast(String s) {
        return ROMAN.matcher(s).matches();
    }

    public static void main(String[] args) {
        int numSets = 2000;
        int numReps = 10000;
        boolean b = false;
        
        long start = System.currentTimeMillis();
        for (int i = 0; i < numSets; i++) {
            for (int j = 0; j < numReps; j++) {
                b ^= isRomanNumeralSlow("MCMLXXVI");  // Change Slow to Fast to see performance difference
            }
        }
        long end = System.currentTimeMillis();
        System.out.println((end - start) + " milliseconds.");

        // Prevents VM from optimizing away everything.
        if (!b)
            System.out.println();
    }
}

----
isRomanNumeralSlow -> 18525 milliseconds.
isRomanNumeralFast -> 3408 milliseconds.

另一种多余的对象:自动装箱

public class Sum {
    private static long sum() {
        // long sum = 0L;
        Long sum = 0L;
        for (long i = 0; i <= Integer.MAX_VALUE; i++)
            sum += i;
        return sum;
    }

    public static void main(String[] args) {
        int numSets = 10;
        long x = 0;

        long start = System.currentTimeMillis();
        for (int i = 0; i < numSets; i++) {
            x += sum();
        }
        long end = System.currentTimeMillis();
        System.out.println((end - start) + " milliseconds.");

    }
}

----
long sum = 0L; -> 10237 milliseconds.
Long sum = 0L; -> 61812 milliseconds.

 

Rule7 消除过期的对象引用

Java开发相比C或者C++来说开发人员是比较幸福的,因为我们一般的开发中并不需要专门针对对象回收做特殊的处理,全部交由垃圾回收机制GC负责。

聊到GC,就会涉及到JVM的年轻代Minor GC、老年代Full GC,分代回收机制算法,以及多种垃圾回收器的选择:Serial收集器、ParNew、Parallel收集器、CMS收集器、G1收集器。(这里不做展开介绍)。

那么既然已经有GC帮助我们来清除过期失效对象了,为什么还会提到需要手动消除过期对象引用呢?我们用ArrayList的源码来说明。

    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
    public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

我们都知道ArrayList的底层是用数组实现的,而在需要删除或者清理List中的元素时,都会有elementData[i] = null;这样的代码,并同时附加了注释// clear to let GC do its work

那么为什么要这样做呢?因为当栈中出现不需要的元素对象时,即使栈不再引用这些对象,但是他们也不会被GC回收。因为栈内部维护着这些对象的过期引用,从而甚至有几率导致程序OOM。

一般来说,只要类是自己管理内存,程序员就应该警惕内存泄露问题。一旦元素被释放掉,则该元素中包含的任何对象引用都应该被清空。

 

Rule8 避免使用终结方法和清除方法

终结方法:finalize
清除方法:cleaner(Java9中用清除方法替代了终结方法)

为什么不推荐用终结方法或清除方法?

因为,我们根本无法控制终结方法和清除方法会被即时的执行。这依赖于JVM的垃圾回收算法。

什么情况下才可以考虑使用终结方法或清除方法?

作为安全网(预防有资源没有关闭)或者是为了终止非关键的本地资源native关键字标注的本地方法。

 

Rule9 try-with-resource优先于try-finally

Java7中引入了try-with-resource写法,前提是try中的资源实现了AutoCloseable接口。

以往的try-finally

public class Copy {
    private static final int BUFFER_SIZE = 8 * 1024;

    // try-finally is ugly when used with more than one resource! (Page 34)
    static void copy(String src, String dst) throws IOException {
        InputStream in = new FileInputStream(src);
        try {
            OutputStream out = new FileOutputStream(dst);
            try {
                byte[] buf = new byte[BUFFER_SIZE];
                int n;
                while ((n = in.read(buf)) >= 0)
                    out.write(buf, 0, n);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
    }

    public static void main(String[] args) throws IOException {
        String src = args[0];
        String dst = args[1];
        copy(src, dst);
    }
}

try-with-resource改造

public class Copy {
    private static final int BUFFER_SIZE = 8 * 1024;

    // try-with-resources on multiple resources - short and sweet (Page 35)
    static void copy(String src, String dst) throws IOException {
        try (InputStream   in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(dst)) {
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        }
    }

    public static void main(String[] args) throws IOException {
        String src = args[0];
        String dst = args[1];
        copy(src, dst);
    }
}

有什么好处?代码更简洁、清晰。而且不用显示的手写close,避免遗忘关闭资源。

 

本文技术菜鸟个人学习使用,如有不正欢迎指出修正。xuweijsnj

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值