说明

这里是阅读《Effective Java中文版第二版》的读书笔记,这里会记录一些个人感觉稍微有些重要的内容,方便以后查阅,可能会因为个人实力原因导致理解有误,若有发现欢迎指出。一些个人还不理解的会用斜线标注。

第一章是引言,所以跳过。

第二章 创建和销毁对象

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

含义

静态工厂方法是指一个返回类的实例的静态方法,例如:

public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean,FALSE;
}

优点

相对于一个类的构造器,静态工厂方法的名称没有限制。

众所周知,构造器的方法名是必须和类名一样的,因此对于有多个参数类型相同的构造方法,一种方法是更改参数的顺序,另一种是增加一个flag来判断执行哪个构造方法。但是这样对于使用者是不友好的,他必须熟悉API或者查阅开发文档。倘若使用静态工厂方法,那么可以通过方法名来给予使用者良好的提示与说明。

不用再每次调用的时候创建一个新的对象。

这句话的典型应用是在设计模式的单例模式中,静态工厂方法能够为重复的调用返回相同的对象。

静态工厂方法可以返回原返回类型的任何子类型的对象。

构造方法是不能使用return语句的,它在使用时也只能产生自身这个类的一个对象,而静态工厂方法可以使用return语句,因此在选择返回对象时就有了更大的灵活性。这个优势的应用很多,比如服务提供者框架模式

小结

应当熟悉静态工厂方法和构造器的各自的长处,在合适的场景使用合适的方法。

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

在面对一个拥有多个属性的类且构造方法拥有多个可选参数时,一个常见的方法是使用重叠构造器模式(创建多个构造方法,每个构造方法比前一个构造方法有新的参数)。例如,第一个构造方法有两个必须参数,第二个构造方法有两个必须参数和一个可选参数,第三个构造方法有两个必须参数和两个可选参数,以此类推。但是当有许多参数的时候,代码会变得很难编写,也很难阅读,甚至会容易出错。

另一个方法是使用javabean模式。因为构造过程被分到了多个调用中(为每个属性的赋值调用该属性的set方法),在构造过程中,javabean可能处于不一致的状态,这种问题难以发现。

第三种方法就是构建器模式(Builder模式)的一种形式。

public class NutritionFacts {

    private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate; public static class Builder {
// 必须属性
private final int servingSize;
private final int servings;
// 可选属性
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0; public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
} public Builder setCalories(int calories) {
this.calories = calories;
return this;
} public Builder setFat(int fat) {
this.fat = fat;
return this;
} public Builder setSodium(int sodium) {
this.sodium = sodium;
return this;
} public Builder setCarbohydrate(int carbohydrate) {
this.carbohydrate = carbohydrate;
return this;
} public NutritionFacts build() {
return new NutritionFacts(this);
}
} private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
} // 使用方法
NutritionFacts n = new NutritionFacts.Builder(200,10).setCalories(20).setFat(30).build();

Builder模式十分灵活,可以利用一个builder来创建多个相同的对象,并且对必须参数和可变参数的实现符合人类的正常思维。另外,对于使用者而言,使用时的代码更容易阅读和编写。

这种方法我在google的protobuf的java实现中见到过。

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

私有构造方法就不提了,这里记录一下第二个:

public enum A {
INSTANCE; public void leaveTheBuilding() {...}
}

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

对于一些只包含静态方法或者静态属性的类(比如工具类),我们不希望他们被实例化。众所周知,在缺少显式构造方法的时候,编译器会默认添加一个无参的构造方法。如果为了严谨,我们可以添加一个私有的构造方法,更可以在这个构造方法中throw异常来中止程序。

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

一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。

除了重用不可变的对象之外,也可以重用那些已知不会被修改的可变对象。

能使用基本数据类型,就尽量不要用对应的封装类。

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

不能以为有了垃圾回收机制后,就不需要考虑内存管理的事情了。

例如用数组来实现栈,当实现出栈操作,size-1后,栈顶坐标后的元素对使用者来说就已经是无效部分了,但是数组仍然拥有对它们的引用,因此垃圾回收机制不会将它们回收。解决办法是在出栈时,将引用置空。

第7条:避免使用终结方法

除了特定情况,不要使用终结方法(finalize)。

子类覆盖了父类的终结方法后,子类的终结方法不会自动调用父类的终结方法,需要手动调用。

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

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

约定的内容:

