数组
数组概念
数组是存储同一种数据类型多个元素的集合,也可以看成是一个容器;
一维数组定义格式
数据类型[ ] 数组名 = new 数据类型[数组的长度];
一维数组初始化
- 动态初始化:只指定长度,由系统给出默认初始化值;
int[] arr = new int[5];
整数类型:byte、short、int、long默认初始化值是0;
浮点类型:float、double默认初始化值是0.0;
布尔类型:boolean默认初始化值是false;
字符类型:char默认初始化值是’\u0000’;
char 在内存中占两个字节,是16个二进制位,\u0000,每个0其实代表的是16进制的0,那么四个0代表的就是16个二进制位;
引用数据类型:初始值为null;
int[] arr = new int[5];
arr : [I@19bb25a;//数组首地址
其中:
[ 代表是数组,几个就是几维;
I 代表是int类型;
@ 是固定的;
19bb25a代表的是16进制的地址值;
- 静态初始化:给出初始化值,由系统决定长度;
java中内存分配
- 栈 :存储局部变量
局部变量:定义在方法声明上和方法中的变量; - 堆:存储new出来的数组和对象;
- 方法区
- 本地方法区:和系统相关
- 寄存器:给CPU使用
上图为:一个数组内存图
上图为:三个引用两个数组图
静态初始化一维数组
- 格式
数据类型[] 数组名 = new 数据类型[ ] {元素1,元素2,.....};
- 简化格式
数据类型[] 数组名 = {元素1,元素2,....};
- 静态初始化的内存分配
int[] arr后,会在堆内存空间中开辟一片连续的地址空间,并初始化值为空间中每个元素值为0,之后再根据{11,22}进行显示赋值;
int[] arr;
arr = new int[] {11,22};//非简化格式可以先声明再赋值
int[] arr2;
arr2 = {11,22};//报错,简化格式不可以先声明再赋值
int[] arr = new int[2]{11,22};//报错,不允许动静结合,即中括号[ ]中有值,大括号中也有值
越界和空指针
ArrayIndexOutOfBoundsException
:数组索引越界异常NullPointerException
:空指针异常(数组已经不在指向堆内存中)
二维数组
二维数组的格式1
int[ ][ ] arr = new int [3][2]
;(建议)数据类型 数组名[ ][ ] = new 数据类型[m][n]
;数据类型[ ] 数组名[ ] = new 数据类型[m][n]
;
int[] x,y[];//x是一维数组,y是二维数组;
二维数组格式1的内存图解
先在堆空间中开辟3个连续空间,初始值为null,然后为每个一维数组开辟两个连续空间,初始值为0,并把每个空间的地址值赋给开辟的3个连续空间,最后把开辟的3个连续空间的地址值返回给栈中的arr变量;
二维数组格式2
int[][] arr = new int[3][]
;//一维数组还未被赋值- arr[0] = new int[3]; //自定义一维数组大小
- arr[1]= new int[5];
int[][] arr = new int[3][]; //三个一维数组都没有被初始化
arr[0] = new int[3]; //初始化一维数组
arr[1]= new int[5]; //初始化一维数组
System.out.println(arr[0]); //初始化过了,所以输出结果为该一维数组的地址:[I@da6bf4
System.out.println(arr[1]); //初始化过了,所以输出结果为该一维数组的地址:[I@1e58cb8
System.out.println(arr[2]); //没有初始化,所以输出null
二维数组格式2的内存图解
二维数组格式3
int[][] arr = {{1,2,3},{4,5},{6,7,8,9}};
int[][] arr = {{1,2,3},{4,5},{6,7,8,9}};
System.out.println(arr); //[[I@19bb25a,二维数组地址值(两个中左括号)
System.out.println(arr[0]); //[I@da6bf4,一维数组地址值
System.out.println(arr[0][0]); //1,一维数组中的元素值
二维数组格式3的内存图解
java中的参数传递问题(重点)
public static void main(){
int a = 20;
int b = 10;
change(a,b);//基本数据类型的值传递
System.out.println(a+" " +b);//**由于change方法改变的是它内部的变量的值,所以主方法中的a和b变量值没有改**
//**变,仍然是a =20 ,b = 10;**
int[] arr = {1,2,3,4,5};
change(arr);//引用数据类型值传递,传入了地址
System.out.println(arr[1]);//chage 方法中的arr变量和main方法中的arr变量指向的是同一片堆空间,所以在
//change方法中做的修改,主方法中可以访问得到;arr[1] = 4;
}
public void change(int a,int b){
a=b;
b = b+a;
}
public void change(int[] arr){
arr[1] = 4;
}
- 基本数据类型的值传递,不改变原值,因为方法调用后就会弹栈,方法中的局部变量随之消失;
- 引用数据类型的值传递,改变原值,因为即使方法弹栈,但是堆内存数组对象还在,可以通过地址继续访问;
问题: java中到底是传值还是传址?
1.既是传值,也是传地址,基本数据类型传递的是值,引用数据类型传递的是地址;
2.java中只有传值,因为地址值也是值;(出去面试说这种,java之父支持这种说法)