《Effective Java》读书笔记

一、引言

  1.几条基本规则:(清晰性和简洁性最为重要)

      模块的用户永远也不应该被模块的行为所迷惑(那样就不清晰了),模块要尽可能小,但又不能太小

      代码应该被重用,而不是被拷贝

      模块之间的依赖性应该尽可能的降到最小

      错误应该尽早的被检测出来,最好是在编译时刻

      PS.你不该盲目的遵从这些规则,但是,你应该只在偶尔的情况下,有了充分理由之后采取打破这些规则

      学习编程艺术首先要学会基本的规则,然后才能知道什么时候可以打破这些规则

二、创建和销毁对象

  第一条:考虑用静态工厂方法代替构造器。它有以下优势:

    1)它有名称

    2)不必再每次调用它的时候都创建一个新对象

    3)它可以返回原类型的任何子类型的对象

    4)在创建参数化类型实例的时候,它使代码变得更加简洁

    缺点:

    1)类如果不含共有的或者受保护的构造器,就不能被子类化(它鼓励程序复用而不是继承)

    2)它与其他的静态方法实际上没有任何区别(API文档中无法明确表示出来,可以用惯用命名来弥补)

  第二条:遇到多个构造器参数时要考虑用构建器。一般常用方法:

    1)重叠构造器(telescoping constructor)

    2) JavaBean模式

    3)build构建器

      PS.语法糖:NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbonydrate(27).build();

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

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

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

    1)不要用String s = new String("xxxx");而应该用String s = "xxxxx";前者复用会创建大量实例,后者只会创建一个

    2)不要使用Boolean(String);而应该用Boolwan.valueOf(String);对于同时提供了静态工厂方法和构造器的不可变类,通常用静态方法,避免创建不必要的对象

    3)用long sum = 0L;而不要用Long sum = 0L;要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱

    4)现代的JVM实现具有高度优化的垃圾回收器,其性能很容易就会超过轻量级对象池的性能,因此除非重量级对象(数据库连接),少用线程池。

  第六条:消除过期的对象引用(主要是为了防范内存泄露):

    1)栈里被弹出的元素,记得要清空引用

    2)缓存用WeakHashMap来实现

    3)注意监听器和其他回调,保存对象的弱引用,例如,只将他们保存成WeakHashMap

  第七条:避免使用终结方法(finalizer)

    原因:它不能及时执行,甚至根本就不保证它们会被执行

    用处:充当安全网,或者是为了终止非关系的本地资源

    用法:记得调用super.finalizer;要记得记录终结方法的非法方法;考虑使用终结方法守卫者

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

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

    1)自反性

    2)对称性

    3)传递性

    4)一致性

    ps.使用诀窍在P36

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

    约定内容:

      1) 如果两个对象相同,那么它们的hashCode值一定要相同。重写equals方法,一定要重写hashCode方法。

      2) 如果两个对象的hashCode相同,它们并不一定相同,这里的对象相同指的是用eqauls方法比较。

    PS.Hash算法原理:当Set接收一个元素时根据该对象的内存地址算出hashCode,看它属于哪一个区间,在这个区间里调用equeals方法。

  第十条:始终要覆盖toString方法

  第十一条:谨慎的覆盖clone

    由于它有许多缺点,另一个实现对象拷贝的好办法是提供一个拷贝构造器或者拷贝工厂

  第十二条:考虑实现Comparable接口

    有些类具有内在的排序功能,但是与equals不一致。有些集合使用comparable而不是equals做同性测试。

四、类和接口

  第十三条:使类的成员的可访问性最小化

    封装:又被称为信息隐藏,是模块化的前提和保证

    模块化的好处:

      1)各个模块可以独立开发、测试、优化、使用、理解和修改

      2)加快系统开发的速度,因为这些模块可以并行开发

      3)减轻了维护的负担,程序员可以更快的理解模块,而且在调试的时候可以不影响其他维护的负担

      4)虽然模块本身不会带来更好的性能,但是它可以帮助调节性能,通过分析知道哪些模块影响了性能

      5)提高软件的可重用性,模块可以在其它系统中使用

      6)模块可以降低系统风险,整个系统不可用,但是模块却有可能是可用的

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

    变量应该用private定义而不是public的,只能用get/set方法取

    但是如果这个类是包级私有(default)的,或者是内部类的话,则没有关系

  第十五条:使可变性最小化

    使类变为不可变的5条规则:

      1)不要提供任何会修改对象状态的方法(也称为mutator)

      2)保证类不会被扩展(使类成为final的,或者限制访问构造器)

      3)使所有域都是final的

      4)使所有域都是私有的

      5)确保对于任何可变组件的互斥访问(如果能set则不能get对象的引用)

    不可变的类(如复数类),被称作函数(functional)式的做法,相对的是过程(procedural)式的做法和命令(imperative)式的做法

    不可变的类往往有一个可变配套类,来弥补它性能上的缺点(String->StringBuilder)

    坚决不要为每个get方法编写一个相应的set方法,除非有很好的理由。不可变类有很多优点,唯一缺点是潜在的性能问题

  第十六条:复合优先于继承:继承的功能非常强大,但是也存在诸多问题,因为它违背了封装原则。只有当子类和超类之间确实存在子类型关系时,使用继承才是恰当的。否则,可以使用复合来代替继承。    

    第十七条:要么为继承而设计,并提供文档说明,要么就禁止继承

  第十八条:接口优于抽象类:除非演变的容易性比灵活性和功能性更为重要的时候

  第十九条:接口只用于定义类型:常量接口模式是对接口的不良使用,应该使用工具类(utility class)

  第二十条:类层次优于标签类:合理设计类,不要在一个类里塞太多东西,而应该使用抽象类之类的方法分层

  第二十一条:用函数对象表示策略:函数指针的主要用途就是实现策略(Strategy)模式

  第二十二条:优先考虑静态成员变量:四种不同的嵌套类及用法

