Java傳值調(diào)用和傳引用調(diào)用方式(參數(shù)引用為null問題)
一、問題
近期在項(xiàng)目中遇到一個(gè)場景,在多層級調(diào)用中需要傳遞上下文,調(diào)用過程中上線文對象可能為空,想通過一個(gè)公共方法處理上下文,當(dāng)上下文為空時(shí)生成上下文對象,執(zhí)行相關(guān)操作后將該上下文對象向后傳遞。
大致邏輯如下:
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 ++; } } }
測試執(zhí)行報(bào)空指針錯(cuò)誤,context沒有按設(shè)想在handleContext方法中生成對象。
原因是main方法在棧中創(chuàng)建了Context的引用并將其指向null,handleContext方法參數(shù)中的Context引用也被指向null,handleContext方法體在堆中創(chuàng)建Context對象,并將對象地址賦給方法參數(shù)中的Context引用,但main方法中的Context引用仍然為null,因此調(diào)用context.getNum()時(shí)報(bào)空指針。
二、java方法傳值與傳引用
2.1 變量存儲
基礎(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ù)存儲在棧中,即是棧中分配內(nèi)存空間存儲所包含的值,其值就代表數(shù)據(jù)本身,值類型的數(shù)據(jù)具有較快的存取速度。
引用類型變量:
除了基礎(chǔ)類數(shù)據(jù)外,其余都是引用類型,包括類、數(shù)組等。
引用類型數(shù)據(jù)的具體對象存放在堆中,而棧中存放的是該對象的內(nèi)存地址。
當(dāng)引用類型沒有賦值時(shí),其引用為null,表示不指向任何對象。
2.2 創(chuàng)建對象與賦值過程
Context context = new Context();
該操作可以分為兩個(gè)部分:
Context context; context = new Context();
第一步:創(chuàng)建Context引用,在棧中開辟一塊空間用于存儲該引用;
第二步:創(chuàng)建Context對象,在堆內(nèi)存中開辟一塊空間存儲該對象,并將該對象的存儲地址賦值給棧中的引用
2.3 引用賦值過程
將一個(gè)引用賦值給另一個(gè)引用時(shí),其實(shí)是將該引用指向的地址賦值給另個(gè)應(yīng)用,讓兩個(gè)引用指向同一個(gè)對象,示意圖如下。
Context context1 = new Context(); Context context2 = context1;
2.4 傳值與傳引用方法調(diào)用
其實(shí)無論是傳值還是傳引用調(diào)用,其本質(zhì)都是將棧中存儲的數(shù)據(jù)復(fù)制一份,傳遞給棧中的另一個(gè)變量。
對于基礎(chǔ)數(shù)據(jù)類型,是將數(shù)據(jù)本身復(fù)制一份傳遞;對于引用類型,是將引用的地址復(fù)制一份傳遞。
因此,上述問題可以描述為如下示意圖。當(dāng)handleContext方法執(zhí)行返回后,main方法中的context引用仍然為null。
注:為方便理解,將null特殊處理。當(dāng)引用為null時(shí)候表示不指向任何對象
值得注意的是包裝類型(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ù)組的值也不可能通過繼承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ù)值超出范圍或通過new方法生成對象則會在堆上分配存儲空間
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)行單元測試的實(shí)例代碼
在Spring Boot應(yīng)用程序中,使用MockMvc進(jìn)行單元測試是一種有效的方式,可以驗(yàn)證控制器的行為和API的正確性,在這篇博客中,我們將介紹如何使用MockMvc對用戶控制器進(jìn)行測試,感興趣的朋友可以參考下2024-01-01MyBatis?核心組件Configuration實(shí)例詳解
Configuration用于描述 MyBatis 的主配置信息,其他組件需要獲取配置信息時(shí),直接通過 Configuration 對象獲取,這篇文章主要介紹了MyBatis核心組件Configuration,需要的朋友可以參考下2023-08-08spring定時(shí)任務(wù)執(zhí)行兩次及tomcat部署緩慢問題的解決方法
這篇文章主要給大家介紹了關(guān)于spring定時(shí)任務(wù)執(zhí)行兩次及tomcat部署緩慢問題的解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01VsCode配置java環(huán)境的詳細(xì)圖文教程
vscode是一個(gè)免費(fèi)的代碼編輯器,支持多種主題,應(yīng)用起來簡單方便,下面這篇文章主要給大家介紹了關(guān)于VsCode配置java環(huán)境的詳細(xì)圖文教程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02SpringBoot?Web項(xiàng)目增刪改查入門實(shí)戰(zhàn)案例
這篇文章主要給大家介紹了關(guān)于SpringBoot?Web項(xiàng)目增刪改查入門實(shí)戰(zhàn)的相關(guān)資料,文中詳細(xì)介紹了如何實(shí)現(xiàn)新增、刪除、修改和查詢員工的功能,并使用統(tǒng)一響應(yīng)結(jié)果進(jìn)行前后端交互,需要的朋友可以參考下2024-11-11Java編程之jdk1.4,jdk1.5和jdk1.6的區(qū)別分析(經(jīng)典)
這篇文章主要介紹了Java編程之jdk1.4,jdk1.5和jdk1.6的區(qū)別分析,結(jié)合實(shí)例形式較為詳細(xì)的分析說明了jdk1.4,jdk1.5和jdk1.6版本的使用區(qū)別,需要的朋友可以參考下2015-12-12JDK基于CAS實(shí)現(xiàn)原子類盤點(diǎn)解析
這篇文章主要為大家介紹了JDK基于CAS實(shí)現(xiàn)原子類盤點(diǎn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Java中s.charAt(index)用于提取字符串s中的特定字符操作
這篇文章主要介紹了Java中s.charAt(index)用于提取字符串s中的特定字符操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10