Java千问:七个问题帮助初学者深入理解Java数组

几乎所有的高级语言当中,都提供了一种叫做”数组”的东西,Java语言当然也不例外。我们通过数组可以很方便的存储和管理一组数据。因为在Java语言当中使用数组非常的方便,所以导致很多初学者忽略了对数组的深入学习,本文就通过七个问题,来帮助初学者深入理解一下Java语言的数组到底是怎么回事。

一、数组是变量的简单叠加吗?

我们在学习Java的时候,知道一个int类型的变量可以存储一个整数,而一个int类想的数组可以存储多个整数。于是很多人认为数组只不过是变量的简单叠加而已,无非是变量存1个数,数组存多个数。其实不然,Java语言是把数组当作一个“对象”来看待的。我们来看下面的代码


我们可以看到,在代码中声明了int型变量a和int型数组b,b的长度为1。a和b都只能存储1个int型数据,似乎它们之间没什么区别。但是请注意:a和b之间其实是有本质区别的,a是一个基础数据类型的变量,而b则是一个对象!正因为它是一个对象,所以我们才可以调用到它的getClass()方法。那么紧接着小伙伴们会问:b既然是一个对象,那么这个对象是什么类型的呢?我们可以通过“b.getClass().getName()”获得它类型的名称是” [I”。读者也可以通过这种方式,获得其他几种基础数据类型数组的类型名称。最重要的是,我们必须搞清楚:数组不是变量的简单叠加!即便是基础数据类型的数组,也是以对象的形式存在的。

二、为什么数组的下标是从0开始的?

同很多编程语言一样,Java语言数组的下标也是从0开始的。很多初学者都不理解,为什么数组下标要从0而不是从1开始?这太不符合我们的日常计数习惯了。为了说清楚这个问题,我们还是先看一段代码


在这段代码中,创建了一个长度为5的数组并命名为a。在程序实际运行的时候,会分配一组连续的空间,这组空间中可以存储5个int型数据。为了能够准确的从这组空间的任意一个单元中找到数据,虚拟机必须能够对这组空间中任意一个单元做出识别。那么如何识别每个单元呢?虚拟机可以把每个单元都进行单独的命名,这样做当然能够达到目的,但是这么做跟使用变量就没什么区别了,更体现不出这组数据是一个连续的整体。实际的情况是:虚拟机给这组空间的第一个单元命名为a,如果希望找到a本身,那么就直接用a[0]来表示,其中方括号中的0表示偏移量。偏移量为0,就表示找的就是a这个空间中的数据,而a[1]表示以a为基准点,偏移1个单元,这样就找到了数组中第2个元素,以此类推,想找到数组中第5个元素,就以a这个单元为基准点,再偏移4个单元即可。因此我们就可以知道,数组的下标本质上并不是数组元素的编号,而是以数组首元素为基准点所偏移的量。这就是数组下标从0开始的原因。

三、数组的长度为什么可以用变量表示?

我们还是先来看一段代码

这段代码中,在创建数组的时候,以变量表示了这个数组的长度,这种做法放到Java语言中似乎很常见,但是如果使用C语言编程,在创建数组的时候,数组的长度必须是一个固定的值,不能用变量来表示。很多人似乎对这个问题不以为然,只是简单的认为这只是Java和C语言之间一点简单的语法差别而已。
其实问题远不是这么简单。这个简单的语法差别其实体现出了Java与C语言之间编译和运行机制本质的不同。变量是运行时才被赋值的,Java语言允许把数组的长度以变量的形式来表示,其背后的根本原因就是允许数组一直等到”运行时”才把长度确定下来。也就是说,程序员可以在编码的时候,不用规定数组的长度,等到程序实际运行的时候,根据实际需要去确定数组的长度,这样大大增加了程序的灵活性。

举个例子:如果我们编写程序,要求用户输入几个数字保存到数组中,然后进行排序,用户输入多少个数字不确定。如果我们用Java语言去完成这个程序,只要先让用户输入他想对多少个数字进行排序,然后把数字的个数保存到一个变量中,接着以这个变量的值作为长度创建数组就可以。但是如果是用C语言来完成程序,那么程序员必须在编写代码的时候就把数组的长度定下来,而不能等到程序运行的时候再去确定。因为C语言不能动态的在运行时确定数组的长度,必须在编译阶段就把长度确定下来。但是程序员在编码的时候并不确定用户要对多少数字完成排序,于是就只能以经验推测,创建一个他认为”足够大”的数组来存放排序的数字。如果创建的数组太大,则浪费空间,如果数组太小,则无法保存全部数字。

说了这么多,重点其实就一句话:Java语言的数组可以在”运行时”确定长度!并且这个特点大大的增加了程序的灵活性。

四、Java数组的长度可以为0吗?

这个问题很简单,只要写一段代码就可以得到答案:可以!关键问题来了:长度为0的数组不能存数据,这种数组有什么意义呢?前面说过,Java语言允许在程序运行过程中动态确定数组长度。那么我们就可以设想这样的一个场景:要求编码查询出考试总分在700分以上的学员,并且把他们的姓名存储到一个字符串数组中。假如经过查询之后,发现没有成绩在700分以上的学员,该如何表示这个查询结果呢?如果查询方法返回null来表示没有查到符合条件的数据,会对后续程序带来一定的风险,因为毕竟空对象可能会导致后面的处理代码中抛出空指针异常。而用一个长度为0的数组来表示查询结果就安全多了,仅能体现出没有查到符合条件的数据,又降低了抛出异常的风险。

五、Java语言中的数组可以扩大容量吗?

答案是不可以!因为数组一旦分配了空间之后,如果想在原来空间的基础上扩大容量,就如同是扩张地盘,势必会影响到内存中其他数据的存储,所以Java语言不允许数组扩大容量。网上有很多资料,讲解了如何扩大数组的容量,并给出了实现代码。如果你仔细去看这些代码,你会发现,这些所谓扩大数组容量的算法,其实现过程都是新创建一个更大的数组,然后把原数组中的数据拷贝到新数组中,最终返回那个新创建的数组。因此这种所谓的扩容算法其实并不是真正的扩容。

六、可以创建抽象类数组吗?

还是看代码


代码中创建了一个抽象类A,并且在main()方法中创建了一个A类型的数组。问题又来了:抽象类A不能创建对象,而现在不仅创建了对象,并且还是一组!这是怎么回事?其实这也是很多初学者在理解引用类型数据数组的一个误区:认为数组中存放的某种类型的对象。其实不然,引用数据类型的数组中,存放的并不是对象,而只是能指向这种对象的引用而已。就拿上面这段代码来说,数组a中并没有存放5个A类型的对象,而只是存放了5个能够指向A类型对象的引用,这些引用在数组初始化的过程中,都指向空对象(null),所以千万不要错误的认为创建了某类型的数组,同时就创建了N个该类型的对象。代码中,数组a中所存放的那些引用,将来所指向的必定也是A类子类的对象,因为A类自身根本就不能创建对象。

七、多维数组的length属性值是多少?

看代码


以上代码运行,输出结果会是多少呢?一部分初学者会认为输出的结果是12,因为这个数组能够存储12个数字。但程序实际运行输出的结果却是3,这是为什么呢?就是因为,无论数组本身的维度是多少,在Java虚拟机看来,这个数组都是一维数组!有小伙伴可能不解:a明明是一个二维数组,怎么会被当作是一维数组呢?我们可以看下图来帮助理解

我们可以看到,图中是一个3行4列的二维数组。但是如果我们把数组中的每一行看作是一个元素,那么这个二维数组就可以被理解为一个一维数组,数组中包含3个元素,只不过每个元素并不是一个简单的数字,而是一个一维数组。也就是说,这个二维数组可以被理解为”由3个一维数组组成的数组”。Java语言就是以这样的方式来管理数组的,无论数组维度是多少,都被当作一维数组,如果数组中的元素又是数组(严格的表述应该是:数组元素是指向一维数组的引用),那么就形成了多维数组。因此,刚才在代码中看到的那个二维数组a,它的length属性值为3而不是12。

希望通过这七个问题,能够帮助初学者深入理解Java语言的数组。

如果希望系统学习Java编程,可以观看我在本站的视频课程。有问题也可以加入我的QQ群291839907一起讨论!

原文地址:https://blog.51cto.com/2266836/2465474

时间: 2024-10-11 08:52:28

Java千问:七个问题帮助初学者深入理解Java数组的相关文章

《Java千问》系列短文写作计划

大家好,我在51CTO开博客啦!我本人从事软件开发和教学多年,在我从事教学过程中,回答过很多学生的问题,但非常遗憾,我只是当场回答了这些问题,但事后并没有把这些问题的解答写成博客.我打算弥补一下这个缺憾,因此产生了写文章解答问题的想法.经过几天酝酿,我打算把这些文章写成系列短文,名字就叫<Java千问系列>.取这个名字并不是说真的有一千个问题,"千"只是表示多的意思.只要我有把握回答的,都发表出来.关于<Java千问系列>的文章,在这里我想做几点说明:一.<

Java千问:Java语言中最大的整数再加1等于多少?

已知Java语言中int类型所能表示的最大整数为2147483647,请问以下代码执行结果是什么?一部分人都会认为这段程序压根就无法通过编译,也有人认为,这段程序能够通过编译,但在运行时会抛出异常,但更多的人面对这道题目根本就无从下手.那么正确答案是什么呢?首先告诉大家,这段程序能够顺利通过编译,并且在运行时也不会出现异常,运行的结果是在控制台上输出了数字-2147483648!而-2147483648正好是Java语言中int类型所能表示的最小整数.这个运行结果可能会让很多人感到大跌眼镜,运行

Java千问:关于Java语言复合赋值运算符的两个问题,快来瞧瞧!

我们知道,在Java以及很多高级编程语言当中,都有一种运算符叫做复合赋值运算符.复合赋值运算符由两个符号组成,它所能完成的运算操作也分为两步:第一步是运算,第二步是赋值.比如说:上面的这两条语句相当于但是,如果碰到下面这样的情况,a的值该应该是多少呢?有人认为应该按以下方式来计算,因为我们都知道,在四则运算规则中,遵循"先乘除,后加减"的原则按照这样的方式来计算,得到a的值应该是7,但实际运行程序所得到的结果是8.这是为什么呢?就是因为复合赋值运算符在完成运算的时候,遵循一个规则:把&

Java千问:Java位运算经典应用(一)

很多人认为位运算在实际开发过程中并没什么用,学习位运算也只是为了应付面试.这种想法是错误的,接下来我们就通过几篇连载文章介绍一下位运算在实际开发过程中的几个经典应用实例.如果对位运算规则掌握还不是很熟练,可以先阅读<Java千问:Java语言位运算符详解>.这篇文章不仅详细讲解了Java位运算的基本规则和一些常用的运算定律,同时还在文中提到了一些常用的位运算实际应用,比如可以用位运算操作的方式快速把某个变量所在的内存单元清零,或者位运算的方式实现某个变量快速倍增等等.但文中所这提到的这几个实际

Java千问:Java位运算经典应用(二)

接上篇 三.不借助中间变量交换两个变量的值 通常情况下,我们要交换两个变量的值都按如下步骤操作: 这种操作方式不难理解,实现交换变量值的关键点就在于中间变量c.而现在的题目要求是不借助中间变量来交换a和b的值.如果不使用位运算的方式,同样可以做到不借助中间变量交换两个变量的值,其实现过程如下. 为了讲解方便,我们把最初a与b的值称之为原始a和原始b,3行代码就是3步操作:第1步:把原始a与原始b相加的和存储到变量a中,变量b的值暂时没有发生变化.第2步:用这个和减去原始b,再赋值到变量b中,经过

Java千问:你知道Java语言布尔型数据到底占多大空间吗?

我们都知道,Java语言中有个boolean类型.每个boolean类型的变量中存储的是一个true或者是false的逻辑值.那么存储这个逻辑值,需要多大的空间呢?从理论上来讲,存储这个逻辑值只需要1个位(bit)就可以了,很多教科书上谈到这个问题的时候,也说boolean类型的数据在内存中只占1个位. 但是稍微有点计算机常识的人都知道:计算机完成寻址操作的时候,是以字节为最小单位进行的.也就是说每次要读取内存中数据的时候,最小只能精确到1个字节,不能单独读取某个位上的信息.如果boolean类

Java千问:开发Java程序为什么要配置环境变量?

我们在学习搭建Java开发环境的时候,老师总是让我们先安装JDK,紧接着又让我们去配置环境变量,之后才能动手编写Java程序.很多同学按照老师的步骤去做,成功的搭建好了Java开发环境,并且运行出了自己的第一个Java程序,感到非常有成就感. 那么,为什么在安装了JDK之后要配置环境变量呢?很多同学对这个问题其实并不清楚,只是知道如果不配置环境变量,就没法进行Java开发.今天,咱们就来聊聊为什么要配置环境变量.其实,配置环境变量的目的很简单:为了在命令行窗口下编译和运行Java程序.大家请注意

【Java学习笔记之二十六】深入理解Java匿名内部类

在[Java学习笔记之二十五]初步认知Java内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始化匿名内部类.匿名内部类使用的形参为何要为final. 一.使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式如下: new 父类构造器(参数列表)|实现接口() { //匿名内部类的类体部分 } 在这里我们看到使用匿名内部类我们必须要继承一个父类或者

Java千问:Java语言中为byte和short类型变量赋值为啥会报错?

咱们先来看一段很简单的Java代码这段代码非常简单,没有任何技术含量.但是,如果我们把这段代码改成下面的样子大家可以看到,我们只是用一个变量a代替了原来赋值表达式当中的常量1,就会出现语法错误,这是为什么呢?今天我们就用一篇短文来聊聊这个话题.我们知道,Java语言中有4种整数类型,分别是byte.short.int和long.其中,Java编译器对byte和short类型的变量在赋值的时候,做了一点点"特殊检查".那么编译器如何"特殊检查"这两种类型的变量呢?当编