Effective笔记(未完待续)

第1章 (略)

第2章 创建和销毁对象

  • 第1条 考虑用静态工厂方法代替构造器
  • 第2条 遇到多个构造器参数时考虑使用构造器
# 重叠构造器模式 编写繁琐、难以阅读
# 无参构造器模式 易于阅读、阻止了类不可变 
# Builder方式兼顾阅读和安全的问题
  • 第3条 用私有构造器或者枚举类型强化Singleton属性
# 公有成员是个final域
# 公有成员是个静态工厂方法
# 公有成员是个单元素枚举类型
  • 第4条 用私有构造器强制不可实例化的能力
  • 第5条 避免创建不必要的对象
# 将必要的常量 放入statis代码块里
# 优先使用基本类型而不是装箱基本类型,切不可无意识的自动装箱
  • 第6条 消除过期的对象引用
# 一旦对象引用已经过期 清空这些引用即可
# 只保存对象的弱引用
  • 第7条 避免使用终结方法
# 使用终结方法 导致非常严重的Severe性能损失 

第3章 对于所有对象都通用的方法

  • 第8条 覆盖equals时请遵守通用约定
# 自反性: 非null x;则 x.equals(x) = true;
# 对称性: 非null x,y;则 x.equals(y) = y.equals(x);
# 传递性: 非null x,y,z,x.equals(y) = true,y.equals(z) = true;则 x.equals(z) =true;
# 一致性: 非null x,y,只要equals操作没有被修改,则 多次调用x.equals(y)返回结果一致;

示例1 通过覆写equals 增加忽略字符串大小写的equals方法(导致违反了一致性)

public class CaseInsensitiveString {
    private final String s;
    public CaseInsensitiveString(String s) {
        if (s == null) {
            throw new NullPointerException();
        }
        this.s = s;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof CaseInsensitiveString) {
            return ((CaseInsensitiveString) obj).s.equalsIgnoreCase(s);
        }
        if (obj instanceof String) {
            return ((String) obj).equalsIgnoreCase(s);
        }
        return false;
    }
    public static void main(String[] args) {
        CaseInsensitiveString cis = new CaseInsensitiveString("Publish");
        String s = "publish";
        System.out.println("s.equals(cis) -> " + s.equals(cis));
        // 输出 s.equals(cis) -> false
        System.out.println("cis.equals(s) -> " + cis.equals(s));
        // 输出 cis.equals(s) -> true
    }
}

示例2


  • 第9条 覆盖equals时总是覆盖hashCode

示例

public class PhoneNumber {
    //延时加载
    private volatile int hashCode;
    private final short areaCode;
    private final short prefix;
    private final short lineNumber;

    public PhoneNumber(int areaCode, int prefix, int lineNumber) {
        ranageCheck(areaCode, 999, "area code");
        ranageCheck(prefix, 999, "prefix");
        ranageCheck(lineNumber, 9999, "line number");
        this.areaCode = (short) areaCode;
        this.prefix = (short) prefix;
        this.lineNumber = (short) lineNumber;
    }

    private void ranageCheck(int arg, int max, String name) {
        if (arg < 0 || arg > max) {
            throw new IllegalArgumentException(name + ": " + arg);
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!(obj instanceof PhoneNumber)) {
            return false;
        }
        PhoneNumber pn = (PhoneNumber) obj;
        return pn.lineNumber == lineNumber && pn.areaCode == areaCode && pn.prefix == prefix;
    }

    @Override
    public String toString() {
        return "PhoneNumber{" +
                "areaCode=" + areaCode +
                ", prefix=" + prefix +
                ", lineNumber=" + lineNumber +
                '}';
    }

    @Override
    public int hashCode() {
        //需要的时候才会去计算
        int res = hashCode;
        if (res == 0) {
            res = 17;
            res = 31 * res + areaCode;
            res = 31 * res + prefix;
            res = 31 * res + lineNumber;
        }

        return res;
    }

    public static void main(String[] args) {
        Map<PhoneNumber, String> m = new HashMap<>();
        PhoneNumber phoneNumber = new PhoneNumber(707, 867, 5390);
        System.out.println(phoneNumber.hashCode());
        m.put(phoneNumber, "Jenny");
        String s = m.get(phoneNumber);
        // 没有覆盖hashCode()方法不会
        System.out.println(s);
        Set<Map.Entry<PhoneNumber, String>> entries = m.entrySet();
        Iterator<Map.Entry<PhoneNumber, String>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            PhoneNumber key = iterator.next().getKey();
            System.out.println(key.toString() + " -> " + m.get(key));
        }
    }
}
  • 第10条 始终要覆盖toString()
// 指定格式
@Override
public String toString() {
    return String.format("(%3d) %03d-%04d", areaCode, prefix, lineNumber);
}
// 不指定格式 但是需要加上必要的说明
@Override
public String toString() {
    super.toString();
}
  • 第11条 谨慎的覆盖clone(或者说能不覆盖就不覆盖)

示例 推荐提供一个拷贝构造器

public class CopyObject implements Cloneable, Serializable {
    private static class Inner implements Serializable {
        private String iName;
        private int iAge;

        public String getiName() {
            return iName;
        }

        public void setiName(String iName) {
            this.iName = iName;
        }

        public int getiAge() {
            return iAge;
        }

        public void setiAge(int iAge) {
            this.iAge = iAge;
        }

        @Override
        public String toString() {
            return "Inner{" +
                    "iName='" + iName + '\'' +
                    ", iAge=" + iAge +
                    '}';
        }
    }

    private List<Inner> elements;
    private Inner inner;
    private String name;
    private int age;

    public List<Inner> getElements() {
        return elements;
    }

