数组介绍:
数组是一种数据结构,系统为数组分配的存储空间是连续的、指定长度的且大小固定不变的,用来存储一组大小固定并且类型相同的数据。
这些数据可以通过索引进行访问,数组的下标索引是从0开始,其取值范围必需是:0 ~ 数组长度length – 1 。
Java数组的特点:
1.多维数组均是一维指针数组(数组实例)。
这里以三维数组为例
int[][][] T = new int[2][2][2];
值得注意的是 在Java中没有矩阵数组,全部都是引用数组,没有选择的余地,但是矩阵数组操作效率比较高
2.Java中的数组是静态数组,数组被创建后,其大小是不能改变的。
3.由JVM扫描时产生数组类型(数组类型动态产生)
4.数组越界,在Java中是运行时错误,产生异常(数组的length字段);在C语言中是逻辑错误。JVM在运行时判断,如果未越界则继续运行,如果越界则抛出异常。例如:数组越界、显式强类型转换等。int[] a = new int[10];
int[] a = new int[10];
int i = 20;
a[i] = 8;
运行时为:
int[] a = new int[10];
int i = 20;
if i<a.length
a[i] = 8;
else
throw 数组越界异常对象
注意以上加粗代码的意思是JVM会进行添加的,是JVM帮的忙
如果你不了解JVM,那很难达到比较高的境界
下面有对数组存在的问题的介绍
5.参考C语言:
C语言中矩阵数组与指针数组的选择能力(数据结构的选择能力)
1)运行效率
运行效率 = 1次 X 生成时间 + n次 X 操作时间
2)产生的垃圾
3)数据结构的大小\
6.对象实例由对象头、实例数据组成,其中对象头包括markword和类型指针,如果是数组,还包括数组长度;
| 类型 | 32位JVM | 64位JVM |
| markword | 32bit | 64bit |
| 类型指针 | 32bit | 64bit ,开启指针压缩时为32bit |
| 数组长度 | 32bit | 32bit |
Java数组存在的问题
(存在的)问题---〉检测(问题)---〉解决(问题)
Java数组:
1)(存在的)问题
a) 数组越界问题
b) 数组元素类型匹配问题
2)检测(问题)
数组越界问题由JVM在运行时来检测,如果存在问题,则抛出异常
数组元素类型匹配,值类型匹配错误是逻辑错误
数组元素类型匹配,引用类型匹配错误是异常
3)解决(问题)
捕获异常,处理异常
public class Test
{
public static void main(String[] arg)
{
float[] a = {1,2,3};
a[0] = 123456789;
System.out.println("float a[0] =" + a[0] + ",int x=" + 123456789);
try{
int i = 3;
a[i] = 8;
}
catch(Exception e){
System.out.println("数组越界异常!");
}
}
}
数组是一种数据结构,系统为数组分配的存储空间是连续的、指定长度的且大小固定不变的,用来存储一组大小固定并且类型相同的数据。
这些数据可以通过索引进行访问,数组的下标索引是从0开始,其取值范围必需是:0 ~ 数组长度length – 1 。
数组是JVM的核心类型,是Java语言的基元类型。数组类和数组实例的数据结构具有特殊性,由JVM决定。运行时,数组越界的检查通过数组实例的length属性来完成,数组元素类型匹配的检查通过数组类中的元素类型属性来完成。
一维数组
创建一维数组:
//*****程序代码:ArrayDemo1.java *******public class ArrayDemo1 { public static void main(String args[]) { // 声明一个整型数组a int[] a; // 给数组a分配存储空间:10*4个字节 a = new int[10]; // 定义一个长度为10的双精度浮点型数组,10*8个字节 double[] b = new double[10]; // 定义一个长度为100的字符型数组c,100*2个字节 char[] c = new char[100]; // 定义一个长度为20的布尔型数组,20*1个字节 //java规范中,没有明确指出boolean的大小。 //在《Java虚拟机规范》给出了4个字节,和boolean数组1个字节的定义, //具体还要看虚拟机实现是否按照规范来,所以1个字节、4个字节都是有可能的。 //boolean数组将会被编码成Java虚拟机的byte数组,每个元素boolean元素占8位 boolean[] d = new boolean[20]; //定义一个长度为5的字符串数组 String[] s=new String[5]; /* 下面输出各数组的数组名,注意输出的内容 */ //在需要出现String类型的地方,出现了引用, //则编译器生成的字节码a.toString() System.out.println("输出数组对象*****************"); System.out.println(a);// 输出数组对象的哈希玛(地址) System.out.println(b);// 输出数组对象的哈希玛(地址) System.out.println(c);// 输出字符数组中的内容 System.out.println(d);// 输出数组对象的哈希玛(地址) System.out.println(s);// 输出数组对象的哈希玛(地址) System.out.println("-------------------------"); System.out.println("输出数组对象的成员************"); /* 下面输出各数组中第一个元素的值,注意输出的内容 */ System.out.println(a[0]); System.out.println(b[0]); System.out.println(c[0]); System.out.println(d[0]); System.out.println(s[0]); System.out.println("-------------------------"); System.out.println("输出数组对象的长度***********"); /* 下面输出各数组的长度 */ System.out.println("a.length=" + a.length); System.out.println("b.length=" + b.length); System.out.println("c.length=" + c.length); System.out.println("d.length=" + d.length); System.out.println("s.length=" + s.length); System.out.println("-------------------------"); System.out.println("输出数组类的超类和反射类*"); // getClass()是Object的方法成员 // getSuperclass()、getName()是Class的方法成员 // 输出类 int[] System.out.println("double数组实例的类:" + b.getClass().getName()); System.out.println("char数组实例的类:" + c.getClass().getName()); System.out.println("boolean数组实例的类:" + d.getClass().getName()); System.out.println("String数组实例的类:" + s.getClass().getName()); System.out.println("int数组实例的类:" + a.getClass().getName()); // 输出类的超类 Object System.out.println("int数组实例的类的父类:" + a.getClass().getSuperclass().getName()); // 输出类的反射类类 Class System.out.println("int数组实例的类的反射类:" + a.getClass().getClass().getName()); Class c1 = a.getClass(); Class c2 = b.getClass(); System.out.println("a、b相同的父类:" + (c1 == c2)); //false } }
运行结果:
1)JVM在运行一个方法之前,先扫描方法体,如果类对象不存在,则先创建该类对象。然后,单步解释执行main()方法体内的代码。
2)数组类对象非常特殊,是由JVM扫描时自动产生的。在扫描main()方法体时,JVM将首先在Java方法区,建立int[]数组类对象、double[]数组类对象、char[]数组类对象、boolean[]数组类对象、String[]数组类对象。
3)然后,单步解释执行main()方法体内的代码。
例如:double[] b = new double[10]; 赋值语句
1)赋值号左边,首先在堆栈压栈一个变量b,该变量b的类型是一个double[]数组类型,是一个引用类型。
2)然后赋值号右边,生成double[]数组类型的一个数组实例。
3)最后赋值号,将生成的数组实例的引用赋予变量b,即变量b引用数组实例。
初始化数组:
// *********程序代码:ArrayDemo2.java**********public class ArrayDemo2 { public static void main(String[] args) { // 定义并初始化数组,使用静态初始化 int[] a = { 5, 7, 20 }; // 定义并初始化数组,使用动态初始化 int[] b = new int[4]; for (int i = 0; i < b.length; i++) { b[i] = i + 1; } // 循环输出a数组的元素 System.out.println("数组a中的元素是:"); for (int i = 0, len = a.length; i < len; i++) { System.out.print(a[i] + " "); } System.out.println(); // 输出b数组的长度 System.out.println("b数组的长度为:" + b.length); System.out.println("数组b中的元素是:"); // 循环输出b数组的元素 for (int i = 0, len = b.length; i < len; i++) { System.out.print(b[i] + " "); } System.out.println(); // 因为a是int[]类型,b也是int[]类型,所以可以将a的值赋给b。 // 也就是让b引用指向a引用指向的数组 b = a; // 再次输出b数组的长度 System.out.println("b数组的长度为:" + b.length); } }
1)JVM在运行一个方法之前,先扫描方法体,如果类对象不存在,则先创建该类对象。然后,单步解释执行main()方法体内的代码。
2)数组类对象非常特殊,是由JVM扫描时自动产生的。在扫描main()方法体时,JVM将首先在Java方法区,建立int[]数组类对象。
3)然后,单步解释执行main()方法体内的代码。
例如:int[] a = { 5, 7, 20 };赋值语句
1)赋值号左边,首先在堆栈压栈一个变量a,该变量a的类型是一个int[]数组类型,是一个引用类型。
2)然后赋值号右边,使用静态初始化语法,生成int[]数组类型的一个数组实例。
3)最后赋值号,将生成的数组实例的引用赋予变量a,即变量a引用数组实例。
运行结果:
foreach遍历数组
for(数据类型 变量名:数组名)
foreach语句中变量的数据类型必须与数组元素或集合的数据类型一致。
使用foreach遍历数组,无需获得数组或集合的长度,也无需根据索引来访问数组元素或集合元素,foreach循环自动遍历数组或集合的每个元素。
// *****程序代码:ForeachDemo.java*****public class ForeachDemo { public static void main(String[] args) { // 定义并初始化数组,使用静态初始化 int[] a = { 5, 7, 20 }; // 使用foreach语句遍历输出a数组中的元素 System.out.println("数组a中的元素是:"); for (int e : a) { System.out.println(e); } } }
运行结果:
二维数组
Java的二维数组实际上是一维的指针数组。Java中不存在矩阵数组。
// ***程序代码:Array2DDemo.java****public class Array2DDemo { public static void main(String[] args) { // 二维数组静态初始化 int[][] a = { { 1, 2, 3 }, { 4, 5, 6 } }; System.out.println("数组a一维长度:" + a.length); System.out.println("数组a二维长度:" + a[0].length); System.out.println("数组a中的元素:"); // 使用嵌套的for循环输出 for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[i].length; j++) { System.out.print(a[i][j] + " "); } System.out.println(); } System.out.println("-------------------------"); // 二维数组动态初始化,一维和二维都指定长度 int[][] c = new int[3][4]; // 使用嵌套的for循环初始化二维数组 for (int i = 0; i < c.length; i++) { for (int j = 0; j < c[i].length; j++) { c[i][j] = i + j; } } System.out.println("数组c中的元素:"); // 使用嵌套的for循环输出 for (int i = 0; i < c.length; i++) { for (int j = 0; j < c[i].length; j++) { System.out.print(c[i][j] + " "); } System.out.println(); } System.out.println("-------------------------"); // 声明二维数组时,只给出一维长度 int[][] d = new int[2][]; // 二维长度不等 d[0] = new int[3]; d[1] = new int[4]; // 初始化 for (int i = 0; i < d.length; i++) { for (int j = 0; j < d[i].length; j++) { d[i][j] = i + j; } } System.out.println("数组d中的元素:"); // 使用嵌套的for循环输出 for (int i = 0; i < d.length; i++) { for (int j = 0; j < d[i].length; j++) { System.out.print(d[i][j] + " "); } System.out.println(); } System.out.println("-------------------------"); //数组类的超类和反射类 System.out.println("a数组实例的类:" + a.getClass().getName()); System.out.println("a[0]数组实例的类:" + a[0].getClass().getName()); System.out.println("a数组实例的类的父类:" + a.getClass().getSuperclass().getName()); System.out.println("a数组实例的类的反射类:" + a.getClass().getClass().getName()); Class c1 = a.getClass(); Class c2 = d.getClass(); System.out.println("a、d相同的父类:" + (c1 == c2)); //true } }// Java中的多维数组本质上是多维的指针数组,均由多个一维数组组成。
运行结果:
内存逻辑图
Java中的多维数组本质上是多维的指针数组,均由多个一维数组组成。