10、Java基础教程之面向对象·第二讲

  • 本节学习目标
  • 1️⃣ 概念
    • 1.1 动态初始化
  • 1.2 静态初始化
  • 2️⃣ 二维数组
  • 3️⃣ 数组与方法参数的传递
  • 4️⃣ 数组排序
  • 5️⃣ 数组转置
  • 6️⃣ 对象数组
  • 7️⃣ 数组操作API
    • 7.1 数组复制
  • 7.2 数组排序
  • * 总结

*

本节学习目标

  • 掌握数组的动态及静态创建方式、使用及特征;
  • 掌握引用类型数据的特征;
  • 掌握数组的排序、转置操作;

1️⃣ 概念

数组可以将多个变量进行统一的命名,这样相同类型的元素就可以按照一定的顺序进行组合排列。在 Java中,数组属于引用类型数据,所以在数组的操作过程中,也一定会牵扯到内存的分配问题,下面详细介绍 Java中数组的基本使用。

1.1 动态初始化

数组指的就是一组相关变量的集合。例如,如果要定义100个整型变量,根据前面文章知识的思路,可能这样定义:
int i1, i2, ..., i100 ,一共定义100个变量。

以上定义形式的确可以满足需求,但是存在一个问题,即这100 多个变量没有任何逻辑上的关联关系,完全独立,就会出现不方便管理的情况。在这种情况下就可以利用数组来解决此类问题,而数组本身也属于引用数据类型,数组的定义语法如下:

  • 第一种方式:声明并开辟数组
数据类型 数组名称[] = new 数据类型[数组长度];
数据类型[] 数组名称 = new 数据类型[数组长度];

  • 第二种方式:分步完成
声明数组: 数据类型 数组名称[] = null;
开辟数组: 数组名称 = new 数据类型[数组长度];

当数组开辟空间后,可以采用 “数组名称 [下标 | 索引]”的形式进行访问,所有数组的下标都是从0开始的,例如一个长度为3的数组,下标的范围为:0~2 (0、1、2 ,一共3个索引,3个元素内容)。而如果访问的时候超过了数组允许下标的长度,则会出现数组越界异常 (Array IndexOutOfBoundsException)。

关于异常体系的更多信息可以浏览我的专栏:《专栏 :JAVA异常体系》

以上给出的数组定义结构使用的是动态初始化的方式,即数组会首先开辟内存空间,但这种方式下数组中的初始元素都是其对应数据类型的默认值,如果现在声明的是 int 型数组,则数组里面的全部元素都是默认值 0

由于数组是一种顺序的结构,并且数组的长度都是固定的,所以可以使用for 循环的方式输出,而Java 为了方便数组的输出,提供了一个“数组名称.length”的属性,可以取得数组长度。

//	范例 1: 定义数组
public class ArrayDemo{
   
     
	public static void main(String  args[]){
   
      
		int data[] = new int[3];		//声明并开辟了一个3个长度的数组
		data[0] =10;					//设置数组内容
		data[1] =20;
		data[2] =30;
		for(int x=0; x<data.length; x++){
   
      	//循环输出数组
			System.out.print(data[x]+"、");	
		}
	}		
}

程序执行结果:

10、20、30、

本程序首先声明并开辟了一个 int 型数组 data, 然后采用下标的方式为数组中的元素进行赋值,由于数组属于有序的结构,所以可以直接使用 for 循环进行输出。

数组最基础的操作就是声明,而后根据索引进行访问。而数组本身也属于引用数据类型,所以上边代码范例依然涉及到内存分配,数组与对象唯一的区别在于:对象中的堆内存保存的是对象属性,而数组中的堆内存保存的是一组信息。上边程序的内存结构关系如图所示:

*

图1程序的内存结构关系

上边案例使用了第一种声明并开辟数组空间的方式完成数组创建,而在数组定义中也可采用第二种先声明后开辟数组空间的方式完成。下边来看一下第二种数组构建方式。

//	范例 2: 分步实现数组创建操作
public class ArrayDemo{
   
     
	public static void main(String args[]){
   
     
		int data[] = null;		//声明数组
		data = new int[3];		//开辟数组空间 
		data[0]=10;				//设置数组内容 
		data[1]=20;
		data[2]=30;
		for(int x=0; x<data.length; x++){
   
     	//循环输出数组
			System.out.print(data[x]+"、");
		}
	}
}

