形式参数
类作为形参
在Java中,当对象作为参数传递时,究竟传递的是对象的值,还是对象的引用,这是一个饱受争议的话题。若传的是值,那么函数接收的只是实参的一个副本,函数对形参的操作并不会对实参产生影响;若传的是引用,那么此时对形参的操作则会影响到实参
基本类型和引用类型作为参数传递有很大的区别:
基本类型作为形参
基本类型变量作为参数,传递的是实参变量的值的副本,在方法中对形参变量的操作仅仅会改变这个副本,并不会改变原本实参的变量值
1 | public class test { |
运行结果:
1 | 实参初始值i:100 |
方法对形参的改变只能改变实参传递的值的副本,这个副本在栈中是一个新的存储单元,在方法调用完毕,这个存储单元即被销毁,而实参的值存储的单元地址仍在
类(对象)作为形参
对象变量作为形参,传递的是对象的引用副本,而不是对象的值的副本,所以方法对形参的改变,直接改变的实参对象的引用副本指向的值,而无法改变实参引用指向的对象地址
对象作为形参,修改实参对象参数
demo:
1 | public class Student { |
1 | public class StudentDemo{ |
运行结果:
1 | 实参student初始值:Student [age=10, name=实参student] |
在上面的例子中,实参student对象赋值在堆内存中开辟空间0x001,调用方法test,将student对象引用拷贝一个引用副本赋值给形参s,此时形参s和实参student都指向的是同一个内存空间对象地址0x001,在test方法中对s指向的对象做操作,所以最后实参student的参数值也改变,但是实参引用指向的地址没有改变,仍旧是0x001
改变形参对象
在方法中给形参指向新的对象,看看结果是否能改变实参
将添加test2方法调用
1 | public Student test2(Student s) { |
运行结果:
1 | 实参student初始值:Student [age=10, name=实参student] |
结果是原本student对象,而不是test2方法中返回的null
在test2方法中,将引用的副本s置为null,是将引用的副本s指向从原本堆中的student对象0x001指向了null,并没有销毁原本的student对象空间,原本实参student的引用也一直存在,还是指向原先的堆内存对象地址0x001,所以输出的student还是最初的值
将添加test3方法调用
1 | public Student test3(Student s) { |
运行结果:
1 | 实参student初始值:Student [age=10, name=实参student] |
上面两个test方法,都没有修改实参student值,因为在test方法中将形参原本指向student引用指向改变了,无论是指向为null还是指向了新的student对象,都跟原先的实参student无关,实参引用所指向地址以及地址中保存的值的参数也没有被改变
形参交换
1 | class Employee { |
结果:
1 | aemployee:Employee [name=zs] |
方法没有改变存储在实参变量 a 和 b 中的对象引用。 swap 方法的形参 x 和 y 初始化的值是实参 a 和 b 对象引用的拷贝, 所以这个方法最终交换的是这两个引用的拷贝
包装类作为形参
Integer、String、Character等包装类呢,他们也是对象,那么是否跟对象作为形参一样呢
int是基本类型,那Integer是对象,是不是最终值就会被改变呢
1 | public static void main(String[] args) { |
结果:
1 | 实参初始值i:500 |
为什么Integer类型是对象,按理来说传递的是引用类型,修改对象的值后,最终输出结果应该是501,可是事实不是这样
原因:来看下Integer源码
1 | private final int value; |
源码中表明Integer的类变量类型被定义为 final ,所以无法被修改,如果对变量做操作,那么会重新生成一个对象指向形参i,最后main中实参i的值还是指向原本的对象,值不变
在 Integer 中,int值在 -128 - 127 之间,会被放入Integer的Cache缓存中,只要访问 -128 - 127 之间的相同的数字,就会被指向同一片缓存空间
1 | /** |
接口作为参数
接口作为形参,需要将接口实现
1 | interface Eat{ |
接口作为形参,需要的是该接口的实现类对象
参数总结
方法参数使用:
- 一个方法不能修改一个基本数据类型的参数(指实参)
- 一个方法可以改变一个对象参数状态(即对象在堆内存中存储的参数)
- 一个方法不能让对象参数引用一个新的对象(指实参)
返回值
类作为返回值
1 | class Student{ |
类作为方法返回值,返回的是该类的对象
接口作为返回值
1 | interface Eat{ |
接口作为返回值,返回的是接口的实现类对象