前言
我们都知道,java中数据类型分为基本数据类型和引用数据类型。
- 基本数据类型
- 整型:byte,short,int,long
- 浮点型:float,double
- 字符型:char
- 布尔型:boolean
- 引用数据类型
- 数组
- 类
- 接口
- 方法的参数分为实际参数和形式参数。
- 形式参数:定义方法时括号内写的参数。
如private void test(int a , byte[] b){……},此时的a和b均为形参。 - 实际参数:调用方法时写的具体数值。
int m=5;
byte[] n={……};
test(m , n);
此处的m和n均为实参。
- 形式参数:定义方法时括号内写的参数。
一般情况下,在数据做为参数传递的时候,基本数据类型是值传递,引用数据类型是引用传递(地址传递)。
值传递:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
int a = 2;
int b = 15;
test1(a, b);
Log.d(TAG, "a = " + a);
Log.d(TAG, "b = " + b);
}
private void test1(int m, int n) {
m += 100;
n += 100;
Log.d(TAG, "m = " + m);
Log.d(TAG, "n = " + n);
}
{//TODO:打印结果为:
m = 102;
n = 115;
a = 2;
b = 15;
}
流程:
1.主函数进栈,a、b初始化。
2.调用test1()方法,test1()进栈,将a和b的值,复制一份给m和n。
3.test1()方法中对m,n的值进行重新赋值运算。
4.test1()方法运行完毕,m,n的值已经被赋为最新值。
5.test1()方法弹栈。
6.主函数弹栈。
说明:
在test1()方法中,m,n数值改变,并不会影响到a、b。因为, m , n中的值,只是从a、b的复制过来的。
也就是说,m、n相当于a、b的副本,副本的内容无论怎么修改,都不会影响到原件本身。
引用传递
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
//引用数据类型——类
Cat c = new Cat();
c.name = "小花"; //①
test2(c); //②
Log.d(TAG, "c.name = " + c.name);//④
//引用数据类型——数组
int[] arr = {1, 2, 3};
test3(arr);
Log.d(TAG, " arr[0] = " + arr[0]);
}
private void test2(Cat cat) {
cat.name = "小黑";//③
}
private void test3(int[] array) {
if (0 == array.length) return;
array[0] = 886;
}
private final class Cat {
String name;
}
{//TODO:打印结果:
c.name = 小黑;
arr[0] = 886;
}
流程:(以数组为例)
1.主函数进栈,int[] arr初始化。
2.调用test3()方法,test3( )进栈,将arr的地址值,复制一份给array。
3.test3()方法中,根据地址值,找到堆中的数组,并将第一个元素的值改为886。
4.test3()方法运行完毕,数组中第一个元素的值已经改变。
5.test3()方法弹栈。
6.主函数弹栈。
说明:
调用test3()的时候,形参array接收的是arr地址值的副本。并在test3()方法中,通过地址值,对数组进行操作。test3()方法弹栈以后,数组中的值已经改变。onCreate()方法中,打印出来的arr[0]也就从原来的1变成了886.
无论是onCreate()方法,还是test3()方法,操作的都是同一个地址值对应的数组。
就像你把自己家的钥匙给了另一个人,这个人拿着钥匙在你家一顿瞎折腾(增删改),然后走了。等你拿着钥匙回到家以后,家里已经变成了被折腾过后,惨不忍睹的样子。。
这里的钥匙就相当于地址值,家就相当于数组本身。
String类型传递
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
//引用数据类型--String类
String s1 = "AreYouOK?";
test4(s1);
Log.d(TAG, "s1 = " + s1);
}
private void test4(String str) {
str = "NO";
}
{//TODO: 打印结果为
s1 = AreYouOK ?;
}
这么诡异??? 有点意思哦!!!
String是一个类,类是引用数据类型,做为参数传递的时候,应该是引用传递。但是从结果看起来却是值传递。
String的API中有这么一句话:“their values cannot be test4()d after they are created”
,
意思是:String的值在创建之后不能被更改。
API中还有一段:
String str = "abc";
//等效于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
也就是说:对String对象str的任何修改 等同于 重新创建一个对象,并将新的地址值赋值给str。
按照这个逻辑,上面代码可以这么写:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
//引用数据类型--String类
String s1 = "AreYouOK?";
test4(s1);
Log.d(TAG, "s1 = " + s1);
}
private void test4(String str) {
char data[] = {'N', 'O'};
String t = new String(data);
str = t;
}
{//TODO: 打印结果为
s1 = AreYouOK ?;
}
流程:
1.主函数进栈,s1初始化。
2.调用test4()方法,test4( )进栈,将s1的地址值,复制一份给str。
3.test4()方法中,重现创建了一个String对象 ”NO”,并将str指向了新的地址值。
4.test4()方法运行完毕,str所指向的地址值已经改变。
5.test4()方法弹栈。
6.主函数弹栈。
说明:
String对象做为参数传递时,走的依然是引用传递,只不过String这个类比较特殊。
String对象一旦创建,内容不可更改。每一次内容的更改都是重现创建出来的新对象。
当test4()方法执行完毕时,str所指向的地址值已经改变。而str本来的地址值就是copy过来的副本,所以并不能改变s1的值。
String类似情况
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
//引用数据类型--类
Cat cc = new Cat();
cc.name="Lily"; //1
test5(cc); //2
Log.d(TAG, "cc.name = "+cc.name);//5
}
private void test5(Cat cat) {
Cat t_cat = new Cat();
t_cat.name = "Tom"; //3
cat = t_cat; //4
}
private final class Cat {
String name;
}
{//TODO:打印结果:
cc.name = Lily;
}
综述:
- 值传递的时候,将实参的值,copy一份给形参。
- 引用传递的时候,将实参的地址值,copy一份给形参。
也就是说,不管是值传递还是引用传递,形参拿到的仅仅是实参的副本,而不是实参本身。
特别说明:
“在Java里面参数传递都是按值传递”这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。