JAVA学习之 Effective JAVA (读书笔记)

EFFECTIVE JAVA 学习笔记

... 21

 

第一章 绪论

 

第二章 创建和销毁对象

Item 1:考虑采用静态工厂方法替代构造函数

      JAVA SE范例:原生基本类型的包装类

       优点:

1.        工厂方法带有名字

2.        不必每次调用都实例化对象(更为灵活、尤其适用于不可变类)

3.        能够返回任意子类对象(适用于基于接口的框架,如集合框架、以及更为灵活的服务提供者框架, 如JCE

       缺点:

1.        如不提供公共或受保护的构造函数则不能被子类化(继承)

2.        难以同其他的静态方法区分(采用命名约定有一定效果)

 

 

Item 2:通过私有化构造函数保证单件特性

      实现单件有两种方式:

1.        采用公共静态成员变量+类静态初始化

2.        采用私有静态成员+静态工厂方法(更为灵活)。     

     

Item 3:通过私有构造函数保证类不可实例化          

      JAVA SE范例:数学、集合等实用工具类

       优点:

1.        保证类不可实例化,达到仅提供工具函数的目的,同时也保证不被继承(导致子类实例化)

       缺点:

1.        容易滥用,导致过程式编程

Item 4:避免创建冗余对象          

      JAVA SE范例:基本对象的包装类

       优点:

1.        减少冗余、提高了复用,同时性能也大为提升(尤其适用于不可变对象)

       Item 5:消除孤立的对象引用         

      JAVA SE范例:堆栈的数组实现方式

       优点:

1.        减少内存泄露,提高性能

      

Item 6:避免使用Finalizer       

       缺点:

1.        不能保证及时执行

2.        不能依赖其保证持久状态

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

Item 7:重写equals()方法时遵守通用协议         

JAVA SE范例:线程、随机、集合等类。

       不进行重写的情况:

1.        类的实例独一无二

2.        并不需要类进行逻辑相等测试

3.        超类已经提供可用equals()方法

4.        类为私有类(重写为抛出异常)

       重写时遵守的协议:

1.        反身性

2.        对等性

3.        传递性

4.        一致性

5.        非空性

高质量equals()方法的特征:

1.        使用==操作符先行测试

2.        使用instanceof操作符测试

3.        使用cast进行类型转换

4.        相应字段进行相等性测试

5.        进行对等性、传递性、一致性

重写equals()方法时的注意事项:

1.        重写equals()方法时重写hashCode()方法

2.        不要过于聪明

3.        不要依赖不可靠资源

4.        不要子类化参数类型

 

Item 8:重写equals()时总重写hashCode()方法          

JAVA SE范例:HashMapHashTableHashSet

重写时遵守的协议:

1.        相等测试字段不变时,hashCode()方法返回相同整数

2.        相等的两个对象返回相同的hashCode整数

3.        不相等的两个对象也可能产生相同的hashCode整数

4.        可以通过一个私有字段缓存hashCode

       构建Hash函数的基本步骤:

1.        存储非零整数值如17到整形变量result

2.        计算相关字段对应的hashc

3.        result = 37*result + c

4.        返回result

5.        测试是否相等对象拥有相同hash

 

 

Item 9:总是重写toString()方法         

JAVA SE范例:Object对象的toString()方法

       优点:

1.        更为方便使用、更为清晰

重写toString()时的注意事项:

1.        应该包含对象中的所有有意义的信息

2.        注释文档的意图(不管采用特定格式与否)

3.        方法返回的值最好能够编程访问

 

Item 10:谨慎地重写clon()方法          

JAVA SE范例:Clonable接口

       优点:

1.        提供更为灵活的clon()方法实现

2.        能够充当构造函数

 

 

Item 11:考虑实现Comparable接口          

JAVA SE范例:ListArray等有序集合

实现compareTo()方法时遵守的规范:

1.        对称性

2.        传递性

3.        等值性

4.        相等性

5.        方法在比较不同类对象时抛出异常

第四章 类和接口

Item 12:最小化类及其成员的访问性          

信息隐藏或封装是这一原则的基本概念,通过封装解藕模块关系。Java中内置有访问控制机制,提供privateprotected、包访问等。

       最小化原则:

1.        尽可能使得类或成员不可访问

2.        顶层类尽可能提供包私有访问

3.        成员尽可能提供私有访问

4.        不要提供public静态数组字段

 

Item 13:青睐不可变性

JAVA SE范例:String、包装类等

类不可变的五大原则:

1.        不提供修改对象的方法

2.        方法都不能被复写

3.        字段均为final

4.        字段私有

5.        排除对可变组件的访问

优点:

1.        简单

2.        线程安全

3.        自由共享

4.        为其他对象提供大型构建快

缺点:

1.        每个值都得提供一个对象

总结:

1.        尽量使得类不可变

2.        限制类的可变性

3.        构造函数提供对像创建的所有信息

 

Item 14:青睐组合多于继承

继承的不足:

1.        破坏了封装

2.        容易导致过深的继承层次

Item 15:继承的设计、编档与禁止

       如何为了类的后续继承而设计、编档?

1.        必修对重写任何方法的效果进行精确的注释

2.        类可能必修通过受保护的方法提供到内部工作的钩子

3.        构造函数禁止调用(直接、间接)可重写的方法

4.        readResolve方法或writeReplace方法设为受保护的

5.        设计一个用于继承的类添加了子类化限制

6.        适当的时候应当禁止子类化

Item 16:相对抽象类偏好接口

1.        已经存在的类能够容易的实现一个新的接口

2.        接口是定义“微类型”的理想选择

3.        接口允许非继承的类型层次结构

4.        接口保证了安全、强大的功能增强

5.        抽象类比接口更容易演化(例外)

Item 17:仅仅用于定义类型的接口

       一个典型范例——常量接口,常量接口是接口的一个比较差的应用。总的来说接口不应当仅仅用于定义类型

Item 18:青睐静态成员类而非非静态

       JAVA中包含四种内联类:

1.        静态成员类

2.        非静态成员类

3.        匿名类

4.        本地类

 

第五章 C构造的替代物

Item 19用类替代结构

类相比于结构体现出更好的封装性,但有时基于性能的考虑也有例外。

Item 20:类继承替代联合

Item 21:类替代枚举结构

       C中的枚举仅定义了命名整数常量,JAVA中提供了新的类型安全枚举模式作为替代物。

Item 22:用类和接口替代函数指针

第六章 方法

Item 23:检查参数的有效性

       通常方法对传入的参数的有效性有一定约束,如索引越界、非法参数、空指针等。具体检测策略:

1.        对公用方法用throws注释

2.        内部方法通过assert结构强化处理

总之,写方法是应该认真考虑参数的限制、予以注释并进行相应的检查。

Item 24:必要时进行防卫式拷贝

       有必要进行防卫式编程,以保证不可变性,通常这需要通过防卫式拷贝予以实现。防卫式拷贝在参数有效性之前执行,并通常不使用Clon方法实现。

Item 25:仔细设计方法签名

1.        仔细的选择方法名

2.        不要在提供便利方法上走过头

3.        避免长参数列表

4.        对于参数类型,青睐接口而非类

5.        谨慎使用功能对象

Item 26:谨慎的使用重载

1.        编译期静态决定调用那个重载方法

2.        重载方法的选择是静态的、重写方法的选择是动态的

3.        要避免重载方法的混淆

4.        不要导出两个参数数相同的重载方法

Item 27:返回零长数组而非NULL

Item 28:对所有的导出API注释

1.        方法的注释应该描述其功能及与客户的契约

2.        以一致的规范编写注释

第七章 通用程序设计

Item 29最小化局部变量的作用域

最小化局部变量作用域,增强了代码的可读性和可维护性、同时降低了犯错的几率。

1.        当局部变量第一次使用时,声明局部变量

2.        几乎每个局部变量的声明都应同时初始化

3.        循环变量最好置于FOR循环之中

4.        保持方法的短小精悍

Item 30:了解并应用库

 

1.        应用库你就利用了专家知识和前人的经验

2.        应用库也节约了开发时间

3.        易于掌握新添特性

4.        降低了学习成本

5.        不用重新发明轮子

 

Item 31:如果要求准确的结果则避免用FLOATDOUBLE

       FLOATDOUBLE型变量不适用于准确十进制计算,尤其在财务计算中,这是做好用BigDicimal等类型。

 

Item 32:其他类型更合适时避免用String类型

1.        String不适于充当其他值类型的替代物

2.        String不适于充当枚举类型的替代物

3.        String不适于充当聚合类型的替代物

4.        String不适于充当功能的替代物

5.        更好的类型存在或能构造时,不要使用String

 

Item 33:明了字符串连接的性能

1.        字符串连接符缺乏性能表现

2.        StringBuffer

 

Item 34:使用接口引用对象

1.        合适的接口存在,则优先使用接口

2.        应用接口,程序更为灵活

3.        不存在接口类型才使用类引用对象

Item 35:相较于反射青睐接口

反射的缺点:

1.        失去了编译期类型检查

2.        不容易书写、繁杂

3.        性能损耗

作为规则,运行时对象不应当以反射的方式访问。总之,反射一般仅用于初始化对象,除非对象完全不可知(接口类型都不存在)。

 

Item 36:谨慎使用JAVA本地方法

 

 

 

Item 37:谨慎地进行性能调优

 

 

 

Item 38:坚持一贯的命名惯例

 

 

 

 

 

第八章 异常

Item 39:仅当异常情况下使用异常

       滥用异常进行程序流程控制的缺点:

1.        异常处理的代价高昂

2.        JVM优化  

3.        用异常进行边界检查没必要

4.        异常不应当用于正常的控制流

5.        设计良好的API不应当让用户处理正常控制异常

Item 40:对可恢复状况使用受检异常、对程序错误使用运行时异常

       JAVA中包含三种异常:受检异常、运行时异常、错误。

       异常的使用时机:

1.        当调用者被期望从异常状况中恢复时使用受检异常

2.        使用运行时异常指明程序错误

3.        所有的非受检异常都应该继承自RuntimeException

Item 41:避免不必要的受检异常

       受检异常往往容易加重使用负担,强迫处理异常状况。仅当下列两种情况同时成立时,才使用受检异常:

1.        异常状况不能通过API的适当使用避免

2.        开发人员希望进行额外的处理

Item 42:青睐标准异常

JAVA中提供了一系列的标准非受检异常,开发人员可以在适当的时机予以重用。重用标准异常的好处:

1.        API学习成本更低

2.        程序更易阅读

3.        更少的类、更快的类加载

常用的标准异常:

1.        非法参数异常

2.        非法状态异常

3.        空指针异常

4.        下标越界异常

5.        同步修改异常

6.        不支持操作异常

Item 43:抛出与抽象级适应的异常

       JAVA中的异常链在进行异常传播时,通常对应着异常抽象级别的降低。高层应该捕获底层异常,抛出解释高层抽象的异常。

 

Item 44:注释方法抛出的所有异常

1.        总是声明受检异常,并通过@Throws精确注释

2.        使用@throws注释非受检异常,但不包含在方法声明中

3.        一个异常因同一原因在类的多个方法出现,则最好在类级别注释该异常

Item 45:在详细信息中包含失败捕获信息

       JAVA中未捕获的异常将打出异常信息,该信息来自异常的toString方法。失败捕获信息中应该包含对异常有贡献的参数的值。

 

Item 46:争取失败原子性

       失败的方法调用应该将对象的状态恢复到调用之前——失败原子。达到失败原子性的途径:

1.        不可变对象

2.        调用前参数校验

3.        排序计算,将修改置于发生失败的计算之后

4.        工作于对象的临时拷贝

Item 47: 不要忽略异常

       通常,开发人员会通过一个空的catch快忽略异常,然而这通常并非所要的处理方式。这种方式违背了异常的目的——要求进行适当处理。至少,catch代码快应该包含为什么忽略异常处理的合理解释。

 

第九章 线程

Item 48:共享可变数据的同步访问

       线程神话:基于性能的考虑,应当避免使用原子数据的读写同步。同步在线程间通信和排除可变性一样是必修的。总之,只要多个线程共享可变数据,读写数据的线程就应该进行加锁。

Item 49:避免越界的同步

1.        为避免死锁,不要在同步方法(代码快)内将控制转到客户端。

2.        作为规则,尽量保持同步代码的短小。

Item 50:禁止在loop循环外调用wait方法

       对象的wait方法用来让线程等待适当的条件,Wait方法的标准使用方法是:在被锁定对象的同步代码中调用。Wait方法的使用方式:总是在循环中调用wait方法,以等待特定条件成立。

 

Item 51:不要依赖于线程调度

1.        依赖线程调度的程序更没有可移植性

2.        线程优先级是JAVA平台中最不具备移植性的特性

3.        Thread.yield方法仅用于提高测试时的并发量

Item 52: 注释线程安全性

       类应该清楚的表明其线程安全性的原因:

1.        同步代码是实现细节,并非导出API的一部分

2.        为保证安全的多线程使用,类必须在规格层面指明其支持的线程安全性

Item 53:避免线程组

1.        线程组没有提供任何的安全功能

2.        线程组在线程安全方面很弱

3.        线程组基本上是多余的

第十章 序列化(略)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(JavaSE)