面向对象高级编程上课笔记(EffectiveJava)

第二章 创建和销毁对象

1.用静态工厂方法代替构造器

Service Provider 框架

样例代码

接口

1
2
3
public interface Animal {
AnimalFactory newService();
}

静态工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class AnimalFactory {

private static final Map<String, Animal> animals =
new ConcurrentHashMap<String, Animal>();
public static final String DEFAULT_ANMIAL_NAME = "<def>";


public static void DefaultAnimal(Animal a) {
registerProvider(DEFAULT_ANMIAL_NAME, a);
}
public static void registerProvider(String name, Animal p){
animals.put(name,p);
}

public static AnimalFactory newInstance() throws IllegalAccessException {
return newInstance(DEFAULT_ANMIAL_NAME);
}
public static AnimalFactory newInstance(String name) throws IllegalAccessException {
Animal a = animals.get(name);
if (a == null)
throw new IllegalAccessException(
"No provider registered with name: " + name);
return a.newService();
}
}

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class TestMain {
public static void main(String[] args) throws IllegalAccessException {
AnimalFactory.registerProvider("Cat", Cat);
AnimalFactory.registerProvider("Dog", Dog);

AnimalFactory Cat = AnimalFactory.newInstance("Cat");
AnimalFactory Dog = AnimalFactory.newInstance("Dog");

System.out.println(Cat);
System.out.println(Dog);

}

// 注册对应的类 利用接口实现匿名内部类
private static final Animal Cat = new Animal() {
public AnimalFactory newService() {
return new AnimalFactory() {
@Override public String toString() {
return "This is Cat";
}
};
}
};

private static final Animal Dog = new Animal() {
@Override
public AnimalFactory newService() {
return new AnimalFactory() {
@Override
public String toString() {
return "This is Dog";
}
};
}
};
}

2.遇到多个构造器参数用构造器

JavaBeans模式

Builder模式

适用于类层次结构

创建Builder对象引入额外开销

Person类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public abstract class Person {
public final String mName;
public int mAge;
public String mLocation;
public String mGender;

public String mJob;


public static abstract class Builder<T extends Builder<T>> {
private final String mName;
private int mAge;
private String mLocation;
private String mGender;

private String mJob;

protected Builder(String mName) {
this.mName = mName;
}

public abstract Person build();
protected abstract T self();

public T SetmAge(int mAge) {
this.mAge = mAge;
return self();
}

public T SetmLocation(String mLocation) {
this.mLocation = mLocation;
return self();
}

public T SetmGender(String mGender) {
this.mGender = mGender;
return self();
}

public T SetmJob(String mJob) {
this.mJob = mJob;
return self();
}
}

Person(Builder<?> builder) {
this.mAge = builder.mAge;
this.mName = builder.mName;
this.mGender = builder.mGender;
this.mLocation = builder.mLocation;
this.mJob = builder.mJob;
}
}

Hight类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hight extends Person{
public double mHight;
public static class Builder extends Person.Builder<Builder>{
private double mHight;

protected Builder(String mName) {
super(mName);
}

@Override
public Hight build() {
return new Hight(this);
}

@Override
protected Builder self() {
return this;
}

public Builder SetmHight(int mHight) {
this.mHight = mHight;
return self();
}
}

private Hight(Builder builder) {
super(builder);
mHight = builder.mHight;
}

@Override
public String toString() {
return "名字:" + mName + "\n" + "性别:" + mGender + "\n"
+ "年龄:" + mAge + "\n" + "身高:" + mHight + "cm" + "\n"
+ "职业:" + mJob + "\n" + "地址:" + mLocation + "\n";
}
}

Weight类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Weight extends Person{
public double mWeight;
public static class Builder extends Person.Builder<Builder>{
private double mWeight;

protected Builder(String mName) {
super(mName);
}

@Override
public Weight build() {
return new Weight(this);
}

@Override
protected Builder self() {
return this;
}

public Builder SetmWeight(int mWeight) {
this.mWeight = mWeight;
return self();
}
}

private Weight(Builder builder) {
super(builder);
mWeight = builder.mWeight;
}

@Override
public String toString() {
return "名字:" + mName + "\n" + "性别:" + mGender + "\n"
+ "年龄:" + mAge + "\n" + "体重:" + mWeight + "kg" + "\n"
+ "职业:" + mJob + "\n" + "地址:" + mLocation + "\n";
}
}

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestMain {
public static void main(String[] args) {
Hight h = new Hight.Builder("张三").
SetmHight(175).SetmAge(24).SetmLocation("深圳").
SetmJob("学生").SetmGender("男").build();

Weight w = new Weight.Builder("李四").
SetmWeight(80).SetmAge(24).SetmLocation("广州").
SetmJob("学生").SetmGender("男").build();

System.out.println(h);
System.out.println(w);
}
}

3.用私有构造器或枚举类型强化Singleton属性

Singleton:只实例化一次

  • 无状态对象,如函数
  • 系统组件

实现Singleton

