疯狂Java笔记:4.2 深入数组

时间:2021-10-14 19:36:06

4.5 数组类型

常用的数据结构,可用于存储多个数据,通过数组元素的索引来访问数组元素。

4.5.1 理解数组:数组也是一种类型

  • 所有的数组元素具有相同的数据类型,因此在一个数组中,数组元素的类型是唯一的。
  • 数组一旦初始化完成,该数组所占用的内存空间也固定,因此数组长度不可改变[即使清空数组元素]
  • 数组是一种数据类型,本身是一种引用类型,可以存储基本类型/引用类型的数据。eg:int是基本类型,int[]是引用类型

4.5.2 定义数组

定义数组时只是定义了一个引用变量[用于存放地址],尚未指向任何有效内存。

因此不能定义数组长度,且该引用变量还不能使用,只有对数组进行初始化后才可以使用

public static void initTest(){
// 定义数组有两种语法格式:
// 1、type[] arrayName;[推荐用法]
int[] arrayName;
// 2、type arrayName[];
int arrayName1[];
}

4.5.3 数组的初始化

  • 数组必需初始化后才可以使用。初始化就是为数组的数组元素分配内存空间,并赋初始值
  • 不能只分配内存空间而不赋初始值,因为一旦为数组元素分配了内存空间,空间里存储的内容就是该数组元素的值[如果内容为空,则该元素是null值]
public static void initTest(){
// 数组的初始化有两种方式:
// 1. 静态初始化:
// 程序员显式指定每个数组元素的初始值,由系统决定数组长度
arrayName=new int[]{1,2,3,4,5};

Object[] objArray;
objArray=new Object[]{"Java","梦无涯"};
// 初始化数组时数组元素的类型可以是定义时指定类型的子类,从而实现 同个数组存储不同类型数据 的假象
objArray=new String[]{"Java","梦无涯"};
// 静态初始化简化写法[定义数组的同时执行初始化才能简化]
int[] intArray={1,2,3};

// 2. 动态初始化:
// 程序员只指定数组长度[即为每个数组元素指定所需的内存空间],由系统分配初始值
arrayName=new int[10];
}
  • 不要同时使用静态/动态初始化,即不要指定长度同时分配初始值
  • 系统自动分配初始值规则:
    • byte/short/int/long->0
    • float/double->0.0
    • char->’\u0000’
    • boolean->false
    • 引用类型[类,接口,数组,String]->NULL

4.5.4 使用数组

使用数组:访问数组元素[赋值和取值]、获得数组长度

public static void useTest(){
int[] intArray={1,2,3};
// 数组元素的索引值从0开始
// 索引值小于0或大于等于数组长度时,编译不出错
// 但运行时出现运行时异常[数组索引越界异常]:java.lang.ArrayIndexOutOfBoundsException: -3
// System.out.println(intArray[-3]);
char[] charArray=new char[5];
charArray[0]='A';
for(int i=0;i<charArray.length;i++){
System.out.println(charArray[i]);
}
}

4.6 深入数组

4.6.1 内存中的数组

  • 数组是引用数据类型,数组引用变量只是一个引用[该引用变量可以指向任何有效内存,只有当指向有效内存后,才可以通过该变量访问数组元素]
  • 数组元素和数组变量在内存里是分开存放的。定义并初始化一个数组后,将在内存中分配两个内存区域
    • 栈内存存放数组引用变量
    • 堆内存存放实际数组对象。
    • ==实际数组对象只能通过引用变量访问==。若将该数组引用变量赋值为null,切断数组引用变量和实际数组之间的引用关系,实际数组对象在堆内存中也就成了垃圾,只能等待系统回收。
  • 数组在内存中的示意图:疯狂Java笔记:4.2 深入数组

注意:

  • 方法执行时会创建自己的内存栈,方法结束时该栈被销毁。局部变量都放在栈中。
  • 创建对象时,对象被保存到运行时数据区,即堆内存,堆内存中的对象不随方法结束而销毁。
  • 只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会回收它。
public class ArrayInRam{
public static void main(String[] args){
// 定义并初始化数组,使用静态初始化
int[] a = {5, 7 , 20};
// 循环输出a数组的元素
for (int i = 0 ,len = a.length; i < len ; i++ ){
System.out.println(a[i]);
}
System.out.println("a数组的长度为:" + a.length);

System.out.println();

// 定义并初始化数组,使用动态初始化
int[] b = new int[4];
// 输出b数组的长度
System.out.println("b数组的长度为:" + b.length);
// 循环输出b数组的元素
for (int i = 0 , len = b.length; i < len ; i++ ){
System.out.println(b[i]);
}

// 只要类型相互兼容,就可以让一个数组变量指向另一个数组变量,从而产生数组长度可变的错觉
// 事实上只是数组引用变量的赋值,即 将实际数组a的地址赋值给变量b,而堆内存中的数组对象a和b并没有任何改变
// 变量a从指向数组对象a变成指向数组对象b,数组对象a没有变量指向,成为垃圾等待系统回收,但其长度仍为3,直至其消失
a = b;
// 再次输出b数组的长度
// 实际上不是数组长度可变,而是数组变量指向的地址改变了而已,即指向的数组对象变了。
System.out.println("a数组的长度为:" + a.length);
}
}

