第38条 检查参数的有效性
在 定义方法 与 构造器时,要进行“有效性检查”与“断言”。
assert 默认是不启用的,这个assert真的不知道是干嘛用的。查阅文档与网上资料说的是,在开发与测试时使用,发布时禁用。
第39条 必要时进行保护性拷贝
如果方法的调用者,客户端是恶意的,或别的程序员没有正确使用类,可能会破坏类的状态与数据。所以,必要时进行保护性拷贝。
保护性拷贝涉及到的时机是 对象构造与方法返回两个时机。
对象构造:保护性拷贝要在参数有效性检测前进行,并且有效性检查是针对拷贝后的对象,而不是传入的对象。
这样做可能有点违背直觉,但是可以避免“危险时期”可能出现的攻击。“危险时期”是指从检测参数有效性开始–>拷贝参数 到类的内部域这段时间,
如果传入的参数本来是正确的,也通过了参数有效性的检测,此时在另外一个线程中,修改了参数的值,新值违反了类的设计约束,此时已经绕过了参数检测,将有问题的参数传到了类的内部,从而产生问题。
“危险时期”:window of vulnerability。在计算机安全社区中,叫Time-of-Check or Time-of-Use 或者 TOCTOU攻击。
将可变域封装在类内部,注意类与外部交流的地方,将有效提升类的安全性与健壮性。
尽量使用不可变对象做为类的内部组件,这样就不必再为考虑使用保护性拷贝而操心。
tips:
设计一个方法,返回数组或列表时,考虑返回不可变list或数组的clone拷贝。
设计成员变量时,如果要用到Date类型,考虑使用long代替。因为Date类型是可变的,long是不可变的。
一般来说,包内间类的互相调用是相对安全的。
总结:如果类具有从客户端得到或者返回到客户端的可变组件,类就必须保护性地拷贝这些组件。如果拷贝的成本受到限制,并且类信任它的客户端不会不恰 当地修改组件,就可以在文档中指明客户端的职责是不得修改受到影响的组件,以此来代替保护性拷贝。
思考:重载 与 覆盖 有什么区别 ?
重载是一个类的多个相似方法,是扁平扩展。是编译时,由参数类型静态地决定执行哪个方法。
覆盖是多个类的同一个方法,是纵深重写。是运行时,根据对象实际类型决定执行哪个方法。
第40条 谨慎设计方法签名
- 谨慎地选择方法的名称:遵循标准的命名习惯,易于理解,并与同一个包中其他名称风格一致。选择大众认可的名称。
- 一个类中不要有过多的公开方法。方法太多会使类难以学习、难以使用、难以文档化、难以测试、难以维护。
- 对于参数类型,要优先使用接口而不是类。这样做方法适用性更广,对客户端更友好。
第41条 尽量避免使用重载overloading
选择要调用哪个重载方法,是在编译时做出决定的(静态判断)。选择的依据是参数的编译时类型。
选择要执行哪个覆盖方法,是在运行时做出决定的(动态判断)。选择的依据是被调用方法所在对象的运行时类型。
覆盖机制是规范,重载机制是例外。所以覆盖机制满足了人们对于方法调用行为的期望,而重载机制很容易让运行结果出人意料,让程序员感到疑惑。这些错误要等到运行时,发生了怪异的行为后,才能被发现,许多程序员在编写代码时,无法诊断出这样的错误。因此,尽量避免使用重载机制。
永远不要设计两个具有相同参数数目的重载方法。
退一步,如果一定要这样做,也要保证两个方法中的参数类型 根本不同。
最差的情况,两个重载方法,参数数目相同,参数类型存在父子关系时,也要保证其方法实现行为一致。
根本不同:不是父子关系或是实现接口。
数组类型 和除了Ojbect之外的类是根本不同的。
泛型和自动装箱 破坏了List接口。在调用List.remove(int) / (Integer)时要小心重载方法调用错误的问题。
确定选择哪个重载方法的规则是非常复杂的。这些规则在语言规范中占了33页篇幅,所以尽量别用重载了。
Java类库中,ObjectOutputStrem是正例。String.valueOf(char[]) String.valueOf(Object)是反例。
第42条 谨慎使用可变参数 varagrs
可变参数的工作原理:可变参数接受0个或多个指定类型的参数,根据传入的参数个数,创建一个同样大小的数组,将参数值设到数组中,最后将数组传给方法。
实践中,经常需要定义1个或多个的方法,以下代码可参考:
void methodOne(int firArg, int... args) //此种形式方便好用
如果需要定义一个方法带有不定数量的参数时,用可变参数非常有效。可变参数是Java1.5发行版加入的,printf和反射机制从可变参数得到了很多受益。
使用注意事项:
List<String> stringList = Arrays.asList("to", "too", "two");
System.out.println(stringList); //[to, too, two]
String[] strArr = {"he", "llo", "world"};
List<String> strings = Arrays.asList(strArr);
System.out.println(strings);//[he, llo, world]
int[] digits = {3, 1, 4, 1, 5};
List<int[]> ints = Arrays.asList(digits);
System.out.println(ints);//[[I@15db9742] 无意义的 非预想值
char[] chars = {'h', 'e', 'l', 'l', 'o'};
System.out.println(Arrays.asList(chars));//[[C@6d06d69c] 无意义的 非预想值
System.out.println(Arrays.toString(digits));//[3, 1, 4, 1, 5]
如上,使用基本类型数组打印时,会打印出非预想的字符串。要打印统一使用Arrays.toString()方法。
不必修改所有具有final数组参数的每个方法;只有确实是在数量不定的时候才用可变参数,不要滥用。
第43条 返回长度为0的数组或集合,而不是null
设计的方法 返回值是数组或是集合时,如果返回值是空的,请返回空数组或空集合,而不是null。
Apple[] EMPTY_APPLE_ARRAY = new Apple[0];
Collections.EMPTY_LIST;
Collections.EMPTY_MAP;
Collections.EMPTY_SET;
请考虑使用以上方法。这样客户端不需要再费力记着要判断取到的值是否是null。
返回null很有可能是从C程序设计语言中沿袭过来的,在C语言中,数组长度与实际数组是分开返回的。如果返回的数组长度为0,再分配一个数组就没有任何好处。
第44条 为所有公开的API元素编写文档注释
Javadoc
{@code} 以代码体呈现,并限制HTML标记和嵌套的Javadoc标签在代码片段中进行处理
{@literal} 不以代码体呈现,其他与code一样
每个文档注释的第一句话,是该注释所属元素的概要描述 summary description
方法的注释:动宾短语
Constructs an list …
Return the number of …
类、接口、域:名词短语
A task that can be …
An entity generated by …
@param
@return
@throws
线程安全性 与 是否可序列化 经常被忽略。

被折叠的 条评论
为什么被折叠?