1
2
3
4
5
6
public class Emperor {
public static final Emperor INSTANCE = new Emperor();
private Emperor() {
System.out.println("新建一个皇帝类");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class TestMain {
public static void main(String[] args) {
Emperor emperor = Emperor.INSTANCE;
Emperor emperor1 = Emperor.INSTANCE;
if(emperor1.equals(emperor)) {
System.out.println("具有单例属性");
}
else {
System.out.println("不具有单例属性");
}
}
}

公有成员是静态工厂方法

  • 灵活性
  • 泛型Singleton工厂
  • 通过方法引用作为提供者

包含单个元素的枚举类型

4.通过私有构造器强化不可实例化的能力

工具类

  • 无需实例化
  • 不可为抽象类,可被子类化,子类可被实例化

5.优先考虑依赖注入来引用资源

某些类会依赖底层资源

  • 静态工具类

  • 单例实现

  • 只可支持单一资源

6.避免创建不必要的对象

同时提供了静态工厂方法和构造器的不可变类

  • 静态工厂方法优先
  • 避免无意识的装箱
  • 重用已知不会被修改的可变对象

7.消除过期对象引用

Java:垃圾回收功能(过期引用)

第三章 对所有类都通用的方法

10.覆盖equals方法的通用约定

equals:判断两个示例是否(逻辑)相等

object中的equals方法:判断是否为同一个实例

无需覆盖

  • 类的每个实例确实是唯一的,例如Thread
  • 无需关心实例之间是否逻辑相等
  • 超类已覆盖
  • 类或包是私有的

需要覆盖

  • 有逻辑相等的概念,如两个内容一样的String

覆盖约定

  • 自反性:对于非空x,有x.equals(x)返回true
  • 对称性
  • 传递性
  • 一致性:对于非空x,y,当内容不变时,x.euqals(y)不变
  • 非空性:对于非空x,x.equals(null),返回false

高质量equals方法:

  • ==判断是否为该对象的引用
  • instanceof判断类型是否正确
  • 转换参数
  • 对域和对象校验是否匹配
  • 满足对称性,传递性,一致性

11.覆盖equals时一定要覆盖hashcode

得以使用HashMap,HashSet,HashTable

  • 忽略冗余域
  • 排除equals中没使用的域

对于不可变类缓存HashCode

12.始终要覆盖toString

13.谨慎覆盖clone方法

cloneable接口决定了Object中受保护的clone方法实现的行为

  • 一个类实现了cloneable接口,Object的clone方法返回该对象的逐域拷贝,否则抛出异常
  • 改变了超类中受保护的方法的行为

14.考虑实现Comparable接口

满足equals约定:自反性,对称性,传递性

或者使用Comparator

1
2
3
4
5
6
7
8
private static final Comparator < PhoneNumber > COMPARATOR =
    comparingInt((PhoneNumber pn) - > pn.areaCode)
    .thenComparingInt(pn - > pn.prefix)
    .thenComparingInt(pn - > pn.lineNum);
public int compareTo(PhoneNumber pn) {
    return COMPARATOR.compare(this, pn);
}

代码样例:

Person类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Person {
public String id;
public String name;
int age;
public String job;

public Person(String id, String name, int age, String job) {
this.id = id;
this.name = name;
this.age = age;
this.job = job;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && id.equals(person.id) && name.equals(person.name) && job.equals(person.job);
}

@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + Integer.hashCode(age);
result = 31 * result + job.hashCode();
return result;
}

@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
}

Phone类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Phone implements Cloneable{
private Screen screen;
private static class Screen {
double length;
double weight;

Screen(double length, double weight) {
this.length = length;
this.weight = weight;
}

Screen deepcopy() {
return new Screen(this.length, this.weight);
}

}

@Override
public Phone clone() throws CloneNotSupportedException {
Phone result = (Phone)super.clone();
result.screen = this.screen.deepcopy();
return result;
}

public int comparaTo(Phone p) {
return Double.compare(this.screen.length*this.screen.weight, p.screen.length*p.screen.weight);
}

public Phone(Phone phone) {
this.screen.weight = phone.screen.weight;
this.screen.length = phone.screen.length;
}
}

主函数

1
2
3
4
5
6
7
public class TestMain {
public static void main(String[] args) {
Person person = new Person("123456", "张三", 24, "学生");
System.out.println(person);
System.out.format("0x%x\n", person.hashCode());
}
}

第四章 类与接口

15.使类和成员的可访问性最小化

  • 信息隐藏/封装
    • 各个模块隐藏其细节
    • 通过API进行通信
  • 信息隐藏的好处
    • 解耦
    • 各个模块可以独立开发测试优化
    • 提高模块的可重用性
  • 尽可能使每个类或成员不被外界访问
  • 顶层类和接口
    • 包级私有
      • 无修饰符
      • 只可以被包内实用
    • 公有(public)
      • Public修饰符
      • 包导出的API的一部分
      • 后续版本需要对其进行支持
    • 若某顶层类(或接口)A只在另一个类B的内部用到,应使A成为B的私有嵌套类
    • 降低不必要公有类的可访问性更重要
  • 成员(域,方法,嵌套类,嵌套接口)
    • 私有的(private)
      • 声明该成员的类内部可访问
    • 包级私有的(package-private)
      • 包内部的任何类都可访问
    • 受保护的(protected)
      • 声明该成员的类的子类可访问
      • 包内部的任何类都可访问
    • 公有的(public)
      • 任何地方都可访问
    • 尽量减少包级私有(相对于私有)
    • 受保护的成员尽量少用(相对于私有)
    • 实例域不能是公有的
    • 静态final常量可以为公有,但不能为可变对象的引用

16.在公有类中使用访问方法而非公有域

17.使可变性最小化

18.复合优先于继承

