Java傳值調(diào)用和傳引用調(diào)用方式(參數(shù)引用為null問(wèn)題)
一、問(wèn)題
近期在項(xiàng)目中遇到一個(gè)場(chǎng)景,在多層級(jí)調(diào)用中需要傳遞上下文,調(diào)用過(guò)程中上線文對(duì)象可能為空,想通過(guò)一個(gè)公共方法處理上下文,當(dāng)上下文為空時(shí)生成上下文對(duì)象,執(zhí)行相關(guān)操作后將該上下文對(duì)象向后傳遞。
大致邏輯如下:
public class Test {
public static void handleContext(Context context) {
if(context == null) {
context = new Context();
}
context.addNum();
}
public static void main(String[] args) {
Context context = null;
handleContext(context);
System.out.println(context.getNum());
}
static class Context {
private int num;
public int getNum() {
return num;
}
public void addNum() {
this.num ++;
}
}
}測(cè)試執(zhí)行報(bào)空指針錯(cuò)誤,context沒(méi)有按設(shè)想在handleContext方法中生成對(duì)象。
原因是main方法在棧中創(chuàng)建了Context的引用并將其指向null,handleContext方法參數(shù)中的Context引用也被指向null,handleContext方法體在堆中創(chuàng)建Context對(duì)象,并將對(duì)象地址賦給方法參數(shù)中的Context引用,但main方法中的Context引用仍然為null,因此調(diào)用context.getNum()時(shí)報(bào)空指針。
二、java方法傳值與傳引用
2.1 變量存儲(chǔ)
基礎(chǔ)類型變量:
Java的8中基礎(chǔ)數(shù)據(jù)類型:byte(8位)、short(16位)、int(32位)、long(64位)、float(32位)、double(64位)、char(16位)、boolean(8位),
基礎(chǔ)類型的數(shù)據(jù)存儲(chǔ)在棧中,即是棧中分配內(nèi)存空間存儲(chǔ)所包含的值,其值就代表數(shù)據(jù)本身,值類型的數(shù)據(jù)具有較快的存取速度。
引用類型變量:
除了基礎(chǔ)類數(shù)據(jù)外,其余都是引用類型,包括類、數(shù)組等。
引用類型數(shù)據(jù)的具體對(duì)象存放在堆中,而棧中存放的是該對(duì)象的內(nèi)存地址。
當(dāng)引用類型沒(méi)有賦值時(shí),其引用為null,表示不指向任何對(duì)象。
2.2 創(chuàng)建對(duì)象與賦值過(guò)程
Context context = new Context();
該操作可以分為兩個(gè)部分:
Context context; context = new Context();
第一步:創(chuàng)建Context引用,在棧中開(kāi)辟一塊空間用于存儲(chǔ)該引用;
第二步:創(chuàng)建Context對(duì)象,在堆內(nèi)存中開(kāi)辟一塊空間存儲(chǔ)該對(duì)象,并將該對(duì)象的存儲(chǔ)地址賦值給棧中的引用
2.3 引用賦值過(guò)程
將一個(gè)引用賦值給另一個(gè)引用時(shí),其實(shí)是將該引用指向的地址賦值給另個(gè)應(yīng)用,讓兩個(gè)引用指向同一個(gè)對(duì)象,示意圖如下。
Context context1 = new Context(); Context context2 = context1;

2.4 傳值與傳引用方法調(diào)用
其實(shí)無(wú)論是傳值還是傳引用調(diào)用,其本質(zhì)都是將棧中存儲(chǔ)的數(shù)據(jù)復(fù)制一份,傳遞給棧中的另一個(gè)變量。
對(duì)于基礎(chǔ)數(shù)據(jù)類型,是將數(shù)據(jù)本身復(fù)制一份傳遞;對(duì)于引用類型,是將引用的地址復(fù)制一份傳遞。
因此,上述問(wèn)題可以描述為如下示意圖。當(dāng)handleContext方法執(zhí)行返回后,main方法中的context引用仍然為null。
注:為方便理解,將null特殊處理。當(dāng)引用為null時(shí)候表示不指向任何對(duì)象

