JavaSe语法 -- 数组基础篇

简介: JavaSe语法 -- 数组基础篇

一. 什么是数组



数组可以简单的看成一个相同元素的集合, 在内存中是一段连续的存储空间.

一个数组简易模型

e2412b3f544e4177b7ae500499ba720a.png


从上述这个简易模型中, 我们可以得到以下信息:

  1. 数组中存放的元素都是相同类型的
  2. 数组的内存空间是相连的
  3. 每个空间都有一个编号(即常说的数组下标), 从 0 开始


二. 数组的创建



T[ ] 数组名 = new T[N]

T : 表示数组类型

N : 表示数组长度


例如: int [ ] array = new int [ 10 ];

意为创建一个数组名为 array 且 数组元素为 10个 的整形数组


三. 数组的初始化



1.动态初始化 : 在创建数组时, 直接指定数组中的元素个数

int[] array1 = new int[10];


2.静态初始化 : 在创建的时候不指定数组元素个数, 而是直接将具体内容进行指定.

int[] array2 = new int[] {1,2,3,4};


3.当我们不知道初始化为何值时, 应当初始化赋值为 null, 而不能是其他基础类型

3c2f93d978754b6aad097f86d56d8fa6.png


并且, 在初始化为 null 后, 不能被引用, 也就是一个不指向对象的引用, 否则会空指针异常

5ad49c9d838d4e42a7ff8e12e6802071.png


静态初始化有几个特点

  1. 虽然没有指定具体长度, 但是数组的长度在编译器编译时会根据 { } 括号中的元素进行确定
  2. { } 括号中的数据类型必须与 [ ] 前的类型一致
  1. 静态初始化可以省略创建时的 new T [ ]
int[] array3 = {1,2,3,4};


对于数组的初始化, 有以下几个特点 :


1.如果数组中存储的元素类型为基本类型, 未初始化则数组中的数据值均为基本类型对应的默认值


例如为 int 类型

int[] array1 = new int[10];
        System.out.println(Arrays.toString(array1));


315fa2c08a5c4d6f86a6711624f9fd05.png


例如为 double 类型

double[] array1 = new double[10];
        System.out.println(Arrays.toString(array1));

809e0ce7d566470993f42c00eb738f46.png


2.如果数组中存储的数据类型为引用类型, 则 默认值为 null

String[] array1 = new String[10];
        System.out.println(Arrays.toString(array1));


7775886953fe433a8ef778c8c921507d.png


3.变量未初始化时, 不允许被使用

49eae929cace4aedb289215f9aabb7d9.png


四. 数组是引用类型



在 Java 中, 数组是为引用类型的, 并非由前面 指定的某个类型它则为该类型. 那么, 该如何去理解数组是引用类型这句话呢? 需要从数组在内存中的存储说起.


了解存储前, 需要先了解 JVM (Java Virtual Machine) 是什么? 简单来说 JVM 就是 Java虚拟机

89b045d399fe4d1a8741a82fe4a606a1.png


如果对于内存中的数据不加以区分, 内存管理起来就会非常麻烦, 因此在 JVM 中, 将其大致划分为图中几个数据区


方法区 : 用于存储已被虚拟机加载的类信息、常量、静态变量, 即编译器编译后的代码等数据


堆 : JVM所管理的最大内存区域. 使用new 创建的对象都是在堆上保存


虚拟机栈 : 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了


本地方法栈 : 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量 (源码中的一些 Native 方法)


程序计数器 : 只是一个很小的空间, 保存下一条执行的指令的地址

大致了解了 JVM 那么, 数组是如何在 JVM 中存储的呢?


int[] array = new int[] {1,2,3,4};
        int a = 10;
        double b = 9.9;


根据上面 JVM 运行时数据区, 数组 array 是 main 方法中的局部变量, a, b 均为局部变量, 因此在 存储在 JVM 的虚拟机栈中(以下简称栈), 而数组的内容属于 new 创建的对象, 位于堆上, 可以画出如下简图 :

b6cdbf605b6447d3aea65c2b00aeea29.png

可以看出, array数组在栈上存储的是 数组空间的地址

80f89ae9cecc42d5b4f87e40ae9a74a5.png


对数组是引用类型有一定了解以后, 看看下面这段代码输出什么?


public static void main(String[] args) {
        int[] array = new int[] {1,2,3,4};
        fun1(array);
        System.out.println(Arrays.toString(array));
        fun2(array);
        System.out.println(Arrays.toString(array));
    }
    public static void fun1(int[] array) {
        array = new int[10];
    }
    public static void fun2(int[] array) {
        array[1] = 99;
    }


这儿很容易犯错, 都以为fun1(array) 过后 输出 array里的内容为 {0,0,0,0,0,0,0,0,0,0}

5e413eb909ed47f59c853b0a0074d76e.png


你答对了嘛? 为什么呢? 我们通过刚刚的 JVM 运行时数据区来结合理解

62e7406ff66f451896e141293b19d8d2.png


, 在栈上面首先创建了 array 这个变量, 并且在堆上开辟了一块儿空间存储 {1,2,3,4}这组数据. 当我们调用 fun1() 方法时, array 作为形参传入, 此时 fun1() 中的 array 也指向同一块空间 即 0x11, 进入fun1() 方法内部后, array 此时开辟了一块儿新的空间, 因此此时 array 指向新的空间, fun1() 中的 array 存储的就是新的地址 0x33