equals方法实现了等价关系。

  • 自反性:对于任何非null的引用值x,x.equals(x)都必须返回true。
  • 对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
  • 传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。
  • 一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。
  • 非空性:对于任何非null的引用值x,x.equals(null)必须返回false。

实现高质量equals方法的诀窍:

  1. 使用==操作符检查“参数是否为这个对象的引用”。如果是,则返回true。
  2. 使用instanceof操作符检查“参数是否为正确的类型”。如果不是,则返回false。
  3. 把参数转化为正确的类型。因为转换前进行过instanceof测试,所以确保会成功。
  4. 对于该类中的每个“关键”字段,检查参数中的字段是否与该对象中对应的字段相匹配。如果这些测试全部成功,则返回true;否则返回false。
  5. 当你编写完成了equals方法之后,应该质问自己并且测试这三个问题:它是否是对称的、传递的、一致的?当然,equals方法也必须满足自反性和非空性,不过通常都会自动满足。

一个简单的列子:

public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof MyClass))
return false;
MyClass obj = (MyClass) o;
return obj.field0 == this.field0 && obj.field1 == this.field1;
}

告诫:

  • 覆盖equals时总要覆盖hashCode。
  • 不要企图让equals方法过于智能。
  • 不要将equals声明中的Object对象替换为其他的类型。
public boolean equals(MyClass o); // Don't do this!

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

如果没有共同覆盖equals方法和hashCode方法,那么该类将无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap、HashSet和HashTable。

约定:相等的对象必须具有相等的散列码(HashCode)。

在散列码的计算过程中,必须排除equals比较计算中没有用到的任何字段,可以把冗余字段(它的值可以根据参与计算的其他字段计算出来)排除在外。

不要试图从散列码计算中排除掉一个对象的关键部分来提高性能。

第10条:始终要覆盖toString

提供好的toString实现可以使类用起来更加舒适。

第11条:谨慎地覆盖clone

如果你继承了一个实现了Cloneable接口的类,那么你除了实现一个行为良好的clone方法外,没有别的选择。否则,最好提供某些其他的途径来代替对象拷贝,或者干脆不提供这样的功能。

另一个实现对象拷贝的好方法是提供一个拷贝构造方法或者拷贝工厂。

// 拷贝构造方法
public MyClass(MyClass mc); // 拷贝工厂
public static MyClass newInstance(MyClass mc);

第12条:考虑实现Comparable接口

类实现了Comparable接口,就表明它的实例具有自然顺序关系(natural ordering)。

约定:(符号sgn(表达式)表示数学中的signum函数,根据表达式的值为负值、零和正值,分别返回-1、0和1)

  • 必须确保所有的x和y都满足sgn(x.compareTo(y)) == -sgn(y.compareTo(x))。(这也意味着,当且仅当y.compareTo(x)抛出异常时,x.compareTo(y)才必须抛出异常)
  • 必须确保这个比较关系是可传递的。x.compareTo(y) > 0 && y.compareTo(z) > 0成立意味着x.compareTo(z) > 0
  • 必须确保x.compareTo(y) == 0意味着所有的z都满足sgn(x.compareTo(z)) == sgn(y.compareTo(z))
  • 强烈建议(x.compareTo(y) == 0) == (x.equals(y)),但这绝非必要。若违反了这个条件,应当给予说明。

比较浮点字段用Double.compare或者Float.compare。

如果一个类有多个关键字段,按照什么样的顺序来比较是非常重要的。

compareTo方法中,如果两个对应字段不相等,可以使用该类的字段与传入参数的字段的差值作为返回值,但应确保差值是绝对正确的。