程序执行结果:

10、20、30、

此程序首先声明了一个数组变量 data, 然后使用关键字 new 为数组开辟空间,通过索引为数组里的元素设置内容。程序的内存关系如下图所示:

*

图2程序的内存结构关系

需要注意的是,不能直接使用未开辟空间的数组。也就是说,在上边案例中,当代码执行到int data [] = null;时,此时此数组还无法使用。
因为数组本身属于引用数据类型,如果现在直接使用未开辟空间的数组,则一定会出现"NullPointerException(空指针异常)"。

数组本身属于引用数据类型,所以在数组的操作中依然可以进行内存空间的引用传递。下面使用一个代码案例来演示数组的引用传递。

//	范例 3: 数组的引用传递
public class ArrayDemo{
   
     
	public static void main(String args[]){
   
     
		int data[] = new int[3];	//声明并开辟了一个3个长度的数组
		data[0]= 10;	//设置数组内容
		data[1]= 20;
		data[2]= 30;
		int temp[] = data;	//数组引用传递
		temp[0]= 99;	//修改数组内容
		for(int x=0; x<data.length; x++){
   
     	//循环输出数组
			System.out.print(data[x]+" 、");
		}
	}
}

程序执行结果:

99、20、30、

此程序首先定义了一个 int型数组,然后为其元素赋值,接着又定义了另一个 temp 数组,并且此数组将直接指向 data数组的引用 (int temp[] = data), 最后利用 temp 变量修改了数组中的数据。程序的内存关系解析如下图所示:

*

图3程序的内存结构关系

1.2 静态初始化

通过以上操作可以发现,在数组的使用过程中首先是开辟新的数组,然后为数组中的每一个元素进行赋值,这种形式的操作属于数组动态初始化,它的操作特点是:先开辟数组空间,再为数组中的内容赋值。而在数组定义中还提供了静态初始化的操作,即数组定义的同时就设置好了相应的数据内容,其格式如下。

  • 格式一:简化格式
数据类型 数组名称[] = (值1, 值2, …);

  • 格式二:完整格式
数据类型 数组名称[] = new 数据类型[](值1, 值2, …);

下面使用一个代码案例来演示数组的静态初始化。

//	范例 4: 数组的静态初始化
public class ArrayDemo {
   
     
	public static void main(String args[]){
   
     
		int data[] = new int[](1,2,3,4,5);  // 数组的静态初始化
		for (int x=0; x<data.length; x++){
   
       // 循环输出数组
			System.out.print(data[x]+" 、");
		}
	}
}

程序执行结果:

1、2、3、4、5、

此程序采用静态初始化的方式实例化一个数组变量,而后采用循环的方式输出数组中的内容。

需要了解的是,虽然数组支持顺序的数据访问操作,但是数组有一个最大的缺点—长度不能被改变,所以正因为如此,在开发中才不会直接应用数组,但是会使用数组的概念(利用类集框架)来解决。Java的类集框架提供了动态调整大小的集合类型,解决了数组长度不可变的问题。应用类集框架可以更加灵活地操作和管理数据,使开发过程更加便捷和高效(后面文章会详细介绍)。

2️⃣ 二维数组

在之前的数组操作中可以发现,数组所保存的数据实际上就像单行多列的结构那样,只需要通过一个索引就可以进行数据的访问,这样的数组可以将其称为 一维数组,如下图所示。

*

图4一维数组

很多时候用户可能需要保存多行多列的数据,则可以使用二维数组来进行描述,而二维数组与一维数组最大的区别是在于,一维数组声明时只会有一个“[]", 二维数组会有两个“[]”(即“[][]”)。
形式上,二维数组就是一张数据表(多行多列), 其基本结构如下图所示。

*

图5二维数组

如果要在二维数组里面确定一个数据,需要行和列一起定位,例如:数字77的索引位置:1行3列 “[1][3]”。而对于二维数组的定义语法也有如下两类:

  • 动态初始化:
数据类型 数组名称[][] = new 数据类型[行数][列数];

  • 静态初始化:
数据类型 数组名称[][] = new 数据类型[][]{
     
        {
     
       值, 值, 值}, {
     
       值, 值, 值}};

通过定义结构可以发现,所谓的二维数组实际上就是将多个一维数组变为一个大的数组,并且为每一个一维数组设置一个行号。

