Java引用传递?值传递?
Posted maerpao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java引用传递?值传递?相关的知识,希望对你有一定的参考价值。
引用传递和值传递,从上学那会儿就开始强调的概念,不管你是计算机相关专业还是自学Java,一定听过这么一句话:
方法调用参数如果是对象,那就是引用传递,如果是基本数据类型,就是值传递。
比如:function(Object o)就是引用传递,function(int i)就是值传递。这两个概念似乎很好理解,我们只需要记住对象和基本数据类型的区别就行了。但是,真的是这样吗?
有一段代码如下:
public static void main(String[] args) int i = 0; System.out.println(i); change(i); System.out.println(i); private static void change(int i) i = 1;
输出结果比较好猜测,也应该都能答对:
0 0
下一个问题,如果将int改成String呢?
public static void main(String[] args) String s = "0"; System.out.println(s); change(s); System.out.println(s); private static void change(String s) s = "1";
输出的结果如下:
0 0
嗯?有疑问了吧?不是引用传递吗?我在方法里命名修改了s的值,为什么输出还是”0“呢?难度String作为Object有什么特殊性?
别急,继续看下一段代码:
public static void main(String[] args) Person p = new Person("0"); System.out.println(p); change(p); System.out.println(p); private static void change(Person p) p = new Person("1"); static class Person String name; public Person(String name) this.name = name; @Override public String toString() return "Person" + "name=\'" + name + \'\\\'\' + \'\';
结果会输出什么?
Personname=\'0\'
Personname=\'0\'
看来String和其他Object没什么不同,可是这样的结果好像不太符合我们对引用传递的认知啊。其实我感觉这两个概念没有必要区分,实质是一回事,都是将栈中引用复制了一份传递到方法中,无论在方法中如何对引用操作,都是操作的副本,只是对于基本数据类型来说,值存储在栈中,引用存储的就是值,而对象来说,引用中存储的是对象在堆中的内存地址,参数传递时生成的副本仍然指向了原来引用指向的对象,所以如果直接操作该对象是有效的。简单画个图方便理解:
如果对p的操作不是将该引用指向一个新的值,而是对p指向的对象进行操作,就能看到所谓引用传递的效果了例如:
public static void main(String[] args) Person p = new Person("0"); System.out.println(p); change(p); System.out.println(p); private static void change(Person p) p.name = "1"; static class Person String name; public Person(String name) this.name = name; @Override public String toString() return "Person" + "name=\'" + name + \'\\\'\' + \'\';
此时,执行结果为:
Personname=\'0\'
Personname=\'1\'
总结:
Java进行方法调用时参数传递是将栈中的引用复制了一份到该方法的工作区,如果引用指向了一个堆中的对象,那么副本也指向这个对象。
java中参数传递--值传递,引用传递
参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递。
在 Java 应用程序中永远不会传递对象,而只传递对象引用。因此是按引用传递对象。Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数。参数可以是对象引用,而 Java 应用程序是按值传递对象引用的。
Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型。当作为参数传递给一个方法时,处理这两种类型的方式是相同的。两种类型都是按值传递的;没有一种按引用传递。
按值传递和按引用传递。按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本。因此,如果函数修改了该参数,调用代码中的原始值也随之改变。
1、对象是按引用传递的
2、Java 应用程序有且仅有的一种参数传递机制,即按值传递
3、按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本
4、按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本
首先考试大来看看第一点:对象是按引用传递的
确实,这一点我想大家没有任何疑问,例如:
class Test01
{
public static void main(String[] args)
{
StringBuffer s= new StringBuffer("good");
StringBuffer s2=s;
s2.append(" afternoon.");
System.out.println(s);
}
}
对象s和s2指向的是内存中的同一个地址因此指向的也是同一个对象。
如何解释“对象是按引用传递的”的呢?
这里的意思是进行对象赋值操作是传递的是对象的引用,因此对象是按引用传递的,有问题吗?
程序运行的输出是:
good afternoon.
这说明s2和s是同一个对象。
这里有一点要澄清的是,这里的传对象其实也是传值,因为对象就是一个指针,这个赋值是指针之间的赋值,因此在java中就将它说成了传引用。(引用是什么?不就是地址吗?地址是什么,不过就是一个整数值)
再看看下面的例子:
class Test02
{
public static void main(String[] args)
{
int i=5;
int i2=i;
i2=6;
System.out.println(i);
}
}
程序的结果是什么?5!!!
这说明什么,原始数据类型是按值传递的,这个按值传递也是指的是进行赋值时的行为下一个问题:Java 应用程序有且仅有的一种参数传递机制,即按值传递
class Test03
{
public static void main(String[] args)
{
StringBuffer s= new StringBuffer("good");
StringBuffer s2=new StringBuffer("bad");
test(s,s2);
System.out.println(s);//9
System.out.println(s2);//10
}
static void test(StringBuffer s,StringBuffer s2) {
System.out.println(s);//1
System.out.println(s2);//2
s2=s;//3
s=new StringBuffer("new");//4
System.out.println(s);//5
System.out.println(s2);//6
s.append("hah");//7
s2.append("hah");//8
}
}
程序的输出是:
good
bad
new
good
goodhah
bad
考试大提示: 为什么输出是这样的?
这里需要强调的是“参数传递机制”,它是与赋值语句时的传递机制的不同。
我们看到1,2处的输出与我们的预计是完全匹配的
3将s2指向s,4将s指向一个新的对象
因此5的输出打印的是新创建的对象的内容,而6打印的原来的s的内容
7和8两个地方修改对象内容,但是9和10的输出为什么是那样的呢?
Java 应用程序有且仅有的一种参数传递机制,即按值传递。
至此,我想总结一下我对这个问题的最后的看法和我认为可以帮助大家理解的一种方法:
我们可以将java中的对象理解为c/c++中的指针
例如在c/c++中:
int *p;
print(p);//1
*p=5;
print(*p);//2
1打印的结果是什么,一个16进制的地址,2打印的结果是什么?5,也就是指针指向的内容。
即使在c/c++中,这个指针其实也是一个32位的整数,我们可以理解我一个long型的值。
而在java中一个对象s是什么,同样也是一个指针,也是一个int型的整数(对于JVM而言),我们在直接使用(即s2=s这样的情况,但是对于System.out.print(s)这种情况例外,因为它实际上被晃猄ystem.out.print(s.toString()))对象时它是一个int的整数,这个可以同时解释赋值的传引用和传参数时的传值(在这两种情况下都是直接使用),而我们在s.XXX这样的情况下时s其实就是c/c++中的*s这样的使用了。这种在不同的使用情况下出现不同的结果是java为我们做的一种简化,但是对于c/c++程序员可能是一种误导。java中有很多中这种根据上下文进行自动识别和处理的情况,下面是一个有点极端的情况:
class t
{
public static String t="t";
public static void main(String[] args)
{
t t =new t();
t.t();
}
static void t() {
System.out.println(t);
}
}
1.对象就是传引用
2.原始类型就是传值
3.String类型因为没有提供自身修改的函数,每次操作都是新生成一个String对象,所以要特殊对待。可以认为是传值。
以上是关于Java引用传递?值传递?的主要内容,如果未能解决你的问题,请参考以下文章