《Effective Java 第二版》读书笔记

想成为更优秀,更高效程序员,请阅读此书。总计78个条目,每个对应一个规则。

第二章 创建和销毁对象

  一,考虑用静态工厂方法代替构造器

  二, 遇到多个构造器参数时要考虑用builder模式

      /**
    * 1.直接传参阅读性差,2.JavaBean多线程下存在安全问题,3.builder适用于大于5个参数并且参数是可选得情况 建造者(builder) 模式
    * @author fancy
    */
    public class Person implements Serializable {
      private String name;
      private String age;
      private String sex;
      private String country;
      private String edc;
      public Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.sex = builder.sex;
        this.country = builder.country;
        this.edc = builder.edc;
      }
      public static class Builder {
        private String name;
        private String age;
        private String sex;
        private String country;
        private String edc;
        public Builder setName(String name) {
          this.name = name;
          return this;
        }
        public Builder setAge(String age) {
          this.age = age;
          return this;
        }
        public Builder setSex(String sex) {
          this.sex = sex;
          return this;
        }
        public Builder setCountry(String country) {
          this.country = country;
          return this;
        }
        public Builder setEdc(String edc) {
          this.edc = edc;
          return this;
        }
        public Person build() {
          return new Person(this);
        }
      }
      public static void main(String[] args) {
         Person person = new Person.Builder().setAge("20").setName("fancy").build();
      }
    }

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

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

     工具类实例对它没任何意义,添加私有构造器是有必要的。

      public class StringUtil {

      private StringUtil () {}

    }

  五,避免创建不必要的对象

     能重用对象的时候不要创建新对象。

     优先使用基本类型,尽量避免自动装箱。

  六,消除过期的对象引用

     长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏。

     引用对象已经过期,清空引用=null。

  七,比用使用终结方法

     终结方法(finalizer)通常是不可以预测的,也是很危险的。终结方法的缺点在于不能保证会被及时地执行。

     final:java中的关键字,修饰符。

     A).如果一个类被声明为final,就意味着它不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为abstract抽象类的和final的类。

     B).如果将变量或者方法声明为final,可以保证它们在使用中不被改变.

         1)被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。

         2)被声明final的方法只能使用,不能重载。

     finally:java的一种异常处理机制。

     finally是对Java异常处理模型的最佳补充。finally结构使代码总会执行,而不管无异常发生。使用finally可以维护对象的内部状态,并可以清理非内存资源。特别是在关闭

数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的几率。

     finalize:Java中的一个方法名。

     Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没被引用时对这个对象调用的。它是

在Object类中定义的,因此所的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

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

  Object是一个具体得类,设计它主要为了扩展。

  八,覆盖equals时请遵守通用约定

     类具有自己独特的“逻辑相等”概念,这个时候需要覆盖equals方法,

  九,覆盖equals时总要覆盖hashcode

     相等的(equals)的对象必须具有相等的hash code。不相等的两个对象,hash code有可能相等。

  十,始终要覆盖toString

     返回对象中所有值得关注得信息

  十一,谨慎地覆盖clone()方法

     除非拷贝数组,否则没必要覆盖

  十二,考虑实现Comparable接口

    类具有非常明显得内在排序关系,比如按照字母排序、按照数值排序,应该考虑使用这个接口。

第四章 类和接口

  十三, 使类和成员的可访问性最小化

     设计良好得模块会隐藏所有得实现细节

     尽可能降低可访问性。

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

     公有类永远都不应该暴露可变得域。

  十五,使可变性最小化

     除非有很好的理由,坚决不要每个get方法编写set方法,尽量每个域都是final的。

  十六,复合优先于继承

     private final Set<E> s;// 增加一个私有的成员变量,继承违背了封装原则,子类父类存在子类型关系时才用继承。

     这样做等同于新建一个类,而这个类的全部作用就是给原来继承的父类的所有已知方法进行代理。

     代理方法是死的,不会随原父类方法的扩充而改变,他只是调用原父类的方法,所以,当原父类新增方法时这个代理的类是不知道也不会受到影响的。

     这样做也是有缺陷的:

     虽然这样不会因为父类的扩展和子类发生冲突,但同样的父类扩展的好处也不会被子类自动继承,这时需要手动去更新代理类,设想一下,在一个比较复杂的系统中,所

有继承关系都使用这种复合关系,那么任何非叶级的类增加方法都要在该类的复合类中增加代理方法,代码维护工作量会增加,而且父类增加方法时无法立即知道新方法与子类可能

存在的冲突,而导致了重复的工作。

     一般父类扩展方法与子类发生冲突是因为子类在扩展方法时没有从对象分类上进行充分的考虑导致在错误的分类范围定义错误的行为,是可以从设计上就避免的问题。

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

  十八,接口优于抽象类

     抽象类允许包含某些方法的实现;接口不允许。

     为实现抽象类定义的类型,类必须成为抽象类的一个子类,Java只允许单继承;可以implement多个接口

  十九,接口只用于定义类型

     接口不应该用来定义常量。

  二十,类层次优于标签类、

     标签类:香蕉苹果放到一个标签类中,冗长

     类层次:水果父类,香蕉苹果分别继承

  二十一,用函数对象表示策略

     函数指针的主要用途就是实现策略(Strategy)模式。Java中声明一个接口表示策略,并且为每个具体策略声明一个实现了该接口的类。例:Arrays.sort(array, comparator);

  二十二,优先考虑静态成员类

     嵌套类有四种:静态成员类,非静态成员类,匿名类,局部类。