《Effective Java中文版第二版》读书笔记的更多相关文章

  1. csapp读书笔记-并发编程

    这是基础,理解不能有偏差 如果线程/进程的逻辑控制流在时间上重叠,那么就是并发的.我们可以将并发看成是一种os内核用来运行多个应用程序的实例,但是并发不仅在内核,在应用程序中的角色也很重要. 在应用级 ...

  2. CSAPP 读书笔记 - 2.31练习题

    根据等式(2-14) 假如w = 4 数值范围在-8 ~ 7之间 2^w = 16 x = 5, y = 4的情况下面 x + y = 9 >=2 ^(w-1)  属于第一种情况 sum = x ...

  3. CSAPP读书笔记--第八章 异常控制流

    第八章 异常控制流 2017-11-14 概述 控制转移序列叫做控制流.目前为止,我们学过两种改变控制流的方式: 1)跳转和分支: 2)调用和返回. 但是上面的方法只能控制程序本身,发生以下系统状态的 ...

  4. CSAPP 并发编程读书笔记

    CSAPP 并发编程笔记 并发和并行 并发:Concurrency,只要时间上重叠就算并发,可以是单处理器交替处理 并行:Parallel,属于并发的一种特殊情况(真子集),多核/多 CPU 同时处理 ...

  5. 读书笔记汇总 - SQL必知必会(第4版)

    本系列记录并分享学习SQL的过程,主要内容为SQL的基础概念及练习过程. 书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL i ...

  6. 读书笔记--SQL必知必会18--视图

    读书笔记--SQL必知必会18--视图 18.1 视图 视图是虚拟的表,只包含使用时动态检索数据的查询. 也就是说作为视图,它不包含任何列和数据,包含的是一个查询. 18.1.1 为什么使用视图 重用 ...

  7. 《C#本质论》读书笔记(18)多线程处理

    .NET Framework 4.0 看(本质论第3版) .NET Framework 4.5 看(本质论第4版) .NET 4.0为多线程引入了两组新API:TPL(Task Parallel Li ...

  8. C#温故知新:《C#图解教程》读书笔记系列

    一.此书到底何方神圣? 本书是广受赞誉C#图解教程的最新版本.作者在本书中创造了一种全新的可视化叙述方式,以图文并茂的形式.朴实简洁的文字,并辅之以大量表格和代码示例,全面.直观地阐述了C#语言的各种 ...

  9. C#刨根究底:《你必须知道的.NET》读书笔记系列

    一.此书到底何方神圣? <你必须知道的.NET>来自于微软MVP—王涛(网名:AnyTao,博客园大牛之一,其博客地址为:http://anytao.cnblogs.com/)的最新技术心 ...

  10. Web高级征程:《大型网站技术架构》读书笔记系列

    一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计 ...

随机推荐

  1. python中的pip

    python中的pip python有两个著名的包管理工具,其中,pip是一个.它对python的包进行管理和升级等操作. 问题一:pip本地的模块安装在哪里? 使用pip install numpy ...

  2. 使用ML.NET + Azure DevOps + Azure Container Instances打造机器学习生产化

    介绍 Azure DevOps,以前称为Visual Studio Team Services(VSTS),可帮助个人和组织更快地规划,协作和发布产品.其中一项值得注意的服务是Azure Pipeli ...

  3. 17-Flink消费Kafka写入Mysql

    戳更多文章: 1-Flink入门 2-本地环境搭建&构建第一个Flink应用 3-DataSet API 4-DataSteam API 5-集群部署 6-分布式缓存 7-重启策略 8-Fli ...

  4. 【Vue】----- computed与watch的区别

    1.computed computed是一种计算属性,用来监听属性的变化: computed里面的方法调用的时候不需要加(),并且里面的方法必须要有一个返回值: computed里面的方法不是通过事件 ...

  5. ES6躬行记(12)——数组

    ES6为数组添加了多个新方法,既对它的功能进行了强化,也消除了容易产生歧义的语法. 一.静态方法 1)of() ES6为Array对象新增的第一个静态方法是of(),用于创建数组,它能接收任意个参数, ...

  6. Python并发编程之实战异步IO框架:asyncio 下篇(十一)

    大家好,并发编程 进入第十一章. 前面两节,我们讲了协程中的单任务和多任务 这节我们将通过一个小实战,来对这些内容进行巩固. 在实战中,将会用到以下知识点: 多线程的基本使用 Queue消息队列的使用 ...

  7. Sql 语句拼接 多条件分页查询

    Create PROCEDURE [dbo].[Proc_B2B_GetBatchMainPaging] @StationNo AS varchar() , --m @StationName AS v ...

  8. C#多线程编程的同步也线程安全

    前一篇文章记录了简单的多线程编程的几种方式,但是在实际的项目中,也需要等待多线程执行完成之后再执行的方法,这个就叫做多线程的同步,或者,由于多个线程对同一对象的同时操作造成数据错乱,需要线程安全.这篇 ...

  9. 微软正式开源Blazor ,将.NET带回到浏览器

    微软 ASP.NET 团队近日正式开源了  Blazor ,这是一个 Web UI 框架,可通过 WebAssembly 在任意浏览器中运行 .Net . Blazor 旨在简化快速的单页面 .Net ...

  10. [PHP] 适配器模式的日常使用

    适配器模式就是将一个类的接口方法转换成我希望用的另一个方法 , 下面是个常见的用处 class Session{ public $mc; public function __construct(){ ...