eff java_Effactive JAVA (II) Note

规则一、用静态方法代替类构造函数。

之前也用过,不过主要是在写单例模式的时候用过,看完觉得使用的场景还比较多,比如1、类的实例化方法可以有具体的名字,方便使用;2、也可以返回类的子类对象。缺点之前还未想过,看完貌似有一点比较注意,类如果没有共有的或者受保护的构造函数,则该类将无法被继承。

规则二、使用私有构造函数强化singleton属性

之前一直是这样用的,具体的singleton模式,讲的很多,不在此处详解了。但是里面有讲到所谓的序列化和反序列化的东西,貌似一般的singleton模式里面很少提及,因为很少有序列化的singleton模式例子,在此点一下。

规则三、通过私有化构造函数强化不可实例化的能力

常见的应用场景,如工具类,貌似之前很少对构造函数下手,仔细想想,还是需要进行私有化构造函数。

规则四、避免创建重复的对象

如果一个函数被频繁的调用,则可以考虑将此函数中的变量放到函数之外,这样可以减少创建对象带来的性能损失。当然,也不要觉得创建对象代价很昂贵,就现在的JVM来看,一般不需要特意去维护对象池,除非是有必要,比如数据库连接池,还是很有必要的,具体还有哪些,还需要继续摸索,可以进行性能上的测试和比对。

规则五、消除过期的对象引用

GC主要是回收没有被引用的对象,如果存在对象被引用,则GC是不会去回收的,除非显示的通知GC,一般来说如果类自己管理它的内存,则需要警惕内存泄漏的问题。

规则六、避免使用终结函数

平常自己写的代码中,基本从未用到终结函数,不过看了这个规则之后,发现,有些地方还是需要的,比如作为安全网(还未明白啥意思),或者终止非关键的本地资源。一旦使用,则将考虑使用终结函数守卫者,保证终结函数被正确的执行。如果可以的化,尽量显示调用终止函数。

public class Foo {

private final Object finalizerGuardian = new Object(){

@Override

protected void finalize() throws Throwable {

//finalize outer Foo Object

super.finalize();

}

};

}

规则七、在改写equals的时候请遵守通用约定

第一次接触这个是使用了Hibernate,而且使用了Set.contains()函数所必须要提供的。当时的逻辑就是通过判断ID是否相等。

通用约定,对于学过数学的人来说还是比较简单:

1、自反性:x.equals(x)=true

2、对称性:x.equals(y)=true => y.equals(x)=true

3、传递性:x.equals(y)=true && y.equals(z)=true => x.equals(z)=true

4、一致性:x.equals(y) is always true or false

5、非空性:x.equals(null) = false,equals方法中最好先进行instanceof检查

规则八、改下equals方法的时总是要改写hashCode方法

没什么好说的,如果用到了hashMap等之类的,必须要重写,比较好的重写方法就是做移位操作?

规则九、总是要改写toString方法

toString的通用约定指出,被返回的字符串应该是一个简洁的,但信息丰富,且易于阅读的表达形式。比如可以将toString变成json格式的输出。

规则十、谨慎的修改clone

貌似还从未接触过这个函数。貌似提供一个比自己复制对象及其内容更快的实现。

规则十一、考虑实现Comparable接口

如果实现此接口,那么使用Array.sort将非常简单

规则十二、使类和成员的可访问能力最小化

使得程序间耦合度降低,模块化程序,使得可以并行开发,提高开发效率,这个属于设计原则的问题。

规则十三、支持非可变性

即实例不可以修改。如String、BigInteger等,和通常的POJO类来比较 就是提供全部成员变量的一个构造函数,然后私有化所有的成员变量和成员函数。

规则十四、复合优先于继承

当子类继承超类时,超类的API会显示给子类,如果超类API发生变化,子类又恰好使用了超类的API,如果超类的API发生变化,则子类也将进行修改,造成子类非常脆弱。比较好的做法是,进行复合,也就是所谓的装饰模式,这样就可以隐藏超类API的缺陷,拥抱变化。只有子类真的是超类的“子类型”的时候,继承才是合适的。

规则十五、要么专为继承而设计,并给出文档说明,要么禁止继承

该类的文档必须精确的描述改写每一个方法所带来的影响。好的API文档应该描述一个方法做了什么工作,而并非描述它是如何做到的。一个例子:超类的构造函数调用了超类中的一个API,如果子类重写了这个API,将导致程序失败。禁止子类化的方案,1、类声明为final,2、把所有的构造函数变成私有或者包级私有。

规则十六、接口优先于抽象类

