《Effective Java》------读后总结

《Effective Java》——读后总结

这本书在Java开发的行业里,颇有名气。今天总算是粗略的看完了...后面线程部分和序列化部分由于心浮气躁看的不仔细。这个月还剩下一周,慢慢总结消化。

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

  • 静态工厂方法有名称,能确切地描述正被返回的对象。
  • 不必每次调用都创建一个新的对象。
  • 可以返回原返回类型的任何子类对象。
  • 创建参数化类型实例时更加简洁,比如调用构造 HashMap 时,使用 Map < String,List < String > m = HashMap.newInstance() ,与 Map < String,List < String > m > = new HashMap < String,List < String > >();

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

静态工厂和构造器不能很好地扩展到大量的可选参数。

JavaBean 模式下使用 setter 来设置各个参数,无法仅通过检验构造器参数的有效性来保证一致性,会试图使用不一致状态的对象。

Builder 的建造者模式:使用必须的参数调用构造器,得到一个 Builder 对象,再在 builder 对象上调用类似 setter 的方法设置各个可选参数,最后调用无参的 build 方法生成不可变对象,new Instance.Builder(必须参数).setter(可选参数).build()。

Builder 模式让类的创建和表示分离,使得相同的创建过程可以创建不同的表示。

3、避免创建不必要的对象

对于 String 类型,String s = new String("") 每次执行时都会创建一个新的实例,而使用 String s = "" 则不会,因为对于虚拟机而言,包含相同的字符串字面常量会重用,而不是每次执行时都创建一个新的实例。

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

4、消除过期的对象引用

缓存时优先使用 WeakHashMap,LinkedHashMap 这些数据结构,及时清掉没用的项。

显示取消监听器和回调,或进行弱引用。对于所有对象都通用的方法

5、覆盖 equals

  • 如果类具有自己特有的"逻辑相等",但超类还没有覆盖 equals 以实现期望的行为。
  • 高质量equals的方法

使用 == 操作符检查”参数是否为这个对象的引用“。

使用 instanceof 操作符检查“参数是否为正确的类型”。

把参数转换成正确的类型。

对于该类中的每个关键域,检查参数中的域是否与该对象中对应的域相匹配。

不要将 equals 声明的 object 对象替换为其他的类型,因为这样是没法覆盖 Object.equals,只是提供了一个重载。

6、覆盖 equals 时总是覆盖 hashCode

相等的对象必须具有相等的散列码,如果没有一起去覆盖 hashcode,则会导致俩个相等的对象未必有相等的散列码,造成该类无法结合所有基于散列的集合一起工作。

7、总是覆盖 toString

Object 提供的 toString,实现是类名[email protected]+散列码的无符号十六进制。

自己覆盖的 toString,返回对象中包含的所有值得关注的信息。

不足:当类被广泛使用,一旦指定格式,那就会编写出相应的代码来解析这种字符串表示法,以及把字符串表示法嵌入持久化数据中,之后若改变这种表示法,则会遭到破坏。

8、考虑实现 Comparable 接口

如果类实现了comparable 接口,便可以跟许多泛型算法以及依赖该接口的集合实现协作,比如可以使用 Array.sort 等集合的排序。

9、使类和成员的可访问性最小化

  • 隐藏内部实现细节,有效解耦各模块的耦合关系
  • 访问级别
private: 类内部才可访问
package-private(缺省的): 包内部的任何类可访问
protected: 声明该成员的类的子类以及包内部的类可访问
public: 任何地方均可访问

10、复合优于继承

继承打破了封装性,除非超类是专门为了扩展而设计的。超类若在后续的发行版本中获得新的方法,并且其子类覆盖超类中与新方法有关的方法,则可能会发生错误。

复合:在新的类中增加一个私有域,引用现有类。它不依赖现有类的实现细节,对现有类进行转发。

11、接口优于抽象类

抽象类允许包含某些方法的实现,但为了实现由抽象类定义的类型,类必须成为抽象类的一个子类,且是单继承。

接口允许我们构造非层次结构的类型框架,安全地增强类的功能。

对每个重要的接口都提供一个抽象的骨架实现类,把接口和抽象类的优点结合(接口不能包含具体的方法,抽象类使用继承来增加功能)。它们为抽象类提供了实现上的帮助,但又不强加抽象类被用作类型定义时所特有的严格限制。

抽象类的演变比接口的演变要容易得多,在后续版本中在抽象类中始终可以增加新的具体方法,其抽象类的所有子类都将提供这个新的方法,而接口不行。

12、接口只用于定义类型

当类实现接口时,接口充当可以引用这个类的实例的类型,为了任何其他目的而定义接口时不恰当的。

常量接口时对接口的不良使用。实现常量接口,会导致把这样的实现细节泄漏给该类的导出 API 中,当类不再需要这些常量时,还必须实现这个接口以确保兼容性。如果非final类实现了该常量接口,它的所有子类的命名空间都将被接口中的常量污染。