//	范例 5: 观察二维数组的定义及使用
public class ArrayDemo  {
   
     
	public static void main(String args[]){
   
     
		int data[][] = new int[][]{
   
      {
   
     1,2,3},{
   
     4,5,6},{
   
     7,8,9} };	//定义二维数组
		for (int x=0; x<data.length; x++){
   
     		//外层循环是控制数组的数据行内容
			for (int y=0; y<data[x].length; y++){
   
      	//内层循环是控制数组的数据列内容
				System.out.print(data[x][y]+"\t");
				System.out.println();
			}
		}
	}
}

程序执行结果:

1	2	3
4	5	6
7	8	9

此程序采用静态初始化的方式定义了一个二维数组,由于二维数组需要两个数据控制索引值,所以 采用了双层循环的方式实现内容的输出。

需要注意的是,在实际应用中不太建议使用多维数组。从二维数组开始实际上就进入了一个多维数组的概念范畴,如果说一维数组表示的 是一行数据,那么二维数组描述的就是一张表的数据,依此类推,三维数组就可以描述出一个三维图形的结构,也就是说数组的维数越多所描述的概念就越复杂。

所以多维数组增加了代码的复杂性,使代码变得更难以理解和维护,引入了额外的维度和索引,使得阅读和修改代码变得困难。特别是在处理更高维度的情况下,代码可能会变得混乱。并且从空间占用来说,多维数组需要连续内存块来存储数据。对于大型多维数组,如果没有正确管理内存,会导致内存碎片问题和消耗较多的内存空间。

综上,在开发中只有很少的情况才会涉及多维开发。

3️⃣ 数组与方法参数的传递

既然数组内容可以进行引用传递,那么就可以把数组给方法中的参数,而如果一个方法要想接收参数,则对应的参数类型必须是数组。下面通过一个程序案例来进行说明。

//	范例 6: 一个数组传递的程序
public class ArrayDemo{
   
     
	public static void main(String args[]){
   
     
		int data[] = new int[]{
   
     1,2,3);		//开辟数组
		change(data);					//引用传递,等价:int temp = data;
		for(int x=0; x<data.length; x++){
   
     
			System.out.print(data[x]+" 、");
		}
	}
	
	/**
	* 此方法的主要功能是进行数组数据的改变操作,在本方法中会将数组中的每个元素内容乘2 
	* *@param  temp 要进行改变内容的数组引用
	*/
	public static void change(int temp[]){
   
     	//此方法定义在主类中,并且由主方法直接调用
		for(int x=0; x<temp.length; x++){
   
      
			temp[x] *= 2;	//将数组的内容乘2保存
		}
	}
}

程序执行结果:

2、4、6、

此程序首先利用数组的静态初始化定义了一个包含3个元素的数组,然后调用 change()方法接收此数组,实现了引用传递,相当于方法中定义的 temp 参数 (int 数组类型) 与主方法中的数组 data 指向了同一块内存空间,最后在 change()方法中修改了数组的内容 (将数组保存的每一个内容乘以2后重新保存)。程序的内存关系图如下所示。

*

图6程序的内存关系

4️⃣ 数组排序

Java数组排序是指对数组中的元素按照一定规则进行重新排列,使其按照升序或降序排列。排序是一种常见的算法操作,能够帮助我们在处理数据时更高效地查找、比较和操作元素。

通过对数组进行排序,可以轻松找到最小值、最大值、中位数等特定元素,提供更好的数据观察和处理能力。在实际应用中,排序也是解决各种问题的重要步骤之一。

以按升序排序为例,其基本原理如下:

  • 原始数据: 2 、1 、9 、0 、5 、3 、7 、6 、8;
  • 第一次排序: 1 、2 、0 、5 、3 、7 、6 、8 、9;
  • 第二次排序: 1 、0 、2 、3 、5 、6 、7 、8 、9;
  • 第三次排序: 0 、1 、2 、3 、5 、6 、7 、8 、9。

以上只是给出了排序的基础原理过程,根据数据的不同会出现不同的排序次数,但是不管有多少个 数据,总的排序次数不会超过数组的长度。所以只要排序的次数达到“长度 * 长度”,那么所有的数据一定可以排序成功。

