38.检查参数的有效性
非公有的方法方法通常用断言检查它们的参数
private static void sort(long a[],int offset,int length){
assert a!=null;
assert offset >=0 && offset <=a.length;
assert length >=0 && length <=a.length-offset;
}
断言如果失败,会抛出AssertionError39.必要时进行保护性拷贝
public final class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
if(start.compareTo(end)>0){
throw new IllegalArgumentException(start +"aftrt"+end);
}
this.start = start;
this.end = end;
}
public Date getStart() {
return start;
}
public Date getEnd() {
return end;
}
}
看似不可变,但date可变
Date start = new Date();
Date end = new Date();
Period period = new Period(start, end);
end.setYear(78);//这边可以被修改
构造器的每个可变参数进行保护性拷贝有必要的,并且使用备份对象作为Period实例的组件,不使用原始的对象public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if(this.start.compareTo(this.end)>0){
throw new IllegalArgumentException(this.start +"aftrt"+this.end);
}
}
获取值后修改
period.getEnd().setYear(79);
不使用原本对象
public Date getStart() {
return new Date(start.getTime());
}
public Date getEnd() {
return new Date(end.getTime());
}
只要有可能,都应该使用不可变对象作为对象内部组件,这样就不必再为保护性拷贝操心
如果类具有从客户端得到或者返回到客户端的可变组件,类就必须保护性地拷贝这些组件,如果类信任客户端,可以在文档中说明
40.谨慎设计方法签名
41.慎用重载
class Wine {
String name(){return "wine";}
}
class SparklingWine extends Wine{
@Override
String name(){return "sparkingwine";}
}
class Champagne extends Wine{
@Override
String name(){return "champagne";}
}
public static void main(String[] args) {
Wine[]wines = {new Wine(),new SparklingWine(),new Champagne()};
for (Wine wine : wines) {
System.out.println(wine.name());
}
}
winesparkingwine
champagne
调用被覆盖的方法时,对象的编译时类型不会影响到哪个方法被执行
重载的方法选择是静态的,覆写的方法的选择是动态的,选择覆写的方法的正确版本是在运行时进行的
public class CollectionClassifier {
public static String classify(Set<?> s){
return "set";
}
public static String classify(List<?> list){
return "list";
}
public static String classify(Collection<?> collection){
return "collection";
}
public static void main(String[] args) {
Collection<?>[] collections = {
new HashSet<>(),new ArrayList<>(),new HashMap<>().values()
};
for (Collection<?> collection : collections) {
System.out.println(classify(collection));
}
}
}
collectioncollection
collection
不要导出俩个具有相同参数数目的重载方法,如果方法使用的是可变参数,保守的策略是根本不要重载它
自动装箱出现后
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();
List<Integer> list = new ArrayList<>();
for(int i = -3;i<3;i++){
set.add(i);
list.add(i);
}
for(int i = 0;i<3;i++){
set.remove(i);
list.remove(i);
}
System.out.println(set+" "+list);
}
[-3, -2, -1] [-2, 0, 2]set,remove(i)调用选择重载方法remove(Object o),这里的E是集合的元素类型,将i从int自动装箱到Integer中,list.remove(i)调用重载方法remove(int i),他从列表的指定位置上去除元素,删除后重新排序,可以通过把list.remove的参数转换为Integer,迫使使用正确的重载方法,另一种方法是调用Integer.valueOf(i),将结果传给list.remove
list.remove((Integer)i);
list.remove(Integer.valueOf(i));
42.慎用可变参数可变参数方法接受0个或者多个指定类型的参数。可变参数机制通过先创建一个数组,数组的大小为在调用位置所传达的参数数量,然后将参数值传到数组,最后将数组传递给方法
public static void main(String[] args) {
int sumss = sum(1);
int sums = sum(1,2,2,4);
System.out.println(sumss);
System.out.println(sums);
}
static int sum(int ...args){
int sum = 0;
for (int i : args) {
sum += i;
}
return sum;
}
检查数组长度
static int min(int ...args){
if(args.length==0){
throw new IllegalArgumentException();
}
int min = args[0];
for(int i=1;i<args.length;i++){
if(args[i]<min){
min = args[i];
}
}
return min;
}
但上述代码有问题,客户端调用这个方法时,并没有传递参数进去,只能在运行时检查,必须在args中包含显式的有效性检查,除非将min初始化为Integer.MAX_VALUE,否则无法使用for-each循环(怀疑)。
下面是更好的方法
static int min(int firstArg,int...remainingArgs){
int min = firstArg;
for (int i : remainingArgs) {
if(i<min){
min = i;
}
}
return min;
}
43.返回零长度的数组或集合,而不是null44.为所有导出的API元素编写文档注释