因为java中只允许单继承,可以实现多个接口,所以接口使得安全地增强一个类的功能称为可能。一般来说,可以将接口和抽象类进行组合,对于每个期望导出的重要接口,都提供一个抽象的骨架实现。

规则十七、接口只是被用于定义类型

不用定义常量接口,比较好的方法就是定义常量工具类,私有化构造函数。

规则十八、优先考虑静态成员类

嵌套类是指被定义在另一个类的内部类,如果这个嵌套类将来可能用于其他的某个环境,那么就应该是顶层类。嵌套类有四种:静态成员类、非静态成员类、匿名类和局部类。除了第一种,其他都被称为内部类。静态成员类就是加了static关键字,如果声明的成员类不要求访问外围实例,那么请使用静态成员类。匿名类用过很多了,很多回调函数里面就应用了匿名类作为最后一个参数,需要注意的一点就是,如果匿名类篇幅很长,设计的时候就考虑独立出来,常见用法如过程对象(Thread等)、静态工厂方法内部、复杂的类型安全枚举类型、通常只实现接口或者超类中的方法。局部类是最少使用的类,在任何可以声明局部变量的地方,都可以声明局部类。总结:如果嵌套类在单个方法之外仍然可见,或者太长,不适合放在一个方法内部,那么就应该使用成员类;如果成员类的每个实例都需要一个指向其外围实例的引用,则把成员类做成非静态的,否则就做成静态的。假设一个成员类属于一个方法的内部,如果你只需要在一个地方创建它的实例,并且已经有了一个预先存在的类型可以说明这个类型可以说明这个类的特征,则把它做成匿名类;否则就做成局部类。

规则十九、用类代替结构

规则二十、用类层次来代替联合

类层次提供了类型安全、代码简洁明了、容易扩展、反映出类型的本质。

规则二十一、用类代替enum结构

定义一个类来代表枚举类型的单个元素,并且不提供任何公有的构造函数。相反,提供共有的静态final域,使枚举类型中的每一个常量都对应一个域。

规则二十二、用类和接口代替函数指针

函数指针更像是策略模式的实现。

规则二十三、检查参数的有效性

没什么好说的,只是不要过度检查有效性。

规则二十四、需要时使用保护性拷贝

为了保护对象实例的内部信息避免受到攻击,对于构造函数的每个可变参数进行保护性拷贝是必要的。注意:保护性拷贝动作是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是原始的对象。如果类提供了内部域的访问,那么要使得它返回可变内部域的保护性拷贝。

规则二十五、谨慎设计方法的原型

谨慎选择方法的名字;不要过于追求提供便利的方法;避免长长的参数列表(考虑将一个方法分解成多个方法;创建辅助类);对于参数类型,优先使用接口而不是类;谨慎的使用函数对象(常见的就是使用匿名类)。

规则二十六、谨慎的使用重载

重载的方法选择是在编译时的参数类型进行,即编译的时候参数类型决定调用的重载方法,而改写的方法选择是在执行时的参数类型,所以谨慎使用重载。一个安全而保守的策略是,永远不要导出两个具有相同参数数目的重载方法。

规则二十七、返回零长度的数组而不是null

规则二十八、为所有导出的API元素编写文档注释

注释该写清楚做了什么,而不是写如何做。同时应该列举所有的前提条件和后置条件,描述其副作用(如开启了一个后台线程)、和该类的线程安全性。

规则二十九、将局部变量的作用域最小化

尽量在其第一次使用它的地方声明,不然等到使用该变量的时候,可能已经不记得前面该变量声明时的类型和初始值了,也不确定其当前值。同时可以变量剪切复制带来的粗心的错误。

规则三十、了解和使用库

每个程序员应该熟悉java.lang java.util java.io中的内容,其他库可以根据需要的时候再学习。

规则三十一、如果要求精确的答案,避免使用float和double。

货币计算请使用BigDecimal、int或者long类型。

规则三十二、如果其他类型更适合,请避免使用字符串。

个人也很排斥这种做法,因为如果使用字符串,则只能使用字符串提供的一些方法和工具,有时候会显的很笨拙,但是偶尔也会使用字符串,在一些特殊的地方。

规则三十三、了解字符串连接的性能

为连接n个字符串而重复的使用字符串连接操作符,要求n的平方级的时间,因为字符串是非可变的,当两个字符串被连接的时候,他们的内容都要拷贝!为了获得可接受的性能,使用StringBuilder代替String。

规则三十四、通过接口引用对象