13、优先考虑静态成员类

静态成员类是最简单的嵌套类,可以当做普通的类,只是被声明在另一个类的内部。

非静态成员类的每个实例都隐含着与外部类的一个外部实例相关联。没有外部实例的情况下,是无法创建非静态成员类的实例。每个非静态成员类的实例都包含一个额外的指向外部对象的引用,会导致外部实例在垃圾回收时仍然保留。

匿名类没有名字,在使用的同时被声明和实例化。当匿名类出现在非静态环境中时有外部实例,在静态环境中也不能拥有任何静态成员。匿名类必须保持简短,保持可读性。

局部类,在任何可以声明局部变量的地方声明局部类,有名字,在非非静态环境中定义才有外部实例,不能包含静态成员,同时必须保持简短。

Java学习交流QQ群:603654340 我们一起学Java!

14、用 enum 代替 int 常量

枚举类型是指由一组固定的常量组成合法值的类型,通过公有的静态 final 域为每个枚举常量导出实例的类,没有构造器,是单例的泛型化。

int 枚举模式在类型安全性和使用方便性没有任何帮助,打印的 int 枚举变量只是一个数字。

String 枚举模式虽然提供了可打印的字符串,但会导致性能问题,还依赖于字符串的比较操作。

枚举类型可以通过 toString 将枚举转换成可打印的字符串,还允许添加任意的方法和域,并实现任意的接口。

性能缺点:装载和初始化枚举时会有空间和时间的成本。

15、检查参数的有效性

对于公有方法,用 Javadoc 的 @throw 标签在文档中说明违反参数限制时会抛出的异常。

对于未被导出的方法(私有的),可以使用断言来检查参数。断言如果失败会抛出 AssertionException,如果没起到作用也不会有成本开销。

每当编写方法或构造器时,要考虑它的参数有哪些限制,应该把这些限制写到文档中,并且在方法体的开头处进行显示的检查。

16、必要时进行保护性拷贝

对方法的每个可变参数,或返回一个指向内部可变组件的引用时,需要进行保护性拷贝,避免在使用过程中可变对象进行了修改。

保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象。

17、 慎用重载

重载方法的选择是静态的,选择工作时在编译时进行,完全基于参数的编译时类型。

覆盖方法的选择是动态的,选择的依据是被调用方法所在对象的运行时类型。

不要导出俩个具有相同参数数目的重载方法,如果参数数目相同,则至少有一个对应的参数在俩个重载方法中具有根本不同的类型,否则就应该保证,当传递同样的参数时,所有的重载方法的行为必须一致。

18、返回零长度的数组或集合,而不是 null

对于返回 null 而不是零长度数组或集合的方法,几乎每次用到该方法时都需要进行 null 值的判断,这样很曲折同时很容易出错。

19、基本类型优于装箱基本类型

基本类型只有值,而装箱基本类型可以具有相同的值和不同的同一性。对装箱基本类型运用 == 操作符几乎总是错误的。

基本类型只有功能完备的值,而每个装箱基本类型除了它对应的基本类型的所有功能值外,还有个非功能值:null。当在一项操作中混合使用基本类型和装箱基本类型时,装箱基本类型会自动拆箱,如果 null 对象引用被自动拆箱,会得到空指针异常。

基本类型通常比装箱基本类型更节省时间和空间,装箱基本类型会导致高开销和不必要的对象创建。

20、当心字符串连接的性能

字符串是不可变的,当俩个字符串连接时需要对其内容进行拷贝,连接 n 个字符串需要 n 的平方级时间。因为第 n 次拼接的字符串,需要 n-1 次的字符串和第 n 次的字符串拷贝,和他们拼接后的拷贝,这样 an - an-1 = n-1+1+n = 2n;这样可以得到 an = n*(n-1),及 O(N^2) 的拼接时间。

21、通过接口引用对象

如果有合适的接口类型存在,那么对于参数、返回值、变量和域来说,就都应该使用接口类型进行声明。如,List<>vector = new Vector<>();List list = new ArrayList<>(); ,这样程序会更加灵活,当更换实现时,所要做的只是改变构造器中的类。

如果没有合适的接口存在,完全可以用类而不是类接口来引用对象。如果含有基类,则优先使用基类来引用这个对象而不是它的实现类。

22、只针对异常的情况才使用异常

异常是为了在异常情况下使用而设计的,不要将他们用于普通的控制流,而不要编写破事他们这么做的 API。

基于异常的循环模式不仅模糊了代码的意图,降低了性能( JVM 不会对异常的代码块进行优化),而且它还不能保证正常工作。

23、对可恢复的情况使用受检异常,对编程错误使用运行时异常

