【effective Java读书笔记】方法(三)
前言:
《第42条到第43条》方法篇的完结篇。(考虑到文档注释作为一个单章,所以以后再写)
正文:
一、慎用可变参数:
本来这章节没什么好说的,只是因为可变参数用的比较少,顺便记录一下可变参数的使用。
1、可变参数的使用
public class VariableTest {
static int sum(int... args){
int sum = 0;
for (int arg:args) {
sum+=arg;
}
return sum;
}
public static void main(String[] args) {
System.out.println(sum(1,2,3));
System.out.println("----------------");
System.out.println(sum());
}
}
执行结果:
6
----------------
0
2、可变参数1个或多个的写法:
先看一个例子:
public class VariableTest2 {
static int min(int... args){
int min = args[0];
for (int i = 0; i < args.length; i++) {
if (args[i]<min) {
min=args[i];
}
}
return min;
}
public static void main(String[] args) {
System.out.println(min(1,2,3));
System.out.println("----------------");
System.out.println(min());
}
}
执行结果:
1
----------------
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at com.function3.VariableTest2.min(VariableTest2.java:5)
at com.function3.VariableTest2.main(VariableTest2.java:17)
当然,如果args[]都没传入,那么arg[0]肯定越界;改进方案一:说明:抛出明确的异常。
public class VariableTest2 {
static int min(int... args){
if (args.length == 0) {
throw new IllegalArgumentException("Too few arguments");
}
int min = args[0];
for (int i = 0; i < args.length; i++) {
if (args[i]<min) {
min=args[i];
}
}
return min;
}
public static void main(String[] args) {
System.out.println(min(1,2,3));
System.out.println("----------------");
System.out.println(min());
}
}
执行结果:
1Exception in thread "main"
----------------
java.lang.IllegalArgumentException: Too few arguments
at com.function3.VariableTest2.min(VariableTest2.java:6)
at com.function3.VariableTest2.main(VariableTest2.java:20)
改进方案二:说明:由原本0个或多个参数,变成1个或多个参数。
public class VariableTest3 {
static int min(int firstArg,int... args){
int min = firstArg;
for (int arg:args) {
if (arg<min) {
min=arg;
}
}
return min;
}
public static void main(String[] args) {
System.out.println(min(1,2,3));
System.out.println("----------------");
System.out.println(min(1,4));
}
}
不得不说这是一个优秀的方案,从编译的角度上避免了错误。因为没法只传递一个参数,编译器会约束,一定至少要有一个参数firstArg。
3、书中提及jdk改进的一个小问题:
jdk1.4及之前Arrays.aslist()通常是用来打印数组的元素,类似这个用法:
int[] digits ={3,1,2,3,4,5,7,6};
System.out.println(Arrays.asList(digits));
当时应该是能打印出这个结果的:(那个时代我没有经历过)
[3, 1, 2, 3, 4, 5, 7, 6]
然而,当1.5之后,这个方法被改写了:改写成一个可变参数。看看这个“新时代”的demo:public class VariableTest4 {
public static void main(String[] args) {
System.out.println(Arrays.asList("to","too","tooo"));
System.out.println("----------------");
List<String> ho = Arrays.asList("to","too","tooo");
System.out.println(ho);
System.out.println("----------------");
int[] digits ={3,1,2,3,4,5,7,6};
System.out.println(Arrays.asList(digits));
System.out.println("----------------");
System.out.println(Arrays.toString(digits));
}
}
执行结果如下:
[to, too, tooo]
----------------
[to, too, tooo]
----------------
[[I@7852e922]
----------------
[3, 1, 2, 3, 4, 5, 7, 6]
大概从第一个输出,第二个输出能看出来,这个是个可变参数的输出。第三个输出,输出了这个对象的引用的值;第四个输出,不是通过Arrays.asList而是另一个方法Arrays.toString,成功输出了想要的效果。4、可变参数的性能损失:
书中提及的是每一次使用可变参数,都会使用一次数组的分配和初始化;
其实很好理解:从可变参数的使用来看,每次都是通过数组args[]去取出参数。则很容易想到每次传入多个参数,都会生成一个数组;生成一个数组必然有新建数组对象的开销,数组的初始化开销。是否完美解释!
所以,书中提及了一个比较优秀的方案:就是改进方案2中的代码的变种。重载多个参数。将参数较少的常用的代码,通过固定长度的参数重载出来。大概代码如下:
public void foo(){}
public void foo(int a1){}
public void foo(int a1,int a2){}
public void foo(int a1,int a2,int a3){}
public void foo(int a1,int a2,int a3,int... rest){}
二、返回零长度的数组或者集合,而不是null;
这个貌似没什么好说的,就是提示返回一个长度为0的数组或者集合。当然,唯一值得一提的做法就是每当需要返回空集合时都返回同一个不可变的空集合。例如ArrayList源码中:
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
当ArrayList长度为0的时候返回这个空集合:
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
猜想应该是为了优化性能才这么做的吧。毕竟如果多次返回都是空的时候需要新建对象分配空间,初始化的话,不如直接指向一个常量来得快。over~