首页 > 编程知识 正文

java 类数组初始化,Java定义数组并初始化

时间:2023-05-06 05:01:36 阅读:188994 作者:2554

Java SE 是什么,包括哪些内容(十三)?

本文内容参考自Java8标准
数组是相同类型的,用一个标识符名称封装到一起的一个对象引用序列或基本数据类型序列,数组是通过方括号下标操作符[ ]来定义和使用的,要定义一个数组,只需要在类型后面加上一个[ ],并取一个合适的标识符名称即可。
上面的内容总共体现在三个方面:
⑴、数组内的数据类型必须是同一类型,也就是说在一个数组内不能同时存储不同数据类型
⑵、数组可以存储引用类型和基本数据类型
⑶、数组的声明形式:类型标识符 [] 数组标识符,比如声名一个int类型的数组:

// 声明一个int类型的数组 int [] all; //int是类型标识符,all是数组标识符。 //[]也可以放置于数组标识符的后面,但是不建议这么做 int all []; //以上两种格式都是合法的,[]在标识符后面的做法更符合C和C++程序员的习惯, //但是前一种的格式更合理,毕竟它表明的是"一个int类型的数组",后续我们的博文 //都将采用这种形式。

下面的描述是非常重要的,希望读者看了就一定要看明白:
数组本身就是一个引用类型!
上面这句话是抛开了数组能存储什么类型的数据来说的,它针对的是数组本身的类型!
我们再来回顾一下数组的代码形式:
int [] all;
以上内容中int是表示了这个数组存储数据的类型。
[]表示这是一个数组。
all是这个数组的标识符,它与对象的标识符一样,是存储在堆栈上的。实际上,单单就以上形式并不是数组的全部,下面会接着说数组的初始化。
编译器不允许指定数组的大小(这个大小指的是堆上开辟的存储空间的大小,因为数组是引用类型,所以它的存储涉及堆栈和堆),现在拥有的仅仅是对数组的引用(all,你已经为这个all分配了足够的存储空间),但是你没有给这个数组对象本身分配任何的存储空间。
为了给数组本身创建相应的存储空间,必须写初始化表达式。
对于数组初始化动作可以分布在代码的任何位置,但是也可以用一种特殊的语法出现在数组声明的地方(这种语法必须出现在数组声明的地方)。
这种特殊的初始化语法是由一对"{ }"组成的,在这种情况下,存储空间的分配(等价于使用new)将由编译器负责,例如:
int [] a1 = {1,2,3,4,5};
那么,在没有数组定义的地方定义一个数组引用有什么用呢?
实际上,在Java中可以将一个数组的引用赋值给另一个数组引用,例如:
a2 = a1;
上面代码仅仅是做了一个引用的赋值,也就是说在堆栈中有两个引用(a1和a2),都指向了堆中同一个对象({1,2,3,4,5})。
具体代码示例:

// 数组引用的赋值 //类ArraysOfPrimitive public class ArraysOfPrimitives{ //程序执行入口main方法 public static void main(String[] args){ //声明一个int类型的数组引用a1,初始化为{1,2,3,4,5} int[] a1 = {1,2,3,4,5}; //声明一个int类型的数组引用a2,暂不进行初始化 int[] a2; //将a1值赋值给a2 a2 = a1; //利用for循环循环a2数组,因为数组是引用类型,一旦初始化成对象 //就能调用属性,其中这个length就是数组对象的一个属性。 //作用就是代表当前数组的长度。 for(int i=0;i<a2.length;i++){ //a2[i]代表的是访问a2数组的每一个值。 //请注意这种访问数组每个元素的形式a2[i] //a1[i]代表的是访问数组a1的每一个值。 //i的初始化值是0。所以最开始是a1[0]和a2[0]. //又因为循环截止的条件是a2.length, //也就是数组的长度,可见这个循环访问的是整个数组。 //i值随着循环的进行,通过步进表达式会自增。 //下面代码的意思是将a1数组的每一个值都+1后赋值给a2 //数组的每一个对应位置的值。 //也就是a1[0]+赋值给a2[0],a1[1]+1赋值给a2[1], //以此类推...直至循环完整个数组。 //还有一个隐藏的条件,a1和a2具有同样的长度。 //也就是a1.length获得的值与a1.length是一致的。 a2[i] = a1[i]+1; } //利用for循环访问数组a1. for(int i=0;i<a1.length;i++){ System.out.print("a1["+i+"] = "+a1[i]); } } }

