1.1为什么需要数组
不接触数组之前,我们存储数据一般都是一个一个去存储,一个一个的去输出,比如像存储学生的考试成绩,我们会这么去写代码:
public class TestStudent{
public static void main(String[] args){
int score1 = 70;
int score2 = 80;
int score3 = 85;
int score4 = 60;
int score5 = 90;
System.out.println(score1);
System.out.println(score2);
System.out.println(score3);
System.out.println(score4);
System.out.println(score5);
}
}
如果需求是10个,20个我也许还可以写写,但是如果是需要存储100、1000个甚至更多的情况之下,那我们就存储下来的效率值会大大下降,通过观察这些数据的基本类型是一样的,来存储这么多相同的数据类型,我们可以用数组来解决这样的问题。
1.2什么是数组
数组:可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。
数组在内存存放数据像上面的图一样,存储数据在内存中时连续的。从图中可以看到:
那么在程序中怎么去创建这个数组呢?
1.3数组的创建及初始化
基本格式:
数据类型 [] 数组名 = new 数据类型 [] {存储的内容};
我们以整型和double类型举例:
int [] array = new int [] {1,2,3,4,5};//整型数组
double [] dd = new double [] {2.0,4.5,3.7};//double类型数组
数组的创建c语言也JavaSE不一样,在javaSE中数组的创建可以写成三种形式:
我们都以整型数组来举例:
int [] array ={1,2,3,4,5};//精简形式
int [] array = new int [] {1,2,3,4,5};//基本形式
int [] array = new int [5];//给定数组大小的形式
这三种形式中,在创建数组的时候你都可以使用。我个人推荐使用第一种,因为比较方便,还有在使用这三种形式的时候只有第三种的[]可指定大小,其他都不可以。
最后在创建数组的写法还有一种:参考c语言的写法:
int array [] = {1,2,3,4,5};//在java中我们不推荐这么去创建数组。
因为本来c语言这么写是存在问题的,我们举一个例子:
像基本类型创建:
int a = 10;
double b= 3.14;
char c = 'z';
这些基本类型在创建的时候都是以 数据类型 变量名 = 存储的内容; 这样的基本格式来写的。
在数组中(我们以整型数组举例):int [] 这个就是基本类型 array这个就是数组名 。按照规则来说,像java这样来写是符合规则的,c语言那样写是不符合规则的。
如果没对数组初始化,数组元素有默认值
如果存储的是基本类型,默认值为基本类型对应的默认值,比如:
类型 | 默认值 |
---|---|
byte | 0 |
int | 0 |
short | 0 |
long | 0 |
float | 0.0f |
double | 0.0 |
char | /u0000 |
boolean | false |
举一个int类型和boolean类型的例子:
代码实现:
public class Test {
public static void main(String[] args) {
int [] array = new int[5];
boolean [] boo = new boolean[5];
}
}
在调试下的结果确实和上方表格的默认值一样。
如果数组中存储的是引用类型,默认值为null。
我们可以创建一个String类型的数组:
public static void main(String[] args) {
String [] str = new String[5];
}
在监视下的结果显示,默认值就是null。
1.4数组的使用
1.4.1 数组中元素访问
数组在内存中是一段连续的空间,空间的编号都是从0开始的,依次递增,该编号称为数组的下标,数组可以通过下标访问其任意位置的元素。比如:
int[]array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);
// 也可以通过[]对数组中的元素进行修改
array[0] = 100;
System.out.println(array[0]);
【注意事项】
int[] array = {1, 2, 3};
System.out.println(array[3]); // 数组中只有3个元素,下标一次为:0 1 2,array[3]下标越界
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException//数组下标越界异常
在访问数组的时候一定要谨慎数组越界。
1.4.2 遍历数组
所谓 “遍历” 是指将数组中的所有元素都访问一遍, 访问是指对数组中的元素进行某种操作,比如:打印。
有三种方式可以去打印:
public static void main(String[] args) {
int [] array = {1,2,3,4,5};
//通过输出一个一个的输出。
System.out.print(array[0]+" ");
System.out.print(array[1]+" ");
System.out.print(array[2]+" ");
System.out.print(array[3]+" ");
System.out.println(array[4]+" ");
//通过循环将数组中的内容一一打印出来
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
System.out.println();
//通过Arrays类中的toString()方法来打印(将数组的内容转化为字符串再输出)
System.out.println(Arrays.toString(array));
}
打印的结果如下:
在上面三种方式中第三种方式用Arrays类中的toString方法,在Arrays类的toString方法中不仅仅可以将整型数组中的内容转化为字符串的形式去输出,还有以下类型:
注意:在数组中可以通过 数组对象.length 来获取数组的长度
在java中还有一个方式可以遍历数组的内容,那就是for-each:
//for (数组中数据的类型 存储返回的值的变量名:数组名) {
// 语句体;
//}
用for-each来遍历这个数组
public static void main(String[] args) {
int [] array = {1,2,3,4,5,6};
for (int x:array) {
System.out.print(x+" ");
}
}
结果如下:
用这个方式去遍历有什么缺点呢?
灵活性不足,这个方式一遍历就将数组中的内容全遍历出来。
这个方式也有优势:
可以避免循环条件和更新语句写错。
2.1初始JVM的内存的分布
数组在内存存储是一段连续的存储空间,主要用来存储程序运行时数据的。比如:
程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
方法区(Method Area): 用于存储已被虚拟机加载
现在我们只简单关心堆 和 虚拟机栈这两块空间.
2.2基本类型变量与引用类型变量的区别
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值;
而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址。
基本类型:
int a = 10;
像基本类型存放的内容是直接存储在虚拟机栈中,以画图的形式展示:
引用类型:
int [] array = new int [] {1,2,3,4,5};
引用类型存放的内容是存储在堆上,通过在栈上创建一个引用变量,通过引用变量指向堆上的对象,以画图的形式展示:
像c语言中,基本类型传参到函数中,传地址可以改变变量存放的值,但在java中是做不到的,因为根本拿不到这个变量存放内容的这个地址。在java中引用类型可以通过地址去找到存放的内容,并且可能可以改变这个存放的内容。
2.3认识null
null 在 Java 中表示 “空引用” , 也就是一个不指向对象的引用。
public static void main(String[] args) {
int [] array = null;
System.out.println(array[0]);
}
null 的作用类似于 C 语言中的 NULL (空指针), 都是表示一个无效的内存位置. 因此不能对这个内存进行任何读写操作. 一旦尝试读写, 就会抛出 NullPointerException.
注意: Java 中并没有约定 null 和 0 号地址的内存有任何关联。
3.1 保存数据
public static void main(String[] args) {
int[] array = {1, 2, 3};
for(int i = 0; i < array.length; ++i){
System.out.println(array[i] + " ");
}
3.2 作为函数的参数
public static void fun(int n) {
n=10;
System.out.println("n="+n);
}
public static void main(String[] args) {
int num = 0;
fun(num);
System.out.println("num="+num);
}
因为基本类型存储在栈上,传参过去也不会改变num的值,n只会重新在栈上开辟一个新的空间,并且当出了fun这个方法n开辟的空间也会被回收。
2. 参数传数组类型(引用数据类型)
public static void fun1(int [] array) {
array[2]=100;
}
public static void main(String[] args) {
int [] array = {1,2,3,4,5};
fun1(array);
System.out.println(Arrays.toString(array));
}
通过上面的运行结果和画图,在main方法中先创建引用变量array通过地址指向堆上的对象,然后通过传参到fun1方法上,也创建了一个array引用变量,这个过程相当一个赋值将main方法中的array引用变量的地址赋给了fun1方法中创建的array引用变量上,然后通过这个地址指向了堆上的对象,这样这两个引用变量都指向同一个对象,然后进行到fun1方法中这个程序array[2]=100;将原来堆上的下标为2的数改为100,当这个fun1方法结束之后,由这个fun1方法创建的引用变量array也被系统回收了。然后执行System.out.println(Arrays.toString(array));这一段代码,将数组的内容打印出来,最后当main方法也结束之后main方法中的array引用变量也被回收,堆上的对象所储存的内容也被销毁回收。
总结: 所谓的 “引用” 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大).
在引用变量传参不一定可以改变实参的内容:
public static void fun2(int [] array) {
array = new int [] {11,22,33,44,55};
}
public static void main(String[] args) {
int [] array = {1,2,3,4,5};
fun2(array);
System.out.println(Arrays.toString(array));
}
这个传参过去就没有改变数组的内容,我们来通过画图来分析一下:
在fun2方法中创建的array引用变量重新指向了一个在堆上的新对象,这样改变不了main方法中的内容。
总结:传参传引用变量不一定会改变实参的内容。
3.3 作为函数的返回值
比如:获取斐波那契数列的前N项
public static int [] fib(int n) {
if(n <= 0) {
return null;
}
int [] array = new int [n];
array[0] = array[1] = 1;
for (int i = 2; i <n ; i++) {
array[i] = array[i-1]+array[i-2];
}
return array;
}
public static void main(String[] args) {
int [] array = fib(10);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
}