//	范例 7: 排序基础实现:实现一个数组排序
public class ArrayDemo{
   
     
	public static void main(String args[]){
   
     
		int data[] = new int[]{
   
     2,1,9,0,5,3,7,6,8};
		System.out.print("排序前的数据:");	//排序前输出数据
		print(data);
		
		for (int x=0; x<data.length; x++){
   
     	//外层控制排序总体的次数
			for (int y=0; y<data.length -1; y++){
   
     	//内层控制每次的排序控制
				if (data[y] > data[y+1]){
   
     		//判断是否需要交换
					int t = data[y];
					data[y] = data[y+1];
					data[y+1] = t;
				}
			}
		}

		System.out.print("排序后的数据:");	//排序后的输出数据
		print(data);
	}
	
	/**
	* 此方法的主要功能是进行数组数据输出操作,在输出完成后会追加一个换行
	*@param  temp 要进行改变内容的数组引用
	*/
	public static void print(int temp[]){
   
       
		for(int x=0; x<temp.length; x++){
   
     
			System.out.print(temp[x]+" 、");
		}
		System.out.println();
	}
}

程序执行结果:

排序前的数据:2、1、9、0、5、3、7、6、8、
排序后的数据:0、1、2、3、5、6、7、8、9、

此程序为了方便专门提供了一个 print() 方法用于数组数据的打印输出,在进行排序时,会依次判断相邻两个数据间的大小关系来决定数据是否要进行交换。这种通过重复地交换相邻的元素将最大(或最小)的元素逐步“冒泡”到数组的末尾的排序法也叫“冒泡排序法”,冒泡排序是一种简单且易于理解的排序算法。

而在代码编写中主方法是作为程序的起点存在的,所有的程序起点都可以称为客户端。既然是客户端,所有的代码编写一定要简单,因此可以采用方法来封装排序的这块逻辑,因此上述案例程序可以改善设计为下边的形式。

//	范例 8: 改善设计实现:实现一个数组排序
public class ArrayDemo{
   
     
	public static void main(String args[]){
   
     
		int data[] = new int[]{
   
     2,1,9,0,5,3,7,6,8};
		sort(data);  	//实现排序
		print(data);
	}
	
	/**
	*数组排序操作,将接收到的数组对象内容进行升序排列
	*@param arr 数组对象的引用
	*/
	public static void sort(int arr[]){
   
     		//这个方法专门负责排序
		for(int x=0; x<arr.length; x++){
   
     		//外层控制排序总体的次数
			for(int y=0; y<arr.length- 1; y++){
   
     	//内层控制每次的排序控制
				if ( arr[y] > arr[y+1]){
   
     	//判断需要交换
					int t = arr[y];
					arr[y] = arr[y+1];
					arr[y+1] = t;
				}
			}
		}
	}

	/**
	* 此方法的主要功能是进行数组数据输出操作,在输出完成后会追加一个换行
	*@param  temp 要进行改变内容的数组引用
	*/
	public static void print(int temp[]){
   
       
		for(int x=0; x<temp.length; x++){
   
     
			System.out.print(temp[x]+" 、");
		}
		System.out.println();
	}
}

程序执行结果:

0、1、2、3、5、6、7、8、9、

此程序为了减少主方法中(客户端)的代码数量,编写了一个 sort()方法实现数组的排序,以及一个 print()方法进行数组内容的输出,可以发现当在主方法中只需要在 sort()方法中传递要排序的数组时,就可利用引用数据类型的特点在方法中实现数组的排序操作。

上述案例讲解了最简单的排序方法冒泡排序法,而Java还提供了许多其他排序算法的实现,如:

  • 插入排序:通过将每个元素插入已排序的子数组中来构建最终有序的数组。它逐个地将元素与其前面的元素进行比较,并插入到正确的位置。
  • 快速排序:使用分治的策略将数组划分为较小的子数组,然后对子数组进行排序。它选择一个基准元素,并根据其值将其他元素分为两个部分。
  • 归并排序(Merge Sort):采用分治的思想进行排序。其逻辑为:先将待排序的数组逐步分解成较小的子数组,直到每个子数组只包含一个元素,划分过程类似于二叉树的不断左右分裂。然后将各个子数组按照顺序两两合并,得到更大的有序子数组,直到最后只剩下一个有序数组,即排序完成。
  • 堆排序(Heap Sort):利用二叉堆这种数据结构进行排序。其逻辑为:先将将无序数组构建成一个二叉堆,即使满足二叉堆的性质,也称为堆有序。然后不断地取出当前堆中的最大值,将其放到已排序数组的末尾,并重新调整堆,重复该步骤直至堆为空。

