文章目录
- 二维数组
- 数组初始化以及匿名数组
- 数组拷贝
- Java中数组作为参数传递
- 递归
二维数组
- 前面已经学习过数组了,了解到数组就是一种容器,用来存储同一类型值的集合,通过
下标 / 索引
可以访问数组中的每个值。 - 一维数组用来表示一列数,二维数组可以表示一张表(比如一个班级里面有若干学生,学校里面有很多班级,这时候数据也需要用数组表示)。由此可见,二维数组其实就是每一个元素为一维数组的数组。
- 二维数组定义:
定义格式1:
数据类型[ ][ ] arr = new 数据类型[m[n];
m表示这个二维数组有多少个一维数组(必须写)
n表示每一个一维数组中有多少个元素(可以不写)
这些元素的数据类型都是相同的。
定义格式2:
数据类型[][] 数组名 = new 数据类型[m][];
m表示这个二维数组有多少个一维数组
这一次没有直接给出一维数组的元素个数,可以动态给出
举例:int[][] arr = new int[3][];
arr[0]=new int[2];
arr[1]=new int[3];
arr[2]=new int[1];
练习:
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
public class MyTest {
public static void main(String[] args) {
Random r=new Random();
int[][] arr1=new int[3][2];
//也可以定义为:int arr1[][] = new int[3][2];
//int[] arr[] = new int[3][2];
//但是这两种格式不推荐,第一种定义方法更为直观
System.out.println("数组arr1的地址值是:"+arr1);
System.out.println("数组arr1中第一个一维数组的地址为:"+arr1[0]);
System.out.println("数组中的第一个元素的缺省值是:"+arr1[0][0]);
System.out.println("数组arr1中的元素分别为:");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
arr1[i][j]=(int)(r.nextDouble()*100+1);
System.out.print(arr1[i][j]+"\t");
}
System.out.println();
}
System.out.println("------------------------------------------");
int[][] arr2=new int[3][];
System.out.println("数组arr2的地址值是:"+arr2);
System.out.println("数组arr2中的第一个一维数组arr2[0]的地址值:"+arr2[0]);
//System.out.println("数组中的第一个元素的缺省值是:"+arr2[0][0]);
arr2[0]=new int[2];
arr2[1]=new int[3];
arr2[2]=new int[1];
System.out.println("数组arr2中的第一个元素的缺省值为:"+arr2[0][0]);
System.out.println("数组arr2中的第一个一维数组arr[0]的地址值:"+arr2[0]);
System.out.println("你猜数组arr1可以存放长度为5的一维数组吗?");
arr1[0]=new int[5];
System.out.println("arr1中第一个一维数组的地址为:"+arr1[0]);
}
}/*输出结果为:
数组arr1的地址值是:[[I@677327b6
数组arr1中第一个一维数组的地址为:[I@14ae5a5
数组中的第一个元素的缺省值是:0
数组arr1中的元素分别为:
62 51
91 56
88 63
------------------------------------------
数组arr2的地址值是:[[I@7f31245a
数组arr2中的第一个一维数组arr2[0]的地址值:null
数组arr2中的第一个元素的缺省值为:0
数组arr2中的第一个一维数组arr[0]的地址值:[I@6d6f6e28
你猜数组arr1可以存放长度为5的一维数组吗?
arr1中第一个一维数组的地址为:[I@135fbaa4*/
【分析】
有两种定义数组的方式。程序首先进入方法区,由JVM将MyTest.java文件编译成MyTest.class文件,并找到程序的入口mian(),将方法调入虚拟机栈区执行。
程序读到关键字new,在堆区为数组arr1分配一段连续的内存空间,并将这个地址值赋给引用arr1,由于分配在堆区的数据类型是一维数组(引用),因此系统默认为arr1[0],arr1[1],arr1[2]赋值为null。因为第一种定义数组的方式直接给定了二维数组的大小,因此系统很快在堆区为arr1中3个一维数组分配两个int类型大小的空间,并返回三个地址值,分别赋给arr1[0],arr1[1],arr1[2],所以它们三个的值被打印输出就是一个地址值。这时候数组中的每一个元素都是int类型数据,它们的缺省值都是0,后面使用两重循环为数组arr1中的每个元素都赋一个随机数,打印输出可以看到每一次编译之后这些元素的值都是不同的。
当程序读到第二个new的时候,在堆区分配连续的内存空间并将地址值返回给arr2,由于arr2中有3个一维数组,所以堆区对应3个引用类型的数据,打印可以看到系统默认给它们初始化为null,相当于没有指向任何内存空间。这次使用第二种定义数组的方式没有给定二维数组中一维数组的长度,因此也就不存在二维数组的元素,此时如果打印数组元素,编译器会报错。我们定义不同长度的一维数组并将它们的地址值赋给arr2中的arr2[0],arr2[1],arr2[2]这三个引用,打印就可以看到地址值,此时再次输出数组元素,发现是默认的整型数据0。
数组arr1给定了数组大小,将新的引用赋给arr1的一维数组仍然可以打印地址值,从这可以看出Java中的数组是很灵活的。
数组初始化以及匿名数组
在Java中,提供了一种创建数组对象并同时赋予初始值的简化书写形式:
int[] arr={1,2,3,4,5,6,7};
请注意,在使用这种语句时,不需要调用new,甚至还可以初始化一个匿名数组
new int[]{1,2,3,4,5,6,7};
这种表示法将创建一个新数组并利用括号中提供的值进行初始化,数组的大小就是初始值的个数,使用这种语法形式可以在不创建新变量的情况下重新初始化一个数组,例如:
arr=new int[]{1,2,3,4,5,6,7};
在Java中,允许数组长度为0
,在编写一个结果为数组的方法时,如果碰巧结果为空,则这种语法形式就显得非常有用,此时可以创建一个长度为0的数组:new arr[0],但是注意,数组长度为0与null不同。
数组拷贝
- 在Java中,允许将一个数组变量拷贝给另一个数组变量,这时,两个变量将引用同一个数组:
int[] a=b;
a[5]=12;//now b[5] is also 12
- 上图显示了拷贝的结果,如果希望将一个数组中的所有值拷贝到另一个新数组里面,需要用到Arrys类里面的copyDf():
int[] a = Arrays.copyOf(b , b.length);
//第 2 个参数是新数组的长度。
- 这个方法通常用来增加数组的大小:
b = Arrays.copyOf(b , 2 * b);
- 如果数组元素是数值型,那么多余的元素将被赋值为 0 ; 如果数组元素是布尔型,则将赋值为 false。相反,如果长度小于原始数组的长度,则只拷贝最前面的数据元素。
Java中数组作为参数传递
练习:
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
public class MyTest {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("a: " + a + ",b: " + b);
change(a,b);
System.out.println("a: " + a + ",b: " + b);
int[] arr = {1,2,3,4,5};
change(arr);
System.out.println(arr[1]);
}
public static void change(int a,int b) {
System.out.println("a: " + a + ",b: " + b);
a = b;
b = a + b;
System.out.println("a: " + a + ",b: " + b);
}
public static void change(int[] arr){
for(int x = 0 ; x < arr.length ; x++){
if(arr[x]%2 == 0){
arr[x] *= 2;
}
}
}
}/*输出结果为:
a: 10,b: 20
a: 10,b: 20
a: 20,b: 40
a: 10,b: 20
4*/
- 分析上面的程序,最一开始a与b的值分别为10、20,调用方法,方法里面两次输出a与b的值,变化前后不同,由于方法没有任何的返回值,因此主方法中的局部变量a与b都没有变化,调用完方法打印输出发现值不变。
- 定义一维数组并将数组名传递给经过重载的方法change(),变化数组中的元素,最终发现main()中数组元素的值改变,因为传递的参数是引用,也就相当于地址,
形参变量与实参变量指向同一块内存空间,它们都可以操纵所指向的数据
。
递归
递归就是指方法自己调用自己,但是注意,只有JVM才可以调用main()
,因此mian()自己不能调用自己。
public class MyTest {
public static void main(String[] args) {
recursion();
}
private static void recursion() {
int i=0;
while(i<50000)
recursion();
}/*StackOverflowError:栈溢出*/
递归次数太多,会造成栈溢出
,因为方法一直在进栈,栈底的方法没有执行完毕,没办法弹栈,造成上述错误。
练习:
根据自己的需求利用递归,下面给出一个解决有关斐波那契额数列问题的程序:
public class MyTest {
public static void main(String[] args) {
System.out.println("请输入你想要查询的月份:");
Scanner sc = new Scanner(System.in);
int month = sc.nextInt();
int num=fibonacci(month);
System.out.println("这个月兔子的个数为:"+num);
}
private static int fibonacci(int y) {
if (y == 1 || y == 2)
return 1;
else
return fibonacci(y - 1) + fibonacci(y - 2);
}
}