EffectiveJava笔记(一) 创建和销毁对象

1. 考虑用静态工厂方法代替构造器

构造器:

public Boolean(boolean value) {
    this.value = value;
}

静态工厂方法:

public static Boolean valueOf(boolean b){
    return b ? Boolean.TRUE : Boolean.FALSE;
}

优点:

  • 静态工厂方法与构造器不同的第一大优势在于, 它们有名称, 这会让代码更容易阅读, 而有多个构造器的类, 会让使用者容易混淆
  • 静态工厂方法与构造器不同的第二大优势在于, 不必在每次调用它们的时候都创建一个新的对象, 这使得不可变类可以使用预先构建好的实例, 进行重复利用, 如果程序经常请求创建相同的对象, 这项技术可以极大提升性能
  • 静态工厂方法与构造器不同的第三大优势在于, 它们可以返回原返回类型的任何子类型的对象, 这样我们在选择返回对象的类时, 就有了更大的灵活性
  • 静态工厂方法的第四大优势在于, 在创建参数化类型实例的时候, 它们使代码变得更加简洁

    如:
    Map<String, List<String>> m = new HashMap<String, List<String>>();
    假设HashMap提供了这个静态工厂:
    public static <K, V> HashMap<K, V> newInstance() {
        return new HashMap<K, V>();     
    }
    则可替换为:
    Map<String, List<String>> m = HashMap.newInstance();
    

缺点:

  • 静态工厂方法的主要缺点在于, 类如果不含有公有的或者受保护的构造器, 就不能被子类化, 但是这样也许会因祸得福, 因为它鼓励程序员使用多复合(多实现接口)来代替继承

2. 遇到多个构造器函数时, 要考虑用构建器

静态工厂和构造器有个共同的局限性, 它们都不能很好地扩展到大量的可选参数, 如下

Person p = new Person(name);
Person p = new Person(name, age);
Person p = new Person(name, age, sex);

随着参数数量的增加, 对象的实例化会要求有更多的构造方法, 代码会很难编写, 仍然较难以阅读, 一长串类型相同的参数会导致一些微妙的错误, 如果客户端不小心颠倒了其中两个参数的顺序, 编译器也不会出错, 那么采用JavaBeans模式呢:

Person p = new Person();
p.setName(name);
p.setAge(age);
p.setSex(sex);

遗憾的是, JavaBeans模式自身有着很严重的缺点, 因为构造过程被分到了几个调用中, 在构造过程中JavaBean可能处于不一致的状态.JavaBeans模式阻止了把类做成不可变的可能, 这就需要程序员付出额外的努力来确保它的线程安全, 幸运的是, 还有第三种替代方法, Builder模式:

public class Person {
    private final String name;
    private final int age;
    private final String sex;

    private Person(Builder builder) {
        name = builder.name;
        age = builder.age;
        sex = builder.sex;
    }
    public static class Builder {
        private String name;
        private int age;
        private String sex;

        public Builder() {
        }

        public Builder name(String val) {
            name = val;
            return this;
        }

        public Builder age(int val) {
            age = val;
            return this;
        }

        public Builder sex(String val) {
            sex = val;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }
}

Person p = new Person.Builder().name("name").age(13).sex("man").build();

这样的客户端代码很容易编写, 更为重要的是, 易于阅读

3. 用私有构造器或者枚举类型强化Singleton属性

就是单例模式

public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis(){}
    public static Elvis getInstance() { 
        return INSTANCE;
    }   
}

4. 通过私有构造器强化不可实例化的能力

public class Elvis {
    private Elvis() {
        throw new AssertionError();
    }
}

这种习惯用法也有副作用, 它使得一个类不能被子类化

5. 避免创建不必要的对象

String实例化

String s = new String("stringette");

该语句每次执行的时候都创建一个新的String实例, 无论字符串字面常量是否相同, 改进后的版本如下所示:

String s = "stringette";

这个版本只用了一个String实例, 而不是每次执行的时候都会创建一个新的实例, 而且对弈同一台虚拟机中运行的代码, 只要它们包含相同的字符串字面常量, 该对象就会被重用

自动装箱拆箱

自动装箱, 它允许程序员将基本类型和装箱基本类型混用, 按需要自动装箱和拆箱

Long sum = 0L;
for(long i = 0; i < Integer.MAX_VALUE; i++) {
    sum += i;
}

这段程序算出的答案是正确的, 但是比实际情况要更慢一些, 因为变量sum被生命成Long而不是long, 以为着创徐构造了大约2的31次方个多余的Long实例, 速度上也是大打折扣, 所以, 要优先使用基本类型而不是装箱基本类型, 要当心无意识的自动装箱

6. 消除过期的对象引用

7. 避免使用终结方法

终结方法的缺点在于不能保证会被及时地执行, 而且会有严重的性能损失, 例如, 用终结方法来关闭已经打开的文件, 这是严重错误, 因为打开文件的描述符是一种很有限的资源, 由于JVM会延迟执行终结方法, 所以大量的文件会保留在打开状态

及时地执行终结方法正是垃圾回收算法的一个主要功能, 这种算法在不同的JVM实现中会大相径庭, 如果程序依赖于终结方法被执行的时间点, 那么这个程序的行为在不同的JVM中运行的表现可能就会截然不同, 一个程序在你测试用得JVM平台上运行得非常好, 哦而在你最重要顾客的JVM平台上却根本无法运行, 这是完全有可能的

Java语言规范不仅不保证终结方法会被及时执行, 而且根本就不保证它们会被执行, 结论是: 不应该依赖终结方法来更新重要的持久状态

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编码兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值