在對(duì)象指向中,我們一定要清楚引用類型數(shù)據(jù)的堆棧二段式內(nèi)存管理,下面通過(guò)一個(gè)小例子,讓我們來(lái)解析堆棧內(nèi)存是如何使用的。
先附一段代碼:

這是為什么呢,其實(shí)是對(duì)象引用的問(wèn)題。
先看這個(gè)例子,里面先new了兩個(gè)對(duì)象,分別問(wèn)p1和p2,然后將p2的值附給了p1,這時(shí),p1的指向就發(fā)生了變化:p1就不再指向原來(lái)的地址了,此時(shí)p1就指向了p2所指向的地址了,也就是說(shuō):p1和p2指向了同一塊堆內(nèi)存。這時(shí)先給p1的屬性賦值,并且調(diào)用了p1的say方法,這個(gè)時(shí)候控制臺(tái)就打印出來(lái)p1所指向的堆地址(實(shí)際此時(shí)p2指向的也是這個(gè)堆地址);然后又給p2的屬性賦值,并且調(diào)用了p2的say方法,這個(gè)時(shí)候控制臺(tái)就會(huì)打印出來(lái)p2所指向的堆地址(實(shí)際此時(shí)p1指向的也是這個(gè)堆地址),兩次打印出來(lái)的字符串是不一樣的,因?yàn)閿?shù)值發(fā)生了改變。
下面附堆棧內(nèi)存圖來(lái)進(jìn)一步解釋。
第一次調(diào)用say方法時(shí)內(nèi)存狀態(tài):

第二次調(diào)用say方法時(shí)的內(nèi)存狀態(tài):

上面第二個(gè)圖畫錯(cuò)了,應(yīng)該是在第一個(gè)name和age改成“狗生”和“22”,第二個(gè)name和age還是空的。
將上面的代碼稍微改動(dòng)一下:
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();
}
}
我們?cè)倏纯催\(yùn)行結(jié)果:

再看這個(gè)例子,里面先new了兩個(gè)對(duì)象,分別問(wèn)p1和p2,然后將p2的值附給了p1,這時(shí),p1的指向就發(fā)生了變化:p1就不再指向原來(lái)的地址了,此時(shí)p1就指向了p2所指向的地址了,也就是說(shuō):p1和p2指向了同一塊堆內(nèi)存。這時(shí)先給p1的屬性賦值(實(shí)際此時(shí)p2指向的也是這個(gè)堆地址);然后又給p2的屬性賦值(實(shí)際此時(shí)p1指向的也是這個(gè)堆地址),這個(gè)時(shí)候就出現(xiàn)了堆內(nèi)存里數(shù)據(jù)的覆蓋,這個(gè)時(shí)候調(diào)用了p1的say方法和p2的say方法,實(shí)際上兩次打印出來(lái)的字符串是一樣的,都是修改后的數(shù)據(jù),因?yàn)閮蓚€(gè)引用都是指向同一塊內(nèi)存地址。
下面附堆棧內(nèi)存圖來(lái)進(jìn)一步解釋。
第一次和第二次調(diào)用say方法時(shí)內(nèi)存狀態(tài):

上面第三個(gè)圖畫錯(cuò)了,應(yīng)該是在第一個(gè)name和age改成“狗生”和“22”,第二個(gè)name和age還是空的。
這就是對(duì)象引用的時(shí)候內(nèi)存管理的變化,希望這個(gè)例子能夠?qū)Υ蠹腋美斫鈨?nèi)存管理起到一定幫助作用。
先附一段代碼:
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();
}
}

這是為什么呢,其實(shí)是對(duì)象引用的問(wèn)題。
先看這個(gè)例子,里面先new了兩個(gè)對(duì)象,分別問(wèn)p1和p2,然后將p2的值附給了p1,這時(shí),p1的指向就發(fā)生了變化:p1就不再指向原來(lái)的地址了,此時(shí)p1就指向了p2所指向的地址了,也就是說(shuō):p1和p2指向了同一塊堆內(nèi)存。這時(shí)先給p1的屬性賦值,并且調(diào)用了p1的say方法,這個(gè)時(shí)候控制臺(tái)就打印出來(lái)p1所指向的堆地址(實(shí)際此時(shí)p2指向的也是這個(gè)堆地址);然后又給p2的屬性賦值,并且調(diào)用了p2的say方法,這個(gè)時(shí)候控制臺(tái)就會(huì)打印出來(lái)p2所指向的堆地址(實(shí)際此時(shí)p1指向的也是這個(gè)堆地址),兩次打印出來(lái)的字符串是不一樣的,因?yàn)閿?shù)值發(fā)生了改變。
下面附堆棧內(nèi)存圖來(lái)進(jìn)一步解釋。
第一次調(diào)用say方法時(shí)內(nèi)存狀態(tài):

第二次調(diào)用say方法時(shí)的內(nèi)存狀態(tài):

上面第二個(gè)圖畫錯(cuò)了,應(yīng)該是在第一個(gè)name和age改成“狗生”和“22”,第二個(gè)name和age還是空的。
將上面的代碼稍微改動(dòng)一下:
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();
}
}
我們?cè)倏纯催\(yùn)行結(jié)果:

再看這個(gè)例子,里面先new了兩個(gè)對(duì)象,分別問(wèn)p1和p2,然后將p2的值附給了p1,這時(shí),p1的指向就發(fā)生了變化:p1就不再指向原來(lái)的地址了,此時(shí)p1就指向了p2所指向的地址了,也就是說(shuō):p1和p2指向了同一塊堆內(nèi)存。這時(shí)先給p1的屬性賦值(實(shí)際此時(shí)p2指向的也是這個(gè)堆地址);然后又給p2的屬性賦值(實(shí)際此時(shí)p1指向的也是這個(gè)堆地址),這個(gè)時(shí)候就出現(xiàn)了堆內(nèi)存里數(shù)據(jù)的覆蓋,這個(gè)時(shí)候調(diào)用了p1的say方法和p2的say方法,實(shí)際上兩次打印出來(lái)的字符串是一樣的,都是修改后的數(shù)據(jù),因?yàn)閮蓚€(gè)引用都是指向同一塊內(nèi)存地址。
下面附堆棧內(nèi)存圖來(lái)進(jìn)一步解釋。
第一次和第二次調(diào)用say方法時(shí)內(nèi)存狀態(tài):

上面第三個(gè)圖畫錯(cuò)了,應(yīng)該是在第一個(gè)name和age改成“狗生”和“22”,第二個(gè)name和age還是空的。
這就是對(duì)象引用的時(shí)候內(nèi)存管理的變化,希望這個(gè)例子能夠?qū)Υ蠹腋美斫鈨?nèi)存管理起到一定幫助作用。