文章目录
前言
李刚老师《JAVA疯狂讲义》第5版,第4章学习笔记。
1.什么是数组
JAVA中的数组也是一种数据类型,一种引用类型。
在JAVA中,要求所有的数组元素具有相同的数据类型,可以是基本数据类型,也可以是引用数据类型。一旦数组初始化完成,该数组在内存中的空间将被固定,其数组长度将不可变。
2.JAVA数组的定义
JAVA支持两种定义数组的语法:
type[] arrayName;
type arrayName[];
推荐使用第一种,语意更清晰,可读性更好。
注意:
- 由于JAVA中的数组为引用类型,因此上方的初始化仅仅是定义了一个引用变量,也就是定义了一个指针。但是,并未明确指向的内存,因此此时数组无法使用,必须在初始化之后才可以使用。
- 在定义数组时,不能指明数组长度。
- 不能只为数组分配内存空间,而不给数组赋初值。只要给数组分配内存空间,数组中的数组元素就有了值,即使内存是空的,也是null值。
3.JAVA数组的初始化
JAVA数组的初始化就是为数组的数组元素分配内存空间,并且为每个数组元素赋初值。JAVA中共有两种为数组初始化的方法:静态初始化、动态初始化。
3.1 静态初始化
人为指定数组中每个元素的初始值,数组长度可由系统自动获得。具体静态初始化方式如下:
//定义一个int类型数组
int[] intArr;
//静态初始化,只指定初值,不指定长度
intArr = new int[]{1,2,3,4,5};
//定义一个Object类型数组
Object[] objArr1;
//静态初始化1
objArr1 = new String[]{"good","good","study"};
//定义一个Object类型数组
Object[] objArr2;
//静态初始化2
objArr2 = new Object[]{"day","day","up"};
//借助var来静态初始化数组,编译器会自动推断变量类型
var a = new int[]{1,2,3,4,5};
//简化的静态初始化写法,定义和初始化可以同时完成
int[] a = {1,2,3,4,5}
通过以上代码可以看到:
- 静态初始化方法无需指明数组长度。
- Object类型数组的元素可以是String,也就是可以为其子类,并且,在静态初始化时,可以指明是new String,也可以不指明,依旧采用new Object进行赋值。因为,JAVA中,子类实例就是特殊的父类实例。
注意:
借助简化的静态初始化写法,同时定义和初始化数组时,不可以用var定义数组变量。
3.2 动态初始化
人为指定数组长度,数组元素由系统分配初值。具体如下:
//定义一个int类型数组
int[] intArr;
//动态初始化
intArr = new int[5];
//定义和动态初始化同时完成1
int[] intArr = new int[5];
//定义和动态初始化同时完成2
Object[] objArr = new String[5];
针对不同类型数组,系统按以下规则分配初值:
- 数组元素的类型为byte、short、int和long时,系统分配初值为0
- 数组元素的类型为float、double时,系统分配初值为0.0
- 数组元素的类型为char时,系统分配初值为’\u0000’
- 数组元素的类型为boolean时,系统分配初值为false
- 数组元素的类型为引用类型时,系统分配初值为null
注意:
不要同时使用静态初始化和动态初始化,也就是在进行数组初始化时,不要即指定数组的长度,也为每个数组元素分配初值。
4.JAVA数组的存储方式
想要真正掌握数组的相关内容,就必须理解JAVA中的数组在内存中的存储方式。
JAVA程序中的方法在执行时,每个方法都会建立自己的内存栈,在方法内部定义的局部变量,会一个一个放入这块栈内存中,当方法执行完毕,这块栈内存就会被销毁。
JAVA程序在执行时,会建立自己的内存堆,在程序中创建一个对象时,这个对象会被保存到这个内存堆中,以便反复利用,堆内存中的对象不会随着某一个方法的结束而销毁。只有当对象没有任何引用变量引用它的时候,系统垃圾回收器才会在合适的时候回收该对象。
综上,JAVA运行时占用的内存空间可分为栈内存和堆内存两部分,栈内存储存引用变量,堆内存储存对象。简单来说,变量就像快捷方式,而对象才是真正安装的软件(在JAVA中就是真正存储数据的部分)。
在理解了JAVA中的栈内存和堆内存后,就可以分析数组在内存中的存储方式了。具体如下:
public class demo{
public static void main(String args[]){
//1.定义并初始化数组
int[] a = {1,2,3};
int[] b = new int[4];
//2.赋值操作
b = a;
}
}
上述代码中,经过1.定义并初始化数组后,a,b在内存中的储存形式为:
可以看到,a,b两个引用变量在栈内存中存储,由于a先定义,因此a先入栈,b后入栈。两个数组内部的元素存储在堆内存中。
上述代码中,经过2.赋值操作后,内存中的存储形式变为:
可见,b和a均指向堆内存中同一块区域,对b的修改会同时影响到a。同时,原始的堆内存中的b数组处于没有引用变量指向的状态,等待系统垃圾回收机制回收。
综上,在看待数组时,一定要把数组看成两部分,一部分是数组的引用,存储在栈内存中;另一部分是实际的数组对象,存储在堆内存中。
对于引用类型数组的内存分析与基础类型数组的内存分析基本相同,只不过,引用类型数组的每个数组元素也是一个引用,具体如下:
public class demo{
public static void main(String args[]){
//1.定义一个引用类型数组并动态初始化(假定Person类已经定义过)
Person[] students;
students = new Person[2];
//2.创建两个Person实例,并赋值
Person zhang = new Person();
Person li = new Person();
zhang.age = 18;
zhang.height = 172;
li.age = 60;
li.height = 180;
//3.把zhang和li两个实例赋值给students这个引用数组
students[0] = zhang;
students[1] = li;
}
}
上述代码中,经过1.引用数组的定义与动态初始化后,内存中状态为:
Person的两个实例zhang和li也是两个引用类型,在经过2.创建两个Person实例,并赋值后,内存形式为:
在经过3.把zhang和li两个实例赋值给students这个引用数组后,内存中存储形式为:
可以想到JAVA中,多维数组本质上也是引用类型数组,只不过数组元素指向的引用对象又指向了一个数组。其在内存中的存储过程基本与上述类似,不再赘述。
5.JAVA数组的使用
5.1 JAVA数组的简单使用
数组最常用的使用方法就是访问数组元素,包括取出数组中元素值和对数组中元素进行赋值,与大多数编程语言相同,JAVA中的索引从0开始,具体如下:
public class demo{
public static void main(String args[]){
//定义并初始化数组
int[] a = {1,2,3,4,5};
//取出数组中某个元素
System.out.println(a[1]);
//对数组中的某个元素赋值
a[2] = 7;
}
}
当访问数组元素时,指定的索引值小于0,或者大于数组长度-1,则会抛出java.lang.ArrayIndexOutOfBoundsException:N(数组越界异常)。可以使用数组的length属性来查看某个数组的长度。
通过循环的方法可以遍历数组中的元素,例如:
int[] a = {1,2,3,4,5};
for(int i = 0 ; i < a.lenght; i++){
System.out.println(a[i]);
}
JAVA中也可以使用foreach循环来更为简洁的遍历数组中的所有元素,如下:
int[] a = {1,2,3,4,5};
for(int num : a){
System.out.println(num);
}
5.2 借助Arrays工具类操作数组
JAVA提供的Arrays类可以用于操作数组,使用时需要首先导入Arrays包(import java.utils.Arrays)具体如下:
- int binarySearch(type[] a,type key):使用二分法查询key元素值在a数组中出现的索引,若a不包含key,则返回负时(注意:调用该方法要求数组元素已经按升序排列)
- int binarySearch(type[] ,int fromIndex,int toIndex,type key):与1基本类似,但只寻找数组从fromIndex到toIndex索引的元素
- type[] copyOf(type[] original, int length):将original数组复制成一个新数组,length是新数组长度。若length>original.length,则新数组前面的元素就是original的元素,后面补0;若length<original.length,则新数组就是原数组前length个元素。
- type[] copyOfRange(type[] original, int length,int from,int to):与3基本类似,但是只复制original中索引从from到to的元素
- boolean equals(type[] a1, type [] a2):如果a1和a2完全相同,长度相等且元素也相同,则返回true,否则false
- void fill(type[] a , type val):该方法把数组a的所有元素赋值为val
- void fill(type[] a , int fromIndex, int toIndex, type val):与6类似,但仅将a中索引从fromIndex到toIndex的部分赋值为val
- void sort(type[] a):对a数组进行排序
- void sort(type[] a ,int fromIndex, int toIndex):与8类似,但是只对fromIndex到toIndex的部分排序
- String toString(type[] a):将数组转化为一个字符串,按顺序把多个数组元素连接,多个数组元素使用英文逗号,和空格隔开