一、数组的概念和定义
- 数组是一类相同元素的集合。
- 数组初始化时可以在定义时就初始化,也可以之后通过 new int[] { }进行初始化。
- 如果数组没有初始化时其默认值就是0(float 是0.0f 、double是0.0、char是/u0000、boolean是false)
Java中数组的写法分为一下三种:
1:
int[] array1 = {1,2,3,4,5};
是array2的简写。
2:
int[] array2 = new int[]{1,2,3,4,5};
3:
int[] array3 = new int[5];
array1是一个变量,其存储的是对象的地址。
在引用变量值存储了对象的地址,引用 指向了/引用了 一个对象。
二、数组使用
2.1 元素的访问
- 当需要访问数组第几个元素时,可以用 数组名[下标]。
- 当需要修改数组元素时,也可以用 数组名[下标] = 数值 进行修改。
public class Test1 {
public static void main(String[] args) {
int[] array1 = {1,2,3,4,5};
System.out.println(array1[2]);
array1[2] = 0;
System.out.println(array1[2]);
}
}
注:数组访问时要注意下标的合法性。
2.2 数组的遍历
在Java中数组的长度是 数组名.length 。
第一种遍历方法:
public class Test1 {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}
第二种遍历方法:for each
public class Test1 {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
for(int x: array){
System.out.println(x);
}
}
}
注:
第一种方法和第二种方法的主要区别在于第二种方法没有用到下标,当在遍历数组时需要用到下标判断数据时,就使用第一种方法。
第三种遍历方法:
public class Test1 {
public static void main(String[] args) {
int[] array = {1,2,3,4,5};
String ret = Arrays.toString(array);
System.out.println(ret);
}
}
三、数组是引用类型
3.1 初识jvm内存分布
- 程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
- 虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
- 本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
- 堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
- 方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域
例如:
public class Test1 {
public static void main(String[] args) {
int a = 10;
int b = 20;
int[] array = {1,2,3};
}
思考下面例题:
public class Test1 {
public static void main(String[] args) {
int[] array1 = {10,20,30};
int[] array2 = new int[]{1,2,3,4,5};
array2[0] = 100;
array2[1] = 200;
array1 = array2;
array1[2] = 300;
array1[3] = 400;
array1[4] = 500;
for (int i = 0; i < array2.length; i++) {
System.out.println(array2[i]);
}
}
}
答案:
解析:
其中最关键的是 array1 = array2 ,array2的地址赋值给了array1,array1就指向了0x22处所指向那块空间,再对array1进行赋值操作时,就是对0X22地址处的数据进行修改,所以打印的结果就为100~500。
3.2 认识null
如果一个引用不指向任何对象,就将它赋值为 null。
public class Test1 {
public static void main(String[] args) {
int[] arr = null;
System.out.println(arr);
}
}
例题:
public class Test1 {
public static void main(String[] args) {
int[] array = {7,8,9};
func1(array);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
System.out.println();
System.out.println();
func2(array);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
System.out.println();
}
public static void func1(int[] array1){
array1 = new int[]{1,2,3};
}
public static void func2(int[] array2){
array2[0] = 0;
}
}
答案:
解析:
本代码中:
func1:用数组作为方法的参数,func1中,array1 又new了一个对象,改变了引用的指向,指向了一个新的地址。对array里的数据不会进行改变。
func2:array2指向的就是array的地址,然后修改数据,改变的就是array数组的内容。
四、数组的应用场景
4.1作为函数的参数
- 实现一个打印函数:
public class Test1 {
public static void print(int[] str){
for (int i = 0; i < str.length; i++) {
System.out.print(str[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
print(arr);
}
}
解析:
数组名作为引用常量传递的是地址,调用方法会在栈上开辟栈帧,通过地址会找到在堆区的数组,进而进行操作。
4.2作为函数的返回值
public class Test1 {
public static int[] test1(){
int[] tmp = new int[]{1,2,3,4,5};
return tmp;
}
public static void main(String[] args) {
int[] ret = test1();
System.out.println(Arrays.toString(ret));
}
}
解析:
五、数组练习
1、模拟实现Tostring函数
public class Test1 {
public static String myToString(int[] arr){
String tmp = "[";
if (arr == null){
return "null";
}else{
for (int i = 0; i < arr.length; i++) {
tmp += arr[i];
if(i < arr.length - 1){
tmp += ",";
}
}
}
tmp += "]";
return tmp;
}
public static void main(String[] args) {
int[] array = new int[]{1,2,3};
String ret = myToString(array);
System.out.println(ret);
}
}
2、模拟数组拷贝
public class Test1 {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3};
int[] str = new int[arr.length];
System.out.println(Arrays.toString(arr));
copyOf(arr,str);
System.out.println(Arrays.toString(str));
}
public static void copyOf(int[] arr,int[] str){
for (int i = 0; i < arr.length; i++) {
str[i] = arr[i];
}
}
}
Java中自带有数组拷贝函数:copyOf
public class Test1 {
public static void main(String[] args) {
int[] array = {1,2,3,4};
int[] ret = Arrays.copyOf(array,array.length);
System.out.println(Arrays.toString(array));
System.out.println("-----------------------");
System.out.println(Arrays.toString(ret));
}
}
注:
当拷贝的数组长度大于原数组的长度,相当于给原数组进行扩容:
Java中也有拷贝数组一部分的函数:copyOfRange、System.arraycopy
在Java中的from:to:是左闭右开的,所以上面的下标范围是 [1,3)。
深拷贝与浅拷贝:
如果拷贝后的数组不影响原来的数组,那么这个拷贝就是深拷贝,繁殖则为浅拷贝。
六、二维数组
二维数组本质是一维数组,只不过每个元素又是一个一维数组。
6.1 二维数组的语法
public class Test1 {
public static void main(String[] args) {
int[][] array1 = {{1,2,3},{4,5,6}};
int[][] array2 = new int[][]{{1,2,3},{4,5,6}};
int[][] array3 = new int[3][3];
}
}
6.2 遍历二维数组
1、基本遍历数组
public class Test1 {
public static void main(String[] args) {
int[][] array1 = {{1,2,3},{4,5,6}};
int[][] array2 = new int[][]{{1,2,3},{4,5,6}};
int[][] array3 = new int[3][3];
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
System.out.print(array1[i][j]+" ");
}
System.out.println();
}
}
}
2、foreach
public class Test1 {
public static void main(String[] args) {
int[][] array1 = {{1,2,3},{4,5,6}};
for (int[] tmp : array1) {
for (int x : tmp) {
System.out.print(x+" ");
}
}
System.out.println();
}
}
3、deepToString
public class Test1 {
public static void main(String[] args) {
int[][] array1 = {{1,2,3},{4,5,6}};
System.out.println(Arrays.deepToString(array1));
}
}
6.3 二维数组的列省略
当列省略时,二维数组为null:
我们也可以指定二维数组的列:
这时该数组的每一行的列是不确定的。