Java形參和實參的實例之Integer類型與Int類型用法說明
經(jīng)常會有這樣一道面試題,有兩個整形變量分別是a = 1 ,b = 2。編寫一個方法swap互換他們的值。
class Main { public static void main ( String [] args ) { Integer a = 1 ; Integer b = 2 ; System . out . println ( 'a=' + a + ',b=' + b ); swap ( a , b ); System . out . println ( 'a=' + a + ',b=' + b ); } private static void swap ( Integer numa , Integer numb ) { //請實現(xiàn) } }
1、首先大家看到這到題目后,仔細看后,在main方法中變量a和b的類型是Integer,而不是Int類型,因為這里涉及到了java的基本類型,Int是屬于Java的基本類型,基本類型在調(diào)用swap的方法時,是修改不了變量a和b的值,說到這里涉及到另一個知識點了,那就是形參和實參的區(qū)別,值傳遞和引用傳遞的區(qū)別,下面慢慢說啊。
2、形參和實參的區(qū)別
實參顧名思義:就是實際參數(shù),用于調(diào)用時傳遞給方法的參數(shù)。實參在傳遞給別的方法之前是要被預先賦值的。
形參顧名思義:就是形式參數(shù),用于定義方法的時候使用的參數(shù),是用來接收調(diào)用者傳遞的參數(shù)的,形參只有在方法被調(diào)用的時候,虛擬機才會分配內(nèi)存單元,在方法調(diào)用結束之后便會釋放所分配的內(nèi)存單元。因此,形參只在方法內(nèi)部有效,所以針對引用對象的改動也無法影響到方法外。
以這到題目為例,方法swap ( Integer numa , Integer numb )中的numa和numb是形參, 而在main方法中 傳遞給swap ( a , b )中的a和b是實參。
3、值傳遞和引用傳遞的區(qū)別
值傳遞調(diào)用過程只能把實參傳遞給形參,而不能把形參的值反向作用到實參上。在函數(shù)調(diào)用過程中,形參的值發(fā)生改變,而實參的值不會發(fā)生改變,函數(shù)接收的是原始值的一個copy,此時內(nèi)存中存在兩個相等的基本類型,即實際參數(shù)和形式參數(shù),后面方法中的操作都是對形參這個值的修改,不影響實際參數(shù)的值。說到這里就明白了題目中聲明a和b的數(shù)據(jù)類型不為Int的原因了吧。
引用傳遞也稱為 地址傳遞, 址傳遞,引用傳遞調(diào)用的機制中,實際參數(shù)的引用(地址,而不是參數(shù)的值)被傳遞給方法中相對應的形式參數(shù),函數(shù)接收的是原始值的內(nèi)存地址在方法執(zhí)行中,形參和實參內(nèi)容相同,指向同一塊內(nèi)存地址,方法執(zhí)行中對引用的操作將會影響到實際對象。說到這里就明白了題目中聲明a和b的數(shù)據(jù)類型為Integer的原因了吧。
4、完成swap 方法實現(xiàn)
static void swap ( Integer numa , Integer numb ) { Integer tmp = numa ; numa = numb ; numb = tmp ; System . out . println ( 'numa=' + numa + ',numb=' + numb ); }
是不是感覺挺簡單,但是運行后main方法中的a和b的值沒有互換,分別還是a = 1 ,b = 2。那這是為什么呢?因為Interger雖然是引用類型,但是Integer在方法中沒有提供value的get和set方法,也是對對象的安全保護,也就是傳遞過程中在Integer里面copy了一個副本指向值,而不是引用地址,是不是沒有辦法了,這就涉及到反射的用法,我們用反射改變Integer內(nèi)部的value屬性值。
static void swap ( Integer numa , Integer numb ) { Integer tmp = numa ; try { Field field = Integer . class . getDeclaredField ( 'value' ); field . setAccessible ( true ); field . set ( numa , numb ); //成功的將numa 引用的 1的對象 值改為 2 field . set ( numb , tmp ); } catch ( Exception e ) { e . printStackTrace (); } }
是不是感覺大功告成了,但是運行以后,a 和b的值都變?yōu)?。難道 Integer tmp = numa ; 是這句話的問題嗎,因為numa對象的值已經(jīng)是2了,那這樣的話tmp對象也是2,所以a 和b的值都變?yōu)?,那咱們把這句話改一下試試對不對。
static void swap ( Integer numa , Integer numb ) { int tmp = numa.intValue(); try { Field field = Integer . class . getDeclaredField ( 'value' ); field . setAccessible ( true ); field . set ( numa , numb ); //成功的將numa 引用的 1的對象 值改為 2 field . set ( numb , tmp ); } catch ( Exception e ) { e . printStackTrace (); } }
這是應該沒有問題,但是運行后,a 和b的值還都是2。我真想說真二,這是為什么呢?這樣試一下,咱們把a和b的初始改為a = 199,b = 299,再試一下。經(jīng)過運行后發(fā)現(xiàn)a 和b的值成功互換。這是為什么呢?難道和數(shù)值的大小有關系嗎?我們再變一種寫法試試。
static void main ( String [] args ) { Integer a = new Integer ( 1 ); Integer b = new Integer ( 2 ); System . out . println ( 'a=' + a + ',b=' + b ); swap ( a , b ); System . out . println ( 'a=' + a + ',b=' + b ); } private static void swap ( Integer numa , Integer numb ) { int tmp = numa . intValue (); try { Field field = Integer . class . getDeclaredField ( 'value' ); field . setAccessible ( true ); field . set ( numa , numb ); field . set ( numb , tmp ); } catch ( Exception e ) { e . printStackTrace (); } }
運行以后,a 和b的值成功互換,a = 2, b = 1。那這又是為什么呢?難道和裝箱和拆箱有關系嗎,為什么 Integer a = 1 和 Integer a = new Integer(1) 效果不一樣了,當Integer a = 1;時,編譯器會將其轉化為Integer a = Integer.valueOf(1); 但是數(shù)值分別是199和299 怎么又正常了呢,通過看源碼Integer.valueOf 的方法
下面大家可以驗證一下,理解默認的Integer緩存int常量值的范圍
System.out.println(127==127); //true , int type compareSystem.out.println(128==128); //true , int type compareSystem.out.println(new Integer(127) == new Integer(127)); //false, object compareSystem.out.println(Integer.parseInt('128')==Integer.parseInt('128')); //true, int type compareSystem.out.println(Integer.valueOf('127')==Integer.valueOf('127')); //true ,object compare, because IntegerCache return a same objectSystem.out.println(Integer.valueOf('128')==Integer.valueOf('128')); //false ,object compare, because number beyond the IntegerCacheSystem.out.println(Integer.parseInt('128')==Integer.valueOf('128')); //true , int type compare
通過閱讀源碼發(fā)現(xiàn),Integer.valueOf 方式初始化一個 Interger因為有 緩存了 -128-127的數(shù)字,再看 field.set(numb,tmp); 我們打斷點,發(fā)現(xiàn)通過反射設置 value時 竟然走了 Integer.valueOf 方法。
大家可以在代碼中驗證一下,在 field . set ( numa , numb ); 后增加 System.out.println('tmp3='+new Integer(tmp));
System.out.println('tmp4='+Integer.valueOf(tmp)); 運行后,發(fā)現(xiàn)打印的tmp3 = 1 ,tmp4 = 2 , 說到這里大家明白其中的原因了吧。
最后正確的swap方法是:
static void swap ( Integer numa , Integer numb ) { int tmp = numa . intValue (); try { Field field = Integer . class . getDeclaredField ( 'value' ); field . setAccessible ( true ); field . set ( numa , numb ); field . set ( numb , new Integer ( tmp )); //避免從緩沖取值 } catch ( Exception e ) { e . printStackTrace (); } }
補充知識:java 傳遞參數(shù)的兩種方式講解
Java中沒有指針,所以也沒有引用傳遞了,僅僅有值傳遞不過可以通過對象的方式來實現(xiàn)引用傳遞 類似java沒有多繼承 但可以用多次implements 接口實現(xiàn)多繼承的功能
值傳遞:方法調(diào)用時,實際參數(shù)把它的值傳遞給對應的形式參數(shù),方法執(zhí)行中形式參數(shù)值的改變不影響實際參 數(shù)的值。
引用傳遞:也稱為傳地址。方法調(diào)用時,實際參數(shù)的引用(地址,而不是參數(shù)的值)被傳遞給方法中相對應的形式參數(shù),在方法執(zhí)行中,對形式參數(shù)的操作實際上就是對實際參數(shù)的操作,方法執(zhí)行中形式參數(shù)值的改變將會影響實際參數(shù)的值。
a.傳遞值的數(shù)據(jù)類型:八種基本數(shù)據(jù)類型和String(這樣理解可以,但是事實上String也是傳遞的地址,只是string對象和其他對象是不同的,string對象是不能被改變的,內(nèi)容改變就會產(chǎn)生新對象。那么StringBuffer就可以了,但只是改變其內(nèi)容。不能改變外部變量所指向的內(nèi)存地址)。
b.傳遞地址值的數(shù)據(jù)類型:除String以外的所有復合數(shù)據(jù)類型,包括數(shù)組、類和接口
值傳遞的例子:
package com.other.test; public class Test { public static void change(int i, int j) { int temp = i; i = j; j = temp; } public static void main(String[] args) { int a = 3; int b = 4; change(a, b); System.out.println('a=' + a); System.out.println('b=' + b); }}
輸出的結果是 a=3 b=4,傳遞的值并不會改變原值
引用傳遞的例子:(數(shù)組)
package com.other.test; public class Test { public static void change(int[] counts) { counts[0] = 6; System.out.println(counts[0]); } public static void main(String[] args) { int[] count = { 1, 2, 3, 4, 5 }; change(count); System.out.println(count[0]); } }
輸出結果是6 6 也就是引用的值改變了原來的值
引用傳遞的例子:(對象)
定義一個A對象:
package com.other.test; public class A { int i = 0;}
對上邊對象操作的類:
package com.other.test; public class Test { public static void add(A a) { //a = new A(); ① a.i++; } public static void main(String args[]) { A a = new A(); add(a); System.out.println(a.i ); } }
當把①注解掉時,輸出的結果是1,當①沒有注解是是0,原因是 a =new A();構造了新的A對象就不是傳遞的那個對象了。
看看String的情況:
package com.other.test; public class Test { String str = new String('old'); char[] ch = { ’a’, ’b’, ’c’ }; public static void main(String args[]) { Test ex = new Test(); ex.change(ex.str, ex.ch); System.out.print(ex.str + ' and '); System.out.println(ex.ch); } public void change(String str, char ch[]) { str = 'new'; ch[0] = ’d’; }}
輸出的結果的是 old and dbc也就是傳遞String并不會改變原值,而是創(chuàng)建了一個新值。 ch[]就是一個簡單的數(shù)組的傳遞。
( 對象包括對象引用即地址和對象的內(nèi)容)
String 比較特別,看過String 代碼的都知道, String 是 final的。所以值是不變的。 函數(shù)中String對象引用的副本指向了另外一個新String對象,而數(shù)組對象引用的副本沒有改變,而是改變對象中數(shù)據(jù)的內(nèi)容.
對于對象類型,也就是Object的子類,如果你在方法中修改了它的成員的值,那個修改是生效的,方法調(diào)用結束后,它的成員是新的值,但是如果你把它指向一個其它的對象,方法調(diào)用結束后,原來對它的引用并沒用指向新的對象。
Java參數(shù),不管是原始類型還是引用類型,傳遞的都是副本(有另外一種說法是傳值,但是說傳副本更好理解吧,傳值通常是相對傳址而言)。
如果參數(shù)類型是原始類型,那么傳過來的就是這個參數(shù)的一個副本,也就是這個原始參數(shù)的值,這個跟之前所談的傳值是一樣的。如果在函數(shù)中改變了副本的值不會改變原始的值.
如果參數(shù)類型是引用類型,那么傳過來的就是這個引用參數(shù)的副本,這個副本存放的是參數(shù)的地址。如果在函數(shù)中沒有改變這個副本的地址,而是改變了地址中的值,那么在函數(shù)內(nèi)的改變會影響到傳入的參數(shù)。如果在函數(shù)中改變了副本的地址,如new一個,那么副本就指向了一個新的地址,此時傳入的參數(shù)還是指向原來的地址,所以不會改變參數(shù)的值。
以上這篇Java形參和實參的實例之Integer類型與Int類型用法說明就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持好吧啦網(wǎng)。
相關文章:
