数组与二维数组

一.数组

1.定义:数组是存储同一种数据类型数据的集合容器

2.数组的定义格式:

(1)格式一:

数据类型[ ]   数组名 = new 数据类型[长度];

如:int[ ]  arr = new int[5];

(2)格式二:

数据类型[ ]  数组名 = new 数据类型[ ]{元素1,元素2,……};

如:int[ ]  arr = new int[ ]{3,5,1,7};

int[ ]  arr = {3,5,1,7};

3.分析数组:

(1)左边:声明数组

int[ ]  arr : 声明了一个int类型的数组变量,变量名为arr。

int : 表明该数组容器只能存储int类型的数据。

[ ] : 这是一个数组类型。

arr : 数组名,变量名。

(2)右边 :创建数组

new int[5] : 创建了一个长度为5的int类型数组对象。

new : 创建数组对象的关键字。

int : 表示该数组对象只能存储int类型数据。

[ ] : 表示数组类型。

5 : 数组的容量,该数组最多能存储5个数据。

4.数组的好处:对分配到数组对象中每一个数据都分配一个编号(索引值、角标、下标),索引值的范围是从0开始,最大是:长度-1。

5.补充:

(1)局部变量:如果一个变量是在一个方法(函数)的内部声明的,那么该变量就是一个局部变量。

(2)成员变量:成员变量就是定义在方法之外,类之内的。

二.数组的内存分析

1.实例一:

(1)需求:求数组的长度并且遍历查看数组中的所有元素。

class Demo1 {
	public static void main(String[] args){
		//定义一个数组
		int[] arr = new int[4];
		arr[0] = 10;
		arr[1] = 30;
		arr[2] = 50;
		arr[3] = 90;
		//数组有一个length属性,可以查看数组的容量
		System.out.println("数组的容量:"+arr.length);
		//查看数组中的所有数据
		for(int index = 0;index < arr.length;index++){
			System.out.println(arr[index]);
		}
	}
}

(2)运行结果如下图所示:


(3)注意:

内存分析图中,arr存储的是对象的内存地址,所以,System.out.println(arr);的结果是输出arr的内存地址,并不能查看数组中的所有数据元素。

(4)实例一内存分析


JVM管理的其中两片内存,一片栈内存,一片是堆内存。arr是在main方法内部声明的,属于局部变量,而局部变量都是存储在栈内存中。所以首先会在栈内存中声明一个名字为arr的变量,接着new一个int类型的数组,JVM会在堆内存中开辟一片空间用于创建一个数组对象,在这片空间中创建了一个数组容量为4的数组对象,把这片空间平均划分成四等份,紧接着会给每一份空间从0开始分配一个编号,也就是我们常说的索引值,索引值从0开始,最大为数组长度-1;而这个数组对象是在内存中进行划分的,每一片内存都有地址,假设这片空间的内存地址为0x98,而等号是赋值运算符,这时候的等号是把数组对象的内存地址赋予给arr变量,一旦执行赋值运算符,此时arr记录的就是0x98,所以arr指向的就是这个内存的内存地址。此时就需要往划分好的每一个格子里存放数据,假设要往索引值为2的格子里存储90,首先要先找到这片内存空间,整个这片内存空间(0x98)可以看作是一栋楼,每栋楼有很多个房子,索引值就相当于每个房间的门牌号,arr这个变量记录了这个数组对象的内存空间,找到arr也只是找到这栋楼而已,要进入某一个房间是需要由门牌号确定的,所以arr[2] = 90;就可以将数据存储在索引值为2的格子里。如果直接输出arr,那么得到的结果会是这个数组对象的内存地址。

2.实例二

(1)实例:

class Demo2 {
	public static void main(String[] args){
		int[] arr1 = new int[2];
		int[] arr2 = new int[2];
		arr1[1] = 10;
		arr2[1] = 20;
		System.out.println(arr1[1]);//10
	}
}

(2)运行结果如下图所示:

(3)实例二内存分析:


   ①第一句代码首先在栈内存中声明了一个变量arr1,然后在堆内存中new一个容量为2的数组对象,把这片内存空间分成两等份,并分配索引值0和1。比如它的内存地址还是0x98,这时候的等号相当于把这个内存地址赋予给了arr1,也就是说,arr1记录了这个数组对象。

   ②第二句代码首先在栈内存中声明了一个变量arr2,然后在堆内存中new一个容量为2的数组对象,把这片内存空间分成两等份,并分配索引值0和1。比如它的内存地址还是0x67,这时候的等号相当于把这个内存地址赋予给了arr2,也就是说,arr2指向了这个数组对象。

   ③第三句代码通过arr1找到第一个数组对象,然后找索引值为1的格子,在这片内存中存储10。

   ④第四句代码通过arr2找到第二个数组对象,然后找索引值为1的格子,在这片内存中存储20。

   ⑤第五句代码输出的是arr1[1],要输出的是第一个数组对象中索引值为1号的内存空间所存储的值,由以上分析可知,这片内存空间中存储的值是10。

3.实例三:

(1)实例:

class Demo3{
	public static void main(String[] args){
		int[] arr1 = new int[2];
		arr1[1] = 100;
		int[] arr2 = arr1;
		arr1[1] = 10;
		arr2[1] = 20;
		System.out.println(arr1[1]);//20
	}
}

(2)运行结果如下图所示:


(3)实例三内存分析:


   第一句代码首先在栈内存中声明了一个变量arr1,然后在堆内存中new一个容量为2的数组对象,把这片内存空间分成两等份,并分配索引值0和1。比如它的内存地址还是0x98,这时候的等号相当于把这个内存地址赋予给了arr1,也就是说,arr1记录了这个数组对象。

   ②第二句代码通过arr1找到第一个数组对象,然后找索引值为1的格子,在这片内存中存储100。

   ③第三句代码是把arr1所存储的值赋予给了arr2,而arr1记录的是内存地址,也就是说此时arr2也记录了0x98。

   ④第四句代码通过arr1找到这个数组对象,然后找索引值为1的格子,在这片内存中存储的值改为10。

   ⑤第五句代码通过arr2找到这个数组对象,然后找索引值为1的格子,又找到了这片内存,然后在这片内存中把存储的值改为20。

   ⑥第六句代码输出的是arr1[1],要输出的是第一个数组对象中索引值为1号的内存空间所存储的值,由以上分析可知,这片内存空间中存储的值变成了20。

综上所述,输出的值变为20的根本原因是它们操作的是同一个数组对象

三.数组中最常见的问题:

1. NullPointerException 空指针异常

(1)原因:

是因为引用类型变量没有指向任何对象,而访问了对象的属性或者是调用了对象的方法所引起的。

(除了8种数据类型以外的所有数据类型都是引用数据类型)

(2)实例:

class Demo1{
	public static void main(String[] args){
		int[] arr = new int[2];
		arr = null;
		arr[1] = 10;
		System.out.println(arr[1]);	//报错
	}
}

(3)运行结果:


(4)内存分析:


   ①第一句代码首先在栈内存中声明了一个变量arr,然后在堆内存中new一个容量为2的数组对象,把这片内存空间分成两等份,并分配索引值0和1。比如它的内存地址还是0x98,这时候的等号相当于把这个内存地址赋予给了arr,也就是说,arr记录了这个数组对象。

   ②第二句代码的意思是让该变量不要引用任何的对象,不要记录任何的内存地址,此时堆内存中的对象已经使用完毕。因为以前是通过了一个变量记录了这个对象的内存地址,而现在没有任何变量记录了这个对象的内存地址,所以此时就没法找到这个对象,这时这个对象就被称为垃圾对象。

   ③第三句代码因为此时arr找不到数据对象,所以更不可能给1号元素赋值。所以输出1号元素的值会报错空指针异常。

2.ArrayIndexOutOfBoundsException 索引值越界(下标越界)。

(1)原因:

访问了不存在的索引值。

(2)实例:

class Demo2 {
	public static void main(String[] args){
		int[] arr = new int[4];
		arr[0] = 10;
		arr[1] = 30;
		arr[2] = 40;
		arr[3] = 50;
		//遍历的时候最容易出现索引值越界,错在了<=,而应该是去掉等号。
		for(int index = 0;index <= arr.length;index++){
			System.out.println(arr[index]+",");
		}
	}
}

(3)运行结果:

(4)分析:

在遍历的时候最容易出现索引值越界,本实例中错在了<=,去掉等于号后则可以正确输出。因为如果加上等于号,相当于要输出System.out.println(arr[4]);访问索引值为4的内存空间存储的值,所以数组下标越界。

四.数组的初始化方式:

1.方式一:动态初始化

数据类型[ ]  变量名 = new 数据类型[长度];

如:

int[] arr = new int[10];

2.方式二:静态初始化

数据类型 [ ]  变量名 = {元素1,元素2,……};

如:

int[] arr = {10,20,30,40,50};

3.注意:

(1)如果程序一开始就已经确定了数据,那么这时候建议使用静态初始化;如果数据一开始还不太明确,这时候就建议使用动态初始化。

(2)静态初始化创建的数组对象也是在堆内存空间中创建的,只不过是JVM帮你省略了new,但是它在内存中实在创建的时候,JVM会帮你加上new关键字

五.实例:

1.实例一

(1)需求:

定义一个函数接收一个int类型的数组对象,找出数组对象中的最大元素返回给调用者。

(2)实例

class Demo3 {
	public static void main(String[] args){
		int[] arr = {-12,-14,-5,-26,-4};
		int max = getMax(arr);
		System.out.println("最大值:"+max);
	}
	public static int getMax(int[] arr){
		int max = arr[0];	//用于记录最大值
		for(int i = 1;i<arr.length;i++){
			if(arr[i] > max){	//如果发现有元素比max大,那么max变量就记录该元素。
				max = arr[i];
			}
		} 
		return max;
	}
}

(3)运行结果如下图所示:


注意:如果开始时定义int max = 0;那么如果要比较的数都是负数,则最大值就变为0,所以应该假设最大值的初始值就是第一个元素。



五.数组的常用工具类:

1.常用工具类(Arrays):