值得注意的是包裝類型(Intger;Long;Short;Double;Float;Char;Boolean;Byte,以及String(char[]的包裝類型)),雖然是引用類型數(shù)據(jù),但其效果等同于傳值調(diào)用,示例如下:
public class Test {
public static void change(String str) {
str = "xyz";
}
public static void main(String[] args) {
String str = "abc";
change(str);
System.out.println(str);
}
}
---
abc其原因是String類型是不可變(immutable)的。
String類型及其成員變量均是final的,這意味著String的value字符數(shù)組不能指向其它地址,同時(shí)value字符數(shù)組的值也不可能通過(guò)繼承String后修改。
在change方法中,參數(shù)String引用str初始化時(shí)指向?qū)ο骯bc,執(zhí)行方法后指向了方法區(qū)的另一個(gè)字符串常量(xyz),而main方法中的String引用str仍然指向方法區(qū)的字符串常量(abc)。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
...
private final char value[];
...
}2.5 Java常量池
基本類型的包裝類型大部分都實(shí)現(xiàn)了常量池技術(shù),包括:Byte、Short、Integer、Long、Character、Boolean,但只有在其值 -128<value<127范圍內(nèi)才可使用常量池,數(shù)據(jù)存放在方法區(qū),數(shù)值超出范圍或通過(guò)new方法生成對(duì)象則會(huì)在堆上分配存儲(chǔ)空間
public class Test {
public static void main(String[] args) {
Integer i = 123;
Integer j = 123;
System.out.println("i == j : " + (i == j));
System.out.println("i.equals(j) : " + i.equals(j));
Integer m = 1234;
Integer n = 1234;
System.out.println("m == n : " + (m == n));
System.out.println("m.equals(n) : " + m.equals(n));
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println("x == y : " + (x == y));
System.out.println("x.equals(y) : " + x.equals(y));
}
}
---
i == j : true
i.equals(j) : true
m == n : false
m.equals(n) : true
x == y : false
x.equals(y) : true總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot使用MockMvc進(jìn)行單元測(cè)試的實(shí)例代碼
在Spring Boot應(yīng)用程序中,使用MockMvc進(jìn)行單元測(cè)試是一種有效的方式,可以驗(yàn)證控制器的行為和API的正確性,在這篇博客中,我們將介紹如何使用MockMvc對(duì)用戶控制器進(jìn)行測(cè)試,感興趣的朋友可以參考下2024-01-01
MyBatis?核心組件Configuration實(shí)例詳解
Configuration用于描述 MyBatis 的主配置信息,其他組件需要獲取配置信息時(shí),直接通過(guò) Configuration 對(duì)象獲取,這篇文章主要介紹了MyBatis核心組件Configuration,需要的朋友可以參考下2023-08-08
spring定時(shí)任務(wù)執(zhí)行兩次及tomcat部署緩慢問(wèn)題的解決方法
這篇文章主要給大家介紹了關(guān)于spring定時(shí)任務(wù)執(zhí)行兩次及tomcat部署緩慢問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01
VsCode配置java環(huán)境的詳細(xì)圖文教程
vscode是一個(gè)免費(fèi)的代碼編輯器,支持多種主題,應(yīng)用起來(lái)簡(jiǎn)單方便,下面這篇文章主要給大家介紹了關(guān)于VsCode配置java環(huán)境的詳細(xì)圖文教程,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
SpringBoot?Web項(xiàng)目增刪改查入門(mén)實(shí)戰(zhàn)案例
這篇文章主要給大家介紹了關(guān)于SpringBoot?Web項(xiàng)目增刪改查入門(mén)實(shí)戰(zhàn)的相關(guān)資料,文中詳細(xì)介紹了如何實(shí)現(xiàn)新增、刪除、修改和查詢員工的功能,并使用統(tǒng)一響應(yīng)結(jié)果進(jìn)行前后端交互,需要的朋友可以參考下2024-11-11
Java編程之jdk1.4,jdk1.5和jdk1.6的區(qū)別分析(經(jīng)典)
這篇文章主要介紹了Java編程之jdk1.4,jdk1.5和jdk1.6的區(qū)別分析,結(jié)合實(shí)例形式較為詳細(xì)的分析說(shuō)明了jdk1.4,jdk1.5和jdk1.6版本的使用區(qū)別,需要的朋友可以參考下2015-12-12
JDK基于CAS實(shí)現(xiàn)原子類盤(pán)點(diǎn)解析
這篇文章主要為大家介紹了JDK基于CAS實(shí)現(xiàn)原子類盤(pán)點(diǎn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Java中s.charAt(index)用于提取字符串s中的特定字符操作
這篇文章主要介紹了Java中s.charAt(index)用于提取字符串s中的特定字符操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10

