Effective java笔记(-)
1、创建和销毁对象
1、用静态工厂方法代替构造器
1、优点:
1、静态工厂方法有名称
2、不必在每次调用它们的时候都创建一个新对象
3、它们可以返回原返回类型的任何子类型的对象
4、所返回的对象的类可以随者每次调用而发生变化,这取决于静态工厂方法的参数值
5、方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在
2、缺点
1、类中如果不含公有的或者受保护的构造器,就不能被子类化
2、程序员很难发现它们
2、遇到多个构造器参数时要考虑使用构建器
1、构造方法参数过多,客户端代码会很难编写,并且仍然较难阅读
2、JavaBeans模式使得把类做成不可变的可能性不复存在,(new 类(),setXXX()方法)
3、通过建造者模式,如果类的构造器或者静态的工厂中具有多个参数,设计这个类时,Builder模式就是一种不错的选择。
public class builderDemo {
private final String name;
private final int a;
private final int b;
public static class Builder{
private final String name;
private int a = 0;
private int b = 0;
public Builder(String name) { this.name = name; }
public Builder a(int val){ a = val;return this; }
public Builder b(int val){ b = val;return this; }
public builderDemo build(){ return new builderDemo(this); }
}
private builderDemo(Builder builder) {
name = builder.name;
a = builder.a;
b = builder.b;
}
public static void main(String[] args) {
final builderDemo yuan = new Builder("yuan").a(12).b(45).build();
System.out.println(yuan);
}
}
3、用私有构造器或者枚举类型强化Singleton属性
1、Singleton 是指仅仅被实例化一次的类。
2、(第一种)保持构造器为是私有的,并导出公有的静态成员final域。
3、(第二种)保持构造器为是私有的,公有的成员是静态工厂方法。
4、(单元素枚举类型)单元素的枚举类型经常成为实现Singleton的最佳方案。
//一
private SingletonDemo() { }
public static final SingletonDemo INSTANCE = new SingletonDemo();
//二
private SingletonDemo() { }
private static final SingletonDemo INSTANCE = new SingletonDemo();
public static SingletonDemo getInstance(){
return INSTANCE;
}
//三
enum Singleton{
INSTANCE;
public void doThing(){
System.out.println("doing...");
}
}
public static void main(String[] args) {
Singleton.INSTANCE.doThing();
}
4、通过私有构造器强化不可实例化的能力
1、让这个类包含一个私有构造器,它就不能被实例化。
5、优先考虑依赖注入来引入资源
6、避免创建不必要的对象
1、不要使用 String s = new Sreing("AB"); 使用String a = "AB";
2、正则字符串显式编译成Pattern实例,可作为静态全局变量,作为类初始化的一部分,并将它缓存起来。
3、要优先使用基本类型而不是装箱类型,要当心无意识的自动装箱 。
7、消除过期的对象引用
1、清空对象引用应该是一种例外,而不是一种规范行为。
2、只要类是自己管理内存,程序员就应该警惕内存泄漏问题。
3、内存泄漏的另一个常见来源是缓存。
4、常见来源是监听器和其他回调
5、WeakHashMap(弱引用Map)
8、避免使用终结方法和清除方法
1、终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的。
2、清除方法(cleaner)清除方法没有终结方法那么危险,但仍然是不可预测、运行缓慢、一般情况下也是不必要的。
3、如果类的对象中封装的资源需要终止,让类实现AutoCloseable,并要求其客户端在每个实例不在需要的时候调用close方法。
9、try-with-resources 优于try-finally
2、对于所有对象都通用的方法
10、覆盖equals 时请遵守通用约定
1、equals实现了等价关系:自反性、对称性、传递性、一致性及对于任何非null的引用值,x.equals(null)返回false;
2、覆盖equals时总要覆盖hashCode
3、不要企图让equals方法过于智能
4、不要将equals声明中的Object对象替换为其他的类型。
11、覆盖equals总要覆盖hashCode
1、相同的对象必须具有相等的散列码。
2、不要试图从散列码计算中排除掉一个对象的关键域来提交性能。
3、不要对hashCode方法的返回值做出具体的规定,因此客户端无法理所应当当然地依赖它;这样可以为修改提供灵活性。
12、始终要覆盖toString
1、提供好的toString实现可以使类用起来更加舒适,使用了这个类的系统也更易于调试。
13、谨慎地覆盖clone
- Cloneable接口的目的是作为对象的一个mixin接口,表明这样的对象运行克隆。
- 事实上,实现Cloneable接口的类是为了提供一个功能适当的公有的clone方法,它无须调用构造器就可以创建对象。
- 不可变的类永远都不应该提供clone方法
- clone方法禁止给final变量赋值。
- 实际上,clone方法是另一个构造器:必须确保它不会伤害原始的对象,并确保正确的创建被克隆对象的约束条件。
- 所有实现Cloneable接口的类都应该覆盖clone方法,并且为公有方法,返回类似是类本身,该方法首先调用super.clone方法,然后修正任何需要修正的域;
- 对象拷贝更好的方法是提供一个拷贝构造器或拷贝工厂;这条规则最绝对的例外是数组,最好使用clone方法复制数组。
- 数组是clone方法唯一吸引人的地方。
public class demo_12 implements Cloneable{
private Object[] elements;
private int size =0;
private static final int final_size = 16;
@Override
protected Object clone() throws CloneNotSupportedException {
final demo_12 clone = (demo_12) super.clone();
clone.elements = elements.clone();
return clone;
}
}
14、考虑实现Comparable接口
- 类实现了Comparable接口,就表明它的实例具有内在的排序关系(natural order)。
- Java的静态导入,通过静态比较器构造方法的简单名称就可以对它们进行引用
- 强烈建议(x.compareTo(y) ==0) == (x.equals(y)),这绝非必要。例如new BigDecimal(“1.0”);new BigDecimal(“1.00”); 不相等,但是compareTo相等。
- 每当对一个排序敏感的类进行排序时,实现Cpmarable接口,以便可以轻松的搜索,分类。
private int age;
private String name;
private String sex;
public static final Comparator<User> COMPARATOR = Comparator.comparing(
(User user )-> user.sex).thenComparingInt(user-> user.age).thenComparing(user -> user.name);
参考《Effective Java》 第三版