五、泛型

   第二十三条:请不要在新代码中使用原生态类型(关于List<?>,一般用于只读模式,因为不知道里面元素的类型,所以不能执行add方法,除非是null。常在方法中出现,限制方法乱add元素,出于安全性考虑。)

   第二十四条:消除非受检警告:使用@SuppressWarnings("unchecked"),并添加注释,尽量加在行上而不要加在方法上。

   第二十五条:列表优先于数组:优先使用泛型而不是数组来存数据,因为泛型是编译时检查类型,而数组是运行时检查,前者更安全。数组和泛型不要混合用。

   第二十六条:优先考虑泛型

   第二十七条:优先考虑泛型方法

  第二十八条:利用有限制通配符来提升API的灵活性:PECS原则(P119)

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

六、枚举和注解

  第三十条:用enum代替int常量

  第三十一条:用实例域代替序数

  第三十二条:用EnumSet代替位域    

  第三十三条:用EnumMap代替序数索引

  第三十四条:用接口模拟可伸缩的枚举

  第三十五条:注释优于命名模式

  第三十六条:坚持使用Override注解

  第三十七条:用标记接口定义类型(标记注解和标记接口各有用处)

七、方法

  第三十八条:检查参数的有效性

  第三十九条:必要时进行保护性拷贝

  第四十条:谨慎设计方法签名

  第四十一条:慎用重载:重载的话,最好保证参数数目不一致,或者所有重载方法的行为一致。否则程序很容易误入重载方法。

  第四十二条:慎用可变参数:3个以下的参数用重载

  第四十三条:返回零长度数组或是集合,而不是null

  第四十四条:为所有导出的API元素编写文档注释

八、通用程序设计

  第四十五条:将局部变量的作用于最小化:

    1.不要过早的声明变量,而应该在他第一次使用的地方声明

    2.声明时应该初始化变量,除非是特殊情况(try块内)

    3.for循环优先于while循环,因为有循环变量可以使用

    4.使方法小而集中,也可以将局部变量的作用域最小化

  第四十六条:for-each循环优于传统的for循环

  第四十七条:了解和使用类库:程序员应该把时间花在应用程序上,而不是底层的细节上。不要重复造轮子

  第四十八条:如果需要精度的答案,请避免使用float和double:应该使用BigDecimal处理小数,或者int和long处理整数

  第四十九条:基本类型优先于装箱基本类型:当装箱基本类型和基本类型比较时,装箱基本类型会自动拆箱。如果null对象被自动拆箱,会报NullPointerException异常

  第五十条:如果其他类型更合适,则尽量避免使用字符串:字符串经常被错误的用来代替基本类型、枚举类型和聚合类型等

  第五十一条:当心字符串连接的性能

  第五十二条:通过接口引用对象:优先使用接口,这样会更灵活更聪明

  第五十三条:接口优于反射机制:应该仅仅使用反射机制来实例化对象,而访问对象时则使用编译时已知的某个接口或者超类

  第五十四条:谨慎的使用本地方法

  第五十五条:谨慎的进行优化:不要费力去编写快速的程序——应该努力编写好的程序,速度自然会随之而来

  第五十六条:遵守普遍接受的命名惯例

九、异常

  第五十七条:只针对异常的情况才使用异常:逻辑控制中,应该使用“状态测试方法”或“可识别的返回值”方法,而不是把异常用于控制流

  第五十八条:对可恢复的情况使用受检异常,对编程错误使用运行时异常

  第五十九条:避免不必要的使用受检的异常

  第六十条:优先使用标准的异常

  第六十一条:抛出与抽象对应的异常

  第六十二条:每个方法抛出的异常都要有文档

  第六十三条:在细节消息中包含能捕获失败的信息

  第六十四条:努力使失败保持原子性

  第六十五条:不要忽略异常

十、并发

  第六十六条:同步访问共享的可变数据

  第六十七条:避免过度同步

  第六十八条:excutor和task优先于线程

  第六十九条:并发工具优先于wait和notify

  第七十条:线程安全性的文档化

  第七十一条:慎用延迟初始化

  第七十二条:不要依赖线程调度器:确保可运行线程的平均数量不明显的多于处理器的数量

  第七十三条:避免使用线程组(thread group),已基本废弃,应该使用线程池executor

十一、序列化

  第七十四条:谨慎的实现Serializable接口

  第七十五条:考虑使用自定义的序列化形式

  第七十六条:保护性的编写readObject方法

  第七十七条:对于实例控制,枚举类型优先于readResolve

  第七十八条:考虑用序列化代理代替序列化实例

原文地址:https://www.cnblogs.com/xujanus/p/4505421.html