  • 包的内部可以使用,在同一个程序员的控制下
  • 专门为继承而设计

复合

  • 为现有的类为新类的一个组件
  • 包装类

20.接口优于抽象类

  • 接口(interface)

    • 不能实例化
    • 可实现方法
    • 类可以实现多个接口
  • 抽象类(abstract class)

    • 不能实例化
    • 可实现方法
    • 类只能继承一个抽象类

接口可以方便的实现混合类型,并构造非层次结构的类型框架

结合接口和抽象类的优点:骨架实现

  • 接口负责定义类型,或提供一些缺省方法
  • 骨架类实现其余方法

定义接口

1
2
3
public interface Machine {
void setPrice(int price);
}
1
2
3
4
public interface Movable  {
int start();
int stop();
}
1
2
3
public interface Vehicle extends Machine, Movable{
int run();
}

Bus实现骨架类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Bus implements Vehicle{
private int isRun;
public int price;
@Override
public void setPrice(int price) {
this.price = price;
}
@Override
public int start() {
if (isRun == 0) {
isRun = 1;
return 1;
}
else {
return 0;
}
}
@Override
public int stop() {
if (isRun == 1) {
isRun = 0;
return 1;
}
else {
return 0;
}
}
@Override
public int run() {
if (start() != 0) {
System.out.println("启动成功");
return 1;
}
else {
System.out.println("已启动");
return 0;
}
}
}

21.为后代设计接口

22.接口只用于定义类型

工具类

  • 不可实例化
  • 静态公有常量
1
2
3
4
5
6
7
8
9
10
11
12
public class PhysicalConstants {
private PhysicalConstants() { }
public static final double AVOGADROS_NUMBER = 6.02214199e23;
public static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
public static final double ELECTRON_MASS = 9.10938188e-31;
}

 // Use of static import to avoid qualifying constants
public class Test {
double atoms(double mols) {
return AVOGADROS_NUMBER * mols;
}

23.类层次优于标签类

类层次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Class hierarchy replacement for a tagged class
abstract class Figure {
abstract double area();
}

class Circle extends Figure {
final double radius;
 
Circle(double radius) { this.radius = radius; }
 
double area() { return Math.PI * (radius * radius); }
}

class Rectangle extends Figure {
final double length;
final double width;
 
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
double area() { return length * width; }
}

class Square extends Rectangle {
Square(double side) {
super(side, side);
}
}

24.优先考虑静态成员类

嵌套类

  • 静态成员类
  • 非静态成员类
  • 匿名类
  • 局部类

匿名类

  • 在使用的同时被声明和实例化
  • 可以出现在任何允许出现表达式的地方
  • 没有静态成员
  • 无法实现多个接口,或者同时扩展类并实现接口
  • 保持简短,否则影响可读性
  • 创建函数对象

第二章 创建和销毁对象

用静态工厂方法代替构造器

Service Provider 框架

样例代码

接口

1
2
3
public interface Animal {
AnimalFactory newService();
}

静态工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class AnimalFactory {

private static final Map<String, Animal> animals =
new ConcurrentHashMap<String, Animal>();
public static final String DEFAULT_ANMIAL_NAME = "<def>";


public static void DefaultAnimal(Animal a) {
registerProvider(DEFAULT_ANMIAL_NAME, a);
}
public static void registerProvider(String name, Animal p){
animals.put(name,p);
}

public static AnimalFactory newInstance() throws IllegalAccessException {
return newInstance(DEFAULT_ANMIAL_NAME);
}
public static AnimalFactory newInstance(String name) throws IllegalAccessException {
Animal a = animals.get(name);
if (a == null)
throw new IllegalAccessException(
"No provider registered with name: " + name);
return a.newService();
}
}

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class TestMain {
public static void main(String[] args) throws IllegalAccessException {
AnimalFactory.registerProvider("Cat", Cat);
AnimalFactory.registerProvider("Dog", Dog);

AnimalFactory Cat = AnimalFactory.newInstance("Cat");
AnimalFactory Dog = AnimalFactory.newInstance("Dog");

System.out.println(Cat);
System.out.println(Dog);

}

// 注册对应的类 利用接口实现匿名内部类
private static final Animal Cat = new Animal() {
public AnimalFactory newService() {
return new AnimalFactory() {
@Override public String toString() {
return "This is Cat";
}
};
}
};

private static final Animal Dog = new Animal() {
@Override
public AnimalFactory newService() {
return new AnimalFactory() {
@Override
public String toString() {
return "This is Dog";
}
};
}
};
}

遇到多个构造器参数用构造器

JavaBeans模式

Builder模式

适用于类层次结构

创建Builder对象引入额外开销

Person类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public abstract class Person {
public final String mName;
public int mAge;
public String mLocation;
public String mGender;

public String mJob;


public static abstract class Builder<T extends Builder<T>> {
private final String mName;
private int mAge;
private String mLocation;
private String mGender;

private String mJob;

protected Builder(String mName) {
this.mName = mName;
}

public abstract Person build();
protected abstract T self();

public T SetmAge(int mAge) {
this.mAge = mAge;
return self();
}

public T SetmLocation(String mLocation) {
this.mLocation = mLocation;
return self();
}

public T SetmGender(String mGender) {
this.mGender = mGender;
return self();
}

public T SetmJob(String mJob) {
this.mJob = mJob;
return self();
}
}

Person(Builder<?> builder) {
this.mAge = builder.mAge;
this.mName = builder.mName;
this.mGender = builder.mGender;
this.mLocation = builder.mLocation;
this.mJob = builder.mJob;
}
}

Hight类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Hight extends Person{
public double mHight;
public static class Builder extends Person.Builder<Builder>{
private double mHight;

protected Builder(String mName) {
super(mName);
}

@Override
public Hight build() {
return new Hight(this);
}

@Override
protected Builder self() {
return this;
}

public Builder SetmHight(int mHight) {
this.mHight = mHight;
return self();
}
}

private Hight(Builder builder) {
super(builder);
mHight = builder.mHight;
}

@Override
public String toString() {
return "名字:" + mName + "\n" + "性别:" + mGender + "\n"
+ "年龄:" + mAge + "\n" + "身高:" + mHight + "cm" + "\n"
+ "职业:" + mJob + "\n" + "地址:" + mLocation + "\n";
}
}

Weight类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Weight extends Person{
public double mWeight;
public static class Builder extends Person.Builder<Builder>{
private double mWeight;

protected Builder(String mName) {
super(mName);
}

@Override
public Weight build() {
return new Weight(this);
}

@Override
protected Builder self() {
return this;
}

public Builder SetmWeight(int mWeight) {
this.mWeight = mWeight;
return self();
}
}

private Weight(Builder builder) {
super(builder);
mWeight = builder.mWeight;
}

@Override
public String toString() {
return "名字:" + mName + "\n" + "性别:" + mGender + "\n"
+ "年龄:" + mAge + "\n" + "体重:" + mWeight + "kg" + "\n"
+ "职业:" + mJob + "\n" + "地址:" + mLocation + "\n";
}
}

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestMain {
public static void main(String[] args) {
Hight h = new Hight.Builder("张三").
SetmHight(175).SetmAge(24).SetmLocation("深圳").
SetmJob("学生").SetmGender("男").build();

Weight w = new Weight.Builder("李四").
SetmWeight(80).SetmAge(24).SetmLocation("广州").
SetmJob("学生").SetmGender("男").build();

System.out.println(h);
System.out.println(w);
}
}

用私有构造器或枚举类型强化Singleton属性

Singleton:只实例化一次

