第二章:创建和销毁对象
第7条:避免使用终结方法
final、finally、finalize的区别:
final:修饰符,关键字,可以修饰成员、方法和类,如果类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。将变量或者方法声明为final,可以保证他们在使用中不被改变。
finally:一般是用于异常处理中,提供finally块来执行任何的清楚操作,try{} catch(){} finally{}。
finalize:用于垃圾回收,在java中,允许使用finalize()方法在垃圾收集器将对象从内存中清理出去之前做必要的清理工作。
第三章对所有对象都通用的方法
第8条:覆盖equals时请遵守通用约定
- 使用==操作符检查“参数是否为这个对象的引用”。
- 使用instanceof操作符检查“参数是否为正确的类型”。
- 把参数转换成正确的类型。
- 对于该类中的每个“关键”域,检查参数中的域是否与该对象中对应域相匹配。
- 当覆盖玩equals方法之后,应该问自己三个问题:它是否对称的、传递的、一致的?
warning:
- 覆盖equals时总要覆盖hashCode;
- 不要企图让equals方法过于智能;
- 不要将equals声明中的Object对象替换为其他的类型;
public boolean equals(Myclass myclass){
.....
}
第9条:覆盖equals时总要覆盖hashCode
每个覆盖了equals方法的类中,也必须改服hashCode方法;
相等的对象必须具有相等的散列码;
为不相等的对象产生不相等的散列码;
不要试图从散列码计算中排除一个对象的关键部分来提高性能;
第10条:始终要覆盖toString()
java.lang.Object提供了一个toString()方法,其结果为类名+@+hashCode十六位无符号表示方法。覆盖toString()方法,获取简洁的类toString()结果,便于程序中类输入结果的阅读。
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
第11条:谨慎地覆盖clone
如果你覆盖了非final类中的clone方法,则应该返回一个通过调用super.clone而得到的对象;
clone方法就是另一个构造器,你必须确保不会伤害到原始的对象,并确保正确地创建克隆对象中的约束条件;
第12条:考虑实现Complarable接口
Object中并没有声明compareTo()方法,compartTo()是Comparable接口的唯一方法,继承此接口的类,可以实现类内部排序;
public interface Comparable{
public int compareTo(T o);
}
其返回结果为int型,当对象小于、等于、大于指定对象T时,返回结果分别为负整数、零和正整数;
第4章:类与接口
第13条:使类和成员的可访问性最小化
尽可能地使每个类或者成员不被外界访问;
可访问性:
- 私有的private;
- 包级私有的package-private,声明该成员包内部的任何类都可以访问这个成员,不对成员做任何访问级别限制,则默认为包级私有;
- 受保护的protected,类及其子类可访问protected级别成员;
- 公开的public,在任何地方都可以访问该成员;
第14条:在公有类中使用访问方法而非公有域
公有类永远不要暴露可变的域。虽然这还是有问题,但是让公有类暴露不可变的域其危害比较小。
第15条:使可变性最小化
为了使类成为不可变,要遵循下面五条规则:
- 不要提供任何会修改对象状态的方法;
- 保证类不会被扩展;
- 使所有的域都是final的;
- 使所有的域都成为私有的;
- 确保对于任何可变组件的互斥访问;
第16条:复合优于继承
只有子类真正是超类的一部分时,才可以使用继承,即当子类只有包含超类的属性时才能使用继承;
继承的功能非常强大,但是由于继承违背了封装原则,因此也存在诸多问题。可以用复合和转发机制来代替继承,尤其是当存在适当的接口实现包装类时,包装类比子类更加健壮,功能也更强大;
第17条:要么为继承而设计,并提交文档说明,要么就禁止继承
第18条:接口优于抽象类
第19条:接口只用于定义类型
接口只应用于定义类型,而不应该被用来到处常量;
第20条:类层次优于标签类
标签类过于冗长,容易出错,并且效率低下;
第21条:用函数对象表示策略
第22条:优先考虑使用静态成员类
嵌套类:是指被定义在另一个类的内部的类;
嵌套类分类:静态成员类、非静态成员类、匿名类、局部类;
第6章 枚举和接口
第30条:用enum代替int常量
枚举类型:是指由一组固定的常量组合成合法值得类型,例如一年的春夏秋冬四个季节;
为了将数据与枚举常量关联起来,得声明实例域,并编写一个带有数据并将数据保存在域中的构造器。
第31条:用实例域代替序数
永远不要根据枚举的序数导出与它关联的值,而是将它保存在一个实例域中。
第32条:用EnumSet代替位域
用OR位运算将几个常量合并到一个集合中,称作位域;
EnumSet类可以有效地从单个枚举类型中提取多个枚举值;
eg:
public enum Style{
BOLD,
ITALIC,
UNDERLINE
}
Set<Style> styleSet = EnumSet.of(Style.BOLD,Style.ITALIC);
由于枚举类型要用在集合set中,所以没有理由用位域表示它。
第33条:用EnumMap代替序数索引
EnumMap继承AbstractMap实现Map接口,其实现Map所有操作的同时,具有获取键列表和值列表功能;
getKeyUniverse()返回键列表,values()返回值列表;
第34条:用接口模拟可伸缩的枚举
虽然无法编写可扩展的枚举类型,却可以通过编写接口以及实现该接口的基础枚举类型,对它进行模拟;
第35条:注解优先于命名模式
一般使用命名模式,表明有些程序需要通过某种工具或者框架进行特殊处理;
java 元注解:
1.@Target,
2.@Retention,
3.@Documented,
4.@Inherited
有了注解,就不需要再使用命名模式了;
第36条:坚持使用override注解
使用@Override注解,表示该方法覆盖超类中的方法;此注解保留源码级别,当java文件被编译后,@Override会被去除;使用此注解便于程序员对程序的理解,此外编译器会根据此注解对无意识的覆盖给予提示;
第37条:用标记接口定义类型
标记接口:是没有包含方法声明的接口,例如:Serializable就是一个标记接口,通过实现此接口,表明类可以被序列化。
第7章 方法
第38条:检查参数的有效性
对参数的任何限制都是件好事。再设计方法时,应该使它们尽可能的通用,并符合实际需要。
每当编写方式或者构造器的时候,应该考虑它的参数有哪些限制。应该把这些限制写在文档中,并且在这个方法体的开头处,通过显式的检查来实施这些限制。
第39条:必要时进行保护性拷贝
- 类的客户端会尽其所能来破坏这个类的约束条件,因此你必须保护性地设计程序。
- 保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是针对原始对象。
- 对于参数类型可以被不可信任方子类话的参数,请不要使用clone方法进行保护性拷贝。
保护性拷贝示例
public Date start(){
return new Date(start.getTime());
}
第40条:谨慎设计方法签名
- 谨慎的选择方法名称,应该始终遵循标准的命名习惯;
- 不要过于追求提供便利的方法;
- 避免过长的参数列表,可以通过辅助类缩短参数列表;
第41条:慎用重载
永远不要导出两个具有相同参数目的的重载方法
第42条:慎用可变参数
在定义参数数目不定的方法时,可变参数方法时不种很方便的方法,但是它们不应该过度滥用。如果使用不当,会产生混乱的结果。
第43条:返回零长度或者集合,而不是null
返回类型为数组或集合的方法没理由返回null,应该返回零长度的数组或集合;
第44条:为所有导出的API元素编写文档注释
第八章 通过程序设计
第45条:将局部变量作用域最小化
将局部变量最小化,可以增强代码的可读性和可维护性,并降低错的可能性。
- 要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的时候声明;
第46条:for-each循环优先于传统的for循环
java1.5发行版本中引入for-each循环,通过完全隐藏迭代器或者索引变量,便面了混乱和出错的可能。eg:
for(Element e : elements){
doSomeThing(e);
}
有三种情况无法使用for-each循环:
- 过滤--如果需要遍历集合或者数组,并删除指定的元素;
- 转换--如果需要遍历列表或者数组,并取代它不分或者全部的元素值;
- 平行迭代--如果需要并行地遍历多个集合,就需要显式地控制迭代器或者索引变量,以便所有迭代器或者索引量都可以得到同步前移;
第47条:了解和使用类库
java.io java.lang java.util java.util.concurrent类库是每个程序员都应该学习的;
多了解类库,多阅读类库实现原理及实现方式,类库的代码比你自己编写的代码更好一些,并随着时间推移而不断改进。
第48条:如果需要精确的答案,避免私用floate和double
使用BigDecimal代替floate和double
第49条:基本类型有限装箱基本类型
基本类型与基本装箱类型的区别:
- 基本类型只有值,装箱基本类型则具有它们的值不同的同一性;
- 基本类型只有功能完备的值,而每个装箱基本类型除了它对基本类型的所有功能值之外,还有个非功能值:null
- 基本类型比装箱基本类型更节约时间和空间
第50条:如果其他类型更合适,避免使用字符串
字符串不适合代替其他值类型
字符串不适合代替枚举类型
字符串不适合代替能力表
如果可以使用更加合适的数据类型,或者可以编写更加适当的数据类型,就应该避免使用字符串
第51条:当心字符串连接的性能
为了获取能够接受的性能,请使用StringBuilder代替String
第52条:通过接口引用对象
如果有合适的接口类型存在,那么对于参数、返回值、变量和域来说,就都应该使用接口类型进行声明;
第53条:接口优先于反射机制
核心反射机制:提供了“通过程序来访问关于已装载的类的信息”的能力;
第54条:谨慎地使用本地方法
本地方法是指用本地程序设计语言(c或者c++)来编写的特殊方法;
本地方法时不安全的,使用本地方法的应用程序不再能免受内存毁坏错误的影响。因为本地语言是平台相关的,使用本地方法的语言不再是可移植的。
第55条:谨慎地优化
要努力编写好的程序而不是快的程序
每次试图优化之前和之后,要对性能进行测量
第56条:要遵循普遍的命名规范
跟着项目走,写代码时重视命名规范
....未完待续