重学java-5.数组
数组的基本概念
在java中,数组属于引用型数据,指的是一组相关便令的集合。
一维数组
三种定义方式
public static void main(String[] args) {
//1
int [] array1 = new int[100];
//2
int array2[] = new int[100];
//3
int array3[] = null;
array3 = new int[100];
}
这三种方法中,1和2只是写法上的不同。但3与1和2在内存的申请上有所不同。
- 1和2在申请内存时,把array1,array2存放在栈中的同时,在堆中申请相应的内存,并让array1和array2指向堆中信息。
- 3分为两步,第一步是把array3存放在栈中,在第二步执行以前,array3都指向null。直到第二部执行,array3才指向堆。
两种初始化
- 静态初始化:数组在申请空间的同时就设置好了相应的数据内容。
- 动态初始化:数组在申请空间后添加数组内容。
下面举个小例子:
public static void main(String[] args) {
//动态初始化
int [] array1 = new int[3];
array1[0] = 1;
array1[1] = 2;
array1[2] = 3;
System.out.print("array1: ");
for(int i = 0; i < array1.length; ++i) {
System.out.print(array1[i] + " ");
}
System.out.println();
//静态初始化
int array2[] = new int[] {4,5,6};
System.out.print("array2: ");
for(int i = 0; i < array2.length; ++i) {
System.out.print(array2[i] + " ");
}
System.out.println();
}
运行结果:
二维数组
二维数组的定义与初始化与一维的大同小异,在这里举个简单的例子:
public static void main(String[] args) {
//1
int array1[][] = new int[2][2];
//2 静态初始化
int [] array2[] = new int[][] {{1,2},{3,4}};
//3 动态初始化
int array3[][] = null;
array3 = new int[2][2];
int cnt = 0;
for(int i = 0; i < array3.length; ++i) {
for(int j = 0; j < array3[i].length; ++j) {
array3[i][j] = ++cnt;
}
}
//输出
System.out.print("array2: ");
for(int i = 0; i < array2.length; ++i) {
for(int j = 0; j < array2[i].length; ++j) {
System.out.print(array2[i][j]+ " ");
}
}
System.out.print('\n'+"array3: ");
for(int i = 0; i < array3.length; ++i) {
for(int j = 0; j < array3[i].length; ++j) {
System.out.print(array3[i][j]+" ");
}
}
}
运行结果:
数组的引用传递
根据 重学java-4.面对对象基本概念 可知,引用数据类型都可以进行引用传递操作,因此数组也可以进行引用传递操作。
举个例子:
public static void main(String[] args) {
int [] array1 = new int[] {1,2,3};
int array2[] = array1;//引用传递,使array2指向array1所指向的堆
array2[0] = 4;//通过array2改变堆内的值
System.out.println("array1: ");
for(int i = 0; i < array1.length; ++i) {
System.out.print(array1[i] + " ");
}
}
运行结果:
方法参数的传递
标题很长,其实就是想讨论一下数组、类等引用数据类型作为一个方法的参数是如何存在的,便于我们加深对栈内存与堆内存的理解。
基本数据类型作为方法的参数
public class test1 {
public static void main(String[] args) {
int a = 1;
change(a);
System.out.println(a);
}
public static void change(int b) {
b = 2;
}
}
运行结果:
我们发现a的值在传入change方法后明明更改为2,但最终输出时却仍是1。暂且不提为什么,我们继续往下看。
引用数据类型作为方法的参数
- 数组
public class test1 {
public static void main(String[] args) {
int arraya[] = new int[] {1,2};
change(arraya);
System.out.println(arraya[0]);
}
public static void change(int[] arrayb) {
arrayb[0] = 3;
}
}
运行结果:
更改成功。
- 类
public class test1 {
public static void main(String[] args) {
Book booka = new Book("野草");
fun(booka);
System.out.println(booka.getName());
}
public static void fun(Book bookb) {
bookb.setName("狂人");
}
}
class Book {
private String name;
public Book(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
运行结果:
更改成功。
在上面的代码中,我们实例化了一个Book类型的对象booka,将booka作为参数传入了fun方法中,调用该方法。而在fun方法中,把Book类型的bookb的name属性更改为"狂人"。fun方法结束后,输出booka的name属性,发现其已然改为"狂人"。
以上两段代码,如果把方法这个载体省去,main函数的过程与Book bookb = booka,int[] arrayb = arraya的引用传递过程并无区别。事实上也正是如此,当我们把booka传入fun方法时,就是实现了bookb = booka的过程。
变量传参后是否影响外界的值
在基本数据类型作为参数的时候,我们发现在方法中更改的数据又回归原值。这是数据类型的原因,还是方法的特性?
还是之前的例子,我加入了一个change方法,负责改变类的指向。
public class test1 {
public static void main(String[] args) {
Book a = new Book("野草");
Book b = new Book("坟");
fun(a);
change(a, b);
System.out.println(a.getName());
System.out.println(b.getName());
}
public static void fun(Book bookb) {
bookb.setName("狂人");
}
public static void change(Book a, Book b) {
b = a;
}
public static void fun2(int b) {
b = 2;
}
}
class Book {
private String name;
public Book(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
运行结果:
可以发现,经过引用传递的值成功改变,而直接改变参数的值则无效。这与方法本身的特性有关,当我们调用一个方法,系统就会在栈中申请一段空间用来存储方法中的值,而这段空间的寿命会伴随着方法的结束而结束。
之所以使用引用传递能改变原来的值,是因为它改变的是参数所指向的值。这样说有点绕,我们可以举个例子:
定义一个方法,传进去的参数是一个门牌号。此时,系统会在栈中申请一段空间,并在这段空间中制作门牌号的副本。显然,无论怎么改变副本,都与原来的门牌号无关。但如果我们要改变门牌号对应的房子,那就一改一个准了,因为无论做了几个副本,只要牌号正确,我们都能按照门牌号找到房子。其实这就是c++中指针的工作机制。