Effective Java : 方法
来源:互联网 发布:汕头有淘宝代运营 编辑:程序博客网 时间:2024/06/09 20:18
38.检查参数的有效性
不检查
如果不对参数进行检查,可能会出现如下错误:
- 处理过程中发生失败,产生令人费解的异常
- 正常返回,但是会计算出错误的结果
- 能正常返回,但是破坏了某个状态,在不确定的将来某个节点上引发错误(相当不好定位)
正确的做法
- 早 javadoc 的 @throws 标签文档中进行说明.(如果违反了参数限制将抛出异常)
- 在计算任务之前,就应该先检查他的参数
- 如果计算过程抛出异常,已改使用
异常转译
技术,
39.必要时进行保护性拷贝
简介
Java 是一门 安全的语言.对于 缓冲区溢出,数组越界,非法指针等内存错误都会自动免疫.\
在下列情况下,需要对类的健壮性做一些工作.
- 有人视图破坏这个类的约束条件时.
- 对API产生误解的程序员,所导致的不可预期的行为.
如下这个类: Period.java
保护性拷贝的重要性
可以通过如下方式破坏实例的内部信息.
Date start = new Date();Date end = new Date();Period period = new Period(start,end);end.setYear(78);//改变了end对象,造成内部错误
为了避免 受到这种攻击,可以对参数的每个可变参数进行保护性拷贝
,并且使用拷贝后的对象,如下:
public Period(Date start, Date end) { if (start.compareTo(end) > 0) throw new IllegalArgumentException(start + " after " + end); this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); //使用的是拷贝对象,这样的话,就不怕上面的改变了,因为不会改变内部对象. }
注意事项
- 在 38 条中,我们说要对
参数进行有效性检查
,检查也是发生在保护性拷贝之后的/ - 对于参数类型,可以
被不可信任方
子类化
的参数,不要使用clone
来实现保护性拷贝. - 采用新的访问方式,可以提高 类型的
不可变能力
public Date start(){ return new Date(start.getTime());}
- 如果有可能,应该使用不可变对象作为类的内部组件,这样就不用考虑
保护性拷贝
了
40.谨慎设计方法签名
设计原则:
- 谨慎的选择方法名称
- 不要过于追求提供便利的方法
- 避免过长的参数列表,可以有如下三种方式
- 把方法分解为多个方法,每个方法只需要参数列表的一个子集.
- 创建辅助类,用于保存参数的分组
- 采用
Builder
模式.
- 对于参数类型,优先考虑
接口
而不是类
- 对于
Boolean
,要优先使用两个元素
的`枚举类型,如下所示
public enum TemperatureScale{FAHRENHRIT,CELSIUS;}
41.慎用重载
示例
- 首先来看一个示例:
public class CollectionClassifier { public static String classify(Set<?> s) { return "Set"; } public static String classify(List<?> lst) { return "List"; } public static String classify(Collection<?> c) { return "Unknown Collection"; } public static void main(String[] args) { Collection<?>[] collections = { new HashSet<String>(), new ArrayList<BigInteger>(), new HashMap<String, String>().values() };//打印了三次 Unknown Collection for (Collection<?> c : collections) System.out.println(classify(c));//这里并不会按照我们期望的那样选择调用某一个 重载方法 }}
因为要 调用 哪个重载方法是在编译时确定的.虽然运行时是不同的.
- 再看另一个示例:
class Wine { String name() { return "wine"; }}class SparklingWine extends Wine { @Override String name() { return "sparkling wine"; }}class Champagne extends SparklingWine { @Override String name() { return "champagne"; }}public class Overriding { public static void main(String[] args) { Wine[] wines = { new Wine(), new SparklingWine(), new Champagne() }; //打印出了 wine,sparkling wine,champagne for (Wine wine : wines) System.out.println(wine.name()); }}
- 这里就不会出现上面的问题,因为对于
重载方法
的选择是静态的
,对于重写方法
的选择则是动态的
- 对于第一个示例中,我们通过如下方式修正:
public static String classify(Collection<?> c){ return c instanceof Set?"Set":c instanceof List?"List":"Unknown Collection";}
建议
- 永远不要到处两个
具有相同参数数目的
重载方法. - 如果是可变参数,保守策略是
永远不要重载它
- 对于每一对 重载方法,应
保持
方法中具有根本不同的类型
(像上面的List
和Collection
在根本上是由关系的 ) |如果两个类都不是对方的后代,则是不相关的
.
包装类引起的误会
比如如下示例:
Set<Integer> set = new TreeSet<Integer>();List<Integer> list = new ArrayList<Integer>(); for (int i = -3; i < 3; i++) { set.add(i); list.add(i); } for (int i = 0; i < 3; i++) { // 这里调用的是 Set#remove(E),//set.remove(Integer); set.remove(i); //而这里确是List#remove(i), list.remove(i); }
为了修正上面的错误,可以使用
list.remove(Integer.valueOf(i));
这里就是 自动装箱 引起的错误.
小结
简而言之,
- 对于多个具有相同参数数目的方法来说,应尽量避免重载
- 对于构造器,如果不能避免,应该保证传递同样的参数时,重载方法的行为一致.
42.慎用可变参数
错误用例
将以 数组当做final 参数
的现有方法,改造成 可变参数
来替代
List<String> list = Arrays.asList("to","too","two");
这么做会引发一些问题:
历史问题:
- 比如之前打印数组,最好使用下面的代码:
System.out.println(Arrays.asList(mArray));//这样是为了避免 调用到 Object.toString
- 这种做法只有在
引用类型
上才有效 ,比如,如下示例:
int[] digits = {1,2,3,5,4,6};System.out.println(Arrays.asList(digits));
这里会产生错误, 因为
asList
的方法声明为asList(Object[]);
可变参数
- 当可变参数引入后,就出现了最开始的用法了,这时候,如下用法
int[] digits = {1,2,3,5,4,6};System.out.println(Arrays.asList(digits));// digits 当做可变参数中的一个参数
这时候,会打印出
[[I@3e25a5
类似无意义的结果,
- 最新可用方法,如果使用下面的方法,则没有问题
int[] digits = {1,2,3,5,4,6};System.out.println(Arrays.toString(digits));
总结
- 可变参数的每次调用都会 进行一次数组的分配和初始化.避免性能损耗,可以使用如下方式,在大多数时候 , 调用
非可变参数
方法
public void foo(){}public void foo(int a){}public void foo(int a,int b){}public void foo(int a,int b,int ... args){}
EnumSet 就是采用这种机制.
- 不必改造具有final数组参数的每个方法.只有确定
它是在数量不定的值上执行
才使用可变参数.
43.返回零长度的数组或者集合,而不是null
返回null的示例
比如,如下示例:
private final List<Cheese> mCheeses = ...; public Cheese[] getCheeses(){ if (mCheeses.size() ==0) return null; }
针对上面的代码,客户端的处理
Cheese[] mCheeses = getCheeses(); if(mCheeses != null &&Arrays.asList(mCheeses).contains(Cheese.STILTON)){ System.out.println("Jolly goog,just the thing"); }
返回
null
,而不是集合的形式,几乎每次客户端都要判空处理
客户端如果忘记处理,则会引起严重的问题
返回集合
public static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0]; private final List<Cheese> mCheeses = ...; public Cheese[] getCheeses(){ // 如果集合是 空的 ,将返回零长度数组,如果达到足够容纳这个集合,就返回这个数组 return mCheeses.toArray(EMPTY_CHEESE_ARRAY); }
不仅服务端代码简单可扩展了,也方便了客户端
小结
简而言之, 返回类型 为数组或集合的方法 没理由 返回 null
,而不是返回一个 零长度的数组或者集合
.
44.为所有导出的API元素编写文档注释
原则
- 为了 正确的编写 API 文档,必须在每个被导出的类,接口,构造器,方法和域声明之前增加一个文档注释
- 方法的文档注释应该简洁的描述出,他和客户端之间的约定
- 插入代码标签,可以使用
<pre>{@code }</pre>
- 为了让
<
,>
,等字符出现在文档中,可以使用{@literal}
标签 - 为了避免混淆,同一个类或者接口中的两个成员或者构造器,不应该使用同样的概要或者描述.
- 为泛型或者方法编写文档时,确保说明所有的类型参数
- 为注解类型编写文档时,确保说明所有成员
0 0
- effective java(方法)
- Effective Java 方法笔记
- Effective Java : 方法
- Effective java笔记-方法
- effective java--方法
- Effective Java --静态工厂方法
- Effective Java——方法
- [Effective Java]第七章 方法
- Effective Java读书笔记六:方法
- Effective Java: 方法的设计
- Effective Java Item10-总是覆盖toString方法
- effective java笔记------重载equals方法
- Effective Java读书笔记之clone方法
- effective java 终结方法守卫者 demo
- Effective Java (7) 避免使用终结方法
- Effective Java (7) - 避免使用终结方法
- Effective Java——对象通用方法
- 【总结】Effective java经验之谈,通用方法
- jvm有两种执行方式
- MySQL
- 【蓝桥杯】试题集入门训练第二题
- python学习-3.一些常用模块用法
- LeetCode OJ-49.Group Anagrams
- Effective Java : 方法
- 抓包工具Fiddler的使用教程(五): 修改response的数据
- Android 滑动
- 77. Combinations 难度:medium
- JIT与JVM的三种执行模式:解释模式、编译模式、混合模式
- Fedora 25U盘启动盘的制作
- windows10安装Scrapy
- 我最近用Python写了一个算法,不需要写任何规则就能自动识别一个网页的内容
- 分布式文件系统FastDFS设计原理