选择合适的排序算法取决于具体的需求和数据集大小,在实际运用中可以根据不同情况选择最适合的算法来对Java数组进行排序。

5️⃣ 数组转置

在Java 中,数组转置是指将一个数组或矩阵的行和列进行交换,即行变成列,列变成行。数组转置可以通过对数组元素的重新排列来实现。

下面以一维数组为例来实现转置操作。
原始数组:1、2、3、4、5、6、7、8;
转置后的数组:8、7、6、5、4、3、2、1。

如果要想实现转置的操作,有以下两种方式。

  • 方式一:定义一个新的数组,然后将原始数组按照倒序的方式插入到新的数组中,最后改变原始数组引用,将其指向新的数组空间。
//	范例 9: 实现数组的转置
public class ArrayDemo {
   
     
	public static void main(String args[]){
   
     
		int data[] = new int[]{
   
     1,2,3,4,5,6,7,8};
		int temp[] = new int[data.length];         //首先定义一个新的数组,长度与原始数组一致
		int foot = data.length-1;                //控制 data数组的索引
		for(int x=0; x<temp.length; x++){
   
     		//对于新的数组按照索引由小到大的顺序循环
			temp[x] = data[foot];
			foot--;
		}									//此时temp 的内容就是转置后的结果
		data = temp;                 		//让data指向temp, 而data的原始数据就成为垃圾
		print(data);                       	//输出数组
	}
	
	public static void print(int temp[]){
   
        	//专门定义一个输出功能的方法
		for(int x=0; x<temp.length; x++){
   
     
			System.out.print(temp[x]+" 、");
		}
		System.out.println();
	}
}

程序执行结果:

8、7、6、5、4、3、2、1、

此程序首先为了实现数组的转置操作专门定义了一个 tempint 型数组,然后采用倒序的方式将 data 数组中的内容依次设置到 temp 数组中,最后修改 data 的引用(会产生垃圾空间)就可以实现数组的转置操作。此操作的内存关系图如下所示。

*

图7程序的内存关系

虽然以上代码实现了转置的操作,但是缺陷是会产生垃圾空间。而在进行程序开发的过程中应该尽可能少地产生垃圾空间,所以这样的实现思路并不是最合理的。

  • 方式二: 利用算法,在一个数组上完成转置操作,下面分析数组长度是奇数以及偶数的这两种情况。

数组长度为偶数时,转换次数等于:数组长度* 2
|-原始数组: 1、2、3、4、5、6 * 转换次数为:6*2=3
|-第一次转置:6、2、3、4、5、1
|-第二次转置:6、5、3、4、2、1
|-第三次转置:6、5、4、3、2、1
数组长度为奇数时,转换次数等于:数组长度* 2(不保留小数)
|-原始数组: 1、2、3、4、5、6、7 * 转换次数为:7*2=3 (不保留小数)
|-第一次转置:7、2、3、4、5、6、1;
|-第二次转置:7、6、3、4、5、2、1;
|-第三次转置:7、6、5、4、3、2、1。

可以发现,不管数组的长度是奇数还是偶数,转置次数的计算结果都是一样的,但是此时还需要有两个索引标记:头部索引标记 (head)、 尾部索引标记 (tail), 靠它们共同作用才可以实现数据的交换,操作形式如下图所示。

*

图8数组转置思路分析

按照上述思路分析及图示流程来编写一个用于数组转置的方法,代码案例如下。

//	范例 10: 数组转置
public class ArrayDemo{
   
     
	public static void main(String args[]){
   
     
		int data[] = new int[]{
   
     1,2,3,4,5,6,7};
		reverse(data);    	//实现转置
		print(data);    	//输出数组内容
	}
	
	/**
	* 实现数组的转置操作,操作过程中会执行“数组长度+2”次循环,以实现首尾依次交换
	*@param  arr 要进行转置的数组引用
	*/
	public static void reverse(int arr[]){
   
      	//此方法专门实现数组的转置操作
		int len = arr.length/2;		//转置的次数
		int head = 0;				//头部索引
		int tail = arr.length-1;	//尾部索引
		for(int x=0; x<len; x++){
   
     	//循环次数为数组长度-2
			int temp = arr[head];      //数据交换
			arr[head]= arr[tail];         
			arr[tail]= temp;          
			head++;                   	//头部索引增加
			tail--;                		//尾部索引减少
		}
	}