可以保持程序的灵活性,有时候,只需要修改一行代码,就可以改变整个程序。如果需要额外的实例的方法,则需要通过类应用对象。

规则三十五、接口优先于反射

性能、可读性等方面会有损失,但是对于一些特殊的用途来说,使用反射还是必须的。

规则三十六、谨慎的使用本地方法

本地方法的用途:1、提供访问与平台相关的设施的能力,2、提供访问老式代码库的能力,3、通过本地语言实现性能关键的部分。但是随着1.3发行版本的推出,使用本地方法来提高性能的做法已经不提倡了。而且使用本地方法不安全。

规则三十七、谨慎的进行优化

努力写好的程序而不是快的程序。应该在设计的时候考虑好的结构,性能的优化可以在完成程序之后,当然也不是说编写程序的时候忽略性能问题。

规则三十八、遵守普遍接受的命名习惯

不严格的讲,命名惯例分为两大类:字面的和语法的。

规则三十九、只针对不正常的条件才使用异常

不要依赖异常去帮你提高性能等。

规则四十、对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常

没什么好说的,编写的时候为了可用性,需要对异常进行合理的使用。

规则四十一、避免不必要的使用被检查的异常

规则四十二、尽量使用标准的异常

了解java提供的常见异常以及相关的含义。

规则四十三、抛出的异常要适合于相应的抽象

如果一个方法抛出的异常与它执行的任务没有明显的关联关系,这种情形会使人不知所措。高层的实现应该捕获低层的异常,同时抛出一个可以按照高层抽象进行解释的异常。

规则四十四、每个方法抛出的异常都要有文档

没什么好说的。

规则四十五、在细节消息中包含失败=捕获消息

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

规则四十六、努力使失败保持原子性

就是说一个失败的方法调用应该使对象保持“它被调用之前的状态”,具有这种属性的方法被称为具有失败原子性。这样就可以从异常中恢复过来。通常的途径是:1、设计一个非可变对象;2、在执行操作前检查参数的有效性;3、对计算过程调整顺序,使得任何可能会失败的计算部分都发生在对象状态被修改之前;4、编写一段恢复代码;5、在对象的一份临时拷贝上执行操作,当操作完成后再把临时拷贝中的结果复制个原来的对象。错误(相对异常)通常是不可恢复的。

规则四十七、不要忽略异常

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

规则四十八、对共享可变数据的同步访问

synchronized关键字。volatile修饰符可以保证任何一个线程在读取一个域的时候都将会看到最近刚刚被写入的值。

规则四十九、避免过多的同步

过多的同步可能会导致性能降低、死锁,甚至不确定的行为。需要对客户代码进行限制,如果客户代码也开启了线程,那么可能会出现死锁的现象。通常,在同步区域内应该做尽可能少的工作。

规则五十、永远不要在循环的外面调用wait

总是使用wait循环模式来调用wait方法。循环一般用于在等待的前后测试条件。

规则五十一、不要依赖于线程调度器

线程调度器会依赖具体的JVM实现,所以不要依赖于线程调度器。

规则五十二、线程安全性的文档化

一个类为了可被多个线程安全使用,必须在文档中清楚的说明它所支持的现场安全性级别。

规则五十三、避免使用线程组

除了线程、锁和监视器以外,线程系统还提供了一个基本的抽象,即线程组,但是它并未提供所提及的任何安全功能!

规则五十四、谨慎的实现Serializable

一旦一个类被发布,则"改变这个类的实现"的灵活性将大大降低。而且增加了错误(bug)和安全漏洞的可能性,范序列化是机制是一种语言之外的对象创建机制。随着一个类的新版本的发行,相关的测试负担增加了。

规则五十五、考虑使用自定义的序列化形式

若没有认真考虑默认序列化形式是否合适,则不要接受这种形式。除非自行设计的自定义序列化形式与默认的基本相同。transient修饰符表明这个实例域将从一个类的默认序列化形式中省略掉。

规则五十六、保护性的编写readObject方法

在调用defaultxx函数之后,需要进行域进行校验。

规则五十七、必要时提供一个readResolve方法

如果一个单例模式实现了序列化接口,那么它就不再是单例。除非编写正确的readResolve方法。对于一个正在范序列化的对象,如果它的类定义了readResolve方法,并且是正确的,那么反序列化之后,新创建对象上的readResolve方法就会被调用。同时readResolve方法可以作为保护性的readObject方法的一种保守的替代选择。

至此,该本书已经阅读完毕,但是很多东西依然有待实践去领悟,所谓温故而知新也。关于序列化的地方,貌似还从来未考虑过,确实应该去体会体会。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值