javaSE学习-面向对象12-形参实参

形式参数

类作为形参

在Java中,当对象作为参数传递时,究竟传递的是对象的值,还是对象的引用,这是一个饱受争议的话题。若传的是值,那么函数接收的只是实参的一个副本,函数对形参的操作并不会对实参产生影响;若传的是引用,那么此时对形参的操作则会影响到实参

基本类型和引用类型作为参数传递有很大的区别:

基本类型作为形参

基本类型变量作为参数,传递的是实参变量的值的副本,在方法中对形参变量的操作仅仅会改变这个副本,并不会改变原本实参的变量值

1
2
3
4
5
6
7
8
9
10
11
public class test {
public static void main(String[] args) {
int i = 100;
System.out.println("实参初始值i:"+i);
method(i);
System.out.println("实参最终值i:"+i); }
public static int method(int i){
i = i+1;
System.out.println("形参i:"+i);
return i; }
}

运行结果:

1
2
3
实参初始值i:100
形参i:101
实参最终值i:100

javaSE_面向对象__参数_基本数据类型作形参

方法对形参的改变只能改变实参传递的值的副本,这个副本在栈中是一个新的存储单元,在方法调用完毕,这个存储单元即被销毁,而实参的值存储的单元地址仍在

类(对象)作为形参

对象变量作为形参,传递的是对象的引用副本,而不是对象的值的副本,所以方法对形参的改变,直接改变的实参对象的引用副本指向的值,而无法改变实参引用指向的对象地址

对象作为形参,修改实参对象参数

demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Student {
private int age;
private String name;
public Student() {}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class StudentDemo{
public static void main(String[] args) {
StudentDemo sDemo = new StudentDemo();
Student student = new Student(10,"实参student");
System.out.println("实参student初始值:"+student);
sDemo.test(student);
System.out.println("实参student最终值:"+student);
}
public static Student test(Student s) {
s.setAge(20);
s.setName("形参s");
System.out.println("形参s值:"+s);
return s;
}
}

运行结果:

1
2
3
实参student初始值:Student [age=10, name=实参student]
形参s值:Student [age=20, name=形参s]
实参student最终值:Student [age=20, name=形参s]

在上面的例子中,实参student对象赋值在堆内存中开辟空间0x001,调用方法test,将student对象引用拷贝一个引用副本赋值给形参s,此时形参s和实参student都指向的是同一个内存空间对象地址0x001,在test方法中对s指向的对象做操作,所以最后实参student的参数值也改变,但是实参引用指向的地址没有改变,仍旧是0x001

javaSE_面向对象__参数_对象作形参

改变形参对象

在方法中给形参指向新的对象,看看结果是否能改变实参

将添加test2方法调用

1
2
3
4
5
public Student test2(Student s) {
s=null;
System.out.println("形参s值:"+s);
return s;
}

运行结果:

1
2
3
实参student初始值:Student [age=10, name=实参student]
形参s值:null
实参student最终值:Student [age=10, name=实参student]

结果是原本student对象,而不是test2方法中返回的null

在test2方法中,将引用的副本s置为null,是将引用的副本s指向从原本堆中的student对象0x001指向了null,并没有销毁原本的student对象空间,原本实参student的引用也一直存在,还是指向原先的堆内存对象地址0x001,所以输出的student还是最初的值

javaSE_面向对象__参数_对象作形参null

将添加test3方法调用

1
2
3
4
5
public Student test3(Student s) {
s=new Student(30,"形参s值");
System.out.println("形参s值:"+s);
return s;
}

运行结果:

1
2
3
实参student初始值:Student [age=10, name=实参student]
形参s值:Student [age=30, name=形参s值]
实参student最终值:Student [age=10, name=实参student]

上面两个test方法,都没有修改实参student值,因为在test方法中将形参原本指向student引用指向改变了,无论是指向为null还是指向了新的student对象,都跟原先的实参student无关,实参引用所指向地址以及地址中保存的值的参数也没有被改变

形参交换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Employee {
public String name;
public Employee(String name) { this.name = name; }
@Override
public String toString() {
return "Employee [name=" + name + "]";
}
}

public class EmployeeDemo {
public static void main(String[] args) {
Employee aemployee = new Employee("zs");
Employee bemployee = new Employee("ls");
swap(aemployee, bemployee);
System.out.println("aemployee:"+aemployee);
System.out.println("bemployee:"+bemployee);
}
public static void swap(Employee x,Employee y) {
Employee temp = x;
x = y;
y = temp;
}
}

结果:

1
2
aemployee:Employee [name=zs]
bemployee:Employee [name=ls]

方法没有改变存储在实参变量 a 和 b 中的对象引用。 swap 方法的形参 x 和 y 初始化的值是实参 a 和 b 对象引用的拷贝, 所以这个方法最终交换的是这两个引用的拷贝

包装类作为形参

Integer、String、Character等包装类呢,他们也是对象,那么是否跟对象作为形参一样呢

int是基本类型,那Integer是对象,是不是最终值就会被改变呢

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
Integer i = 500;
System.out.println("实参初始值i:"+i);
method(i);
System.out.println("实参最终值i:"+i);
}
public static Integer method(int i){
i = i+1;
System.out.println("形参i:"+i);
return i;
}

结果:

1
2
3
实参初始值i:500
形参i:501
实参最终值i:500

为什么Integer类型是对象,按理来说传递的是引用类型,修改对象的值后,最终输出结果应该是501,可是事实不是这样

原因:来看下Integer源码

1
2
3
4
private final int value;
public Integer(int value) {
this.value = value;
}

源码中表明Integer的类变量类型被定义为 final ,所以无法被修改,如果对变量做操作,那么会重新生成一个对象指向形参i,最后main中实参i的值还是指向原本的对象,值不变

在 Integer 中,int值在 -128 - 127 之间,会被放入Integer的Cache缓存中,只要访问 -128 - 127 之间的相同的数字,就会被指向同一片缓存空间

1
2
3
4
5
6
7
8
9
/**
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

Integer拆箱装箱、做形参以及形参实参概念讲解

接口作为参数

接口作为形参,需要将接口实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface Eat{
public abstract void eatFish();
}
class Person implements Eat{
publi void eatFish(){
System.out.println("去吃鱼");
}
}
class EatDemo{
public static void main(String[] args) {
EatDemo eatDemo = new EatDemo();
Eat eat = new Person();
eatDemo.test(eat);
}
public void test(Eat e){
e.eatFish();
}
}

接口作为形参,需要的是该接口的实现类对象

参数总结

方法参数使用:

  • 一个方法不能修改一个基本数据类型的参数(指实参)
  • 一个方法可以改变一个对象参数状态(即对象在堆内存中存储的参数)
  • 一个方法不能让对象参数引用一个新的对象(指实参)

返回值

类作为返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Student{
public void study(){
System.out.println("我要去学习");
}
}
class StudentDemo{
public Student getStudent(){
return new Student();
}
}
class StudentTest{
public static void main(String[] args) {
StudentDemo sd = new StudentDemo();
Student s = sd.getStudent();
}
}

类作为方法返回值,返回的是该类的对象

接口作为返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface Eat{
public abstract void eatFish();
}
class Person implements Eat{
publi void eatFish(){
System.out.println("人去吃鱼");
}
}
class EatDemo{
public static void main(String[] args) {
EatDemo eatDemo = new EatDemo();
Eat eat = eatDemo.goEat(); //多态:Eat eat = new Person();
eat.eatFish();
}
public Eat goEat(){
return new Person();
}
}

接口作为返回值,返回的是接口的实现类对象