目录

第2章 创建和销毁对象

第1条:用静态工厂方法替代构造器

第2条:遇到多个构造器参数时要考虑使用构建器

例子

使用

第3条:用私有构造器或者枚举类型强化Singleton属性

例子

使用

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

第5条:优先考虑依赖注入来引用资源

第6条:避免创建不必要的对象

例子

第7条:消除过期的对象引用

第8条:避免使用终结方法和清楚方法

第9条:try-with-resources优先于try-finally

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

第10条:覆盖equals时请遵守通用约定

第11条:覆盖equals时总要覆盖hashCode

第12条:始终要覆盖toString

第13条:谨慎地覆盖clone

第14条:考虑实现Comparable接口

第4章 类和接口

第15条:使类和成员的可访问性最小化

第16条:要在公有类而非公有域中使用访问方法

第17条:使可变性最小化

第18条:复合优于继承

第19条:要么设计继承并提供文档说明,要么禁止继承

第20条:接口优于抽象类

第21条:为后代设计接口

第22条:接口只用于定义类型

第23条:类层次优于标签类

第24条:静态成员类优于非静态成员类

第25条:限制源文件为单个顶级类

第5章 泛型

第26条:请不要使用原生态类型

第27条:消除非受检的警告

第28条:列表优于数组

第29条:优先考虑泛型

第30条:优先考虑泛型方法

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

第32条:谨慎并用泛型和可变参数

第33条:优先考虑类型安全的异构容器

第6章 枚举和注解

第34条:用enum替代int常量

第35条:用实例域代替序数

第36条:用EnumSet代替位域

使用EnumSet代替

第37条:用EnumMap代替序数索引

第38条:用接口模拟可扩展的枚举

第39条:注解优先于命名模式

第40条:坚持使用Override注解

第41条:用标记接口定义类型

第6章 Lambda和Stream

第42条:lambda优先于匿名类

第43条:方法引用优先于lambda

第44条:坚持使用标准的函数接口

第45条:谨慎使用Stream

第46条:优先选择Stream中无副作用的函数

第47条:Stream要优先用Collection作为返回类型

第48条:谨慎使用Stream并行


第2章 创建和销毁对象

第1条:用静态工厂方法替代构造器

  1. 有名称
  2. 不用每次调用都创建对象
  3. 可以返回原类型的子类型

第2条:遇到多个构造器参数时要考虑使用构建器

例子

public class Human {
	private String name;
	private int age;
	private String id;

	private Human(Builder builder) {
		name = builder.name;
		age = builder.age;
		id = builder.id;
	}

	public static class Builder {
		private String name;
		private int age = 20;
		private String id;

		public Builder name(String name) {
			this.name = name;
		}

		public Builder age(int age) {
			this.age = age;
		}

		public Builder id(String id) {
			this.id = id;
		}

		public Human build() {
			return new Human(this);
		}
	}
}

使用

Human.Builder builder = new Human.Builder();
Human human = builder.name("lili").id("123456").build();

第3条:用私有构造器或者枚举类型强化Singleton属性

例子

public enum Singleton {
    INSTANCE;
    public int func() {
        ...
    }
}

使用

int a = Singletion.INSTANCE.func();

单元素的枚举类型经常成为实现Singleton的最佳方法

  1. 提供了序列化机制
  2. 绝对防止多次实例化

但如果Singleton必须扩展一个超类(非Enum时),这种方法不适用。

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

例如工具类。

第5条:优先考虑依赖注入来引用资源

第6条:避免创建不必要的对象

例子

String s = new String("bob");

传递给String构造器的"bob"本身就是一个String实例。

要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。

第7条:消除过期的对象引用

清空对象引用应该是一个例外,而不是一种规范行为。

第8条:避免使用终结方法和清楚方法

第9条:try-with-resources优先于try-finally

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

第10条:覆盖equals时请遵守通用约定

  1. 自反性
  2. 对称性
  3. 传递性
  4. 一致性
  5. 对任何非null的引用x,x.equals(null)必须返回false