b21f5a95f7804c6288b05cf7273a7130.png


当调用方法结束时, 我们打印的是实参的 array, fun1() 方法中的 array 指向 和实参中的array不一样, 并不影响我们输出, 因此输出结果为 {1,2,3,4}


再来看调用 fun2() 方法时, 传入 array 作为形参 此时 fun2() 中的 形参 array 指向同一块儿空间 {1,2,3,4}, 当我们去修改这块空间中的 array[1]元素时, 此时就变成了{1,99, 3,4}, 当我们调用完 fun2() 以后, 该空间中的 array[1] 已经被修改了 再来打印 array 输出时, 结果就为 {1,99,3,4}

1f5e7f7337cf4f0c9ce4ae62aa7cb474.png


五. 二维数组



1. 二维数组的创建


1 数据类型 [ ] [ ] 数组名 = new 数据类型 [行数] [列数]

2.Java 中二维数组的行数, 列数可用变量来指定

int row = 3;
int line = 3;
int[][] array = new int[row][line];


2. 数组的初始化

1.给数组分配空间大小, 但不能被修改, 在赋值

T[ ][ ] 数组名= new T[行数][列数];

int[][] array1 = new int[3][3];


2.通过 new 给数组直接赋值, 但不给定空间大小

T[ ][ ] 数组名= new T[行数][列数]{{值1, 值2, 值3}, {值4, 值5, 值6}, {值7, 值8, 值9}};

int[][] array2 = new int[][]{{1,2,3},{4,5,6},{7,8,9}};
// 编译器会自动计算 array 数组的大小


3.直接赋值, 不予分配空间大小

T[ ][ ] 数组名= {{值1, 值2, 值3}, {值4, 值5, 值6}, {值7, 值8, 值9}};

int[][] array3 = {{1,2,3},{4,5,6},{7,8,9}};


4.指定行数但不指定列数

T[ ][ ] 数组名= new T[行数][ ]

int[][] array4 = new int[3][];


3. 二维数组是特殊的一维数组

在一维数组中, 我们可以通过Arrays.toString() 方法来直接打印, 那么二维数组是不是也可以?

int[][] array1 = new int[3][3];
        System.out.println(Arrays.deepToString(array1));

ffb4aefb70244eb1a982bd6fca0adc5d.png

打印的结果却像我们在一维数组中直接输出一维数组看到的地址, 由此可以说明二维数组是特殊的一维数组, 借助前面的 JVM 运行时数据分区图可以知道


46e41d8b1b0d486387d65c2edcbe0518.png


那么, 在 Java 中我们是如何打印二维数组呢? 需要借助到一个方法

System.out.println(Arrays.deepToString(array1));


a58dae22a3c8454fa28144c2422d6f7c.png


4. 打印二维数组


  1. 常规打印
  int[][] array = new int[row][line];
        for (int i = 0; i < row; i++) {
           for (int j = 0; j < line; j++) {
               System.out.println(array[i][j] + " ");
           }
           System.out.println();
       }


此处并未进行初始化赋值, 因此每个值都是对应基本类型默认值

a16f7a9fcb894db7b1de9f228476be0f.png


对于常规打印循环条件来说, 如果行列数过大, 数是不现实的, 我们可以借助二维数组是特殊的一维数组这一特点, 写出 行, 列之间的关系

for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array[i].length; j++) {
                System.out.print(array[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println(Arrays.toString(array));


此时, array.length 描述的是行数, array[i],length 描述的是 每一行有多少列, 即可得出列数.

3834090388ff4814a848af2cdae01ece.png


2.迭代器打印

  int row = 3;
  int line = 3;
  int[][] array = new int[row][line];
        for (int[] arr : array) {
            for (int x : arr) {
                System.out.print(x+" ");
            }
            System.out.println();
        }


由于二维数组是特殊的一维数组, 因此在第一层循环中, 接收 array 的类型为 一个一维数组, 第二层才是这个一维数组的基本类型


b5f0fef1ab174d11add81e29d773e84c.png

相关文章
|
JavaScript 前端开发
JavaScript简介--语句--变量
JavaScript简介--语句--变量
|
存储 Java 编译器
数组的定义与使用【JavaSE】
数组的定义与使用【JavaSE】
40 0
|
存储 机器学习/深度学习 Java
【javaSE】 数组的定义与使用
【javaSE】 数组的定义与使用
|
7月前
|
存储 搜索推荐 Java
JavaSE学习值之--String类(三)
JavaSE学习值之--String类(三)
69 0
JavaSE学习值之--String类(三)
|
7月前
|
Java
JavaSE基础篇:枚举的高级用法示例
JavaSE基础篇:枚举的高级用法示例
|
7月前
|
Java
JavaSE基础篇:枚举
JavaSE基础篇:枚举
|
7月前
|
存储 Java 编译器
JavaSE学习--数据类型和运算符
JavaSE学习--数据类型和运算符
90 0
|
7月前
|
Java
JavaSE学习值之--String类(二)
JavaSE学习值之--String类(二)
65 0
|
7月前
|
存储 Java 编译器
JavaSE学习值之--String类(一)
JavaSE学习值之--String类(一)
58 0
|
存储 机器学习/深度学习 Java
【JavaSE】数组的定义与使用
【JavaSE】数组的定义与使用