  • 无状态对象,如函数
  • 系统组件

实现Singleton

1
2
3
4
5
6
public class Emperor {
public static final Emperor INSTANCE = new Emperor();
private Emperor() {
System.out.println("新建一个皇帝类");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class TestMain {
public static void main(String[] args) {
Emperor emperor = Emperor.INSTANCE;
Emperor emperor1 = Emperor.INSTANCE;
if(emperor1.equals(emperor)) {
System.out.println("具有单例属性");
}
else {
System.out.println("不具有单例属性");
}
}
}

公有成员是静态工厂方法

  • 灵活性
  • 泛型Singleton工厂
  • 通过方法引用作为提供者

包含单个元素的枚举类型

通过私有构造器强化不可实例化的能力

工具类

  • 无需实例化
  • 不可为抽象类,可被子类化,子类可被实例化

优先考虑依赖注入来引用资源

某些类会依赖底层资源

  • 静态工具类

  • 单例实现

  • 只可支持单一资源

避免创建不必要的对象

同时提供了静态工厂方法和构造器的不可变类

  • 静态工厂方法优先
  • 避免无意识的装箱
  • 重用已知不会被修改的可变对象

7.消除过期对象引用

Java:垃圾回收功能(过期引用)

第三章 对所有类都通用的方法

10.覆盖equals方法的通用约定

equals:判断两个示例是否(逻辑)相等

object中的equals方法:判断是否为同一个实例

无需覆盖

  • 类的每个实例确实是唯一的,例如Thread
  • 无需关心实例之间是否逻辑相等
  • 超类已覆盖
  • 类或包是私有的

需要覆盖

  • 有逻辑相等的概念,如两个内容一样的String

覆盖约定

  • 自反性:对于非空x,有x.equals(x)返回true
  • 对称性
  • 传递性
  • 一致性:对于非空x,y,当内容不变时,x.euqals(y)不变
  • 非空性:对于非空x,x.equals(null),返回false

高质量equals方法:

  • ==判断是否为该对象的引用
  • instanceof判断类型是否正确
  • 转换参数
  • 对域和对象校验是否匹配
  • 满足对称性,传递性,一致性

11.覆盖equals时一定要覆盖hashcode

得以使用HashMap,HashSet,HashTable

  • 忽略冗余域
  • 排除equals中没使用的域

对于不可变类缓存HashCode

12.始终要覆盖toString

13.谨慎覆盖clone方法

cloneable接口决定了Object中受保护的clone方法实现的行为

  • 一个类实现了cloneable接口,Object的clone方法返回该对象的逐域拷贝,否则抛出异常
  • 改变了超类中受保护的方法的行为

14.考虑实现Comparable接口

满足equals约定:自反性,对称性,传递性

或者使用Comparator

1
2
3
4
5
6
7
8
private static final Comparator < PhoneNumber > COMPARATOR =
comparingInt((PhoneNumber pn) - > pn.areaCode)
.thenComparingInt(pn - > pn.prefix)
.thenComparingInt(pn - > pn.lineNum);
public int compareTo(PhoneNumber pn) {
return COMPARATOR.compare(this, pn);
}

代码样例:

Person类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Person {
public String id;
public String name;
int age;
public String job;

public Person(String id, String name, int age, String job) {
this.id = id;
this.name = name;
this.age = age;
this.job = job;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && id.equals(person.id) && name.equals(person.name) && job.equals(person.job);
}

@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + Integer.hashCode(age);
result = 31 * result + job.hashCode();
return result;
}

@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
'}';
}
}

Phone类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Phone implements Cloneable{
private Screen screen;
private static class Screen {
double length;
double weight;

Screen(double length, double weight) {
this.length = length;
this.weight = weight;
}

Screen deepcopy() {
return new Screen(this.length, this.weight);
}

}

@Override
public Phone clone() throws CloneNotSupportedException {
Phone result = (Phone)super.clone();
result.screen = this.screen.deepcopy();
return result;
}

public int comparaTo(Phone p) {
return Double.compare(this.screen.length*this.screen.weight, p.screen.length*p.screen.weight);
}

public Phone(Phone phone) {
this.screen.weight = phone.screen.weight;
this.screen.length = phone.screen.length;
}
}

主函数

1
2
3
4
5
6
7
public class TestMain {
public static void main(String[] args) {
Person person = new Person("123456", "张三", 24, "学生");
System.out.println(person);
System.out.format("0x%x\n", person.hashCode());
}
}

第四章 类与接口

15.使类和成员的可访问性最小化

