1.考虑使用静态工厂方法替代构造方法
优势:
- 静态工厂方法相比构造器有名称
- 使用静态工厂方法, 不比在每次调用的时候都创建一个新对象
- 使用静态工厂方法, 可以返回原返回类型的任何子类型的对象
- 使用静态工厂方法, 在创建参数化类型示例的时候可以使代码更加简洁
缺点:
- 类如果不含有共有的或是受保护的构造器, 就不能被子类化
- 它们与其他静态方法实际上没有任何区别
静态工厂方法的命名:
valueOd, of, getInstance, newInstance, getType, newType
补充: 静态工厂方法返回的对象所属的类, 在编写包含该静态工厂的方法的类时可以不必存在, 这种灵活的静态工厂方法构成了服务提供者框架的基础
服务提供者框架(Service Provider Framwork)
- 服务接口, 提供者要实现的接口
- 提供者注册API, 系统用来注册实现, 让客户端访问的
- 服务访问API, 是客户端用来获取服务的实例的
服务提供者接口(可选), 提供者负责创建其服务实现的实例 ```java public interface Provider {
Service newService(); }
public interface Service {
}
public class Services {
private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = "<def>";
private Services() {
}
public static void registerDefaultProvider(Provider p) {
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
private static void registerProvider(String name, Provider p) {
providers.put(name, p);
}
public static Service newInstance() {
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name) {
Provider p = providers.get(name);
if (p == null) {
throw new IllegalArgumentException("No provider registered with name: " + name);
}
return p.newService();
}
}
<a name="f8XMd"></a>
## 2.遇到多个构造器参数是要考虑用构建器
简言之, 方便拓展大量的可选参数.<br />构造器示例:
```java
/**
* @author gavin
* @date 2020-07-02
*/
public class DemoBuilder {
private int certainArg1;
private int certainArg2;
private int unCertainArg3;
private int unCertainArg4;
private int unCertainArg5;
private DemoBuilder(Builder builder) {
certainArg1 = builder.certainArg1;
certainArg2 = builder.certainArg2;
unCertainArg3 = builder.unCertainArg3;
unCertainArg4 = builder.unCertainArg4;
unCertainArg5 = builder.unCertainArg5;
}
public static class Builder {
// 必须的参数
private final int certainArg1;
private final int certainArg2;
// 非必须的参数, 初始化给默认值
private int unCertainArg3 = 0;
private int unCertainArg4 = 0;
private int unCertainArg5 = 0;
public Builder(int certainArg1, int certainArg2) {
this.certainArg1 = certainArg1;
this.certainArg2 = certainArg2;
}
public Builder unCertainArg3(int val) {
unCertainArg3 = val;
return this;
}
public Builder unCertainArg4(int val) {
unCertainArg4 = val;
return this;
}
public Builder unCertainArg5(int val) {
unCertainArg5 = val;
return this;
}
public DemoBuilder build() {
return new DemoBuilder(this);
}
}
}
使用:
@Test
public void testDemo(){
DemoBuilder demo = new DemoBuilder.Builder(1, 2).unCertainArg3(3).unCertainArg4(4).unCertainArg5(5).build();
}
3.用私有构造器或者枚举类型强化Singleton属性
4.通过私有构造器强化不可实例化的能力
5.避免创建不必要的对象
- 对于同时提供了静态工厂方法和构造器的不可变类, 通常可以使用静态工厂方法而不是构造器, 以避免创建不必要的对象, 例如, Boolean.valueOf(String), 优先于Boolean(String).
- 重用那些已知的不会被修改的可变对象, 可以在类初始化的时候只创建实例一次, 比如正则的Pattern
- 优先使用基本类型而不是装箱基本类型, 要当心无意识的自动装箱
示例: 下面代码构造了大约2的31次方个多余的Long实例, 每次往Long sum中增加long时构造了一个实例
/**
* 计算所有int数的值
* @param args
*/
public static void main(String[] args) {
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += 1;
}
System.out.println(sum);
}
6.消除过期的对象引用
- 只要类是自己管理内存, 程序员就应该警惕内存泄漏问题, 一旦元素被释放掉, 则该元素中包含的任何对象引用都应该被清空
示例代码: 下面的栈实现, 若栈先增长, 后收缩; 栈还维护者>size的元素的过期引用, 实际上这些元素不会被访问到, 这导致了程序的内存泄露
/**
* 栈实现
*
* @author gavin
* @version 2020/7/5
*/
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];
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
return elements[--size];
}
}
修复方案: 一旦对象引用已经过期, 只需清空这些引用即可; 且清空了过期引用, 若以后他们被错误的解除引用, 程序会立即抛出NPE, 而不是悄悄的错误运行下去.
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object element = elements[--size];
// 出栈则清除引用
elements[size] = null;
return element;
}
- 内存泄漏的另一个常见来源是缓存, 缓存注意维护过期时间
- 内存泄漏的第三个常见来源是监听器以及其他回调.
如果你实现了一个API, 客户端在这个API种注册回调, 却没有显示的取消注册, 那么除非你采取某些动作, 否则它们就会积聚, 确保回调立即被当做垃圾回收的最佳方法时只保存它们的弱引用, 例如, 只将它们保存成WeakHashMap中的键.
7.避免使用终结方法
终结方法是不可预测的, 也是很危险的, 一般情况下是不必要的.
Java语言规范不仅不保证终结方法会被及时的执行, 而且根本就不保证它们会被执行, 不应该依赖终结方法来更新重要的持久状态.