Fork me on GitHub
0%

Effective Java - 创建和销毁对象

遇到多个构造器参数时要考虑用构建器

当我们的类的一些属性是必须的,有些属性是可选的,参数较多的情况下我们可以适当考虑使用构建器,其实在 Java 开发中我们也经常遇到这种通过构建器来创建类实例的情况,然后再结合链式调用写出来的代码还是比较美观的。下面结合一个简单的例子来说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package com.ecjtu;

import java.util.Arrays;
/**
* @author zhouxh
*/
public class User {

private String username;
private String password;
private String phone;
/**
* 可选参数
*/
private Integer sex;
/**
* 可选参数
*/
private String birthday;
/**
* 可选参数
*/
private String[] hobby;

public static class Builder {
private String username;
private String password;
private String phone;
/**
* 可选参数,给个默认值
*/
private Integer sex = 1;
/**
* 可选参数,给个默认值
*/
private String birthday = "0-0-0-0";
/**
* 可选参数,给个默认值
*/
private String[] hobby = new String[]{};

public Builder(String username, String password, String phone) {
this.username = username;
this.password = password;
this.phone = phone;
}

public Builder sex(Integer sex) {
sex = sex;
return this;
}

public Builder birthday(String birthday) {
birthday = birthday;
return this;
}

public Builder hobby(String[] hobby) {
hobby = hobby;
return this;
}

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

private User(Builder builder) {
username = builder.username;
password = builder.password;
phone = builder.phone;
sex = builder.sex;
birthday = builder.birthday;
hobby = builder.hobby;
}

@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", phone='" + phone + '\'' +
", sex=" + sex +
", birthday='" + birthday + '\'' +
", hobby=" + Arrays.toString(hobby) +
'}';
}
}

User 对象的创建:

1
2
3
4
5
6
public class App {
public static void main(String[] args) {
User user = new User.Builder("admin", "123456", "13999999999").sex(0).build();
System.out.println(user.toString());
}
}

当然,这里只是一个简单的小例子,开发中一般不会用这种方式来创建实体类,可能还是需要看个人开发时具体情况决定是否采用这种方式,这里只对这种创建方式做一个简单的介绍。

避免创建不必要的对象

一般来说,最好能重用对象而不是在每次需要时都去频繁的创建,重用的方式快,而且能够避免频繁创建浪费资源。像下面这种情况就推荐重用对象,可以对比以下两种方式的差别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.ecjtu;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
* 不推荐这样做
*
* @author zhouxh
*/
public class Person1 {

private final Date birthDate = new Date();

public boolean isBabyBoomer() {
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
Date boomStart = calendar.getTime();
calendar.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
Date boomEnd = calendar.getTime();
return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.ecjtu;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

/**
* 推荐这样做
*
* @author zhouxh
*/
public class Person2 {

private final Date birthDate = new Date();

private static final Date BOOM_START;
private static final Date BOOM_END;

static {
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
calendar.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_START = calendar.getTime();
calendar.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END = calendar.getTime();
}

public boolean isBabyBoomer() {
return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0;
}

}

消除过期的对象引用

我们在日常开发中可能稍有不慎就会遇到内存泄漏的问题,这需要我们从细节去防止这种情况的发生,一般而言,只要类是自己管理内存,就应该警惕内存泄漏的问题。下面展示一种可能会发生内存泄漏的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.ecjtu;

import java.util.Arrays;
import java.util.EmptyStackException;

/**
* @author zhouxh
*/
public class Stack {

private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;

public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}

public void push(Object object) {
ensureCapacity();
elements[size++] = object;
}

public Object pop1() {
if (size == 0) {
throw new EmptyStackException();
}
return elements[--size];
}

public Object pop2() {
if (size == 0) {
throw new EmptyStackException();
}
Object result = elements[--size];
/*
将弹出的值赋空,以便 jvm 回收,如果不赋空将不会被 jvm 回收,因为栈内部依然维护着这些对象的过期引用。
过期引用是指永远不会被解除的引用,在这里就是指在 elements 数组活动之外的引用,即元素下标小于 size 的那些元素。
所以一旦数组元素变成了非活动部分的一部分就应该手动清空这些数组元素。
*/
elements[size] = null;
return result;
}

private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}

这个随着栈的不断的弹栈和压栈,每次弹出如果不清空该引用,随着量的增加就可能造成内存泄漏的情况。

 wechat
扫描上面图中二维码关注微信公众号