4.6.2 基本类型数组的初始化

数组元素的值直接存储在对应的数组元素中,因此初始化数组时,先为该数组分配内存空间,然后直接将数组元素的值存入对应的数组元素中

// 1. 定义数组变量:尚未指向任何内存,也就无法指定数组长度
int[] iArr;
// 2. 动态初始化:在堆内存中分配空间创建一个长度为5的数组对象,且为所有数组元素分配默认值0.并将其地址赋值给数组引用变量iArr
iArr=new int[5]
// 3. 循环为数组元素赋值
for(int i=0;i<iArr.length;i++){
iArr[i]=i;
}

执行上面3步代码时内存中的数组示意图分别如下:

疯狂Java笔记:4.2 深入数组

疯狂Java笔记:4.2 深入数组

疯狂Java笔记:4.2 深入数组

4.6.3 引用类型数组的初始化

引用类型数组的数组元素是引用,每个数组元素存储的是引用,指向另一块内存,该内存才存放了有效数据[对象]

package jwz;

class Person{
public int age; // 年龄
public double height; // 身高
// 定义一个info方法
public void info(){
System.out.println("我的年龄是:" + age + ",我的身高是:" + height);
}
}
/**
* 引用类型数组测试
*/

public class Test{
public static void main(String[] args){
// 对比:定义一个int类型的数组引用变量students,int类型只是基本类型
// int[] students;
// 对比:定义一个Person类型的数组引用变量students,Person类型本身也是引用类型
Person[] students;
// 执行动态初始化
students = new Person[2];
// 创建一个Person实例,并将这个Person实例赋给Person类引用变量zhang
Person zhang = new Person();
// 为zhang所引用的Person对象的age、height赋值
zhang.age = 15;
zhang.height = 158;
// 创建一个Person实例,并将这个Person实例赋给Person类引用变量lee
Person lee = new Person();
// 为lee所引用的Person对象的age、height赋值
lee.age = 16;
lee.height = 161;
// 将zhang变量的值赋给数组引用变量students的第一个数组元素
students[0] = zhang;
// 将lee变量的值赋给数组引用变量students的第二个数组元素
students[1] = lee;
// 下面两行代码的结果完全一样,因为lee和students[1]指向的是同一个Person实例。
lee.info();
students[1].info();
System.out.println(lee.equals(students[1]));
lee.height=180;
lee.info();
students[1].info();
}
}

引用类型数组的内存示意图:

疯狂Java笔记:4.2 深入数组

4.6.4 没有多维数组

多维数组的实质是一维数组,即一维数组的元素是另一个数组的引用时构成二维数组

package jwz;
/**
* 二维数组测试
*/

public class TwoDemisionTest{
public static void main(String[] args){
// 定义一个二维数组
int[][] a;
// 把a当成一维数组进行初始化,初始化a是一个长度为4的数组
// a数组的数组元素又是引用类型
a = new int[4][];
// 把a数组当成一维数组,遍历a数组的每个数组元素
for (int i = 0 , len = a.length; i < len ; i++ ){
System.out.println(a[i]);
}
// 初始化a数组的第一个元素
a[0] = new int[2];
// 访问a数组的第一个元素所指数组的第二个元素并为其赋值
a[0][1] = 6;
// a数组的第一个元素是一个一维数组,遍历这个一维数组
for (int i = 0 , len = a[0].length ; i < len ; i ++ ){
System.out.println(a[0][i]);
}

// 同时初始化二维数组的2个维数
int[][] b = new int[3][4];
System.out.println(b[0]); // 已经指向有效内存,输出b[0]指向的数组对象
System.out.println(b[0][0]);// b[0]所指向的数组的第一个元素

// 使用静态初始化的语法来初始化一个二维数组
String[][] str1 = new String[][]{new String[3] , new String[]{"hello"}};
System.out.println(str1[1][0]);

// 使用简化的静态初始化语法来初始化二维数组
String[][] str2 = {new String[3] , new String[]{"hello"}};
System.out.println(str2[0]);
System.out.println(str2[0][0]);
System.out.println(str2[1][0]);
}
}

示意图:

疯狂Java笔记:4.2 深入数组

疯狂Java笔记:4.2 深入数组

疯狂Java笔记:4.2 深入数组

4.6.5 增强的工具类 Arrays

Arrays 工具类,查阅API自主学习

4.6.6 数组的应用

浮点数转换成人民币读法

控制台五子棋