第11条:覆盖equals时总要覆盖hashCode

第12条:始终要覆盖toString

第13条:谨慎地覆盖clone

第14条:考虑实现Comparable接口

实现该接口就表明它的实例具有内在的排序关系。

第4章 类和接口

第15条:使类和成员的可访问性最小化

第16条:要在公有类而非公有域中使用访问方法

然而,如果类是包级私有的,或者是私有的嵌套类,直接暴露它的数据域并没有本质的错误。

第17条:使可变性最小化

使类成为不可变,要遵循下面五条规则:

  1. 不要提供任何会修改对象状态的方法
  2. 保证类不会被扩展(一般做法是声明类为final)
  3. 声明所有的域都是final
  4. 声明所有的域都为private
  5. 确保对于任何可变组件的互斥访问 如果类具有指向可变对象的域,则必须确保类的使用者无法获得这些对象的引用
public final class FinalClass {
	private final double id;
	private final String name;
	
	public FinalClass(double id, String name) {
		this.id = id;
		this.name = name;
	}
	
	public String info() {
		return name + id;
	}
}

不可变对象本质上是线程安全的,它们不要求同步。

当多个线程并发访问这样的对象时,它们不会遭到破坏。

不可变类真正唯一的缺点是,对于每个不同的值都需要一个单独的对象。

第18条:复合优于继承

继承打破了封装性

第19条:要么设计继承并提供文档说明,要么禁止继承

第20条:接口优于抽象类

接口是定义mixin(混合类型)的理想选择。

第21条:为后代设计接口

第22条:接口只用于定义类型

第23条:类层次优于标签类

第24条:静态成员类优于非静态成员类

第25条:限制源文件为单个顶级类

第5章 泛型

第26条:请不要使用原生态类型

第27条:消除非受检的警告

第28条:列表优于数组

第29条:优先考虑泛型

第30条:优先考虑泛型方法

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

第32条:谨慎并用泛型和可变参数

第33条:优先考虑类型安全的异构容器

第6章 枚举和注解

第34条:用enum替代int常量

每当需要一组固定常量,并且在编译时就知道其成员的时候,就应该使用枚举

第35条:用实例域代替序数

永远不要根据枚举的序数导出与它关联的值,而是要将它保存在一个实例域中。 例子

public enum EnumTest {
	SOLO(1),DUET(2),TRIO(3);
	
	private final int id;
	EnumTest(int id) {
		this.id = id;
	}
	
	public int getId() {
		return id;
	}
}

第36条:用EnumSet代替位域

public class Text {
	public static final int STYLE_BOLD		 	= 1 << 0; // 1
	public static final int STYLE_ITALIC 	 	= 1 << 1; // 2
	public static final int STYLE_UNDERLINE  	= 1 << 2; // 4
	public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8
	
	public void apply(int style) {
		...
	}
}

使用时

text.apply(STYLE_BOLD | STYLE_ITALIC);

上述定义在类Text的成员STYLE_BOLD等称作位域。

使用EnumSet代替

public class Text {
	public enum Style { BOLD, ITALIC, UNDERLINE,STRIKETHROUGH}
	
	public void apply(Set<Style> styleSet) {
		...
	}
}

使用时

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

第37条:用EnumMap代替序数索引

第38条:用接口模拟可扩展的枚举

第39条:注解优先于命名模式

第40条:坚持使用Override注解

第41条:用标记接口定义类型

第6章 Lambda和Stream

第42条:lambda优先于匿名类

第43条:方法引用优先于lambda

例如

manlist.stream().forEach(System.out::println());

第44条:坚持使用标准的函数接口

6个基础函数接口:

![1602838537165](.assets/Effective Java(第3版) 90条经验法则/1602838537165.png)

第45条:谨慎使用Stream

通常不建议在Stream上调用parallel,使其并发执行。 如果实在不确定用Stream还是用迭代比较好,那么就两种都试试,看看哪一种更好用吧。

第46条:优先选择Stream中无副作用的函数

第47条:Stream要优先用Collection作为返回类型

第48条:谨慎使用Stream并行