在对象指向中,我们一定要清楚引用类型数据的堆栈二段式内存管理,下面通过一个小例子,让我们来解析堆栈内存是如何使用的。
先附一段代码:

class People{
 public String name;
 public int age;
 public void say(){
  System.out.println("姓名:"+name+"  年龄:"+age);
 }
}
public class Test16 {

 public static void main(String[] args) throws InterruptedException {
  People p1 =new People();
  People p2 =new People();
  p1=p2;
  p1.name="大雨";
  p1.age=21;
  p1.say();
  p2.name="狗生";
  p2.age=22;
  p2.say();
 }
}

运行结果如下图:


这是为什么呢,其实是对象引用的问题。

先看这个例子,里面先new了两个对象,分别问p1和p2,然后将p2的值附给了p1,这时,p1的指向就发生了变化:p1就不再指向原来的地址了,此时p1就指向了p2所指向的地址了,也就是说:p1和p2指向了同一块堆内存。这时先给p1的属性赋值,并且调用了p1的say方法,这个时候控制台就打印出来p1所指向的堆地址(实际此时p2指向的也是这个堆地址);然后又给p2的属性赋值,并且调用了p2的say方法,这个时候控制台就会打印出来p2所指向的堆地址(实际此时p1指向的也是这个堆地址),两次打印出来的字符串是不一样的,因为数值发生了改变。

下面附堆栈内存图来进一步解释。

第一次调用say方法时内存状态:


第二次调用say方法时的内存状态:



上面第二个图画错了,应该是在第一个name和age改成“狗生”和“22”,第二个name和age还是空的。


将上面的代码稍微改动一下:
class People{
 public String name;
 public int age;
 public void say(){
  System.out.println("姓名:"+name+"  年龄:"+age);
 }
}
public class P1withP2 {
 public static void main(String[] args) throws InterruptedException {
  People p1 =new People();
  People p2 =new People();
  p1=p2;
  p1.name="大雨";
  p1.age=21;
  p2.name="狗生";
  p2.age=22;
  p1.say();
  p2.say();
 }
}

我们再看看运行结果:


再看这个例子,里面先new了两个对象,分别问p1和p2,然后将p2的值附给了p1,这时,p1的指向就发生了变化:p1就不再指向原来的地址了,此时p1就指向了p2所指向的地址了,也就是说:p1和p2指向了同一块堆内存。这时先给p1的属性赋值(实际此时p2指向的也是这个堆地址);然后又给p2的属性赋值(实际此时p1指向的也是这个堆地址),这个时候就出现了堆内存里数据的覆盖,这个时候调用了p1的say方法和p2的say方法,实际上两次打印出来的字符串是一样的,都是修改后的数据,因为两个引用都是指向同一块内存地址。

下面附堆栈内存图来进一步解释。

第一次和第二次调用say方法时内存状态:



上面第三个图画错了,应该是在第一个name和age改成“狗生”和“22”,第二个name和age还是空的。


这就是对象引用的时候内存管理的变化,希望这个例子能够对大家更好理解内存管理起到一定帮助作用。