第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> {
...
// 代码
...
}