目录
一、数组概述与定义
数组是用来存储多个同种数据类型变量的一个容器集合。
存储的数据类型不仅可以包括基本数据类型,也可以存储引用数据类型。
数组定义的格式一般如下:
数据类型 [] 数据名=new 数据类型[数组长度]
or
数据类型 数据名[]=new 数据类型[数组长度]
二、数组的初始化
数组的初始化就是为数组开辟一段连续的内存空间,并且为每个数组元素赋予值。
初始化一般有两种方式:静态初始化和动态初始化。
2.1动态初始化
动态初始化的格式一般为:
int arr[]=new int[3];
动态初始化一般只指定数组的长度,而初始值则由系统给出:
- 整数类型:byte、short、int、long默认初始化值都是0
- 浮点类型:float、double默认初始化值都是0.0
- 布尔类型:boolean默认初始化值为false
- 字符类型:char默认初始化值为‘\u0000’
这里char的默认初始值为‘\u0000‘,‘\u’表示unicode编码,char在内存中占两个字节即16个二进制位,
而\u0000中的0位16进制的0,转换为二进制位0000,那么4个16进制的0就代表16个二进制位。
对上述整型的数组进行初始化之后,就可以通过arr这个名字访问数组的元素arr[i],
其中i为第i个元素的下标,i从0开始。
那我们直接输出arr这个变量,他存储的就是这个数组在内存空间存放的首地址:
"["的数量代表的是数组的维度,一维数组就有一个"[",二维数组就为"[[",以此类推;
“I”代表的是int类型的数组,同样"D"代表double型,"F"代表float型,"D"代表double型;
“@”为固定的标识符;
“@”后面的字符串代表的是地址值,是十六进制数来表达的。
2.2 静态初始化
静态初始化的格式一般如下:
数据类型 [] 数组名=new 数据类型[](元素1,元素2,...);
or
数据类型 [] 数组名={元素1,元素2,...}
比如:
int []arr=new int[]{1,2,3};
int []arr={1,2,3};
注意在进行静态初始化中不能在中括号中指定数组的长度,
静态初始化会根据后面集合元素的个数自己确定数组的长度。
2.3数组越界异常和空指针异常
当使用静态或动态初始化数组的时候,如果访问不当可能会造成数组越界异常和空指针异常。
数组越界异常:当访问到申请数组长度大小之外的下标位置时,会引起数组越界异常。
比如如下代码:
public class Main {
public static void main(String[] args){
int a[]=new int[5];
System.out.println(a[5]);
}
}
其报错结果如下:
空指针异常:当动态申请的数组地址如果赋值给了空指针null,就无法在对该数组进行访问,否则会引起空指针异常。
比如以下代码:
public class Main {
public static void main(String[] args){
int a[]=new int[5];
a=null;
System.out.println(a[0]);
}
}
其报错结果如下:
三、Java中堆和栈的区别
当Java进行代码编译时,会生成.class后缀的文件,然后将.class文件放入java虚拟机中进行运行,
虚拟机会根据变量创建的类型将变量放入内存中,具体存储规则如下:
- 栈:存储的是局部变量,即定义在方法声明上和方法中的变量
- 堆:存储new出来的数组或者对象
存储在栈空间的数据满足先进后出的原则,
存储在堆空间的数据满足
四、数组的查找
如果需要对数组中存储的元素进行访问,一般通过数组名+下标的形式,形如:
数据类型 变量名=数组名[下标]
我们以查找指定元素在数组中第一次出现的下标为例:
public class Main {
public static void main(String[] args){
int key=87;
int a[]=new int[]{15,33,20,40,87};
for(int i=0;i<a.length;i++){
if(key==a[i]) {
System.out.println(key + "在数组中下标为:" + i);
return;
}
}
System.out.println(key+"未出现在数组中");
}
}
输出结果如下:
五、二维数组
二维数组即数组的数组,数组中存储的元素为数组,在线性代数中我们称为矩阵。
一般二维数组的定义如下:
int[][] arr=new int[2][3];
or
int arr[][]=new int[2][3];
or
int[] arr[]=new int[2][3];
其中中括号参数第一个2代表二维数组中有2个一维数组;
第二个3代表一维数组中有3个int类型数据。
同样我们使用下标对数组元素进行访问:
public class Main {
public static void main(String[] args){
int[][] a=new int[2][2];
System.out.println(a);
System.out.println(a[0]);
System.out.println(a[0][0]);
}
}
下面是输出结果:
第一个输出“[[I@1b6d3586”为二维数组的地址,
第二个输出“[I@4554617c”为二维数组的第一个一维数组的地址,
第三个输出“0”为二维数组中第一个一维数组的第一个元素的值。
我们还可以对二维数组进行直接赋值:
int[][] arr={{1,2},{3,4},{5,6}};
二维数组的遍历和一维数组相似,通过数组的下标来访问对应元素或者数组:
int element=arr[i][j]
其中i,j代表是第i个一维数组中第j个元素。
对二维数组进行遍历的代码如下:
public class Main {
public static void main(String[] args){
int[][] a={{1,2},{3,4},{5,6}};
for(int i=0;i<3;i++){
for(int j=0;j<2;j++)
System.out.println(a[i][j]);
}
}
}
其结果如图所示:
六、Java中参数传递问题
参数传递是一个非常经典的问题,在多个编程语言中该问题都有涉及,这里我们测试普通数据类型int和数组数据来进行参数传递:
public class Main {
public static void main(String[] args){
int m=1;int n=2;
exchange(m,n);
System.out.println("m="+m+" n="+n);
int[] a={1,2,3,4};
exchange(a);
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
}
public static void exchange(int m,int n){
int temp=m;
m=n;
n=temp;
}
public static void exchange(int []a){
int temp=a[0];
a[0]=a[a.length-1];
a[a.length-1]=temp;
}
}
代码的功能是将两个int型数据作为参数传给方法exchange,在方法中进行两数互换,
然后回到main方法中对两数进行打印输出,得到的结果是两数并没有互换,还是原来的值,
第二个是将数组作为参数传给方法exchange,在方法中对数组的首尾元素进行互换,
然后回到main方法中对数组的元素进行打印输出,得到的结果是数组的首尾元素互换了,下面是输出结果:
从上述结果可知基本数据类型的值传递,不会改变原来的值,调用之后方法就会被弹栈,里面的局部变量也就消失了;
而数组这种引用数据类型的值传递,会改变原来的值,即使调用之后方法被贪占,但是堆内存储的数组对象依然存在,可以通过地址继续访问。
在Java中,参数的传递都是值传递,引用类型数据传递的值是地址值,所以方法内的操作会通过地址访问改变原值;
而基本数据类型就是变量值传递,不是地址值的传递,所以方法弹栈时局部变量被清空就不会改变原值。