    public void setElements(List<Inner> elements) {
        this.elements = elements;
    }

    public Inner getInner() {
        return inner;
    }

    public void setInner(Inner inner) {
        this.inner = inner;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public CopyObject(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 深拷贝克隆对象
     *
     * @param copyObject
     * @return
     */
    public static CopyObject deepCopyObj(CopyObject copyObject) {
        CopyObject object = null;
        ByteArrayOutputStream baos = null;
        ObjectOutputStream oos = null;
        try {
            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(copyObject);

            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            object = (CopyObject) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {

        }
        return object;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "CopyObject{" +
                "elements=" + elements +
                ", inner=" + inner +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        CopyObject obj = new CopyObject("zhangsan", 23);
        Inner inner = new Inner();
        inner.setiName("innerName");
        inner.setiAge(100);
        obj.setInner(inner);

        System.out.println(obj.toString());
        CopyObject deepCopyObj = CopyObject.deepCopyObj(obj);

        // clone() opreation
        CopyObject objByClone = (CopyObject) obj.clone();
        objByClone.setName("zhangsanUpdate by clone()");
        objByClone.getInner().setiAge(999999);
        objByClone.getInner().setiName("innerName by clone()");
        System.out.println("obj origin -> " + obj.toString());
        System.out.println("objByClone -> " + objByClone.toString());

        // deepCopyObj() opreation
        deepCopyObj.setName("zhangsanUpdate by deepCopy");
        deepCopyObj.setAge(-9);
        deepCopyObj.getInner().setiAge(-9);
        System.out.println("deepCopyobj -> " + deepCopyObj.toString());
    }
}

  • 第12条 考虑实现Comparable接口

示例

public class CompareTest implements Comparable<CompareTest> {
    private int code1;
    private int code2;
    private int code3;

    public CompareTest(int code1, int code2, int code3) {
        this.code1 = code1;
        this.code2 = code2;
        this.code3 = code3;
    }

    public int getCode1() {
        return code1;
    }

    public void setCode1(int code1) {
        this.code1 = code1;
    }

    public int getCode2() {
        return code2;
    }

    public void setCode2(int code2) {
        this.code2 = code2;
    }

    public int getCode3() {
        return code3;
    }

    public void setCode3(int code3) {
        this.code3 = code3;
    }

    @Override
    public int compareTo(CompareTest o) {
        int diff1 = code1 - o.code1;
        if (diff1 != 0) {
            return diff1;
        }
        int diff2 = code2 - o.code2;
        if (diff2 != 0) {
            return diff2;
        }
        return code3 - o.code3;
    }

    public static void main(String[] args) {
        CompareTest ct1 = new CompareTest(2, 5, 6);
        CompareTest ct2 = new CompareTest(2, 3, 7);
        System.out.println(ct1.compareTo(ct2));
    }
}

第4章 类和接口

  • 第13条 使类和成员的访问性最小化
修饰符当前类同包子类其他包
public
protected×
default×
private×××

其中 private 不可被重写

  • 第14条 在公有类中使用访问方法而非公有域
    示例
// 错误的方式
class Point{
    public double x;
    public double y;
}
// 正确的方式 暴露公有的方法修改变量域
class Point{
    private double x;
    private double y;
    ...
    public set/get 
    ...
}
  • 第15条 使得可变性最小化
# 除非有令人信服的理由让域变成非final,否则每个域都是final的
# 构造器应该创建初始化对象,并建立所有的约束关系。

示例 虚数的加减乘除 只暴露构造和方法

public final class ComplexNumber {
    private final double re;
    private final double im;

    public ComplexNumber(double re, double im) {
        this.re = re;
        this.im = im;
    }

    public double realPart() {
        return re;
    }

    public double imaginaryPart() {
        return im;
    }

    /**
     * 加法
     *
     * @param c
     * @return
     */
    public ComplexNumber add(ComplexNumber c) {
        return new ComplexNumber(re + c.re, im + c.im);
    }

    /**
     * 减法
     *
     * @param c
     * @return
     */
    public ComplexNumber subtract(ComplexNumber c) {
        return new ComplexNumber(re - c.re, im - c.im);
    }
    /**
     * 乘法
     *
     * @param c
     * @return
     */
    public ComplexNumber mutilpy(ComplexNumber c) {
        return new ComplexNumber(re * c.re - im * c.im, im * c.re - re * c.im);
    }
    /**
     * 除法
     *
     * @param c
     * @return
     */
    public ComplexNumber divide(ComplexNumber c) {
        double temp = c.re * c.re + c.im * c.im;
        return new ComplexNumber(re * c.re + im * c.im / temp, im * c.re - re * c.im / temp);
    }
}

  • 第16条 复合优先于继承

示例 由于HashSet addAll方法基于add实现的所以输出 6

public class InstrumentedSet<E> extends HashSet<E> {
    private int addCount = 0;

    public InstrumentedSet(Set<E> s) {
        super(s);
    }

    @Override
    public boolean add(E e) {
        addCount++;
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }

    public static void main(String[] args) throws InterruptedException {
        InstrumentedSet<Integer> set = new InstrumentedSet<>(new TreeSet<>());
        HashSet<Integer> hashSet = new HashSet<>();
        hashSet.add(new Integer(1));
        hashSet.add(new Integer(2));
        hashSet.add(new Integer(3));
        set.addAll(hashSet);
        System.out.println(set.getAddCount());
    }
}

改造 使用包装类

public class ForwardingSet<E> implements Set<E> {
    private final Set<E> s;

    public ForwardingSet(Set<E> s) {
        this.s = s;
    }
    
    ...
    // 代码 
    ...
}
public class InstrumentedSet<E> extends ForwardingSet<E> {
    ... 
    // 代码
    ...
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值