一/简单回顾
基本知识点
- 数据类型
- 常量变量
- 运算符号
- 分支结构:if switch
- 循环结构: for while do...while
循环嵌套关系: break continue 标记
二/ 引子
之前我们在学循环的时候,讲到一个例子,不知道大家还记不记得。
int score=90;
根据score成绩来进行区间的判定,不及格/及格/中等/良好/优秀/满分。
之前解决这个问题的时候,我们用到if else
语句,后来又可以用switch
语句来解决。
那么现在,我们重新来想一下这个问题。
①score
是一个变量空间(栈内存中的一块空间),可以理解为一个小容器。小容器里面存储的是一个学生的成绩。
②变量有什么特点?
1.变量在创建的时候,类型是固定的。
2.空间内的内容只能存放一份。
现在再来想一个问题。
如果想将5个同学的成绩都存起来,那我们就需要5个变量。
每次多一个变量,就会多一个名字,不方便。
在现实中,我们就会给他们编一个班级,以班级为的单位。这样就方便多了。
那么,我们同样可以用这种方式来存储数据。
下面要讲到,这种方式就是用数组来存储。
三/ 数组
(1)概念
来看看数组这俩字:
数:数据类型
组:一组元素
数组是一组数据类型相同的数据的结合。
数组也可以看作一份容器。
和变量不同的是,变量空间只能存放一份,而数组可以存储一组内容。
将这些数据统一在一起管理起来,更加方便。
(2)性质
之前说过的基本数据类型有byte
/short
/int
/long
/float
/double
/char
/boolean
。
而引用数据类型有数组
/类
/接口
/枚举
/注解
等,包括之前咱们接触过的String
/Scanner
/Math
。
我们可以看到,数组是一个引用数据类型。
(3)写法
1)数组的声明(创建)
①起名字
在学变量的时候,咱们是先定义一下,如int score
。
那么,同样,在用数组之前,也需要定义一下。
刚才说过,数组和变量类似,可以当作一个容器,存在于栈内存或堆内存当中,在里面开辟了一个空间。
既然是开辟了一个空间,那么就要先给它起一个名字。
②添加数据类型
那么数组里面存什么呢?
通过上面的概念,我们知道,数组是一组数据类型相同的数据的结合。
虽然数组里面是一组数据,但是要求,数据类型要相同!!!
所以我们在给数组声明的时候,要说明这里面存的是什么。
在名字前面,我们需要添加数据类型。
③添加中括号
现在我们得到这样的效果:数据类型 数组名字;
咦,这样不就和变量声明格式一样了吗?怎么表示它是一个数组呢?
为了区分,我们在数据类型之后,添加一个中括号[ ]
,来表示它是一个数组形式。
如果没有中括号,就表示是一个变量空间,加了中括号,就是数组。
2)举例
通过上面的分析,我们可以得到数组的定义:数据类型[] 数组名字;
。
那么我们现在举几个例子。
我想创建一个数组,
来存储一些int类型的整数,可以这样写:int[] x;
。
又比如,存储一组字符类型的数据,可以这样写:char[] y;
。
存储一些布尔类型的值,可以这样写:boolean[] z;
。
存储一些字符串形式的数据,可以这样写:String[] m;
。
:star: ==数组本身是一个引用数据类型,但是,数组内存储的类型,可以是基本类型,也可以是引用类型。==
(里面的每一个元素,什么都可以,只要是相同的类型就行)
3)补充
在书写数组的时候,我们会加上一组中括号表示它是数组。
那这个中括号的位置可以写在哪里呢?
在上面,我们将它写在了数据类型的后面,就是这样:int[] x;
。
但是,写在数组名字后面也是可以的,就像这样:int x[];
。
在这里,推荐大家写第一种,写在数据类型后边的,比较标准。
int[] x;
这种写法,可以理解为,x
是变量名,前面的int[]
整体算作一个类型。
在这里,主要想告诉大家,两种写法都可以,都好用,编译器都可以识别,但是规范来说,还是第一种。
在笔试题中,以下这几种写法都是可行的啦。
int[] x; -->规范
int []x; -->别扭的很(和上面的写法类似,只不过将中括号后移了一点)
int x[]; -->也可以这样写,但是识别的时候,不好看。
(4)数组的初始化
数组声明之后,我们需要往里面存东西。
数组是一个引用数据类型,既然是引用数据类型,赋值有一个专业的名词,叫做初始化
。
数组的初始化:创建数组并且给数组赋值。
数组的初始化有两种:静态初始化/动态初始化。
1)静态初始化
举个例子,现在我们要创建一个数组,里面存储整数类型,起个名字叫array。
那么可以这样定义:int[] array;
现在要将它初始化,直接将值赋给它?
有的小伙伴想这样写,都不对哈,看下面的情况(都不对的呦):
<1> 给一个常量是不对的
int[] array=10; -->直接给一个10,肯定不对,数组里面又不是只有一个10,是有好多个这样的类型。
<2> 给一个字符也不对
int[] array=`a`;
<3> 给一个字符串也不对
int[] array="abc"; -->类型不统一
:star:==数组是一个引用数据类型,我们以后,见到的所有引用数据类型,只要想创建,通过的方式是统一的。要new
一下。==
这里,就是开辟一个新的数组,给array赋上。
那new
什么呢?
前面都规定好了,是一个数组类型(int[]
)。
那么我们肯定要new
一个和前面类型一致的!前面的类型是什么,后面就要new什么。
现在我们要的是一个int[]
数组类型,那么后面就要new
一个数组类型啦。
就像这样:int[] array=new int[];
还记得之前我们说过的Scanner吗?这样写的来着:Scanner x=new Scanner();
,这里是圆括号,数组是方括号。
这两个写法很像,x
和array
都是变量名,Scanner
和int[]
都是一个类型,后面new
的过程,都相当于开辟了一个新的空间。
其实这里我们称之为对象
更合理一点。
现在我们new过了,那数组里面有什么呢?
我们就需要在后面加上一组大括号,就像这样:int[] array=new int[]{};
。
大括号表示一组元素,每一个元素都是一个int类型的整数。
现在我们只需要在大括号里面添加数值即可,比如添加10,20,30,40,50这些数据。
那么就可以这样写:int[] array=new int[]{10,20,30,40,50};
。
这样就写好了,数组的静态初始化的标准写法。
从这个写法上,我们可以看到,数组的长度是5(数组里面的数据的个数);还可以看到每一个元素。
静态初始化,可以看到数组的长度和内容。
好啦,标准的静态初始化写法,我们都已经知道了。
现在再来看一下,它的简洁写法:int[] array={10,20,30,40,50};
。
可以将new
的过程省去不写。
为啥可以这样呢?
数组变量名(array
)之前,已经有了定义类型( int[]
)。
那么,后面new
出来的类型肯定和前面定义的一致。
==在数组定义的时候,前面如果有定义,那么后面的new
可以省去不写,变成简洁的写法。==
但是,如果现在只是一个变量名,就不能省去new
的过程了!!!
就像这样:
int[] array;
... ...
... ...
array=new int[]{10,20,30,40,50};
1.前面如果有数组类型的定义,就可以省去后面new的过程;2.但是,如果前面只是一个变量,虽然之前这个变量array已经定义好了类型,但是此时编译的时候,它分不清变量到底什么类型的。这个时候,就必须new一下。
最后,总结一下,静态初始化的两种写法:
int[] array=new int[]{10,20,30,40,50}; -->标准写法
int[] array={10,20,30,40,50}; -->简洁写法
2)动态初始化
:walking: 提示:先跳过这个往后看,最后再来看这个,要不然可能看不懂。跳过看不影响后边的啦,不用担心。
大致和静态初始化一样,但是在创建过程,只给数组的长度,没有元素。
比如,我们有5个元素,可以这样写:int[] array=new int[5];
。
注意几点:
①创建0长度是可以的。
只不过是创建一个0长度的数组而已。
可以来试一下:
public class TestArray{
public static void main(String[] args){
int[] array=new int[0];//创建了一个长度为0的数组
}
}
看一下执行结果:
我们可以看到,编译(javac)和执行(java)都是可以的。(这里没有做输出,就没有输出什么啦)
②负数也是可以的,但只能通过编译(语法结构正确),不能执行结果。
再来试一试:
public class TestArray{
public static void main(String[] args){
int[] array=new int[-1];//创建了一个长度为-1的数组
}
}
看一下错误,是运行时异常:
报错:Negative Array Size Exception
,数组的长度不合法(创建数组的时候长度给了负数)。
总结:它认为0是一个正常的整数,可以用。 但是,0长度的数组,创建出来,什么东西都塞不进去,创建出来没有意义。
③动态初始化,只有长度,没有元素,是真的没有元素吗?
我们来试一下:
public class TestArray{
public static void main(String[] args){
int[] array=new int[5];
System.out.println(array[0]);
//访问数组的第一个元素,如果数组里面没有元素,第一个元素也就不存在
}
}
看一下运行结果:
运行结果是0。
大家可以自行尝试其他的位置,可以发现,出来的结果都是0。
我们来全部访问一遍,加强for循环来试一试。(加强for循环后边有讲,不懂的小伙伴转移后方)
public class TestArray{
public static void main(String[] args){
int[] array=new int[5];
for(int value:array){
System.out.println(value);
}
}
}
运行结果:
可以看到,最终结果都是0。
由此可见,这里说的,动态初始化,有长度,没有元素,不是真的没有元素,默认值是0。
为啥是0呢?
1.因为是整数呀,整数默认值就是0。
2.如果是浮点数,默认值就是0.0。
3.如果是字符型,默认值就是0对应的char值(97-->a ,65-->A,48-->'0')。
这个大家可以去试一下,打印不出来的,输出的值看不见。
emm,给大家示范一下吧。
代码如下:
public class TestArray{
public static void main(String[] args){
char[] array=new char[5];
for(char value:array){
System.out.println(value);
}
}
}
来看一下执行结果:
可以看到,打印出来了5个数,但是没有显示。
4.如果是布尔型,默认值就是false。
5.如果是引用数据类型,默认值就是null。
(比如数组,虽然数组是引用数据类型,但是数组里面存储的可以是基本数据类型,也可以是引用数据类型)
如果数组里面每个元素是基本数据类型,那么默认值就是上面的其他情况。
如果数组里面存储的是引用数据类型。比如创建一个数组,存储String
类型的数(引用数据类型)。
数组里面每个元素都是引用数据类型,那么默认值就是null
。
咱们可以试一试:
public class TestArray{
public static void main(String[] args){
String[] array=new String[5];
for(String value:array){
System.out.println(value);
}
}
}
看一下运行结果:
④动态初始化之后,都是默认值,并不是我想要的呀。
我们往里面存了东西,这个数组才有效。
还是拿最开始的整数类型举例。
访问元素,然后存值即可(看不懂如何访问的小伙伴,看后边的讲解哈)。
代码:
public class TestArray{
public static void main(String[] args){
int[] array=new int[5];
array[0]=10;
array[1]=20;
array[2]=30;
array[3]=40;
array[4]=50;
//可以有array[5]吗?不可以的,会出现Array Index Out Of Bounds Exception的错误(后边有讲呦)
}
}
最后,总结一下,动态初始化的写法:
int[] array=new int[5];
3)总结
静态和动态都有new
的过程。
静态初始化:有长度,有元素。
动态初始化:有长度,没有元素(不是真的没有,是默认值),需要往里面放东西才能用。
==所有引用类型,都得用new
关键字来创建。==
除非有特殊情况,比如Math.random();
,虽然是引用类型,但从不用new
对象。
为啥呢?
因为random()
方法是一个静态方法,是一个static修饰的静态方法。
这个静态方法,在整个类(Math类)中就一份,不需要创建对象,通过类名可以直接访问!!!
(5)数组元素的访问并应用
1)访问数组元素
数组的定义和初始化,咱们都已经学会了。
那么,数组作为一个容器,里面存的值得用呀。
那么我们如何访问数组里面的元素呢?
举个小例子:
现在我们定义了如下数组,想访问10这个元素,该怎么办呢?
public class TestArray{
public static void main(String[] args){
int[] array=new int[]{10,20,30,40,50};
}
}
以前定义变量的时候,想要拿出来用,只需要把变量名拿过来即可。
但是现在,是一个数组,数组名array代表的不是一个数,而是5个数。
我们这样来理解:
array相当于一个班级,里面有5个人,叫10,20,30,40,50。
现在只有一个班级名array,而没有5个人的人名,现在想要10,咋办?
我们可以这样,array班级中的第一个同学。
通过班级名找到第一个同学,就找到10啦!
那么,同样,我们可以访问第二个,第三个同学。
:star:==可以通过访问元素在数组中的位置,来访问每个元素。==
数组中元素的位置如何定义?
利用索引(index),即数组中每个元素所在的位置。
计算机中,是从0开始数的。
索引号是从0开始数的。
我们要访问元素,就可以这样访问:array[index];
。
现在我们要访问数组第一个位置,就可以这样写:array[0];
。
2)取出数组元素
既然拿出来了,就要存着用啊,可以用一个变量将它存起来,这个变量的数据类型必须要和这个拿出来的数据的数据类型一致。
比如这里,就要用int类型的变量来存储拿出来的数据。
这里定义一个变量value来存储这个数。
可以这样写:int value=array[0];
。
我们可以输出一下这个value,来看看输出的值是不是数组第一个数。
代码如下:
public class TestArray{
public static void main(String[] args){
//数组array的初始化
int[] array=new int[]{10,20,30,40,50};
//从数组内取得某一个位置的元素,并存入变量value中
int value=array[0];
//输出
System.out.println(value);
}
}
运行一下(这里用dos窗口运行的,具体怎么操作可以看我之前的文章):
可以看到,结果输出就是10,就是数组第一个数。
3)替换数组元素
现在我们想让40变成60。
①先访问40这个元素,它是在数组里面的第4个数,索引号是3。(索引是从0开始计数的呦)这样写array[3]
。
②将400存入这个位置即可,这样写array[3]=400;
。就当这个位置是个变量,将400赋值给这个变量即可。
③输出这个位置的元素,看一看是不是真的换啦。
代码如下:
public class TestArray{
public static void main(String[] args){
int[] array=new int[]{10,20,30,40,50};
//向数组内的某一个位置存入元素
array[3]=400;
System.out.println(array[3]);
}
}
输出结果:
可以看到,数组第4个元素,变成了400。
数组元素的每一个位置,可以认为是一个小变量,一个小格子。可以从这个小格子里面取值,也可以往里面放值。
4)数组元素的遍历(轮询)
数组元素的遍历即取出数组所有元素。
刚才我们取的都是一个元素,现在我想取出数组中所有的元素。
<1> 分析
我们访问每个元素,是这样写的:array[index]
。
这时候,我们要想把元素都拿出来看一看,就需要用到循环。
【第一次是array[0],第二次是array[1]……(每次都是做同样的事情,而且索引号有规律)】
用什么循环呢?for
和while
都一样。
我们这里用for
循环演示。(为啥想到循环,是因为我们想让程序每次都给咱们做同样的事情:取数组的值)
:question: 这里需要循环几次呢?5次(一共5个数嘛)。
5次循环,从几数到几,比较方便呢?
循环的目的只是执行5次,我想从几开始数都可以,只要是5次就好啦。比如,100-104,或者1000-1004,都可以。
循环跟取值从几开始没有关系的,但是5次从几次开始数比较方便?
如果循环乱开始数,里边每次取数组元素,就不太方便。因为数组本身还有索引号,还得重新找个变量来控制它。
如果这五次刚好和索引号对上,那么就可以将循环的变量来当索引号!!!
<2> 正常循环遍历
:red_car: 根据上面的分析,循环的开始值,可以从0开始(索引号index从0开始的)即index=0
。
用它既当循环的变量,又可以当索引的变化。这样比较方便,省了一个变量啦。
那么,到什么时候结束?index<=4
或者index<5
。
这里的index就有两个作用。第一,控制循环次数变化(5次);第二,控制索引变化(index变化和索引变化对上了)。
综上,循环部分就可以这样写for(int index=0;index<5;index++){}
。
代码如下:
public class TestArray{
public static void main(String[] args){
int[] array=new int[]{10,20,30,40,50};
for(int index=0;index<5;index++){ //每一次做一样的事情,即取数组的值,一共5次
int value=array[index]; //将取得的值放入变量value中
System.out.println(value); //将它们输出看一看
}
}
}
编译运行结果:
<3> 增强for循环
上面的for循环,是一般写法,还有一种增强的写法。不过对JDK版本有要求。
JDK 1.5版本之后,有了很多新的特性,其中有一个就是:增强for循环(ForEach)。
刚才我们写的for循环有3个要素,用两个分号表示,三个要素必须执行,即:
for(;;){
}
加强for循环之后呢,小括号之间不再是三要素了,中间也不是分号了。
中间写的是冒号。
冒号将括号隔成两个部分,分别是,自己定义的变量(作用是接收数组内每一个元素):遍历的数组array,即:
for(自己定义的变量(作用是接收数组内每一个元素):遍历的数组array){
}
那么上面的问题,就可以这样来写啦:
public class TestArray{
public static void main(String[] args){
int[] array=new int[]{10,20,30,40,50};
for(int value:array){
//冒号后面是循环的数组,即array
//冒号前面是人为定义的变量,用来接收数组内每一个元素
//(array数组内每一个元素都是int类型,那么用来接收的变量也应该是int类型的)
System.out.println(value);
}
}
}
然后编译运行,看一下结果:
<4> 总结
正常的for循环和加强的for循环,都要会!!!
存在即合理,两个for循环都有各自的好处。
①正常的for循环
优点:
有三个必要条件,可以通过index索引找到某一个元素的位置;
而且可以往数组里面存值或取值,因为是直接访问数组的元素位置。
缺点:
写法相对比较麻烦而已。
②增强for循环
优点:
有两个条件,一个是用来取值的变量,一个是用来遍历的数组。
没有index索引,写法相对比较容易。
缺点:
没有index索引,找不到元素到底是哪一个。
而且只能取值不能存值。(如果取值,直接拿出来用就好了;但如果存值,可不行)
有的小伙伴可能会这样写,让value直接等于100。
这样写是不对的,100存到value里面了,而不是数组里面!
for(int value:array){
value=100; //这样写是不对的,100是存到value里面的,而不是数组里面!
System.out.println(value);
}
做正常遍历输出的时候(只想看看结果), 加强for比较容易。想要操作数组中每个元素,甚至是某一个元素,索引号很重要,正常的for循环就很好。
(6)索引的范围问题
我们之前提到了索引,这个索引,是有范围限制的。
比如上面的数组,我们可以访问第6个元素吗?(索引是5)
来试一试。
代码如下:
public class TestArray{
public static void main(String[] args){
int[] array=new int[]{10,20,30,40,50}; //目前这个数组有5个元素,索引为0~4
int value=array[5];
System.out.println(value);
}
}
运行结果:
明显可以看到,是可以编译的,但不能运行!这里是运行时异常。
我们说一个错误,得说明是编译不行,还是运行有问题。想要知道最终结果,是有两步操作的。
第一步翻译(javac),翻译了不一定要执行呀。翻译是,写完代码,遵循了正确的语法结构。翻译过去了,就是遵循了合适的语法结构。但是遵循了语法结构,就一定可以用吗?不见得啊。那就要看第二步运行(java)了。
上面的错误,就是明显的翻译正确,但不能用。
当然不能输出啦。
翻译的时候,5是一个有效数字,当然可以编译。
但是执行的时候,在数组的第5个位置上没有这个元素,所以不能输出。
这里出现的错误,就是运行时异常(javac可以过去,一旦java运行就不行了)。
还有一种是编译时异常(javac就过不去),即语法错误。
在未来开发过程中,找异常也是一个很重要的环节,不能小视。光写不会调,那是百搭。
在这里,说一下几个异常,大家见一个记一个就好(记英文)。
不仅要记异常的英文,还要知道异常点在哪里(即因为什么引起的异常),以后遇到这种异常,要学会自己调试过来。
<1>之前说过一个输入,Scanner input.nextInt();
。
这个输入,只能输入一个数字。
如果输入的不是数字,就会出现这样的异常Input MisMatch Exception
,输入类型不匹配。
<2>这次的异常叫Array Index Out Of Bounds Exception
,数组索引超出边界异常(数组索引越界)。
这个异常如何产生的?就是数组索引越界了。数组的index索引是:开始(0)~结束(数组长度-1)。
本次案例中,索引是0~4,出现在这个范围之外的都是越界。
<3>上面讲动态初始化的时候,说过一个不常见的错误,在这里一起说了吧。
Negative Array Size Exception
,数组的长度不合法(创建数组的时候长度给了负数)。
(7)总结
数组元素的访问:
通过元素在数组中的位置来访问。可以存值或取值。
位置--->index索引
索引是有取值范围的,范围是从【(0)开始~(数组长度-1)结束】,是闭区间,两边都能取到。
如果数组的索引超出了上述范围,会出现一个运行时异常:Array Index Out Of Bounds Exception
。