Effective-Java_读书笔记09-10

92 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情

13 Minimize the accessibility of classes and members 最小化类和类成员的访问权限

结论

尽可能的减小类和类成员的访问权限(其实就是题目拉~). 降低访问权限可以有效减少类之间的耦合程度, 从而降低代码的影响范围. 有利于优化, 测试等.

实现方式

这些道理大家都懂, 下面列一些实际的干货~

  1. 类属性要设置为private. 这应该是从第一天开始学习Java就知道的知识点, 属性需要通过getset方法使用(后面有一节专门讨论这个).
  2. 内部类. 当有一个类A只有在类B中使用, 那么就可以考虑将类A构造为类B的内部类, 极大降低类A的作用范围. 举个例子, 在lion中配置了一些过滤规则, 格式为json, 解析后业务只关心请求是否被过滤, 不关心具体的配置内容. 所以可以提供一个类做对外提供方法判断当前请求是否被过滤, 在该类中设置解析需要的内部类(json解析对象).
  3. final修饰可变对象. 通常来说final修饰的属性表示不可更改, 但是如果修饰的是一个可变对象, 则有可能修改其中的内容. 所以尽量不要使用final修饰可变对象, 如果确实有这样的需求(虽然想象不出), 可以将final修饰的对象的权限设置为private, 然后有相关需求可以通过方法暴露.

总结

减小类和类成员的访问权限, 一方面可以方便测试, 提高开发效率(并行开发). 另一方面也可以减小优化过程中对其他模块的影响. 想一想平常工作中有没有遇到不敢改代码的情况? 牵一发而动全身的感觉. 所以需要时刻关注访问权限, 保持模块间或者类之间干净的关系.

57 Use exceptions only for exceptional conditions 只在异常情况才使用exception

结论

题目即结论: exception只有在异常情况下使用.

还会有其他的使用场景? 还真有...

先看下错误例子

try {
    while (true){
        array[i++].doSomething();
    }
} catch (Exception e) {
    // 停止循环
}

第一眼没看明白, 第十眼的时候才明白代码是通过异常控制循环结束. 一个不够就再来一个~

void search( TreeNode node, Object data ) throws ResultException {
	if (node.data.equals( data ))
		throw new ResultException( node );
	else {
		search( node.leftChild, data );
		search( node.rightChild, data );
	}
}

啊~这...写return是不是犯法?

以上都是exception乱用的例子, 但是它们都有使用的理由: 为了性能(为了部落感觉更有说服力...).

先看第一个, 使用exception可以减少每次对数组大小的检查. 第二个呢, exception无论搜索层级多深在查找到数据后都可以跳出递归.

对于现代JVM来说, 这些优化点都已经被考虑在内, 相反这些"代码优化"其实会降低性能, 原因是exception对象的创建是非常消耗性能的(各种堆栈信息), java在设计时认为抛出异常是一种小概率事件, 大部分情况程序都会正常运行, 所以不会为了提高异常性能去影响正常流程.

本地机器通过以下代码验证, 对于100长度的数组计算时间相差非常大, 可见exception创建性能的消耗是很大的. (后面想办法从原理上分析下)

public static void main(String[] args) {

    int size = 100;
    int[] arr = new int[size];
    for (int i = 0; i < size; i++) {
        arr[i] = i;
    }

    long start = System.nanoTime();
    int sum = 0;
    try {
        int i = 0;
        while (true) {
            sum += arr[i++];
        }
    } catch (Exception e) {
        System.out.println("end");
    }
    long end = System.nanoTime();
    System.err.println("exception: " + (end - start));

    start = System.nanoTime();
    for (int i = 0; i < size; i++) {
        sum += arr[i++];
    }
    end = System.nanoTime();
    System.err.println("normal: " + (end - start));
}

// 结果
end
exception: 277664
normal: 3647

所以这些骚操作并不会带来性能上的提升, 相反还会影响性能. 而且从语义上来说如果使用exception控制正常流程, 那么异常情况又要使用什么呢(以德报怨?) ?

总结

正常写代码, 异常就在异常时候用. 代码可读性是第一优先级(99%的情况下).

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情