说明

这里是阅读《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. 从锅炉工到AI专家(8)

    ImageNet 基础部分完成,从本篇开始,会略微的增加一些难度. 通常说,在解决问题的时候,大多程序员都会在网上搜索,寻找一些相似相近的案例作为参考.这个方式在机器学习领域同样有效.可惜早期的时候, ...

  2. PyQt:无边框自定义标题栏及最大化最小化窗体大小调整

    环境 Python3.5.2 PyQt5 陈述 隐藏掉系统的控制栏,实现了自定义的标题控制栏,以及关闭/最大化/最小化的功能,自由调整窗体大小的功能(跟随一个大佬学的),代码内有详细注释 只要把Mai ...

  3. 线程安全(上)--彻底搞懂volatile关键字

    对于volatile这个关键字,相信很多朋友都听说过,甚至使用过,这个关键字虽然字面上理解起来比较简单,但是要用好起来却不是一件容易的事.这篇文章将从多个方面来讲解volatile,让你对它更加理解. ...

  4. 决策树 Decision Tree

    决策树是一个类似于流程图的树结构:其中,每个内部结点表示在一个属性上的测试,每个分支代表一个属性输出,而每个树叶结点代表类或类分布.树的最顶层是根结点.  决策树的构建 想要构建一个决策树,那么咱们 ...

  5. web前端安全

    之前对web前端安全进行了总结,想给大家分享一下,有不对的地方,大家多多交流,由于写在了PPT上,只好给大家一张一张粘上来,希望大家不要在意,了解知识为主

  6. .NET快速信息化系统开发框架 V3.2 -> WinForm“组织机构管理”界面组织机构权限管理采用新的界面,操作权限按模块进行展示

    对于某些大型的企业.信息系统,涉及的组织机构较多,模块多.操作权限也多,对用户或角色一一设置模块.操作权限等比较繁琐.我们可以直接对某一组织机构进行权限的设置,这样设置后,同一组织机构的用户就可以拥有 ...

  7. [图解]ARP协议(一)

    一.ARP概述 如果要在TCP/IP协议栈中选择一个"最不安全的协议",那么我会毫不犹豫把票投给ARP协议.我们经常听到的这些术语,包括"网络扫描"." ...

  8. springboot情操陶冶-web配置(四)

    承接前文springboot情操陶冶-web配置(三),本文将在DispatcherServlet应用的基础上谈下websocket的使用 websocket websocket的简单了解可见维基百科 ...

  9. MySQLSource-Flume

    1. 自定义Source说明 实时监控MySQL,从MySQL中获取数据传输到HDFS或者其他存储框架,所以此时需要我们自己实现MySQLSource. 2. 自定义MySQLSource步骤 根据官 ...

  10. 第17章 社区快速入门和模板 - Identity Server 4 中文文档(v1.0.0)

    IdentityServer组织不维护这些示例.IdentityServer组织愉快地链接到社区模板,但不能对模板做出任何保证.请直接与作者联系. 17.1 各种ASP.NET核心安全样本 https ...