结果示例:

从代码中看出,a1初始化的时候,是1,2,3,4,5,后代码操作的是a2(a2没有进行初始化),但是最后输出的a1却变成了2,3,4,5,6。很显然的是,代码中间操作a2的时候与a1产生了联系,这种联系的关键就在于这句:
a2 = a1;
将a1指向的堆中的那个对象赋值给了a2(a1和a2是同一个数组的两个别名),那么这样两个引用a1、a2指向的就是堆中同一个对象了。所以操作a1的时候影响a2,操作a2的时候影响a1。
下面对上面的循环中出现的a1.length解释一下:
所有的数组(无论它存储的类型是基本类型还是引用类型),都有一个固定的成员属性,可以随时通过它获知数组内存储了多少个元素,它就是length。
关键的部分是:
与C和C++一样:Java数组的计数是从第0个元素开始的!
所以如果你访问一个数组的话,最大的下标是length-1(我们前面已经知道了数组的访问方式,是标识符+[]+数组下标(表示的是第几个元素),比如a1[1])。如果超出了这个边界,C和C++会默默接受,并允许你访问所有的内存,这会造成程序的很大问题,许多声名狼藉的程序由此诞生。Java很好地解决了这个问题,一旦你访问的下表越界,就会出现运行时错误(异常)。
数组其实是对内存空间的完全模拟
因为实际的内存空间(内存,存储器等),我们都知道是一段连续的存储空间,你可以想象成是一组连续的格子,所有需要存储的内容都依次放入这些格子中,类似下面图示的排列:

同时,你将数组的访问方式和上面的图示对比,你就会发现一个问题:
数组的访问代码形式也是对存储形式的一种完全模拟!非常的形象
数组的访问方式:
比如创建并初始化一个数组:
int [] a1 = {1,2,3,4,5};
如果想访问数组中的第一个元素怎么办?
**a1[0]**代表的就是第一个元素1(千万要记住数组的下标计数是从0开始的)。
**a1[2]**代表的就是第三个元素3。
这种以下标访问数组的方式就是形象的模拟了数组的存储形式(第几个格子里面存储的是什么?)。
在后面的博文中,你会深刻的体会到,数组是Java中存取速度最快的形式。
数组的初始化有两种方式
除了上面提到了一种初始化方式外(用"{ }"括起来的元素列表),还有另外的一种数组的初始化方式(通过new关键字):
如果在编写程序时,并不能确定在数组里面需要存储多少个元素,那么怎么处理呢?
这个时候你需要用new关键字创建一个数组:
例如:

// 数组通过new关键字创建元素 //类ArrayNew public class ArrayNew{ //程序的执行入口main方法 public static void main(String[] args){ //定义一个int类型的数组标识符a int[] a; //Random是一个Java中提供的工具类,专门用来生成随机数。 Random rand = new Random(47); //rand.next()是调用了Random的nextInt()方法, //用来生成一个随机的整数, //对应的,还会有nextFloat()、nextDouble()方法等 //方法括号里面的20表示的是生成的这个整数的范围,是在 //0-20之间,表明了数组的创建是在代码运行的时候进行的。 a = new int[rand.nextInt(20)]; //打印数组的长度 System.out.println("legnth of a="+a.length); //看过Java开发文档的就知道,Arrays与System类是类似的。 //是专门针对数组的一个工具类,它里面的属性和方法都是static的。 //使用的时候直接用类名就可以了,而不用特别的去创建对象。 //它的toString()方法是专门打印出数组内的所有元素。 System.out.println(Arrays.toString(a)); } }

以上代码的结果示例:

之前的数组初始化的方式是直接在大括号"{ }"里面一次性将数组的所有元素写出来了,但是这种方式不是很灵活,而第二种方式是直接给你一个数组以及数组空间,同时利用了Java中的默认初始化功能给数组的所有元素赋值(基本类型都有默认的初始化值,引用类型都是null),在后期你需要的时候,可以对这些默认值进行覆盖:
注意上面的示例代码中的关键代码:
int [] a;
a = new Int[rand.nextInt(20)];

