以下仅为个人观点!如有差错希望和大家进行讨论得到改正。
有的人说JAVA有值传递还有引用传递,也有人说JAVA只有值传递,那么到底JAVA是值传递还是引用传递呢?
首先我们看C++。
C++在传参时支持三种传递方式:
传值调用:该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。
指针调用:该方法把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
引用调用:该方法把参数的引用复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
我之前的理解如下:
- 8大基本类型(例如int,double,char)都是值传递(String也是这类)。传递时传参(int)a,函数体内操作a,原来的i不会改变。基本类型的变量在创建的时候直接把内容放在了栈里,没有在堆里。(String放在了堆里)。
例子如下
//代码块1
public class Test
{
static void c(int a)
{
a++;
}
public static void main(String[] args)
{
int i = 1;
c(i);
System.out.println(i);
}
}
//输出为1
- 对象都是引用传递。传参时传入了对象的引用,对引用操作就是直接对堆内存对应地址的内容数据进行操作
例子如下
//代码块2
class User
{
int id;
//构造函数
public User(int id)
{
this.id = id;
}
}
public class Test
{
static void c(User u)
{
u.id++;
}
public static void main(String[] args)
{
User user = new User(1);
c(user);
System.out.println(user.id);
}
}
//输出为2
当我遇到了String时,传参为String,但是原对象没有更改,我姑且认为String也是个特殊的值传递。
这一切的一切似乎都那么明显又简单?这不就简简单单的可以把值传递和引用传递分开了吗?只不过String特殊一点罢了~
但是当我遇到了下面这个问题,我蒙了。。。
//代码块3
class User
{
int id;
public User(int id)
{
this.id = id;
}
}
public class Test
{
static void c(User u)
{
u = new User(2);
}
public static void main(String[] args)
{
User user = new User(1);
c(user);
System.out.println(user.id);
}
}
//输出为1 or 2 ?
这段代码块3和代码块2差别只有c的函数体内。
代码块2:
static void c(User u)
{
u.id++;
}
代码块3:
static void c(User u)
{
u = new User(2);
}
按照原来我的逻辑下来代码块3输出的应该是2啊,因为引用传递传入的是user这个引用啊,那u=new User(2)的话user不是也应该执行user=new User(2)吗!?为什么结果不一样呢???
原来 static void c(User u) 这段代码里User u传的并不是user这个引用,而是user引用的拷贝!这个拷贝,对应的地址没有变,还是原来user这个引用指向的地址(堆内存)。
当函数体里面的"u"发生改变,就是指向的对应的堆内存数据发生了改变,也等价于的"user"发生了改变。
但是当执行了 u = new User(2); 时候发生如下:
由于u=new User(2)是新开辟了空间,并把u的地址指向改成了新开辟的User(2),所以以后u改变不会影响原来user。
这就解释了传递对象引用并且u=new User(2)时为什么没有改变值的原因了!
原来传入的u并不是user这个引用,而是user引用的完整的拷贝!
而传入user的拷贝而不是user,这就是值传递和引用传递的区别所在!
所以看来我以前的看法错了,Java确实只有值传递。
(但是如果不遇上u=new User(2)这个问题时完全可以说java是分引用传递和值传递的)