重温经典《Thinking in java》第四版之第五章 初始化与清理(三十四)

简介: 重温经典《Thinking in java》第四版之第五章 初始化与清理(三十四)

“5.8 数组初始化”,该章节重点讲的是数组这一种数据类型的初始化方法。数组的定义就不去讲了,表示方法如下:

int[] a1;

也可以表示为:

int a1[];

上面定义了一个int型的数组。

首先要清楚一点就是int[] a1;在没有给a1数组做初始化的时候只是定义了一个数组引用,此时只有个引用地址,并没有给数组分配内存空间。如果需要为数组分配用于存储的内存空间,需要用以下方式进行初始化:

int[] a1 = { 1, 2, 3, 4, 5 };

那么,为什么还要在没有数组的时候定义一个数组引用呢?

int[] a2;

在java中可以将一个数组赋值给另一个数组,所以可以这样:

a2=a1;

其实真正做的只是复制了一个引用,就像下面演示的那样:

importstaticnet.mindview.util.Print.*; 
publicstaticvoidmain(String[] args) { 
int[] a1= { 1, 2, 3, 4, 5 }; 
int[] a2; 
a2=a1; 
for(inti=0; i<a2.length; i++) 
a2[i] =a2[i] +1; 
for(inti=0; i<a1.length; i++) 
print("a1["+i+"] = "+a1[i]); 
    } 
}

/* Output:

a1[0] = 2

a1[1] = 3

a1[2] = 4

a1[3] = 5

a1[4] = 6

*///:~

从上面的例子可以看到a1赋值给了a2,在对a2进行修改以后,a1也发生了变化。因为a2和a1是相同数组的别名,拥有一样的引用地址,所以对a2的修改可以在a1中看到。

所有数组都有一个固有成员,可以通过它获知数组内包含了多少个元素,但是不能对其修改,这个成员就是length。如果在编写程序时,并不能确定在数组里需要多少个元素,那么该怎么办呢?可以直接使用new在数组里创建元素。

importjava.util.*; 
importstaticnet.mindview.util.Print.*; 
publicclassArrayNew { 
publicstaticvoidmain(String[] args) { 
int[] a; 
Randomrand=newRandom(47); 
a=newint[rand.nextInt(20)]; 
print("length of a = "+a.length); 
print(Arrays.toString(a)); 
    } 
}

/* Output:

length of a = 18

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

*///:~

程序输出表明:数组元素中的基本数据类型值会自动初始化成空值(数字和字符就是0,布尔型的是false)。数组中的元素还可以是非基本类型的数据类型,比如String或者自定义类,下面一个例子展示了String类型的可变数组的使用方法:

//: initialization/DynamicArray.java // Array initialization. publicclassDynamicArray { 
publicstaticvoidmain(String[] args) { 
Other.main(newString[]{ "fiddle", "de", "dum" }); 
    } 
} 
classOther { 
publicstaticvoidmain(String[] args) { 
for(Strings : args) 
System.out.print(s+" "); 
        } 
    }
}

/* Output:

fiddle de dum

*///:~

上面的例子中一个类的main方法直接调用另一个类的main方法,并且将String类型的数组作为参数传递过去,可以看到,这里面的String数组并没有指定长度,这种写法使得数组的传递更加灵活通用。

“5.8.1 可变参数列表”提供了一种方便的语法来创建对象并调用方法,该方法使用Object数组作为参数,然后使用foreach语法遍历数组,因为Java中所有的类都直接或者间接继承于Object类,所以通过使用Object数组作为参数,可以接受其他任何类的数组作为入参。比如:

classA {} 
publicclassVarArgs { 
staticvoidprintArray(Object[] args) { 
for(Objectobj : args) 
System.out.print(obj+" "); 
System.out.println(); 
    } 
publicstaticvoidmain(String[] args) { 
printArray(newObject[]{ 
newInteger(47), newFloat(3.14), newDouble(11.11) 
        }); 
printArray(newObject[]{"one", "two", "three" }); 
printArray(newObject[]{newA(), newA(), newA()}); 
    } 
}

/* Output: (Sample)

47 3.14 11.11

one two three

A@1a46e30 A@3e25a5 A@19821f

*///:

可以看到数值和字符串都输出的正确的结果,A类由于没有定义toString方法,所以数出来的是类名和对象的地址。

在Java SE5中提供了新的特性来定义可变参数列表:

//: initialization/NewVarArgs.java // Using array syntax to create variable argument lists. publicclassNewVarArgs { 
staticvoidprintArray(Object... args) { 
for(Objectobj : args) 
System.out.print(obj+" "); 
System.out.println(); 
    } 
publicstaticvoidmain(String[] args) { 
// Can take individual elements: printArray(newInteger(47), newFloat(3.14), 
newDouble(11.11)); 
printArray(47, 3.14F, 11.11); 
printArray("one", "two", "three"); 
printArray(newA(), newA(), newA()); 
// Or an arrayprintArray((Object[])new Integer[]{ 1, 2, 3, 4 }); printArray(); // Empty list is OK     } 
}

/* Output: (75% match)

47 3.14 11.11

47 3.14 11.11

one two three

A@1bab50a A@c3c749 A@150bd4d

1 2 3 4

*///:~

有了可变列表,就不用再显式的编写数组语法了,当你指定参数时,编译器实际上会为你填充数组,你获取的仍旧是一个数组。这就是为什么你可以使用foreach来迭代该可变列表的原因。

printArray(); // Empty list is OK一行表明将0个参数传递给可变参数列表是可行的。下面一个例子展示了可变参数列表变为数组的情形,并且如果在该列表中没有任何元素,那么转变成的数据的尺寸为0:

//: initialization/VarargType.java publicclassVarargType { 
staticvoidf(Character... args) { 
System.out.print(args.getClass()); 
System.out.println(" length "+args.length); 
    } 
staticvoidg(int... args) { 
System.out.print(args.getClass()); 
System.out.println(" length "+args.length); 
    } 
publicstaticvoidmain(String[] args) { 
f(‘a’); 
f(); 
g(1); 
g(); 
System.out.println("int[]: "+newint[0].getClass()); 
    } 
}

/* Output:

class [Ljava.lang.Character; length 1

class [Ljava.lang.Character; length 0

class [I length 1

class [I length 0

int[]: class [I

*///:~

getClass()方法属于Object的一部分,我们将在后面章节做全面介绍。它将产生对象的类,并且在打印该类时,可以看到表示该类类型的编码字符串。前导的“[”表示这是一个后面紧随的类型的数组,而紧随的“I”表示基本类型int。为了进行双重检查,最后一行创建了一个int数组,并且打印了其类型。这样也就验证了使用可变参数列表不依赖于自动包装机制,而实际上使用的是基本类型。然而可变列表于自动包装机制可以和谐共处,例如:

//: initialization/AutoboxingVarargs.java publicclassAutoboxingVarargs { 
publicstaticvoidf(Integer... args) { 
for(Integeri : args) 
System.out.print(i+" "); 
System.out.println(); 
    } 
publicstaticvoidmain(String[] args) { 
f(newInteger(1), newInteger(2)); 
f(4, 5, 6, 7, 8, 9); 
f(10, newInteger(11), 12); 
    } 
}

/* Output:

1 2

4 5 6 7 8 9

10 11 12

*///:~

可变列表会使得重载过程变得复杂,尽快咋一看会显得足够安全:

//: initialization/OverloadingVarargs.java publicclassOverloadingVarargs { 
staticvoidf(Character... args) { 
System.out.print("first"); 
for(Characterc : args) 
System.out.print(" "+c); 
System.out.println(); 
    } 
staticvoidf(Integer... args) { 
System.out.print("second"); 
for(Integeri : args) 
System.out.print(" "+i); 
System.out.println(); 
    } 
staticvoidf(Long... args) { 
System.out.println("third"); 
    } 
publicstaticvoidmain(String[] args) { 
f(‘a’, ‘b’, ‘c’); 
f(1); 
f(2, 1); 
f(0); 
f(0L); 
//! f(); // Won’t compile -- ambiguous     } 
}

/* Output:

first a b c

second 1

second 2 1

second 0

third

*///:~

f()方法的调用会让编译器无法知道应该调用哪一个方法。当然你可以通过在方法中增加一个非可变参数来解决这个问题:

publicclassOverloadingVarargs3 { 
staticvoidf(floati, Character... args) { 
System.out.println("first"); 
    } 
staticvoidf(charc, Character... args) { 
System.out.println("second"); 
    } 
publicstaticvoidmain(String[] args) { 
f(1, ‘a’); 
f(‘a’, ‘b’); 
    } 
}

/* Output:

first

second

*///:~

上面的写法虽然可行,但是为了标注不同的可变参数列表的写法会让代码看起来可读性很差,一般也不会这么去设计自己的方法参数。

目录
相关文章
|
2天前
|
存储 Java
【Java开发指南 | 第七篇】静态变量生命周期、初始化时机及静态变量相关性质
【Java开发指南 | 第七篇】静态变量生命周期、初始化时机及静态变量相关性质
17 4
|
2天前
|
XML 存储 Java
11:Servlet中初始化参数的获取与应用-Java Web
11:Servlet中初始化参数的获取与应用-Java Web
27 3
|
2天前
|
存储 Java 测试技术
滚雪球学Java(30):多维数组:定义和初始化一次搞定
【5月更文挑战第5天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
7 0
滚雪球学Java(30):多维数组:定义和初始化一次搞定
|
2天前
|
存储 算法 搜索推荐
滚雪球学Java(27):从零开始学习数组:定义和初始化
【5月更文挑战第2天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
10 3
|
2天前
|
存储 IDE Java
Java一分钟之变量声明与初始化基础
【5月更文挑战第7天】本文介绍了Java编程中的变量声明与初始化,包括变量的类型和命名规则,以及显式和默认初始化。文章强调了局部变量必须初始化的重要性,并列举了三个常见问题:类型不匹配、未初始化和作用域混淆。为避免这些问题,建议明确类型、主动初始化、注意作用域,并利用IDE辅助。通过示例代码展示了正确使用变量的方法,鼓励读者通过实践加深理解。
18 0
|
2天前
|
Java
Java为什么建议初始化HashMap的容量大小?
【5月更文挑战第3天】Java中初始化HashMap容量能提升性能。默认容量16,扩容按当前的1/2进行。预估元素数量设定合适容量可避免频繁扩容,减少性能损耗。过大浪费内存,过小频繁扩容,需权衡。Java 8后扩容策略调整,但核心仍是预估初始容量以优化性能。
40 1
|
2天前
|
Java 编译器
【Java探索之旅】解密构造方法 对象初始化的关键一步
【Java探索之旅】解密构造方法 对象初始化的关键一步
17 1
|
2天前
|
存储 Java 编译器
【Java探索之旅】数组概念与初始化指南:动静结合
【Java探索之旅】数组概念与初始化指南:动静结合
23 0
|
2天前
|
Java
Java类 初始化顺序 | 静态数据的初始化
Java类 初始化顺序 | 静态数据的初始化
9 0
|
2天前
|
存储 Java 索引
Java数组的初始化
Java数组的初始化
13 0