JAVA建议

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

静态工厂方法的好处

1.与构造函数不同,静态工厂方法具有名字

2.与构造函数不同,它们每次被调用的时候,不要求非得创建一个新的对象

3.与构造函数不同,它可以返回一个原返回类型的子类型的对象

缺点

主要:类如果不含公有的或者受保护的构造函数,就不能被子类化

2.它们与其他的静态方法没有任何区别

2.使用私有构造函数强化singleton属性

3.通过私有构造函数强化不可实例化的能力

4.避免创建重复的对象

5.消除过期的对象引用

6.避免使用终结函数

7.在改写equals的时候请遵守通用约定

8.在改写equals时总是要改写hashCods

9.总是要改写toString

10.谨慎改写clone

11.考虑实现Comparable接口

12.使类和成员的可访问能力最小化

尽可能使每一个类或成员不被外界访问

13.支持非可变性

1.不要提供任何会修改对象的方法(也称为mutator)
2.保证没有可被子类改写的方法
3.使所有的域都是final的
4.使所有域都是成为私有的
5.保证对于任何可变组件的互斥访问

14.复合优先于继承

15.要么专门为继承而设计,并给出文档说明,要么禁止继承

一个类必须通过某种形式提供适合的钩子,以便能够进入到它的内部工作流程中,这样的形式可以是精心选择的受保护(protected)方法

构造函数一定不能调用可被改写的方法

无论是clone还是readObject,都不能调用一个可改写的方法,不管是直接的方式,还是间接的方式

为了继承设计一个类,要求对这个类有一些实质性的限制

对于那些并非为了安全地进行子类化而设计和编写文档类,禁止子类化

禁止子类化的两种方法

1.直接把这个类声明为final的

2.把所有的构造函数变成私有的,或者包级私有的,并且增加一些公有的静态工厂来替代构造函数的位置

16接口优于抽象类

接口和抽象类最大的区别是:抽象类允许包含某些方法的实现,但是接口是不允许的

已有的类可以很容易被更新,已实现新的接口
接口是定义mixin(混合类型)的理想选择
接口使得我们可以构造出非层次结构的类型框架
接口使得安全地增强一个类的功能成为可能

你可以把接口和抽象类的优点结合起来,对于你期望导出的每一个重要接口,都提供一个抽象的骨架实现(skeletal implementation)类

抽象类的演化比接口的演化要容易得多

17.接口只是被用于定义类型

常量接口模式是对接口的不良使用

18.优先考虑静态成员类

如果你声明的成员类不要求访问外围实例,那么请记住把static修饰符放到成员类的声明中

19.用类代替结构

20.用类层次来代替联合

21.用类来代替enum结构

22.用类和接口来代替函数指针

23.检查参数的有效性

24.需要时使用保护性拷贝

假设类的客户会尽一切手段来破坏这个类的约束条件,在这样的前提下,你必须保护性地设计程序
对于构造函数的每个可变参数进行保护性拷贝(defensive copy)是必要的
保护性拷贝动作是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是原始的对象
对于“参数类型可以被不可信方子类化”的情形,请不要使用clone方法进行参数的保护性拷贝

25.谨慎设计方法的原型

谨慎选择方法的名字
不要过于追求提供便利的方法
避免长长的参数列表
对于参数类型,优先使用接口而不是类
谨慎地使用函数对象

26.谨慎地使用重载

对于重载该方法(overloaded method)的选择是静态的,而对于被改写的方法(overridden method)的选择是动态的
避免方法重载机制的混淆用法
一个安全而保守的策略是,永远不要导出两个具有相同参数数目的重载方法

27.返回零长度的数组而不是null

没有理由从一个取数组值(array-valued)的方法中返回null,而不是返回一个零长度数组

28.为所有导出的API元素编写文档注释

为了正确地编写API文档,你必须在每一个被导出的类,接口,构造函数,方法和域声明之前增加一个文档注释
每一个方法的文档注释应该简洁地描述出它和客户之间的约定

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

使一个局部变量的作用域最小化,最有力的技术是在第一次使用它的地方声明
几乎每一个局部变量的声明都应该包含一个初始化表达式

30.了解和使用库

通过使用标准库,你可以充分利用这些编写标准库的专家的知识,以及在你之前其他人的使用经验
在每一个主要的发行版本中,都会有许多新的特性被加入到库中,所以与这些库保持同步是值得的

31.如果要求精确的答案,请避免使用float和double

32.如果其他类型更合适,则尽量避免使用字符串

字符串不适合代替其他的值类型
字符串不适合代替枚举类型
字符串不适合代替聚集类型
字符串也不适合代替能力表

33.了解字符串连接的性能

为连接n个字符串而重复地使用字符串连接操作符,要求n的平方级的时间
为了获得可接受的性能,请使用StringBuffer替代String

34.通过接口引用对象

如果你养成了使用接口作为类型的习惯,那么你的程序将会更加灵活
如果没有合适的接口存在的话,那么,用类而不是接口来引用一个对象,是完全合适的

35.接口优先于映像机制

映像机制的代价
损失了编译时类型检查的好处
要求执行映像访问的代码非常笨拙和冗长
性能损失
通常,普通应用在运行时刻不应该以映像方式访问对象
如果只是在很有限的情况下使用映像机制,那么虽然也会付出少许代价,但你可以获得许多好处

36.谨慎地使用本地方法

37.谨慎地进行优化

