Java形参和实参的实例之Integer类型与Int类型讲解

经常会有这样一道面试题,有两个整形变量分别是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
)
 
{




         
//请实现




        
}




   }




1、首先大家看到这到题目后,仔细看后,在main方法中变量a和b的类型是Integer,而不是Int类型,因为这里涉及到了java的基本类型,Int是属于Java的基本类型,基本类型在调用swap的方法时,是修改不了变量a和b的值,说到这里涉及到另一个知识点了,那就是形参和实参的区别,值传递和引用传递的区别,下面慢慢说啊。

2、形参和实参的区别

实参顾名思义:就是实际参数,用于调用时传递给方法的参数。实参在传递给别的方法之前是要被预先赋值的。

形参顾名思义:就是形式参数,用于定义方法的时候使用的参数,是用来接收调用者传递的参数的,形参只有在方法被调用的时候,虚拟机才会分配内存单元,在方法调用结束之后便会释放所分配的内存单元。因此,形参只在方法内部有效,所以针对引用对象的改动也无法影响到方法外。

以这到题目为例,方法swap(Integer numa,
 
Integer numb)中的numa和numb是形参,
而在main方法中 传递给swap(a, b)中的a和b是实参。

3、值传递和引用传递的区别

值传递调用过程只能把实参传递给形参,而不能把形参的值反向作用到实参上。在函数调用过程中,形参的值发生改变,而实参的值不会发生改变,函数接收的是原始值的一个copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。说到这里就明白了题目中声明a和b的数据类型不为Int的原因了吧。

引用传递也称为 地址传递 址传递,引用传递调用的机制中,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象。说到这里就明白了题目中声明a和b的数据类型为Integer的原因了吧。

4、完成swap
方法实现


 
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内部的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的值都变为2。难道
Integer tmp = numa;


 是这句话的问题吗,因为numa对象的值已经是2了,那这样的话tmp对象也是2,所以a 和b的值都变为2,那咱们把这句话改一下试试对不对。


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,再试一下。经过运行后发现a 和b的值成功互换。这是为什么呢?难道和数值的大小有关系吗?我们再变一种写法试试。


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); 但是数值分别是199和299 怎么又正常了呢,通过看源码Integer.valueOf 的方法

Java形参和实参的实例之Integer类型与Int类型讲解_第1张图片

下面大家可以验证一下,理解默认的Integer缓存int常量值的范围
System.out.println(127==127); //true , int type compare
System.out.println(128==128); //true , int type compare
System.out.println(new Integer(127) == new Integer(127)); //false, object compare
System.out.println(Integer.parseInt("128")==Integer.parseInt("128")); //true, int type compare
System.out.println(Integer.valueOf("127")==Integer.valueOf("127")); //true ,object compare, because IntegerCache return a same object
System.out.println(Integer.valueOf("128")==Integer.valueOf("128")); //false ,object compare, because number beyond the IntegerCache
System.out.println(Integer.parseInt("128")==Integer.valueOf("128")); //true , int type compare
 

通过阅读源码发现,Integer.valueOf 方式初始化一个 Interger因为有 缓存了 -128-127的数字,再看 field.set(numb,tmp); 我们打断点,发现通过反射设置 value 竟然走了 Integer.valueOf 方法。大家可以在代码中验证一下,在        field.set(numa, numb);


后     增加    System.out.println("tmp3="+new Integer(tmp));
            System.out.println("tmp4="+Integer.valueOf(tmp)); 运行后,发现打印的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,int和Integer区别,引用类型和值类型的区别,field
.
set,Integer
,valueO,IntegerCache)