  • 信息隐藏/封装
    • 各个模块隐藏其细节
    • 通过API进行通信
  • 信息隐藏的好处
    • 解耦
    • 各个模块可以独立开发测试优化
    • 提高模块的可重用性
  • 尽可能使每个类或成员不被外界访问
  • 顶层类和接口
    • 包级私有
      • 无修饰符
      • 只可以被包内实用
    • 公有(public)
      • Public修饰符
      • 包导出的API的一部分
      • 后续版本需要对其进行支持
    • 若某顶层类(或接口)A只在另一个类B的内部用到,应使A成为B的私有嵌套类
    • 降低不必要公有类的可访问性更重要
  • 成员(域,方法,嵌套类,嵌套接口)
    • 私有的(private)
      • 声明该成员的类内部可访问
    • 包级私有的(package-private)
      • 包内部的任何类都可访问
    • 受保护的(protected)
      • 声明该成员的类的子类可访问
      • 包内部的任何类都可访问
    • 公有的(public)
      • 任何地方都可访问
    • 尽量减少包级私有(相对于私有)
    • 受保护的成员尽量少用(相对于私有)
    • 实例域不能是公有的
    • 静态final常量可以为公有,但不能为可变对象的引用

16.在公有类中使用访问方法而非公有域

17.使可变性最小化

18.复合优先于继承

  • 包的内部可以使用,在同一个程序员的控制下
  • 专门为继承而设计

复合

  • 为现有的类为新类的一个组件
  • 包装类

20.接口优于抽象类

  • 接口(interface)

    • 不能实例化
    • 可实现方法
    • 类可以实现多个接口
  • 抽象类(abstract class)

    • 不能实例化
    • 可实现方法
    • 类只能继承一个抽象类

接口可以方便的实现混合类型,并构造非层次结构的类型框架

结合接口和抽象类的优点:骨架实现

  • 接口负责定义类型,或提供一些缺省方法
  • 骨架类实现其余方法

定义接口

1
2
3
public interface Machine {
void setPrice(int price);
}
1
2
3
4
public interface Movable  {
int start();
int stop();
}
1
2
3
public interface Vehicle extends Machine, Movable{
int run();
}

Bus实现骨架类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Bus implements Vehicle{
private int isRun;
public int price;
@Override
public void setPrice(int price) {
this.price = price;
}
@Override
public int start() {
if (isRun == 0) {
isRun = 1;
return 1;
}
else {
return 0;
}
}
@Override
public int stop() {
if (isRun == 1) {
isRun = 0;
return 1;
}
else {
return 0;
}
}
@Override
public int run() {
if (start() != 0) {
System.out.println("启动成功");
return 1;
}
else {
System.out.println("已启动");
return 0;
}
}
}

21.为后代设计接口

22.接口只用于定义类型

工具类

  • 不可实例化
  • 静态公有常量
1
2
3
4
5
6
7
8
9
10
11
12
public class PhysicalConstants {
private PhysicalConstants() { }
public static final double AVOGADROS_NUMBER = 6.02214199e23;
public static final double BOLTZMANN_CONSTANT = 1.3806503e-23;
public static final double ELECTRON_MASS = 9.10938188e-31;
}

// Use of static import to avoid qualifying constants
public class Test {
double atoms(double mols) {
return AVOGADROS_NUMBER * mols;
}

23.类层次优于标签类

类层次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Class hierarchy replacement for a tagged class
abstract class Figure {
abstract double area();
}

class Circle extends Figure {
final double radius;

Circle(double radius) { this.radius = radius; }

double area() { return Math.PI * (radius * radius); }
}

class Rectangle extends Figure {
final double length;
final double width;

Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
double area() { return length * width; }
}

class Square extends Rectangle {
Square(double side) {
super(side, side);
}
}

24.优先考虑静态成员类

嵌套类