	public static void print(int temp[]){
   
       	//数组输出
		for(int x=0; x<temp.length; x++){
   
     
			System.out.print(temp[x]+" 、");
		}
		System.out.println();
	}
}

程序执行结果:

7、6、5、4、3、2、1、

此程序为了实现转置定义了一个 reverse()方法,在方法中首先计算要进行转置的次数,然后利用循环实现数据的交换,这样就可以实现在一个数组上的数据转置,也不会有垃圾空间产生。

以上一系列操作实现了方法接收数组的操作情况,而方法本身除了接收数组的引用外,也可以返回数组。

//	范例 11: 方法返回数组
public class ArrayDemo {
   
     
	public static void main(String args[]){
   
     
		int data[] = init();	//接收数组
		print(data);
		System.out.println("数组长度:"+ data.length);	//返回的数组可直接使用length 取得长度
	}

	/**
	* 数组初始化的操作方法,此方法可以返回一个数组的引用
	*@return 包含3个元素的数组对象
	*/
	public static int[] init(){
   
      		//方法返回数组
		return new int[]{
   
     1,2,3};      	//直接返回匿名数组
	}
	
	public static void print(int temp[]){
   
        	//数组输出
		for(int x=0; x<temp.length; x++){
   
     
			System.out.print(temp[x]+" 、");
		}
		System.out.println();
	}
}

程序执行结果:

1、2、3、
数组长度:3

此程序中 init() 方法的功能是返回一个数组,可以发现如果方法要返回数组时,只需要将其返回值类型定义为数组即可,而返回的数组可以直接接收 int data[]= init(), 也可以直接调用 length 属性取得长度。

6️⃣ 对象数组

数组是引用类型,而类也同样是引用类型,所以如果是对象数组的话则表示一个引用类型里面嵌套其他的引用类型。

在之前使用的数组都属于基本数据类型的数组,但是所有的引用数据类型也同样可以定义数组,这样的数组称为对象数组。如果要定义对象数组(以类为例),可以采用如下格式完成。

  • 格式:对象数组的动态初始化
类名称 对象数组名称[] = new 类名称[长度];

如果使用了对象数组的动态初始化,则默认情况下,数组的每一个元素都是其对应的默认值 null, 都需要分别进行对象的实例化操作。

  • 格式:对象数组的静态初始化
类名称 对象数组名称[] = new 类名称[]{
     
       实例化对象, 实例化对象, …};

下面通过代码案例分别验证两种实例化格式的使用。

//	范例 12: 对象数组的动态初始化
class Book{
   
     
	private String title;
	private double price;
	
	public Book(String t, double p){
   
     
		title = t;
		price = p;
	}
	// setter、getter、无参构造略
	
	public String getInfo(){
   
     
		return "书名:"+title+",价格:"+price;
	}
}

public class ArrayDemo {
   
     
	public static void main(String args[]){
   
     
		Book books[] = new Book[3];			//开辟了一个3个长度的对象数组,内容为 null
		books[0] = new Book("Java",79.8);	//对象数组中的每个数据都需要分别实例化
		books[1] = new Book("JSP",69.8);
		books[2] = new Book("Android",89.8);
		for(int x=0; x<books.length; x++){
   
      	//循环对象数组
			System.out.println(books[x].getInfo());
		}
	}
}

程序执行结果:

书名:Java, 价格:79.8
书名:JSP, 价格:69.8
书名:Android, 价格:89.8

此程序首先采用动态初始化的方式开辟了3个空间大小的 Book 对象数组,动态初始化后数组中的每个元素都是 null, 所以需要根据索引对数组中每一个元素的对象进行实例化操作。

//	范例 13: 对象数组的静态初始化
public class ArrayDemo {
   
     
	public static void main(String args[]){
   
     
		Book books[] = new Book[]{
   
     
			new Book("Java",79.8),
			new Book("JSP",69.8),
			new Book("Android",  89.8)};             //开辟了一个3个长度的对象数组
		
		for (int x=0; x<books.length; x++){
   
      		//循环输出对象数组内容
			System.out.println(books[x].getInfo());
		}
	}
}

程序执行结果:

书名:Java, 价格:79.8
书名:JSP, 价格:69.8
书名:Android,价格:89.8

