您的位置:首页 > Web前端

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

2017-07-01 14:41 232 查看

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语言规范不仅不保证终结方法会被及时执行, 而且根本就不保证它们会被执行, 结论是: 不应该依赖终结方法来更新重要的持久状态
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: