一、介绍
所谓数组,是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式。这些无序排列的同类数据元素的集合称为数组。
数组是用于储存多个相同类型数据的集合。
各个语言中都有数组,但Java中是有区别的,Java中只有一维数组,没有多维数组,多维数组使用数组的数组来表示的。所以下面介绍的数组主要以一维数组来表示。
当数组中每个元素都只带有一个下标时,称这样的数组为一维数组。
一维数组是由数字组成的以单纯的排序结构排列的结构单一的数组。一维数组是计算机程序中最基本的数组。二维及多维数组可以看作是一维数组的多次叠加产生的。
二、内容
1、数组概念
软件的基本功能是处理数据,而在处理数据时,必须先进行数据持有,将数据持有之后,再对数据进行处理。我们将程序中可以临时存储数据的部分叫做容器。
Java当中具有持有数据功能的容器中,数组是最基本的,也是运算速度最快的。
2、数组的定义格式
数组是引用类型的一种,其使用方式与引用类型类似,均使用new关键字创建对象为变量赋值。
格式1:数据类型[] 数组名 = new 数据类型[元素个数或数组长度];
格式2:数据类型 数组名[] = new 数据类型[元素个数或数组长度];
注意:这两种定义做完了,数组中是没有元素值的。如何对数组的元素进行初始化呢?
示例:int[] arr = new int[5];
还有其他格式如下:
int[] arr = new int[]{20,5,100,30,5}
int[] arr = {20,5,100,30,5}
int arr[] = new int[4];//源码中经常会看到的定义方式
3、数组初始化
概述
Java中的数组必须先初始化,然后才能使用。
所谓初始化:就是为数组中的数组元素分配内存空间,并为每个数组元素赋值。
数组的初始化方式
动态初始化:初始化时只指定数组长度,由系统为数组分配初始值。
静态初始化:初始化时指定每个数组元素的初始值,由系统决定数组长度。
3.1、数组内元素均有默认初始化值
在创建数组对象后,数组中元素会被自动赋予一个默认值,这个过程叫做默认初始化。根据元素类型的不同,默认初始化的值也是不一样的。具体如下表所示。
表2-1 元素默认值
数据类型 默认初始化值
byte、short、int、long 0
float、double 0.0
char 一个空字符(空格),即’\u0000’
boolean false
引用数据类型 null,表示变量不引用任何对象
3.2、数组的初始化(静态初始化)
静态初始化:初始化时指定每个数组元素的初始值,由系统决定数组长度。
格式:数据类型[] 数组名={元素1,元素2,....,元素n}
举例:
int arr[]={1,22,3,4,3,3}
解释:定义了一个int类型的数组,这个数组中存放了6个整型的值,分别为:1 22 3 4 3 3
3.3、数组的初始化(动态初始化)
动态初始化:初始化时只指定数组长度,由系统为数组分配初始值。
格式:数据类型[] 数组名 = new 数据类型[数组长度];
数组长度其实就是数组中元素的个数。
举例:
int[] arr = new int[3];
解释:定义了一个int类型的数组,这个数组中可以存放3个int类型的值
4、访问数组元素
直接打印数组名,打印的是数组堆内存中的地址
数组中的每个元素都是有编号的,编号是从0开始的,最大的编号就是数组的长度-1
用数组名和编号的配合我们就可以获取数组中的指定编号的元素
数组底层是依次将数据进行编号后直接通过编号访问(线性序列)的。这个编号叫做索引。
格式:数组名[编号] -- 数组名[索引]
数组名称为arr,索引为0,1,2
获取数组中元素的格式:
arr[0]获取数组中的第一个元素
arr[1]获取数组中的第二个元素
arr[2]获取数组中的第三个元素
如:
int[] arr = {20,5,100,30,5};
arr[0]访问的为20,arr[2]访问的100。
这里的”访问”包含以下两个动作:获取值与赋值。如:
为元素赋值: arr[1] = 33; 则第2个元素5会被33替代。
获取元素的值: int x = arr[3]; 则x的值为30。
使用数组名.length可以获取数组长度。
/*
数组:它是引用数据类型的一种,它是存放同种数据类型数据的容器(集合)
数组定义格式:
数据类型[] 数组名 = new 数据类型[数据长度/元素个数];
访问数组中指定编号的元素:数组名[索引]
给数组中的元素赋值: 数组名[索引] = 数据值;
使用数组名.length可以获取数组长度。
直接打印数组名是获取了数组的堆内存地址值(内存图详细了解)
*/
public class Demo02Array{
public static void main(String[] args){
//定义一个长度为3的整数数组
int[] arr1 = new int[3];
//int[] arr2 = new int[]{1,2,3,4,5};//有几个元素,长度就为几
int[] arr2 = {1,2,3,4,5};
// 索引 0 1 2 3 4
//访问arr2数组中第一个元素
//System.out.println("数组的第一个元素: " +arr2[0]);
int x = arr2[0];
System.out.println("数组的第一个元素: " + x);
//给数组中的元素赋值: 数组名[索引] = 数据值;
arr2[1] = 100;
System.out.println("arr2[1]: " +arr2[1]);
int length1 = arr1.length;
System.out.println("数组arr1的长度:" +length1);
int length2 = arr2.length;
System.out.println("数组arr1的长度:" +length2);
System.out.println(arr1);//[I@104c575
System.out.println(arr2);//[I@3fa5ac
char[] ch = new char[4];
System.out.println(ch);//[I@3fa5ac
//定义一个长度为10的String类型数组
//String[] arr2 = new String[10];
}
}
直接打印数组名是获取了数组的堆内存地址值(内存图详细了解)
5、数组的注意事项
5.1、数组的长度是固定的
数组在创建对象过程当中,必须指定数组长度,无法创建对象进而无法给变量赋值。
5.2、一个数组中只能存储一种类型的数据
在数组的定义格式中有显式地写出该数组中存储的数据类型,所以一个数组只能存储同一种数据类型。(在多态的讲解后,我们会有新的理解)
5.3、数组运行期报错
在使用数组时,因为不当的操作,我们可能会通过编译,但是在运行期间遇到一些程序报错类似这样编译时不报错,运行期报错的错误叫运行时错误
数组最常见的两个运行时错误:空指针异常和数组索引越界异常
5.3.1、空指针异常
编程语言中对空的处理与我们平常所理解的略有不同。这里我们区分两种空的不同
变量完全没有值:
定义了变量根本没有值:int a; int[] b
这样根本没有赋值过的变量是无法使用的。数组也是一种数据类型,可以指定变量,没有赋值是不能使用的。
变量有值,但值为null:
定义了变量并且赋值了,但是值是空值,是常量。这种情况只针对于引用数据类型,基本数据类型不成立。
int a = null; 编译失败
int[] arr = null; 正常赋值
当使用int[] arr = null给arr赋值时,变量arr作为数组名通过编号访问数组元素时编译不报错,运行时报运行错误NullPointerException空指针异常
5.3.2、数组索引越界异常
当数组中不存在该索引却访问该索引时,运行时报错:
ArrayIndexOutOfBoundsException 数组索引越界
/*
数组的注意事项:
(1)数组在创建对象过程当中,必须指定数组长度,否则无法创建对象进而无法给变量赋值。
(2)在数组的定义格式中有显式地写出该数组中存储的数据类型,所以一个数组只能存储同一种数据类型。(在多态的讲解后,我们会有新的理解)
(3)在创建数组对象后,数组中元素会被自动赋予一个默认值,这个过程叫做默认初始化。根据元素类型的不同,默认初始化的值也是不一样的。
数组运行期错误:
空指针异常:给数组变量赋值的时候,赋值成null,数组不指向一个堆内存的地址,索引会报NullPointerException
数组索引越界:访问了不存在的索引
*/
public class Demo03Array{
public static void main(String[] args){
//(1)数组在创建对象过程当中,必须指定数组长度,否则无法创建对象进而无法给变量赋值。
//定义一个长度为5的int类型数组
//int[] arr = new int[];//错误: 缺少数组维
int[] arr = new int[5];
//(2)一个数组只能存储同一种数据类型。
//arr[0] = "hello";// 错误: 不兼容的类型
arr[0] = 18;
System.out.println(arr[0]);//18
//int有默认的初始化值 0
System.out.println(arr[1]);//0
System.out.println(arr[2]);//0
System.out.println(arr[3]);//0
System.out.println(arr[4]);//0
String[] arr2 = new String[3];
arr2[0] = "詹姆斯";
System.out.println(arr2[0]);//詹姆斯
//String有默认的初始化值 null
System.out.println(arr2[1]);//null
System.out.println(arr2[2]);//null
System.out.println(arr2[3]);//ArrayIndexOutOfBoundsException: 3
/*
double[] arr3;
System.out.println(arr3[0]);//错误: 可能尚未初始化变量arr3
*/
//int a = null;// 错误: 不兼容的类型
//double[] arr3 = null;
//System.out.println(arr3[0]);//NullPointerException
}
}
5.4、数组遍历
在操作数组时,经常需要依次访问数组中的每个元素,这种操作称作数组的遍历。如:
public class Demo {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 }; // 定义数组
// 使用for循环遍历数组的元素
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]); // 通过索引访问元素
}
}
}
运行结果如下图所示。
图1-1 运行结果
上述代码中,定义一个长度为5的数组arr,数组的角标为0~4。由于for循环中定义的变量i的值在循环过程中为0~4,因此可以作为索引,依次去访问数组中的元素,并将元素的值打印出来。
/*
数组遍历:依次访问数组中的每一次元素
*/
public class Demo04Array{
public static void main(String[] args){
String[] arr = {"柳岩","唐嫣","贾玲","韩红","杨幂"};
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[3]);
System.out.println(arr[4]);
System.out.println("---------------------------");
for(int i=0; i<5; i++){//i = 0,1,2,3,4
System.out.println(arr[i]);
}
System.out.println("---------------------------");
for(int i=0; i<arr.length; i++){//i = 0,1,2,3,4
System.out.println(arr[i]);
}
/*
3:一个String数组中存储一些人名,奇偶报数
String[] arr = {“张三”,”李四”,”王五”,”赵六”};
奇数同学为: 张三, 王五
偶数同学为: 李四, 赵六
*/
String name1 = "奇数同学为:";
String name2 = "偶数同学为:";
for(int i=0; i<arr.length; i++){
if(i % 2 == 0){
name2 = name2 +"" + arr[i];//"偶数同学为:" + ""+ "柳岩" + "贾玲"
}else{
name1 = name1 +"" + arr[i];//"偶数同学为: " + "" + "唐嫣"
}
}
System.out.println(name1);
System.out.println(name2);
}
}
6、数组的内存解释
6.1、内存分区
内存是计算机临时存储数据的区域,我们会将内存在逻辑上分配成不同区域方便对数据进行分类高效管理。
寄存器:最快的存储区域直接与CPU打交道,是程序员无法控制的计算区域。
堆栈:又叫栈,仅次于寄存器。用于存储局部变量。
堆:通用内存池,用于存放所有引用数据类型对象。每个对象均有地址,且有默认初始化值。
常量存储区域:用于存放永远不会被改变的值。
7、Java中的内存分配
Java 程序在运行时,需要在内存中的分配空间。
为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
内存分区:
栈 又叫栈,仅次于寄存器。用于存储局部变量
局部变量是定义在方法中或者语句中的变量
局部变量使用完毕,立即回收
堆 通用内存池,用于存放所有引用数据类型对象。每个对象均有地址,且有默认初始化值。New出来的东西。
每一个对象都有首地址值,是一个十六进制的地址
每一个对象内的数据都有默认值
byte,short,int,long 0
float,double 0.0
char ‘\u0000’,一个空格
boolean false
引用类型:null
使用完毕后,会被垃圾回收器空闲的时候回收。
方法区:(方法区)又叫静态区,存放所有的class和静态变量,方法区存放的是整个程序中唯一的元素,如class和static变量。
本地方法区 (和系统相关)
寄存器 (给CPU使用)最快的存储区域直接与CPU打交道,是程序员无法控制的计算区域
画图说明:数组的内存图
8、数组内存图解
定义一个数组,输出数组名及元素。然后给数组中的元素赋值,再次输出数组名及元素
定义两个数组,分别输出数组名及元素。然后分别给数组中的元素赋值,分别再次输出数组名及元素。
定义两个数组,先定义一个数组赋值,输出。然后定义第二个数组的时候把第一个数组的地址赋值给第二个数组。然后给第二个数组赋值,再次输出两个数组的名及元素。
9、数组的初始化(静态初始化)
概述
初始化时指定每个数组元素的初始值,由系统决定数组长度。
格式
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,…};
举例
int[] arr = new int[]{1,2,3};
解释:定义了一个int类型的数组,这个数组中可以存放3个int类型的值,并且值分别是1,2,3。
简化格式
数据类型[] 数组名 = {元素1,元素2,...};
简化格式举例
int[] arr = {1,2,3};
内存图
9.1、引用数据类型数组的内存图
数组是我们系统介绍的第一个引用数据类型,了解内存结构将有助于后期整个面向对象的学习。
int[] x = new int[100];
引用变量:数组属于引用数据类型,引用数据类型定义的变量x存储在栈内存当中,这个x变量叫做这个数组实例的引用变量。
=:与数学当中的意义不同,是赋值的意思,并非相等。
真正的数组实例是通过new关键字创建出来,存储于堆内存中,并产生一个十六进制表示的内存地址0x3000。
这个引用变量会指向这个数组的内存地址
则引用变量的值是这个数组实例的地址值,引用变量通过地址可以表示这个数组对象
10、数组练习
10.1、遍历数组
依次输出数组中的每一个元素
获取数值长度:数值名.length
public class Demo07ArrayTest {
public static void main(String[] args) {
// 定义数组
int[] arr = { 11, 22, 33, 44, 55 };
// 原始做法
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[3]);
System.out.println(arr[4]);
System.out.println("--------------------");
// 用for循环改进
for (int x = 0; x < 5; x++) {
System.out.println(arr[x]);
}
System.out.println("--------------------");
//为了解决我们去数数组中元素个数的问题,数组就提供了一个属性:length
//用于获取数组的长度
//格式:数组名.length
System.out.println("数组共有:"+arr.length+"个元素");
System.out.println("--------------------");
for(int x=0; x<arr.length; x++) {
System.out.println(arr[x]);
}
}
}
10.2、获取最值
获取数组中的最大值最小值
public class Demo08ArrayTest {
public static void main(String[] args) {
//定义数组
int[] arr = {12,98,45,73,60};
//定义参照物
int max = arr[0];
//遍历数组,获取除了0以外的所有元素,进行比较
for(int x=1; x<arr.length; x++) {
if(arr[x] > max) {
max = arr[x];
}
}
System.out.println("数组中的最大值是:"+max);
}
}
11、二维数组
11.1、二维数组概念
当数组中存储的元素类型仍然为数组时,该数组称为二维数组。
11.2、二维数组定义和使用格式
二维数组定义格式
数据类型[][] 数组名;
数据类型 数组名[][]; 不推荐
数据类型[] 数组名[]; 不推荐
初始化方式
数据类型[][] 变量名 = new 数据类型[m][n];
数据类型[][] 变量名 = new 数据类型[][]{{元素…},{元素…},{元素…}};
简化版格式:数据类型[][] 变量名 = {{元素…},{元素…},{元素…}};
public class Demo01ArrayArray {
public static void main(String[] args) {
// 数据类型[][] 数组名 = {{元素...},{元素...},{元素...},...};
int[][] arr = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
System.out.println(arr); // [[I@104c575
System.out.println(arr.length); // 二维数组中的一维数组的个数
System.out.println(arr[0]);// [I@3fa5ac
System.out.println(arr[0].length);
System.out.println(arr[1]);// [I@95cfbe
System.out.println(arr[2]);// [I@179dce4
//我如何获取到一个二维数组的元素呢?
System.out.println(arr[0][0]);
System.out.println(arr[1][1]);
System.out.println(arr[2][0]);
}
}
二维数组定义方式展示。
a) 第一种方式
int[][] arr = new int[3][4];
上面的代码相当于定义了一个3*4的二维数组,即二维数组的长度为3,二维数组中的每个元素又是一个长度为4的数组,接下来通过一个图来表示这种情况,如下图所示。
图1-2 二维数组
b) 第二种方式
int[][] arr = new int[3][];
第二种方式和第一种类似,只是数组中每个元素的长度不确定,接下来通过一个图来表示这种情况,如下图所示。
图1-3 二维数组
c) 第三种方式
int[][] arr = {{1,2},{3,4,5,6},{7,8,9}};
上面的二维数组中定义了三个元素,这三个元素都是数组,分别为{1,2}、{3,4,5,6}、{7,8,9},接下来通过一个图来表示这种情况,如图所示。
图1-4 二维数组
11.3、二维数组的元素访问
对二维数组中元素的访问也是通过索引的方式,如需访问二维数组中第一个元素数组的第二个元素,具体代码如下:
arr[0][1]; //访问的为二维数组中第1个一维数组的第2个元素
请参照以为数组的遍历求和,完成二维数组的遍历求和。
/*
二维数组:数组中的元素仍为一个一维数组
*/
public class Demo05ArrayArray{
public static void main(String[] args){
//定义一个长度为3,每个数组元素都是一个长度为4的数组的二维数组
int[][] a = new int[3][4];
System.out.println(a);//[[I@32784a
System.out.println(a.length);//3
System.out.println(a[0]);//[I@104c575
System.out.println(a[1]);//[I@3fa5ac
System.out.println(a[2]);//[I@95cfbe
//访问二维数组中的第一个元素的第一个值
System.out.println(a[0][0]);//0
//赋值
a[0][0] = 100;
System.out.println(a[0][0]);//100
System.out.println("-------------------------");
int[][] b = {{1,2},{3,4,5,6},{7,8,9}};
System.out.println(b);//[[I@95cfbe
System.out.println(b.length);//3
System.out.println(b[0]);//[I@179dce4
System.out.println(b[1]);//[I@1950198
System.out.println(b[2]);//[I@19bb25a
System.out.println(b[0][0]);//1
System.out.println(b[2][2]);//9
//赋值
b[0][0] = 100;
System.out.println(b[0][0]);//100
System.out.println("-------------------------");
int[][] c = new int[3][];
System.out.println(c);//[[I@179935d
System.out.println(c.length);//3
System.out.println(c[0]);//null
System.out.println(c[1]);//null
System.out.println(c[2]);//null
//System.out.println(c[0][0]);//NullPointerException
c[0] = new int[]{1,2,3};
System.out.println(c[0][0]);//1
}
}
11.4、二维数组的遍历
int[][] arr = {{1,2,3},{4,5,6},{7,8,9}};
public class Demo02ArrayArrayTest {
public static void main(String[] args) {
// 定义二维数组
int[][] arr = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
// 二维数组中的一维数组名称:二维数组名[索引]
// arr[0] 其实就是二维数组中的第一个一维数组的名称
// arr[1] 其实就是二维数组中的第二个一维数组的名称
// arr[2] 其实就是二维数组中的第三个一维数组的名称
// for (int x = 0; x < arr[0].length; x++) {
// System.out.println(arr[0][x]);
// }
// System.out.println("hello");//输出内容并换行
// System.out.println("world");//输出内容并换行
// System.out.print("hello");//输出内容不换行
// System.out.print("world");//输出内容不换行
/*
// 第一个一维数组的元素
for (int x = 0; x < arr[0].length; x++) {
System.out.print(arr[0][x] + " ");
}
System.out.println();
// 第二个一维数组的元素
for (int x = 0; x < arr[1].length; x++) {
System.out.print(arr[1][x] + " ");
}
System.out.println();
// 第三个一维数组的元素
for (int x = 0; x < arr[2].length; x++) {
System.out.print(arr[2][x] + " ");
}
System.out.println();
*/
/*for(int y=0; y<3; y++) {
for (int x = 0; x < arr[y].length; x++) {
System.out.print(arr[y][x] + " ");
}
System.out.println();
}*/
for(int y=0; y<arr.length; y++) {
for (int x = 0; x < arr[y].length; x++) {
System.out.print(arr[y][x] + " ");
}
System.out.println();//换行
}
}
}