此程序采用静态初始化的方式定义对象数组,这样数组在开辟之后每一个元素都会对应有一个具体的实例化对象。

对象数组的最大好处是将多个对象统一进行管理,并且除了数据类型改变外,和之前的数组没有任何区别,而且数组本身就属于引用数据类型,因此对象数组就是在一个引用数据类型中嵌入其他引用数据类型。如果用内存图表示的话,可以简单地理解为下图所示的结构。

*

图9程序内存关系

7️⃣ 数组操作API

Java 本身针对数组提供了类库的支持,下面来介绍及几个与数组有关的类及操作方法。

7.1 数组复制

数组复制可以将一个数组的部分内容复制到另外一个数组之中。其语法如下。

System.arraycopy(源数组名称, 源数组复制开始索引, 目标数组名称, 目标数组复制开始索引, 长度)

使用案例如下:

//	范例 14: 实现数组复制
public class ArrayDemo {
   
     
	public static void main(String args[]){
   
     
		int dataA[] = new int[]{
   
     1,2,3,4,5,6,7,8};                    //定义数组
		int dataB[] = new int[]{
   
     11,22,33,44,55,66,77,88};			//定义数组
		System.arraycopy(dataA, 4, dataB, 2,3);                  	//数组复制
		print(dataB);
	}
	
	public static void print(int temp[]){
   
                             	//打印数组内容
		for(int x=0; x<temp.length; x++){
   
     
			System.out.print(temp[x]+" 、");
		}
		System.out.println();
	}
}

程序执行结果:

11、22、5、6、7、66、77、88、

本程序直接利用 System.arraycopy() 方法实现了数组内容的部分复制。将数组 A 的从索引为4开始的3个元素替换到数组 B 中的从索引为2开始的位置。

7.2 数组排序

数组排序可以按照由小到大的顺序对基本数据类型的数组(例如: int 数组、 double 数组都为基本类型数组)进行排序。其语法如下:

java.util.Arrays.sort(数组名称);

java.util 是一个Java的系统包的名称,对于包的分析可以参考我的专栏:《JAR包解析》

Arrays是一个JDK提供的工具类,在后面的文章中会详细讲解,此处只做一个大概了解。

//	范例 15: 实现排序
public class ArrayDemo {
   
     
	public static void main(String args[]){
   
     
		int data[] = new int[]{
   
     3,6,1,2,8,0};
		Arrays.sort(data);   		//数组排序
		print(data);
	}
	
	public static void print(int temp[]){
   
                      //数组输出
		for(int x=0; x<temp.length; x++){
   
     
			System.out.print(temp[x]+" 、");
		}
		System.out.println();
	}
}

程序执行结果;

0、1、2、3、6、8、

此程序直接使用 java.util.Arrays.sort() 方法实现了整型数组的排序操作,但是在现阶段使用的过程中,此类排序方式只适合基本数据类型数组,如 int[]double[]char[] 等,而由于有其他的开发要求,引用数据的排序暂不适用。

* 总结

本文介绍了一些与数组相关的概念和技术。首先,讲解了动态初始化和静态初始化,它们是创建数组的两种方式。然后,探讨了二维数组的概念和用法,以及如何通过索引来访问二维数组的元素。

接下来,探讨了数组作为方法参数的传递方式。在 Java 中,数组是按引用传递的,意味着数组作为参数传递给方法时,实际上传递的是数组的引用地址,可以在方法中修改原始数组。

随后,简单介绍了常见的数组排序算法,主要介绍了冒泡排序,当然还包括选择排序、插入排序、归并排序、快速排序、堆排序等。每种排序算法都有不同的特点和适用场景,根据需求选择合适的算法可以提高数组排序的效率。

此外,阐述了数组转置的概念及实现方法。数组转置是将数组的行和列进行交换,可以使用新数组或在原数组上操作来进行转置。还介绍了对象数组的概念,说明了如何创建和使用一个包含对象的数组。

最后,简单提到了数组操作的 API,Java 提供了一系列用于操作数组的方法,例如复制数组、查找元素、填充数组等。这些方法可以方便地对数组进行各种常见操作,提高编码效率。

综上所述,理解和掌握了动态及静态初始化、二维数组、数组与方法参数的传递、数组排序、数组转置、对象数组以及数组操作 API 等概念和技术,能够更灵活地运用数组来解决实际问题,并提高程序的效率。


[* ]nbsp_nbsp 10