根据代码,判断哪段代码可以使打印出来的1跟2的顺序相反
class Demo{
int a;
public Demo(int a){
this.a=a;
}
}
public class TestQuote{
public static void main(String args[]){
Demo d1=new Demo(1);
Demo d2=new Demo(2);
System.out.println(d1.a);
System.out.println(d2.a);
function(d1,d2);
System.out.println(d1.a);
System.out.println(d2.a);
}
private static void function(Demo d1,Demo d2){
int a;
a=d1.a;
d1.a=d2.a;
d2.a=a;
}
}
class Demo{
int a;
public Demo(int a){
this.a=a;
}
}
public class TestQuote{
public static void main(String args[]){
Demo d1=new Demo(1);
Demo d2=new Demo(2);
System.out.println(d1.a);
System.out.println(d2.a);
function(d1,d2);
System.out.println(d1.a);
System.out.println(d2.a);
}
private static void function(Demo d1,Demo d2){
Demo temp;
temp=d1;
d1=d2;
d2=temp;
}
}
运行以下代码就可以知道答案啦
要了解参数传递,那么我们首先要了解一下基本类型和引用类型的变量有什么区别。基本类型的变量中存储的是一个基本类型的值;而引用类型的变量中,存储的是一个指向内存某处空间的地址,也就是引用类型的变量指向了内存中某处。当我们用“=”操作基本类型变量的时候, 就是直接改变了变量的值如 int a = 0; a = 33;当我们用“=”操作引用类型变量的时候, 改变的是引用变量中所存的地址,也就是改变了它的指向,如String a ="aaaa"; a = "bbbb"; a一开始指向了常量池里的aaaa字符串,后来又指向了常量池里的bbbb字符串。
而参数传递就是一个赋值的过程,不管这个值是一个基本类型,还是一个地址,都是赋值。
private static void function(Demo d1,Demo d2){
Demo temp;
temp=d1;
d1=d2;
d2=temp;
}
d1,d2完全可以用a b来替代
private static void function(Demo a,Demo b){
Demo temp;
temp = a;
a = b;
b = temp;
}
也就是说,在调用function(d1,d2);这个方法的时候,a = d1,b = d2;因为d1是一个变量名,是一个引用变量,因此它里面存放的是Demo的一个名为d1的对象地址而已吧(比如@0x0002),那么这个时候呢,d1里面的值就赋给了a,a也就同样指向了Demo的对象d1的地址;b则指向了对象d2的地址。这在两段代码中都是一样的。
然后在第二段代码中,temp随后也指向了d1(temp = a),而a随后指向了d2(a = b), b随后指向了d1(b = temp)。在这个过程中只是局部变量a b 指向的内存地址改变了,并没有改变d1 d2这两个引用变量的值。所以d1还是指向Demo的一个对象d1所在的内存地址(@0x0002),d2还是指向Demo的一个对象d1所在的内存地址d2。
再回头看第一段代码中的function方法
private static void function(Demo ff,Demo ee){
int a;
a = ff.a;
ff.a = ee.a;
ee.a = a;
}
因为方法体中已经有了局部变量a,所以这里我就在参数列表中将d1,d2用ff和ee替换。那么,这个时候假设d1指向了内存地址@0x1111 d2指向内存地址@0x2222,那么调用了function方法之后,ff也指向了@0x1111,ee指向了@0x2222, 然后a = ff.a则是对@0x1111这个地址存放的Demo的对象d1中的成员变量a的值赋值给了a这个变量,也就是a现在的值为1,随后ff.a = ee.a则是将Demo的另一个对象d2中的成员变量a的值赋值给了ff.a,那么ff.a的值就变成了2,ee.a = a 则是把1赋值给了ee.a,这两个操作改变了Demo两个对象的成员变量a在内存中的值,所以后面打印的时候,看到的结果是原来d1.a d2.a的值1跟2分别变成了 2跟1。
一般网上讨论的Java参数传递是值传递还是引用传递问题,我觉得可以用Yolanda在知乎上的回答来理解。一直以来大家对于值传递,引用传递都只是有一个模糊的概念而已,所以才会有这种讨论。值传递和引用传递,属于函数调用时参数的求值策略(Evaluation Strategy),这是对调用函数时,求值和传值的方式的描述,而非传递的内容的类型(内容指:是值类型还是引用类型,是值还是指针)。也就是说,假设我用某一门参数传递是值传递的语言,我在调用方法的时候,不管给参数传递的是具体的一个整型变量,还是只是一个对象的引用,这都无法改变我是使用值传递的这个事实。值传递的传值方式是原值的副本,所以函数中无法改变原始的对象;引用传递不会创建副本,它的传值方式是原值,因此函数可以改变原始对象。而Java是Pass by value
有些同学可能会疑惑,能够改变对象中的成员变量的值,为什么说Java是值传递呢?这里需要明确的是,我们并没有改变原始对象,也就是我们并没有改变传进来的d1 d2这两个引用变量的值,我们只是在方法中创建了两个新的变量ff ee,指向了d1 d2所指向的内存地址,然后通过这个内存地址修改了对象中的值。
这里还有几道题可以练习一下
http://www.xuebuyuan.com/897461.html
http://blog.darkmi.com/2010/11/28/1430.html
然后呢,这里有一个比较特殊的地方,就是String类。
public class Test {
public static void change(String s)
{
s = "bbbb";
}
public static void main(String[] args)
{
String a = new String("aaaa"); //String a = "aaaa";也可以达到一样的效果
System.out.println(a); //结果为aaaa
change(a);
System.out.println(a); //结果为aaaa
}
}
String类是纯粹的对象,特殊的地方在于有 字符串直接量 和 字符串常量池 和 字符串拼接的运算符重载。而且与基本类型无关,之所以看起来像是基本类型,主要是因为String类是不可变类(构造后不可改变,没有提供可改变自身状态的方法)。