一、概念
数组是相同类型的、用一个标识符名称封装到一起的一个元素序列。元素可以是基本类型的数据或者对象引用,但不会是对象本身。
数组与容器类之间的区别有两方面:效率和类型。
就存储和访问元素序列而言,数组的效率师最高的,而代价就是一旦创建则数组的大小就是固定了的,不能再改变。
数组可以持有基本类型,而容器不能。尽管有泛型支持,但容器也只能存储基本类型的包装类。
创建数组时,自带类型检查;而容器则需要泛型配合才能类型检查,否则放入其中的元素都被视为Object。
二、声明
要声明一个数组,只需要在类型名后面加上一对方括号,加上变量名即可:
int[] a;
加上方括号是为了区别原来的类型,本质上int和int[ ]是两个不同的类型,后者代表一个新的“数组类型”。“数组类型”加上引号是因为实际上不存在跟其名字所暗示的这么一个类,比如double
[ ]也可以看作是一个“数组类型”,但是它和int[ ]却是不同的类型。。。好像有点绕,不过后面说到数组的底层实现时会更清晰一些,现在只需要把int[ ]看作一个类名即可。
数组是有容量大小的,但是这个大小却不能在声明的时候指定,而只能在实际创建一个对象的时候指定。简单的说,这是因为每个“数组类型”的对象大小可能是不同的,所以不能在声明处指定大小。复杂的说,声明的时候,只是定义了一个对象引用,表明这个变量可以指向“数组类型”类型的对象,但是它不是对象本身,它存放的只是对象的地址(当然刚声明的时候还没有为其赋值,所以为null)。此时对象尚未创建,怎么会有大小?
三、创建
创建一个“数组类型”的对象仍然是使用new操作符,但不同于普通对象的创建,它长这样:
new int[4];也可以在声明的同时直接创建:
一个显然的区别是,类名后面没有圆括号,这不是意味着没有调用构造方法?或者没有构造方法?所以还是要看底层实现,数组类型到底是什么?int[ ] a = new int[4];
另一个就是指定了容量大小,在方括号内写上一个数字。这个数字最终被赋值给“数组类型”的length属性,可以通过
a.length的方式访问。
上面的代码可以说是创建数组对象的标准方式。不过虽然创建了对象,但是并没有给元素赋值。说穿了,仅仅是为这个数组对象分配了一块空间而已。由于它“没有构造方法”,所以没有在创建的同时初始化。不过话又说回来,调用构造方法创建普通对象时,可以初始化它的字段;然而数组对象的创建过程中确实初始化了length属性,只不过没有初始化数组内每个元素的值而已,但是各个元素算是数组对象的属性吗?恩,还是要看底层。
接下来要给各个元素赋值只能一个一个来了,比如a[0] = 4;a[1] = 43;或者使用一个循环。需要注意的是,下标是从0开始的。
四、初始化
上面说“没有在创建的同时初始化”是有问题的,比如length属性就被初始化了。其实除了length属性外,数组对象各个元素也被初始化了,这个初始化类似与普通对象的字段的默认初始化,也就是清零。比如int型的元素赋值0,引用型的赋值null。
但是很多时候,我们希望元素初始化时能够更加“定制”一点,而不是默认值。因为创建完对象后再一个个赋值实在太麻烦了,所以最好是在创建数组对象的时候就可以为元素赋值。Java 还真为这种需求发明了几种语法,如下:
1)聚集初始化
语法上,这行代码省略了数组对象的创建过程,没有new关键字的出现,直接用花括号代替了。声明完变量直接在花括号内给元素赋值,编译器自动计算花括号内有几个元素,然后赋值给length属性。int[] a = {1, 2, 3};
很简洁,但是限制只能在声明的时候同时使用,其他情况不允许使用,比如:
以及:int[] a = new int[3]; a = {1, 2, 3};
int[] a; a = {3,4,5,6};
2)动态聚集初始化
跟上面的差别不大,但是它显然把声明和创建分开了,也就是放开上面那个只能在声明的时候使用的限制。可以看出,语法核心就是 new int[]{3,4,5,6}; 本质上就是在之前的数组对象创建语法后面加了一对花括号而已,但是这应该是非常完美的解决方案了,这个核心可以出现在任何需要一个数组对象的地方,比如int[] a; a = new int[]{3,4,5,6};
省去了先声明再创建然后逐个赋值的麻烦。return new int[]{3,4,5,6};
五、待续。