受检异常:如果期望调用者能适当地恢复,这时应该使用受检的异常。通过抛出受检的异常,强迫调用者在一个 catch 中处理该异常或传播出去。

未受检异常:不需要也不应该被捕获的可抛出结构。

运行时异常:表明编程错误,是 RuntimeException 的子类,运行时检查。

错误:表示资源不足,约束失败,或其他使程序无法继续执行的条件。

设计受检异常抛出 API 的条件:正确地使用 API 不能阻止这种异常条件的产生 & 产生异常后可以立即采取有用的动作。

24、抛出与抽象相对应的异常

当方法传递由低层抽象抛出的异常与所执行的任务没有明显联系时,会导致困扰且让实现细节污染了更高层 API。

更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常(异常转译)。

25、努力使失败保持原子性

失败原子性:失败的方法调用应该使对象保持在被调用之前的状态。

设计不可变对象,永远不会使已有的对象保持在不一致的状态中。

对于可变对象:

执行操作之前检查参数的有效性。

调整计算处理过程的顺序,使得任何可能失败的计算部分都在对象状态被修改之前发生。

编写一段恢复代码,由它来拦截操作过程中发生的失败,以及对象回滚到操作开始之前的状态上,主要用于永久性的数据结构。

在对象的一份临时拷贝上执行操作,不破坏传入对象的状态。

26、同步访问

同步可以阻止一个线程看到对象处于不一致的状态之中,还能保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护的之前所有的修改效果。

多个线程共享可变数据时,每个读或者写数据的线程都必须执行同步,否则可能导致活性失败和安全性失败。

活性失败:线程A对某变量值的修改,可能没有立即在线程B体现出来。

安全性失败:并发访问共享资源导致状态不一致造成的安全问题。

过度同步可能会导致性能降低、死锁,甚至不确定的行为。

在同步区域内做尽可能少的工作,过度的同步会丢失并行的机会,限制 VM 优化代码执行的能力

不要从同步区域内部调用外来方法,避免死锁和数据破坏。

CopyOnWriteArrayList 通过重新拷贝整个底层数组实现所有的写操作,适用于读操作远大于写操作的场景,当写操作频繁时性能损耗很大。

27、谨慎地实现 Serializable 接口

一旦一个类被发布,就大大降低了“改变这个类的实现” 的灵活性。若接受了默认的序列化形式,并且以后要改变类的内部结构,会导致序列化形式的不兼容。其次序列化对应流的唯一标识符 UID,在没有显示声明序列版本 UID,那么改变类的信息,将产生新的序列版本 UID,破坏它的兼容性。

时间: 2024-11-30 16:48:34

《Effective Java》------读后总结的相关文章

《你的灯亮着吗》——读后总结

今天竟然一口气看完了这本书,晚上11点整,刚刚好写点总结. 这本书并不是一下子就能学会的,而是在生活中慢慢去习惯这种思考方式. 还是老样子,先看看这本书都讲了什么? 这本书是一种教别人遇到问题的解决思路的一本书.本书讲了如下的问题: 1 人们遇到问题如何思考? 2 遇到棘手的问题如何思考并解决? 3 对于某个问题何种角度来思考! 针对以上的问题,书中按照下面的章节进行介绍: 1 问题是什么? 2 什么是真正的问题? 3 这是谁的问题? 4 问题是从哪来的? 5 我们真的想解决问题吗? 如何解释“

惠普笔记本按开机键后电源灯亮的,但是屏幕一直是黑的,只有大写锁定键闪烁,闪3次一个循环,听得到风扇

现象: 惠普笔记本按开机键后电源灯亮的,但是屏幕一直是黑的,只有大写锁定键闪烁,闪3次一个循环,听得到风扇转动的声音,热风,cpu应该也在工作,请问是什么问题. 解决(我按照红色的作了,就可以了,真是奇怪...): 您好,感谢您选择惠普产品. 1.您的笔记本开机黑屏,请您回忆一下最近是否进行过什么方面特殊操作,比如最近是否进行过硬件改动(例如添加过新内存)或者连接了外接设备,如果是,建议您恢复之前硬件配置及断开所有外接设备看是否可以正常开机. 2.如果您没有进行过特殊操作,使用电池或电源适配器供

你的灯亮着吗?(3)

读后感言 读一本书就能够学会如何解决问题是不可能的,所以该书也不是按部就班的教你如何解决问题,更多的我觉得应该是大家通过阅读该书有所思,有所悟,最终形成自己的分析和解决问题的方法论. 1.问题的定义 问题是你期望和和你体验间的差别,要分析和解决问题时候首先需要搞清楚什么是真正的问题,问题从哪里来是谁的问题等内容.在工作和生活中常犯的毛病是扭曲问题定义,自己人为的去解释和翻译问题从而导致把问题的解决方法做为问题的定义. 从而导致后续一连串的错误. 2.分析和思考问题 分析和思考问题是对问题定义的进

