Effective Java摘录(三)

7.方法

38.检查参数的有效性
    在方法执行它的计算任务之前,应该先检查它的参数
    在设计方法时,应该使他们尽可能地通用,并符合实际的需要
    每当编写方法或者构造器时,应该考虑它的参数有哪些限制,应该把这些限制写在文档中,并在方法体的开头处,显示的检查来实施这些限制
39.必要时参数进行保护性拷贝
保护性拷贝时在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是针对原始对象,请不要使用clone方法进行保护性拷贝
长度非零的数组总是可变的,在把内部数组返回给客户端之前,应该总是进行保护性拷贝,或者返回给客户端该数组的不可变视图
只要有可能,都应该使用不可变的对象作为对象内部的组件
40.谨慎设计方法的签名
谨慎选择方法名称:首先要选择易于理解的,并与同一个包中的其他风格一致的名称,二选择大众认可的名称
不雅过于追求提供便利的方法:每个方法都应该尽其所能,方法太多会使类难以学习,使用
避免过长的参数列表:目标是4个,或者更少,1.把方法拆解成对个小方法,注意可能导致方法过多;2,创建辅助类,用来保存参数,如果一个频繁出现的参数序列可以被看做是代表了一个独特的实体,建议使用这种方式
对于参数类型,要优先使用接口而不是类
对于boolean参数,优先使用两个元素的枚举类型
41.慎用重载
永远不应该导出两个相同参数数目的重载方法
42.慎用可变参数
43,返回零长度的数组或者集合,而不是null
44.为所有导出API元素编写文档注释

8.通用程序设计

45.将局部变量的作用域最小化
46.for-each循环优先于传统的for循环
47.了解和使用类库
48.如果需要精确的答案,请避免使用float和double
请使用BigDecimal,如果性能非常关键,并且你又不介意自己记录十进制小数点,而且所涉及的数值又不太大,就可以使用int或者long
49.基本类型优先于装箱基本类型
Java包括两种类型:基本类型(primitive)和引用类型(reference type)
对装箱基本类型运用==0操作符几乎总是错误的
50.如果其他类型更合适,则尽量避免使用字符串
如果可以使用更加合适的数据类型,或者可以编写更加适当的数据类型,就应该避免用字符串来表示对象
51.当心字符串连接的性能
52.通过接口引用对象
53.接口优先于反射机制
55.谨慎的进行优化
很多计算上的过失都被归咎于效率(没有必要达到的效率),而不是任何其他的原因-甚至包括盲目地做傻事
不要去计较效率上的一些小小的得失,在97%的情况下,不成熟的优化才是一切问题的根源
在优化方法,我们应该遵循两个原则:
    1.不要进行优化
    2.在你还没有绝对清晰的未优化方案之前,请不要进行优化
要努力编写好的程序而不是快的程序,好的程序体现了信息隐藏:只要有可能,它们就会把设计决策集中在单个模块中,因此可以改变单个决策,而不会影响到系统的其他部分
56.遵守普遍接受的命名规范

11.序列化

74.谨慎地实现Serializable接口
    为了继承而设计的类,应该尽可能少的去实现Serializable接口,用户的接口也应该尽可能少地继承Serializable接口
    private void readObjectNoData() throws InvalidObjectException{
    throw new InvalidObjectException("Stream data required");
    }
        public abstract class AbstractFoo {
    private final AtomicReference<State> init = new AtomicReference<State>(State.NEW);
    private int x, y;

    public AbstractFoo(int x, int y) {
        initialize(x, y);
    }

    public AbstractFoo() {

    }

    protected void initialize(int x, int y) {

        if (!init.compareAndSet(State.NEW, State.INITIALIZING)) {
            throw new IllegalStateException("Already initialized");
        }
        this.x = x;
        this.y = y;
        init.set(State.INITALIZED);
    }

    protected int getX() {
        checkInit();
        return x;
    }

    protected int getY() {
        checkInit();
        return y;
    }

    private void checkInit() {

        if (init.get() != State.INITALIZED) {
            throw new IllegalStateException("Uninitialized");
        }


    }

    private enum State {
        NEW, INITIALIZING, INITALIZED;
    }

    public static class Foo extends AbstractFoo implements Serializable {
        private static final long serialVersionUID = 7768591925619612747L;

        public Foo(int x, int y) {
            super(x, y);
        }

        private synchronized void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
            s.defaultReadObject();
            int x = s.readInt();
            int y = s.readInt();
            initialize(x, y);
        }

        private synchronized void writeObject(ObjectOutputStream s) throws IOException {
            s.defaultWriteObject();
            s.writeInt(getX());
            s.writeInt(getY());
        }
    }

}
内部类不应该实现Serializable,它们使用编译器产生的合成域来保存指向外围实例的引用,以及保存来自外围作用域的局部变量的值,"这些域如何对应到类定义中"并没有明确的规定,就好像没有指定匿名类和局部类的名称一样,因此内部类的默认序列化形式是定义不清楚的,然而, 静态成员类却可以实现
75.考虑使用自定义的序列化形式
transient修饰符表明这个实例域将从一个类的默认序列化形式中省略掉

猜你喜欢

转载自blog.csdn.net/a90123/article/details/80642522