首先是声明了一个数组的引用a,然后再通过new关键字给这个引用a关联上一个实际的数组对象,这个数组对象的长度是由rand.nextInt(20);得出的结果决定的(从上面的结果示例中可以看出是18),你需要注意的是,这两行代码不但创建了一个数组,而且对数组进行了初始化,虽然使用的是Java的默认初始化(结果示例中,通过Arrays类的toString()方法输出数组内的所有元素,全部是0,这些0并不是我们主动添加进去的,而是这个数组的长度为18,Java的编译器默认这18个位置为0,也就是Java中的默认初始化的作用,再啰嗦一句:这个数组是int类型的,int类型默认初始化的值就是0,如果是boolean类型的,默认的初始化值就是false)
当然,如果你想省事,你也可以这么写(在定义数组的时候就进行了初始化):
int [] a = new int[18];
所以综上所述,Java中创建数组的方式有两种:
①、创建的时候就初始化:
int [] a = {1,2,3,4,5};
②、用new关键字先创建一个数组,利用Java的默认初始化功能进行初始化:
int [] a = new int[18];
这种方式创建的数组,数组元素都是对应的类型的默认初始化值,后期有需要的话,可以对初始化值进行覆盖
下面再来看下非基本类型的数组:
非基本类型的数组和基本类型的数组不一样,基本类型的数组存储的就是基本类型本身的值,而非基本类型数组存储的仅仅只是对象的引用,所以你还可以称呼它为引用数组,对象本身还是存储在堆中,或者更清楚明白的说,非基本类型数组存储的是对象在堆中的地址,它实际也是一个
地址数组(数组并不存储对象本身!)

以整数类型的包装器Integer为例,它不是基本类型,它是一个类:

// 引用数组/地址数组 //类名称ArrayClassObj public class ArrayClassObj{ //程序的执行入口main方法 public static void main(String[] args){ //Java的标准工具类Random. Random rand = new Random(47); //声明并初始化数组a,这个时候数组元素全部被默认初始化为null. Integer[] a = new Integer[rand.nextInt(20)]; //打印数组长度 System.out.println("length of a = "+a.length); //通过for循环为数组赋值 for(int i = 0;i<a.length;i++){ //a[i]表达式随着数组循环的步进,也就是i的自增,将能代表每一个数组元素。 //将rand.nextInt(500);表达式得出的0-500以内的值赋值给数组的每一个元素。 a[i] = rand.nextInt(500); } //打印出数组a的所有元素 System.out.println(Arrays.toString(a)); } }

结果示例:

为了正式默认初始化的作用,我将上面代码中的for循环去除,再来看下结果:

在上面的示例中,即使是在使用了new创建了数组之后,
Integer[] a = new Integer[rand.nextInt(20)];
它还只是一个引用数组,并且直到通过创建新的Integer对象(示例中是通过自动包装机制创建的),并把对象赋值给引用,初始化进程才算是结束(下面这行代码实际上是在用真实的整数覆盖现在数组中的初始化默认值null,如果你还有需要的话,可以继续覆盖!):
a[i] = rand.nextInt(500);------这里你需要知道的是数组元素的访问方式:标识符加上中括号以及数组的下标。
如果你忘记了创建对象,并且试图使用数组中的空引用,那么程序就会出现异常!因为数组中的元素全部是null。
再来回顾一下数组的创建/初始化:因为它确实是很重要的!
①、大括号列出所有的初始化元素(没有new关键字,这种形式比较受限,只能在定义处进行初始化):
以int类型为例:
int [] a = {1,2,3,4,5};
②、大括号列出所有的初始化元素(有new关键字,):
int [] a = new int []{1,2,3,4,5};

int [] a;

中间若干代码…
a = new int []{1,2,3,4,5};

所以大括号"{ }"创建/初始化数组有两种形式!

③、没有大括号,使用Java默认初始化机制创建/初始化数组(这种形式比较灵活)
int [] a = new int[18];

int [] a;

中间若干代码…
a = new int[18];

这种形式需要在后期对默认的初始化数组元素进行覆盖!

PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正!

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。