  • 静态成员类
  • 非静态成员类
  • 匿名类
  • 局部类

匿名类

  • 在使用的同时被声明和实例化
  • 可以出现在任何允许出现表达式的地方
  • 没有静态成员
  • 无法实现多个接口,或者同时扩展类并实现接口
  • 保持简短,否则影响可读性
  • 创建函数对象

局部类

  • 在可以声明局部变量的地方声明

  • 有名字,可被重复使用

  • 没有静态成员

  • 保持简短,否则影响可读性

25.限制源文件为单个顶级类

第五章 泛型

为什么引入泛型

  • 与C++ template类似,但目的有区别
  • 减少程序运行错误的发生
  • 减少方法的重载

命名规则

  • E - Element (通常代表集合类中的元素)
  • K - Key
  • N - Number
  • V - Value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Util {  
//该方法用于比较两个Pair对象是否相等。
//泛型参数必须写在方法返回类型boolean之前
public static <K, V> boolean compare(Pair<K,V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey())&&
p1.getValue().equals(p2.getValue());
}
}

Pair<Integer,String> p1 = new Pair<>(1, "apple");
Pair<Integer,String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);
//实际上,编译器可以通过Pair当中的类型来推断compare需要使用的类型,所以可以简写为:
boolean same2= Util. compare(p1, p2);

限制类型参数在某个范围内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Box<T extends Number>{  //类型参数限定为Number的子类         
private T t;
public Box(T t){
this.t = t;
}
public void print(){
System.out.println(t.getClass().getName());
}
public static void main(String[] args) {

Box<Integer> box1 = new Box<Integer>(new Integer(2));
box1.print(); //打印结果:java.lang.Integer
Box<Double> box2 = new Box<Double>(new Double(1.2));
box2.print(); //打印结果:java.lang.Double

Box<String> box2 = new Box<String>(new String("abc")); //报错,因为String类型不是Number的子类
box2.print();

1
2
3
4
class Box<T extends Number & Cloneable & Comparable >{   
//该类型必须为Number的子类并且实现了Cloneable接口和Comparable接口。
……
}

泛型类的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
Object someObject = new Object();
Integer someInteger = new Integer(10);
someObject = someInteger; // 因为Integer是Object的子类  
Box < Number > box = new Box < Number > ();
box.add(new Integer(10)); // Integer是Number的子类  
box.add(new Double(10.1)); // Double同样是Number的子类  
//该方法接受的参数类型为Box<Number>  
public void boxTest(Box < Number > n) {
    ……}
//下面两种调用都会报错  
boxTest(Box < Integer > );
boxTest(Box < Double > );

通配符

1
2
3
4
5
6
7
8
9
10
//该方法接受的参数类型为Box<Number>  
public void boxTest(Box < Number > n) {
    ……}
//下面两种调用都会报错  
boxTest(Box < Integer > );
boxTest(Box < Double > );
public void boxTest(Box < ? extends Number > n) {
    //……
}

26.不要使用原生态类型

原生态类型(raw type)

  • 不带任何实际类型参数的泛型名称
  • 每个泛型都定一个原生态类型
  • List<E>的原生态类型是List

27.消除非受检警告

  • 改写代码
  • @SuppressWarnings(“unchecked”)
    • 无法通过修改代码消除警告
    • 证明代码是安全的
    • 在尽可能小得范围内使用
    • 添加对应注释

28.列表优先数组

  • 数组与泛型的不同
    • 数组是协变的,泛型是不可变的
    • 数组是具体化的(reified),泛型是通过擦除(erasure)来实现的
      • 数组和泛型不能很好的混合使用
      • 无法创建泛型(List<E>[])、参数化类型(List<String>[])或类型参数(E[])的数组
      • 优先利用集合类型list<E>,而不是数组类型E[]

29.优先考虑泛型

不能创建不可具体化类型的数组

  • 方法1:创建Object数组,转换为泛型数组类型
  • 方法2:将elements域的类型从E[]改为Object[]。获取元素时进行类型转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Generic stack using Object[] 
public class Stack<E> {
    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(E e) {
        ensureCapacity();
        elements[size++] = e;
    }
    // Appropriate suppression of unchecked warning
    public E pop() {
        if (size == 0)
            throw new EmptyStackException();
       // push requires elements to be of type E, so cast is correct
        @SuppressWarnings("unchecked") E result =
                (E) elements[--size];
        elements[size] = null// Eliminate obsolete reference
        return result;
    }

30.优先考虑泛型方法

编写泛型方法与编写泛型类型类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Generic method
public static < E > Set < E > union(Set < E > s1, Set < E > s2) {
    Set < E > result = new HashSet < > (s1);
    result.addAll(s2);
    return result;
}
// Simple program to exercise generic method
public static void main(String[] args) {
    Set < String > guys = Set.of("Tom""Dick""Harry");
    Set < String > stooges = Set.of("Larry""Moe""Curly");
    Set < String > aflCio = union(guys, stooges);
    System.out.println(aflCio);
}

泛型单例工厂

递归类型限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RecursiveTypeBound {
    // Returns max value in a collection - uses recursive type bound
    public static <E extends Comparable<E>> E max(Collection<E> c) {
        if (c.isEmpty())
            throw new IllegalArgumentException("Empty collection");
        E result = null;
        for (E e : c)
            if (result == null || e.compareTo(result) > 0)
                result = Objects.requireNonNull(e);
        return result;
    }
    public static void main(String[] args) {
        List<String> argList = Arrays.asList(args);
        System.out.println(max(argList));
    }
}

31.利用有限制的通配符来提升API的灵活性

33.优先考虑类型安全的异构容器

容器需要更多的灵活性

  • 将键(key)参数化,而不是将容器(container)参数化

第六章 枚举和注解

34.用enum代替int常量

枚举类型

  • 提供编译时的类型安全
  • 有命名空间,可以有同名常量
  • 可以添加任意的方法和域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public enum Operation {
PLUS("+") {
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
public double apply(double x, double y) {
return x - y;
}
},
TIMES("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;

Operation(String symbol) {
this.symbol = symbol;
}

@Override
public String toString() {
return symbol;
}

public abstract double apply(double x, double y);

}
1
2
3
4
5
public class Welcome {
public static void main(String[] args) {
System.out.println(Operation.PLUS.apply(1,2));
}
}

35.用实例域代替序数

1
2
3
4
5
6
7
8
9
10
11
12
public enum Ensemble {
    SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),
    SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8),
    NONET(9), DECTET(10), TRIPLE_QUARTET(12);
    private final int numberOfMusicians;
    Ensemble(int size) {
        this.numberOfMusicians = size;
    }
    public int numberOfMusicians() {
        return numberOfMusicians;
    }
}

36.用EnumSet代替位域

1
2
3
4
5
6
7
8
9
public class Text {
    public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
    // Any Set could be passed in, but EnumSet is clearly best
    public void applyStyles(Set < Style > styles) {
        ...
    }
}

text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));

37.用EnumMap代替序数索引

枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MixColor {
public enum Color {
Red("Red"), Blue("Blue"), Green("Green"),
Cyan("Cyan"), Purple("Purple"), White("White"),
Yellow("Yellow");

private final String symbol;
Color(String symbol) {
this.symbol = symbol;
}

@Override
public String toString() {
return symbol;
}
}
public static final Map<EnumSet<Color>, Color> m = new HashMap<>();
public static void applyStyles() {
m.put(EnumSet.of(Color.Red), Color.Red);
m.put(EnumSet.of(Color.Blue), Color.Blue);
m.put(EnumSet.of(Color.Green), Color.Green);
m.put(EnumSet.of(Color.Red, Color.Green), Color.Yellow);
m.put(EnumSet.of(Color.Green, Color.Blue), Color.Cyan);
m.put(EnumSet.of(Color.Red, Color.Blue), Color.Purple);
m.put(EnumSet.of(Color.Red, Color.Blue, Color.Green), Color.White);
}

MixColor() {
applyStyles();
}

}

主函数

1
2
3
4
5
6
public class Main {
public static void main(String[] args) {
MixColor mixColor = new MixColor();
System.out.println(MixColor.m.get(EnumSet.of(MixColor.Color.Blue, MixColor.Color.Red)));
}
}

38.用接口模拟可伸缩的枚举类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Emulated extensible enum using an interface
public interface Operation {
    double apply(double x, double y);
}
public enum BasicOperation implements Operation {
    PLUS("+") {
        public double apply(double x, double y) { return x + y; }
    },
    MINUS("-") {
        public double apply(double x, double y) { return x - y; }
    },
    TIMES("*") {
        public double apply(double x, double y) { return x * y; }
    },
    DIVIDE("/") {
        public double apply(double x, double y) { return x / y; }
    };
    private final String symbol;
    BasicOperation(String symbol) {
        this.symbol = symbol;
    }
    @Override public String toString() {
        return symbol;
    }
}

39.注解优先于命名模式

40.坚持使用Override注解

第七章 Lamda和Stream

42.Lambda优先于匿名类

Lambda表达式

