【读薄Effective Java】创建和销毁对象
1. 考虑用静态工厂方法代替构造器
1.1 静态工厂的优点
静态工厂就是通过静态方法来代替构造器。相比构造函数,它有几个优势。
- 构造器没有名称。而静态工厂能指定名称,当一个类有多组构造函数的时候,可以用名称来把他们区分开来。
- 构造器每次调用都会新建一个对象,而静态工厂可以在每一次调用都返回同一个对象,在这样的前提下可以用==代替equals来提升性能
- 可以返回任何类型的类,例如Collections中通过静态工厂返回许多不可修改的类,这样做那些类不会暴露出来,使得API简洁许多
#!java
public class Point {
private double x;
private double y;
private Point(double x,double y) {
this.x = x;
this.y = y;
}
public static Point valueOfXY(double x,double y) {
return new Point(x,y);
}
public static Point valueOfPolar(double r,double theta) {
return new Point(r * Math.cos(theta),r * Math.sin(theta));
}
}
一个点的类,可以根据坐标或者极坐标来生成,用静态工厂方法就能很清楚的描述意图。
1.1 静态工厂的缺点和克服方法
- 由静态工厂封装起来的类(优点第三点)不能被继承
- 它很难和其他静态方法区分出来
为了克服缺点,我们要遵守标准的命名习惯,让使用者一看就知道这个是静态工厂方法。
- valueOf 这个方法一般返回与调用类相同值的实例。
- of 是用于代替valueOf的缩写
- getInstance 这个方法保证返回的实例都有所不同
- newInstance 同上
- getType Type表示返回的对象类型
- newType 同上
静态方法给我们在写构造器的时候多了一个选择,一般情况下,采用静态工厂更适合。
2. 遇到多个参数的构造器用Builder模式
我们看一下多个参数的构造器
#!java
public Student(Builder builder) {
this.sex = builder.sex;
this.name = builder.name;
this.major = builder.major;
this.club = builder.club;
}
以上代码major和club是可选的,如果要用构造器实现的话要重写3个构造器。
如果用set方法实现,破坏了不可变性,而且显得非常啰嗦
关于不可变性的好处在
我们有多个理由来用Builder
2.1 Builder的优点
- 如果参数很多的话,我们很难记住他们的调用顺序
- 如果有些量是可选的,我们要重载多个构造器
- 用get set模式来设置可选参数破坏了把类变成不可变的可能,类有多重状态增加了调试的难度
看一下Builder的代码
#!java
public class Student {
private final String sex;
private final String name;
private final String major;
private final String club;
public Student(Builder builder) {
this.sex = builder.sex;
this.name = builder.name;
this.major = builder.major;
this.club = builder.club;
}
public static class Builder {
private String club;
private String major;
private String sex;
private String name;
Builder(String sex, String name) {
this.sex = sex;
this.name = name;
}
public Builder club(String club) {
this.club = club;
//返回builder方便后面连续调用
return this;
}
public Builder major(String major) {
this.major = major;
return this;
}
public Student built() {
return new Student(this);
}
}
public static void main(String[] arg) {
//创建一个名字为nene,茶道部,计算机专业的学生
Student student = new Builder("female","nene")
.club("tea")
.major("computer")
.built();
}
}
2.2 Builder的缺点
- 首先必须先创建Builder类,虽然平时开销不明显,但是在特别注重性能的情况下可能会有问题
- 如果参数不多的时候,可能比重叠构造器还啰嗦,所以一般当我们有4个参数以上的时候才会用
3. 用私有构造器强化不可实例化的能力
有时候我们会需要写只包含静态方法和静态域的类,我们并不希望这些类被实例化或者被继承
这些类也被叫做工具类(utility class)
对于这样的类我们只需要要把构造方法设置成私有的就可以避免被实例化了。
但是要注意这样做的话,类不能被继承。
#!java
public class Utility {
public static void hello() {
System.out.println("hello");
}
private Utility() {}
}
4. 用静态方法或者枚举类型强化Singleton属性
Singleton指的是只实例化一次,唯一的类
实现它有几种方法
4.1 用静态方法
创建一个构造器是私有的对象然后用静态方法来返回该对象,提供了灵活性,但是可能会被通过反射机制攻击,所以我们需要在第二次调用构造方法时抛出异常
#!java
public class Utility {
private static final Utility INSTANCE = new Utility();
//设置为私有的构造器
private Utility() {
...
}
public static Utility getInstance() {
return INSTANCE;
}
}
4.2 用枚举类型实现
JAVA1.5加入了枚举类型。
5. 避免创建不必要的对象
为了避免标题的误导首先我们需要知道一点,创建对象的开销并不是特别大,除非是很大的对象(比如数据库的连接)。小对象的创建与回收操作非常廉价。
5.1 重用不可变对象
对于提供了静态工厂方法和构造器的对象,我们一般可以用静态工厂方法而不是构造器,因为构造器每次调用都会创建一个新的对象而静态工厂不要求也不会这样做。
//错误示范
String s = new String("不要干这种傻事")
5.2自动装箱中的陷阱
JAVA在1.5版本引入了自动装箱新的特性,它允许程序员混用基本类型和装箱基本类型(比如int 和 Integer)。
考虑下面的代码
#!java
Integer sum = 0;
for(int i = 0 ; i < 500 ;i++) {
sum += i;
}
System.out.println(sum);
当i > 128时,sum不会重复利用,而是在每次循环中创建一个新的sum(
相当于sum = new sum(sum + i))。在这个例子中多创建了372个对象。
本文来自博客园,作者:zjmeow,转载请注明原文链接:https://www.zjmeow.com/