[书籍分享]0-003.你的灯亮着吗:发现问题的真正所在

封面 内容简介 本书是由唐纳德·高斯和杰拉尔德·温伯格著作.它主要是向读者阐述了一些关于问题定义和看待问题的方式方法,帮助读者解放自己的思维禁锢,多方面的去寻找问题的定义,并解决问题. 这本书分六篇列举了20个故事.以它极其幽默的方式让读者去接收作者的思维和思考方式,读完这本书后你会发现,你会更容易的去全面看待一个问题.在我看来,这本书的主要目的就是发散你的思维. 作者 唐纳德·高斯,知名畅销书作家,目前写书27本,涉足领域:经济营销.伦理学.心理学等.知名图书:<上12堂说故事的营销课>.&

《你的灯亮着吗》第五篇、第六篇总结

第五篇讲的是问题是从哪儿来的?由一个叫珍妮特去波兰拜访她的祖母在旅途中遇到的麻烦引出主题.一开始她把问题推到“官僚主义”上,她感觉这似乎是最恰当的.但是最后她发现她十分倾向于把整件事归咎于“官僚主义”,之后她提出了一个至关重要的问题:问题是从哪儿来的?从这点出发,她成功找到乐各种备选答案.后来,珍妮特想这个问题的根源也许是她自己.她开始以正常的心态面对曾被她称作灰脸先生的人,他们顺利的交谈,最后事情的到了解决.一切都正常的进行着,一旦你确定问题真正从哪来,尤其是因为问题的根源常常在你自己身上.

四、按键控制LED灯亮灭

材料: 1.SAGOO UNO 1块: 2.按键模块 1块: 3.杜邦线若干. 步骤: 1.按照下图连接按键模块和UNO: SAGOO UNO引脚                                      按键模块引脚 3V3  <------------------------------------>    V(电源) GND <------------------------------------>    G(电源) Pin2  <----------

《你的灯亮着吗》读者笔记(三)

解决问题是最后一步,建立在分析问题基础上,解决问题有多条途径,如果通过各种约束条件选择最佳的途径来解决问题是需要考虑的重要因素.问题解决后要及时进行归纳和总结,形成自己的知识库. 当你在寻找问题定义的道路上疲倦地游荡时,不要忘记随时都回头看看,看看你是不是已经迷路了. 一旦你用文字来表达一个问题,请仔细推敲这些文字以使这种表述在每个人的头脑中都是一个意识. 如果这是他们的麻烦,就让它成为他们的麻烦. 当别人能够很好地解决自己问题的时候,千万不要越俎代庖. 如果某人能够解决这个问题,但是他本人却并

《你的灯亮着吗?》阅读笔记1

<你的灯亮着吗?> 目前才看了这本书的三分之一,不过对我来说很有针对性,感觉有达到那种一针见血的目的,也就是主旨大意很明确吧,让我真切能读进东西. 这两章的标题是“问题是什么”和“这个问题是什么?”它从不同的深度和层次上通过故事向我们阐述了当我们面对一些问题时的心理路程,我们不难从中得到体会. 当面对产生的问题时,在提出解决问题的方案之前,我们必须了解谁有问题,或者说这是谁的问题,其次再针对这些人给出对用问题的解释或定义,认识到问题的本质核心.在我们认识问题的过程中你会发现问题其实就是期望的东

《你的灯亮着吗》阅读笔记二

开头就是一个关于打印机的故事,说的是一家很大的计算机生产商打算开发一种新型打印机,丹用简单的思路就设计出了一种惊人有效的工具,铝条上欠着小针,这样就可以精确的在纸上扎出小洞来.按照我们的思路,丹这是立了功,应该发奖金加工资甚至升个职也不为过啊,但是后来却出现了戏剧性的一幕,因为丹的老板被针扎到了,于是丹的这一切也就泡汤了. 一开始的问题,丹确实是解决了,但是解决了这个问题,又会伴随着新的问题的出现.我们的生活中也是这样,往往遇见了问题,你解决了,但马上又会因为你解决的方式,或者这个问题的本身,又

《你的灯亮着吗》阅读笔记3

问题是从哪里来的?珍妮特•乔瓦斯基遇到的麻烦,来自哪里?她有多个答案,这些答案可能错也可能对.但是事实是什么没有人会知道. “问题的根源常常在你自己身上”,珍妮特以微笑的礼貌方式与公众服务人民进行了交流,虽然不知道问题的真正出现原因在哪,可是珍妮特却可以得到一个好的结果.我们在做事时是不是经常会忽略礼貌问题.跟人交流时,感觉对方态度恶劣,自己很反感,所以自己态度也会很恶劣,却从没想过自己应该态度好一点,珍妮特给自己上了一课. “在这世上有两种人,一种人做事,另一种人给别人找事做.离第二种人远一点