第五章 泛型

  二十三,不要在新代码中使用原生态类型

     原生态类型可能导致运行时异常,保留是为了兼容性

     泛型<E>,<Object>参数化类型,<?>无限制通配符类型

  二十四,消除非受检警告

     尽可能消除每一个非受检警告,运行时可能抛出ClassCastException。

     无法消除使用注释,@SuppressWarnings("unchecked")

  二十五,List优先于数组

     String类型放入int类型数组,运行时才会抛出异常

  二十六,优先考虑泛型

  二十七,优先考虑泛型方法

  二十八,利用有限制通配符来提升API的灵活性

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

第六章 枚举和注解

  三十,用enum代替int常量

     enum type:由一组固定的常量组成合法值的类型。

     功能强大,int数字翻译成字符串

  三十一,用实例域代替序数

     除了设计基于EnumSet,EnumMap外数据结构外基本用不到。

  三十二,用EnumSet代替位域

  三十三,用EnumMap代替序数索引

  三十四,用接口模拟可伸缩的枚举

  三十五,注解优先于命名模式

  三十六,坚持用Override注解

     覆盖父类方法上使用,编辑器可以替你防止大量错误。

  三十七,用标记接口定义类型

     标记接口(marker interface)是没有包含方法声明的接口。例:Serializable

第七章 方法

  三十八,检查参数有效性

     在方法体得开头处检查参数。比如对象是否为null。

  三十九,必要时进行保护性拷贝

    public Date getBrithday() {  

        return (Date) this.birthday.clone();  // 我造个新的实例扔给你,你随便折腾吧,影响不到我  ,防止使用者改变。

    } 

  四十,谨慎设计方法签名

     谨慎选择方法名、不要过于追求提供便利的方法、避免过长参数列表

  四十一,慎用重载

     尽量不要出现有两个相同参数的重载方法。

  四十二,慎用可变参数

  四十三,返回零长度的数组或者集合而不是null

     简化调用者非null判断工作

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

第八章 通用程序设计

  四十五,将局部变量的作用域最小化

     第一次使用局部变量的地方声明

  四十六,for-each循环优先于传统的for循环

     简洁、预防bug、性能优势

     以下三种情况无法使用for-each:1.过滤,2.转换,3.平衡迭代

  四十七,了解和使用类库

     不要重新发明轮子

  四十八,如果需要精确的答案,请避免使用float和double

     如果数值范围不超过9位十进制用int

     如果数值范围不超过18位十进制用long

     如果数值范围超过18位十进制用BigDecimal

  四十九,基本类型优先于装箱类型

     基本类型更加简单,也更加快速。

  五十,如果其他类型更适合,则避免使用字符串

  五十一,当心字符串链接的性能

     认识String类

       1)String类是final类

       2)String类其实是通过char数组来保存字符串的。

       3)无论是sub操作、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。

     StringBuffer类的成员方法前面多了一个关键字:synchronized,在多线程访问时起到安全保护作用的

     StringBuilder不是线程安全的。

     String、StringBuilder、StringBuffer三者的执行效率:StringBuilder > StringBuffer > String

  五十二,优先使用接口而非类来引用对象

     List list = new ArrayList();

  五十三,接口优先于反射机制

     反射适用于部分对象实例化,不适合访问对象。

  五十四,谨慎地使用本地方法

     本地方法指本地程序设计语言(C,Python)来编写的特殊方法。

     必须使用需进行全面测试。

  五十五,谨慎地进行优化

     没有好的优化方法不要优化。

  五十六,遵守普遍接受的命名惯例

第九章 异常

  五十七,只针对异常的情况才使用异常

     不要用于普通的控制流。

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

  五十九,避免不必要地使用受检的异常

  六十,优先使用标准的异常

     常用异常。

  六十一,抛出与抽象相对应的异常

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

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

  六十四,努力使失败保持原子性

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

       1.设计不可变的对象,重新初始化常量

       2.操作之前检查有效性

       3.编写恢复代码

  六十五,不要忽略异常

第十章 并发

  六十六,同步访问共享的可变数据

     多个线程共享可变数据得时候,每个读或写操作得线程都必须是同步的。

  六十七,避免过度同步

     同步区域内做尽可能少的工作。避免性能降低和其他不确定性影响。

  六十八,executor和task优先于线程

     《Java并发编程实践》

  六十九,并发工具优先于wait和notify

     java.util.conconcurrent提供了更高级的语言,没有必要使用wait和notify。

  七十,线程安全性的文档化

     线程安全注解或说明。

  七十一,慎用延迟初始化

  七十二,不要依赖于线程调度器

  七十三,避免使用线程组

     过时了,可忽略。

第十一章 序列化

  定义:将一个对象编码成字节流,称作将该对象序列化(serializing),相反的过程称为反序列化(deserializing)

  好处:对象被序列化后,它的编码就可以从一台正在运行的虚拟机被传递到另一台虚拟机上,或者被储存到磁盘上,供反序列化使用。

  应用:序列化技术为远程通信提供了标准的小路级(wire-level)对象表示法,也为JavaBeans组件结构提供了标准的持久化格式。

  七十四,谨慎地实现Serializable接口

     为了继承而设计的类、用户接口,应该尽少实现Serializable接口.

     java.lang.throwable实现了Serializable接口,所以RMI的异常可以从服务器端传到客户端

     httpservlet实现了Serializable接口,会话状态(session state)可以被缓存。

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

  七十六,保护性地编写readObject方法

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

  七十八,考虑用序列化代理代替序列化实例

posted @ 2018-12-12 17:25  fancybox  阅读(176)  评论(0编辑  收藏  举报