数组
一、数组的理解
数组(Array)是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式。这些有序排列的同类数据元素的集合称为数组。
数组变量名存储的是数组第一个元素的地址,数组是引用类型
二、数组的相关概念
数组名、元素、角标/下标/索引、数组长度:元素的个数
三、数组的特点
- 数组是有序排列的
- 数组属于引用类型的变量。数组的元素既可以是基本数据类型、也可以是引用数据类型
- 创建数组对象会在内存中开辟一整块连续的空间
- 数组的长度一旦确定,就不能改变。
特殊说明原来的C89标准中是不允许可变长数组出现的,但是在C99标准中,加入了对VLA的支持 [3] ,但是支持的编译器不多,而且由于栈溢出的安全问题,没有太多的人敢用这个可变长数组,所以在C11标准中又把它规定为可选实现的功能了 [4] 。
四、数组的分类
4.1 按照维数分类
4.1.1 一维数组
//一维数组的声明与初始化
int[] array;//声明
//静态初始化 :数组的初始化和元素的赋值可以同时进行
int array1[] = new int[] {1001,1002,1003};
//动态初始化:数组的初始化和数组元素的赋值分开
String [] array2 = new String[5];
int[] array3 = {1,2,3,5,4};//类型推断
对于一维基本数据类型数组来说,是在栈空间内开辟了一段连续的空间,存储的数据就存在了栈空间中,而对于引用型一位数组,栈空间中也开辟了一段连续的空间,但是里面并没有直接存储具体数据,而是存储了一系列地址值,这里的地址值指向堆空间中的实例对象所在的首地址
一维数组内存解析图
遍历一维数组
public static void main(String[] args) {
int arr1[] = new int[] {1,2,3,4,5,6,7};
//for循环1
//length获取数组长度
for(int i = 0;i<arr1.length;i++) {
System.out.print(arr1[i]);
}
System.out.println("\n");
//for循环2
for(int x:arr1) {
System.out.print(x);
}
}
输出
1234567
1234567
public static void main(String[] args) {
String arr2[] = new String[] {"I","Love","China"};
int i = 0;
while(i<arr2.length) {
System.out.println(arr2[i]+" ");
i++;
}
}
输出
I
Love
China
4.1.2 二维数组
//二维数组的声明与初始化
//静态初始化
int[][] arr = new int[][] {{1},{2,3},{4,5,6}} ;
//动态初始化1
String[][] arr1 = new String[3][3];
//动态初始化2
String[][] arr3 = new String[3][];
//其他正确写法
int[] arr4[] = new int[][] {{1},{2,3},{4,5,6}} ;
int[][] arr5 = {{1},{2,3},{4,5,6}} ;//类型推断
遍历二维数组
//遍历二维数组——嵌套循环
int[][] arr = new int[][] {{1,2,3},{3,4,5}};
for(int i = 0;i<arr.length;i++) {
for(int j = 0;j < arr[i].length;j++) {
System.out.print(arr[i][j]);
}
System.out.println("");
}
如果从键盘读入二维数组也需要使用嵌套循环
二维数组内存解析:
借鉴一位博主写好的
链接: link.
4.1.3 n维数组
目前二维数组已经能满足编程需要,一般不涉及三维及多维数组,如果用到多维数组,原理也是和二维数组差不多的
4.2 按照数组元素的类型分类
4.2.1 基本数据类型的数组
由基本数据类型为元素构成的数组
未初始化的默认值
public static void main(String[] args) {
//引用型数组
int intArr[] =new int[2];
double dobArr[] = new double[2];
long longArr[] = new long[2];
char charArr[] = new char[2];
short shtArr[] = new short[2];
float flArr[] = new float[2];
boolean bolArr[] = new boolean[2];
byte byArr[] = new byte[2];
for(int i = 0;i<intArr.length;i++) {
System.out.println("int:"+intArr[i]+"\t"+"double"+dobArr[i]+"\t"+"long"+longArr[i]+"\t"+"char:"+charArr[i]+"\t"+"short:"+shtArr[i]+"\t"+"float:"+flArr[i]+"\t"+"boolean:"+bolArr[i]+"\t"+"byte:"+byArr[i]+"\t");
}
}
输出
4.2.2 引用数据类型的数组
由引用数据类型为元素构成的数组
未初始化的默认值
public class Main {
public static void main(String[] args) {
//引用型数组
String str[] = new String[2];
Car car[] = new Car[2];
for(String s:str) {
System.out.println(s);
}
System.out.println("******");
for(Car c:car) {
System.out.println(c);
c.drive();
}
}
}
输出
注释 c.drive(); 之后的输出
可以看出在未初始化时,引用型数组的默认初始值为null,且这时如果去调用对象的方法,就会出现空指针异常 java.lang.NullPointerException
赋值引用型数组并输出
public class Main {
public static void main(String[] args) {
//引用型数组
String str[] = new String[2];
str[0] = "hello";
str[1] = "china";
Car car[] = new Car[2];
car[0] = new Car("Annie", "凯迪拉克", 4);
car[1] = new Car("Lisa", "BMW", 2);
for(String s:str) {
System.out.println(s);
}
System.out.println("******");
for(Car c:car) {
c.drive();
}
}
}
//创建汽车类Car
class Car{
String name;//品牌
String driver;//驾驶员
int peopleCount;//可载人数
Car(String driver,String name,int peopleCount){
this.driver = driver;
this.name = name;
this.peopleCount = peopleCount;
}
//开车方法
public void drive() {
System.out.println(driver+"在开"+name);
}
}
输出效果
hello
china
******
Annie在开凯迪拉克
Lisa在开BMW
五、结构形式
5.1 栈内存
在方法中定义的一些基本类型的变量和对象的引用变量都在方法的栈内存中分配,当在一段代码中定义一个变量时,java就在栈内存中为这个变量分配内存空间,当超出变量的作用域后,java会自动释放掉为该变量所分配的内存空间。
5.2 堆内存
堆内存用来存放由new运算符创建的对象和数组,在堆中分配的内存,由java虚拟机的自动垃圾回收器来管理。在堆中创建了一个数组或对象后,同时还在栈内存中定义一个特殊的变量。让栈内存中的这个变量的取值等于数组或者对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,引用变量实际上保存的是数组或对象在堆内存中的地址(也称为对象的句柄),以后就可以在程序中使用栈的引用变量来访问堆中的数组或对象。 [5]
六、初始化
6.1 静态初始化
定义时赋值
6.2动态初始化
先定义,元素采用默认值,之后在赋值。