  • 代码简短
  • 无需名称和文档
  • 不可创建或继承抽象类的实例
  • 不适用于创建具有多个抽象方法的接口实例
1
2
3
4
5
6
7
8
9
10
11
// Anonymous class instance as a function object - obsolete!
Collections.sort(words, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
});

// Lambda expression as function object (replaces anonymous class)
Collections.sort(words,
(s1, s2) -> Integer.compare(s1.length(), s2.length()));

43.方法引用优先于Lambda

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Frequency table implemented with map.merge, using lambda and method reference (Page 197)
public class Freq {
    public static void main(String[] args) {
        Map<String, Integer> frequencyTable = new TreeMap<>();
        
        for (String s : args)
            frequencyTable.merge(s, 1, (count, incr) -> count + incr); // Lambda
        System.out.println(frequencyTable);
        frequencyTable.clear();
        for (String s : args)
            frequencyTable.merge(s, 1, Integer::sum); // Method reference
        System.out.println(frequencyTable);
   }
}

default V merge(K key, V value,
  BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
    Objects.requireNonNull(remappingFunction);
    Objects.requireNonNull(value);
    V oldValue = get(key);
    V newValue = (oldValue == null) ? value :
               remappingFunction.apply(oldValue, value);
    if (newValue == null) {
        remove(key);
    } else {
        put(key, newValue);
    }
    return newValue;
}

44.优先使用标准函数接口

函数对象:实现了特定函数的对象

45.谨慎使用Stream

