第二章创建和销毁对象
2020-03-11 本文已影响0人
后来丶_a24d
目录
- 创建和销毁对象
- 静态工厂方法
- 遇到多个构造器参数时要考虑使用构造器
- 用私有构造器或者枚举类型强化 Singleton 属性
- 通过私有构造器强化不可实例化的能力
- 避免创建不必要的对象
- 清除过期的对象引用
- 避免使用终结方法
- 使用依赖注入取代硬连接资源
- 使用 try-with-resources 语句替代 try-finally 语句
创建和销毁对象
静态工厂方法(就是类里面的static方法创建对象)代替构造器
优点:
- 静态工厂方法有名字;
- 不必要每次调用的时候都创建一个新的对象。
- 返回原返回类型的任何子类型对象。
- 创建参数化类型实例的时候使代码变得更加简洁。
遇到多个构造器参数时要考虑使用构造器
Builder 模式,代码易读,模拟了具名的可选参数,build 方法可检验约束条件。 builder 可以自动填充某些域,比如每次创建对象的时自动增加序列号。
用私有构造器或者枚举类型强化 Singleton 属性
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
//私有化
private Elvis() { … }
public void leaveTheBuilding() {…}
}
通过私有构造器强化不可实例化的能力
希望一个类不能被实例化,则私有化构造方法,比如一个很多都是static方法的类,肯定不希望被实例化
避免创建不必要的对象
- string类具有不可变性(内部状态不会被改变),每个string类的方法比如replace都是返回new String, String s指向一个内存块,当s = ‘abc’在常量池已有时不会再创建,而是指定这块已有的区域。
- 不可变有点, map的key是String的,string不可变则意味着每次hashcode是一样的,不需要重复计算。不可变则线程安全。
String s1 = "string";
String s2 = new String("string"); // don‘t do this
- 当心无意识的基本类型自动装箱,自动装箱类型初始化是null,==的时候小心出问题。能用基本类型就用基本类型,性能提高不少。
清除过期的对象引用
Java 内存泄露可能发生在:
-
类自我管理内存
比如在实现 Stack 栈时,弹出栈时,消除对象引用,结束变量的生命周期。
-
缓存
- 代码举例, 当我们调用pop方法是,该方法将返回当前栈顶的elements,同时将该栈的活动区间(size)减一,然而此时被弹出的Object仍然保持至少两处引用,一个是返回的对象,另一个则是该返回对象在elements数组中原有栈顶位置的引用。这样即便外部对象在使用之后不再引用该Object,那么它仍然不会被垃圾收集器释放,久而久之导致了更多类似对象的内存泄露。
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 e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copys(elements,2*size+1);
}
}
改进
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; //手工将数组中的该对象置空
return result;
}
避免使用终结方法
- Java的语言规范中并没有保证该方法会被及时的执行,甚至都没有保证一定会被执行。即便开发者在code中手工调用了System.gc和System.runFinalization这两个方法,这仅仅是提高了finalizer被执行的几率而已。finalizer方法可以作为安全网使用。
- 提供显式的终止方法,要求类客户端在每个实例不再有用的时候调用这个方法。 Java 中 FileInputStream, FileOutputStream,Timer 和 Connection 都具有终结方法。
使用依赖注入取代硬连接资源
- 许多类依赖于一个或多个底层资源。例如,拼写检查器依赖于字典。将此类类实现为静态实用工具类并不少见
public class SpellChecker {
private static final Lexicon dictionary = ...;
private SpellChecker() {} // Noninstantiable
public static boolean isValid(String word) { ... }
public static List<String> suggestions(String typo) { ... }
}
- 这两种方法都不令人满意,因为他们假设只有一本字典值得使用。在实际中,每种语言都有自己的字典,特殊的字典被用于特殊的词汇表。
- 静态实用类和单例对于那些行为被底层资源参数化的类来说是不合适的
- 使用依赖注入
public class SpellChecker {
private final Lexicon dictionary;
public SpellChecker(Lexicon dictionary) {
this.dictionary = Objects.requireNonNull(dictionary);
}
public boolean isValid(String word) { ... }
public List<String> suggestions(String typo) { ... }
}
在处理必须关闭的资源时,使用 try-with-resources 语句替代 try-finally 语句。 这会使生成的代码更简洁、更清晰,并且抛出的异常在调试时更有用。
- 多个资源需要关闭时,代码嵌套不合适,参考Effective Java 第三版——9. 使用try-with-resources语句替代try-finally语句