Java对象内存存储,引用传递,值传递详细图解

问题:
Java在调用函数时,对象作为参数传递,执行函数后参数对象的值是否发生改变。
正文:
在解决这个问题之前首先得说说Java对象在内存中的存储机制。
我们知道Java数据类型基本分为两种,一是基本类型,还一种是引用类型。
基本类型:
对象类型是固定的,如下:
byte,short,int,long,float,double,char,boolean
并且被创建后的值是存放在内存栈空间中。
引用类型:
除了以上8种数据类型的数据对象都是引用数据对象,一般来说就是我们自定义的类的对象(如Person类,Info类等)。
其创建后的值是存放内存的堆空间中的,在栈空间中只存有该对象的引用。
图解说明:
1,基本类型
Java对象内存存储,引用传递,值传递详细图解_第1张图片
2,引用类型
Java对象内存存储,引用传递,值传递详细图解_第2张图片

那么接下来再分析,Java在函数调用时,参数传递的过程。
我们知道函数本身也是放在栈空间中的,调用函数后函数体内部将为形参开辟空间。
1,基本类型做形参函数调用

int a = 100;
public void add_self(int x)
{
    x = x + x;
}
log.i("tag",a);

Java对象内存存储,引用传递,值传递详细图解_第3张图片
经过上图分析,add_self函数中只对内部变量x地址中的值产生了改变,而a变量指向地址的值一直没有发送改变,所以函数结束后log打印出来的还是100;

当然如果有返回类型的这种,那情况就不一样了。

int a = 100;
public int add_self(int x)
{
    return x = x + x;
}
a = add_self(a);
log.i("tag",a);

Java对象内存存储,引用传递,值传递详细图解_第4张图片
这种情况基本同上,只是函数最后一步将x地址中的值传递出来了,并且a变量接收了,那么实质上就是a变量ox0008地址区域中的值被重新赋值为x变量中的值,故函数结束后a值发送改变。log输出200。
2,引用类型做形参函数调用
经过开始的分析我们知道,在栈中引用类型对象地址空间中存放的只是一段指向堆空间的地址值。那么在参数传递的过程中本质和基本类型参数传递是一样的。都是将函数外部对象的值传递给函数体中对象,然后在函数体内部做的任何操作与外部对象的地址都无关。(注意这是是说外部对象地址,但是其引用堆中的对象却不一定)。

Person p = new Person();
p.name = "小明";
p.sex = "boy";

public void changePerson(Person person)
{
    person.name = "小辉";
    p.sex = "boy";
}

changePerson(p);
log.i("tag",p.name);

Java对象内存存储,引用传递,值传递详细图解_第5张图片
接下来再看函数体中对person对象进行操作后,内存中数据的变化情况。
Java对象内存存储,引用传递,值传递详细图解_第6张图片
函数执行完成后,堆中的值却发生改变了,整个函数操作间接的对p对象的值造成了影响。故log出来的是“小辉”。
从这里就可以看出,基本类型与引用类型在参数传递中的本质是一样的,都是属于值传递!但是由于引用类型对象的数据存放在堆中,而且函数中可以改变堆中的值。故导致他们作为参数传递的结果又不一样。但是万变不离其宗,根据内存分析还是很容易理解的。

同理这里有没有返回值都没什么影响了,如果函数有return,并且被p对象接收,p对象中存放的还是ox8008地址。如果没有return ,p对象中存放的值任然是ox8008。
PS:当在函数体中对形参变量重新引用了别的对象,那么情况是这样的。

public void changePerson(Person person)
{
    Person p1 = new Person();
    p1.name = "小花";
    person = p1;
}
public void changePerson2(Person person)
{
    person = new Person();
    person.name = "小花";
}

这两种情况的本质都是改变了函数内部person对象的引用对象。
Java对象内存存储,引用传递,值传递详细图解_第7张图片
所以在person对象指向别的引用后,其对属性发生的任何改变都对p对象无关。除非函数有返回值,且让p对象指向person对象的引用空间。

总结:
1,基本类型对象存放在栈中,引用类型对象数据存放在堆中。
2,无论是基本类型对象还是引用类型对象,作为参数传入函数中本质都是值传递。
3,判读一个对象的值是否发送改变,关键是看其本身的值,如果是引用对象则要看其值引向的堆空间的值。

你可能感兴趣的:(java)