Stream是元素的集合,支持对元素进行顺序或者并行操作

foreach:

filter:

map:

sorted:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// forEach
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

// filter
List<String>strings = Arrays.asList("abc""""bc""efg""abcd","""jkl");
long count = strings.stream().filter(string -> string.isEmpty()).count();

// map
List<Integer> numbers = Arrays.asList(3223735);
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

// sorted
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

避免使用Stream处理Char

46.优先选择Stream中无副作用的函数

第八章 方法

49.检查参数的有效性

对参数值进行限制

  • 索引非负
  • 对象引用不为null

50.必要时进行保护性拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class RaceTime {
private final List<Date> times;

public RaceTime(Date... end) {
times = new ArrayList<>();
for (Date date : end) {
Date tmp = new Date(date.getTime());
times.add(tmp);
}
// times.addAll(Arrays.asList(end));
}
public Date retTime(int i) {
if (i > times.size() || i < 0) {
throw new IllegalArgumentException(
"out of index");
}
// 保护性拷贝
return new Date(times.get(i).getTime());
}
}

51.谨慎设计方法签名

谨慎选择方法的名称

不要过于追求提供便利的方法

避免过长的参数列表

52.明智地使用重载

在java中,具体调用的重载方法是编译时决定的

重载方法的选择是静态的,被覆盖的方法的选择是动态的

请勿写出具有相同参数数目的重载方法

53.明智地使用可变参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// The WRONG way to use varargs to pass one or more arguments!
static int min(int... args) {
    if (args.length == 0)
        throw new IllegalArgumentException("Too few arguments");
    int min = args[0];
    for (int i = 1; i < args.length; i++)
        if (args[i] < min)
    min = args[i];
    return min;
}

// The right way to use varargs to pass one or more arguments
static int min(int firstArg, int... remainingArgs) {
    int min = firstArg;
    for (int arg : remainingArgs)
        if (arg < min)
    min = arg;
    return min;
}

54.返回零长度的数组或者集合而不是null

1
2
3
4
5
6
// Optimization - avoids allocating empty arrays
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
public Cheese[] getCheeses() {
    return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}

55.明智地返回optional

方法在某些时候无法返回任何值

  • 抛出异常
  • 返回null
  • 返回Optional<T>
1
2
3
4
5
6
7
8
9
10
// Returns maximum value in collection - throws exception if empty
public static <E extends Comparable<E>> E max(Collection<E> c) {
    if (c.isEmpty())
        throw new IllegalArgumentException("Empty collection");
    E result = null;
    for (E e : c)
        if (result == null || e.compareTo(result) > 0)
    result = Objects.requireNonNull(e);
    return result;
}
1
2
3
4
5
6
7
8
9
10
11

// Returns maximum value in collection as an Optional<E>
public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) {
    if (c.isEmpty())
        return Optional.empty();
    E result = null;
    for (E e : c)
        if (result == null || e.compareTo(result) > 0)
    result = Objects.requireNonNull(e);
    return Optional.of(result);
}

Stream的终止操作返回Optional

1
2
3
4
// Returns max val in collection as Optional<E> - uses stream
public static <E extends Comparable<E>> Optional<E> max(Collection<E> c) {
    return c.stream().max(Comparator.naturalOrder());
}

第九章 通用编程

57.将局部变量的作用域最小化

与“使类和成员的可访问性最小化”本质上类似

在第一次使用局部变量的地方进行声明

局部变量的声明尽量包含一个初始化表达式

58.for-each循环优于传统循环

for-each循环可遍历任何实现Iterable接口的对象

无法使用for-each循环的情况

  • 解构过滤:遍历集合,删除特定元素
  • 转换:遍历集合,替换特定元素
  • 并行迭代:并行的遍历多个集合,同步前进

59.了解和使用类库

使用标准类库的好处

  • 其实现经过检验,且会不断更新
  • 无需花费时间在底层细节上
  • 性能往往会逐渐提高
  • 方便其他人阅读,重用

总之,不要重新发明轮子

60.如果需要精确的答案,避免使用float和double

float和double并没有提供完全精确的结果,使用BigDecimal

61.基本类型优先于装箱基本类型

  • 基本类型:int,double,Boolean
  • 引用类型:String,List
  • 装箱基本类型:Integer,Double,Boolean
  • 自动装箱,自动拆箱

基本类型和装箱类型的区别

  • 基本类型只有值比较,装箱基本类型具有同一性比较(identity comparison)
  • 基本类型只有功能值,装箱基本类型有null
  • 基本类型比装箱基本类型节省时间和空间

何时应采用装箱基本类型

  • 作为集合中的元素、键和值
  • 参数化类型中,必须使用装箱基本类型作为类型参数
  • 进行反射的方法调用,必须使用装箱基本类型

62.若其他类型更适合,尽量避免使用字符串

63.注意字符串连接的性能

64.通过接口引用对象

65.接口优先于反射机制

核心反射机制:通过程序来访问关于已装载的类的信息

执行反射访问所需要的代码非常笨拙和冗长

应用:frida

66.谨慎地使用本地方法

本地方法:用本地程序设计语言(如C或C++)编写的方法

本地方法可方便完成一些特殊的任务,并可能提高性能

当Java本身的性能得到提升时,程序无法得到改善

67.谨慎地进行优化

68.遵守普遍接受的命名惯例