努力避免那些限制性能的设计决定
考虑你的API设计决定的性能后果
为了获得好的性能而对API进行曲改,这是一个非常不好的想法
在每次试图做优化之前和之后,请对性能进行测量

39.只针对不正常的条件才使用异常

38.遵守普遍接受的命名惯例

异常只应该被同于不正常的条件,它们永远不应该被用于正常的控制流
一个设计良好的API不应该强迫它的客户为了正常的控制流而使用异常

40.对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常

如果期望调用者能够恢复,那么,对于这样的条件应该使用被检查的异常
用运行时异常来指明程序错误
你所实现的所有的未被检查的抛出结构都应该时RuntimeException的子类(直接的或者间接的)

41.避免不必要地使用被检查的异常

42.尽量使用标准的异常

43.抛出的异常要适合于相应的抽象

高层的实现应该捕获底层的异常,同时抛出一个可以按照高层抽象进行解释的异常
尽管异常转译比不加选择地传递低层异常的做法有所改进,但是它也不能被滥用

44.每个方法抛出的异常都要有文档

总是要单独地声明被检查的异常,并且利用Javadoc的@throws标记,标准地记录下每个异常被抛出的条件
使用Javadoc的@throws标签记录下一个方法可能会抛出的每个未被检查的异常,但是不要使用throws关键字将未被检查的异常包含在方法的声明中
如果一个类中的许多方法出于同样的原因而抛出同一个异常,那么在该类的文档注释中对这个异常做文档,而不是为每个方法单独做文档,这是可以接受的

45.在细节消息中包含失败-捕获消息

为了捕获失败,一个异常的字符串表示应该包含所有“对该异常有贡献”的参数和域的值

46.努力使失败保持原子性

一般而言,一个失败的方法调用应该使对象保持“它在被调用之前的状态”

47.不要忽略异常

空的catch块会使异常达不到应有的目的
至少catch块也应该包含一条说明,用来解释为什么忽略掉这个异常是适合适的

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

为了提高性能,在读或写原子数据的时候,你应该避免使用同步。这个建议是非常危险而错误的
为了在线程之间可靠地通信,以及为了互斥访问,同步是需要的
一般情况下,双重检查模式并不能正确地工作
简而言之,无论何时当多个线程共享可变数据的时候,每个读或者写数据的线程必须获得一把锁

49.避免过多的同步

为了避免死锁的危险,在一个被同步的方法或者代码块中,永远不要放弃对客户的控制

50.永远不要在循环的外面调用wait

总是使用wait循环模式来调用wait方法

51.不要依赖于线程调度器

任何依赖于线程调度器而达到正确性或性能要求的程序,很有可能是不可移植的
线程优先级是Java平台上最不可移植的特征了
对于大多数程序员来说,Thread.yield的惟一用途是在测试期间人为地增加一个程序的并发性

52.线程安全性的文档化

在一个方法的声明中出现synchronization修饰符,这是一个实现细节,并不是实现细节,并不是导出的API的一部分
一个类为了可破多个线程安全地使用,必须在文档中清楚地说明它所支持的线程安全性级别
安全级别

非可变的

线程安全的

有条件的线程安全

线程兼容的

线程对立的

53.避免使用线程组

线程组基本上已经过时了

54.谨慎地实现Serialization

因为实现Serialization而付出的最大代价是,一旦一个类被发布,则“改变这个类的实现”的灵活性将大大降低
实现Serialization的第二个代价是,它增加了错误(bug)和安全漏洞的可能性
实现Serialization的第三个代价是,随着一个类的新版本的发行,相关的测试负担增加了
实现Serialization接口不是一个很轻松就可以做出的决定
为了继承而设计的类应该很少实现Serialization,接口也应该很少会扩展它
对于为继承而设计的不可序列化的类,你应该考虑提供一个无参数的构造函数

55.考虑使用自定义的序列化形式

若没有认真考虑默认序列化形式是否合适,则不要接受这种形式
如果一个对象的物理表示等同于它的逻辑内容,则默认的序列化形式可能是合适的
即使你确定了默认序列化形式是合适的,通常你仍然要提供一个readObject方法以保证约束关系和安全性
当一个对象的物理表示与它的逻辑数据内容有实质性的区别时,使用默认序列化形式有4个缺点:

1.它使这个类的导出API永远地束缚在该类的内部表示上

2.它要消耗过多的空间

3.它要消耗过多的时间

4.它会引起栈溢出

transient修饰符表明这个实例域将从一个类的默认序列化形式中省略掉
如果所有的实例域都是transient的,那么,从技术角度而言,省去调用defaultWriteObject和defaultReadObject也是允许的,但是不推荐这么做
在决定将一个域做成非transient之前,请一定要确信它的值将是该对象逻辑状态的一部分
不管你选择了那种序列化形式,你都要为自己编写的每个可序列化的类声明一个显式的序列版本UID(serial version UID)

56.保护性地编写readObject方法

当一个对象被反序列化的时候,对于客户不应该拥有的对象引用,如果哪个域包含了这样的对象引用,则必须要做保护性拷贝,这是非常重要的

57.必要时提供一个readResolve方法

readResolve方法不仅仅对于singleton对象是必要的,而且对于所有其他的实例受控的(instance-controlled)类也是必需的
readResolve方法的第二个用法是,就像在第56条中建议的那样,作为保护性的readObject方法的一种保守的替代选择
尽管保护性readResolve模式并没有被广泛使用,但是它值得认真考虑
readResolve方法的可访问性(accessibility)是非常重要的
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2025 Immanuel
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

微信