(1)遍历:

toString( ):将数组的元素以字符串的形式返回。

(2)排序:

sort( ):将数组按照升序排列。

(3)查找:

binarySearch( ):在指定数组中查找指定元素,返回元素的索引,如果没有找到则返回插入点-1。

注意:使用查找功能的时候,数组一定要先排序。

2.实例:

import java.util.*;
class Demo5{
	public static void main(String[] args){
		int[] arr = {12,3,1,10,8};
		//排序的方法:
		Arrays.sort(arr);
		System.out.println("----------toString()方法的运行结果----------");
		String info = Arrays.toString(arr);
		System.out.println("数组的元素:"+info);
		System.out.println("----------binarySearch()方法的运行结果----------");
		int index = Arrays.binarySearch(arr,5);
		System.out.println("找到的索引值:"+index);
	}
}

运行结果如下图所示:

注意:

数组工具类中的二分法查找binarySearch();如果能在数组中找到对应的元素,那么就返回该数据的索引值,如果没有找到就返回一个负数表示。

六.二维数组:

1.定义:

二维数组就是数组中的数组。实质是存储着一维数组。

2.二维数组的定义格式:

数据类型[ ]  变量名 = new 数据类型[长度1][长度2];

可以简单地理解为一根烟是一个变量;一盒烟是一维数组;一条烟是二维数组。长度1指的是一条烟有多少盒,长度2指的是一盒烟有多少根。

3.二维数组的初始化方式:

(1)动态初始化

数据类型[ ]  变量名 = new 数据类型[长度1][长度2];

(2)静态初始化

数据类型[ ]  变量名 = {{元素1,元素2,……},{元素1,元素2,……},{元素1,元素2,……}……};

4.实例一

(1)实例

class Demo6{
	public static void main(String[] args){
		//定义了一个二维数组
		int[][] arr = new int[3][4];
		arr[1][1] = 100;
		System.out.println("二维数组的长度:"+arr.length);//3
		System.out.println("arr[1]的长度:"+arr[1].length);//4
		System.out.println("数组的元素:"+arr[1][1]);
	}
}

运行结果如下图所示:


(2)内存分析:


   ①为什么System.out.println(arr.length);的输出结果是3?

首先在栈内存中声明一个arr的变量,然后在堆内存中new一个int类型的长度为3的数组对象,把这片空间分成3等份,然后给它们分配对应的索引值,假如这个数组对象的内存地址是0x98。紧接着还会有三个长度为4的一维数组,每一个一维数组又都会有一片内存地址,假如是0x67,0x56,0x45。也就是说刚开始的三个格子分别记录了0x56,0x67,0x45.而栈内存的arr记录了0x98,实际上它的数据都是存储在了下面的一维数组中了,这时候就体现了二维数组是数组的数组。

   ②为什么System.out.println(arr[1].length);的输出结果是4?

因为arr是记录了刚开始的那个一维数组的内存地址,而这个一维数组的长度正好是3,而如果换成了arr[1].length,那么arr找到了索引值为1号的内存空间,而这个内存空间记录了0x67这个内存地址,所以这时候输出的是4.

5.实例二:遍历输出二维数组中的元素

(1)实例

class Demo7{
	public static void main(String[] args){
		int[][] arr = {{10,11,9},{67,12},{33,35,39,40}};
		//遍历二维数组
		for(int i = 0;i<arr.length;i++){
			for(int j = 0;j<arr[i].length;j++){
				System.out.print(arr[i][j]+",");
			}
			//换行
			System.out.println();
		}
	}
}

运行结果如下图所示:


七.总结:

1.数组的特点:

(1)数组只能存储同一种数据类型的数据。

(2)数组是会给存储到数组中的元素分配一个索引值的,索引值从0开始,最大的索引值是length-1。

(3)数组一旦初始化,长度固定

(4)数组中的元素与元素之间的内存地址是连续的。

注意:并不是说数组对象是连续的。比如在二维数组中,arr[1][1]和arr[1][2]它们的内存地址是连续的,如果arr[1][1]的内存地址是0x98,那么arr[1][2]是0x99。并不是说如果arr[1]是0x98,则arr[2]是0x99,因为arr[1]和arr[2]是数组对象。

2.实例:

(1)代码:

class Demo8{
	public static void main(String[] args){
		int[] arr = new int[3];//长度为3
		arr = new int[4];//长度为4,并没有改变之前的数组对象的长度,只是新指向了另一个数组  					//对象。
		System.out.println(arr.length);
	}
}
(2)运行结果:


(3)分析:

首先在内存中声明一个arr的变量,在堆内存中new一个int类型的长度为3的数组,把这片内存空间分成3份,假如说内存地址是0x98,此时arr就记录了0x98,arr指向这个数组对象。

紧接着arr = new int[4];这时候在堆内存会创建一个新的数组对象,它的长度是4,假设它的内存地址是0x86,而等号就会把这个内存地址赋值给arr,也就是说这时候这个变量不再记录0x98,而是记录0x86,也就是arr指向了新的数组对象,所以最后的输出结果会是4而不是3。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

处女座的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值