形参和实参

我们知道,在Java中定义方法时,是可以定义参数的,比如:

public static void main(String[] args){

}

这里的args就是一个字符串数组类型的参数。

在程序设计语言中,参数有形式参数和实际参数之分,先来看下它们的定义:

形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数,简称“形参”。

实际参数:在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”,简称“实参”。

举个栗子:

public class ParamTest {
public static void main(String[] args) {
ParamTest pt = new ParamTest();
// 实际参数为“张三”
pt.sout("张三");
} public void sout(String name) {
// 形式参数为 name
System.out.print(name);
}
}

上面例子中,ParamTest类中定义了一个sout方法,该方法有个String类型的参数name,该参数即为形参。在main方法中,调用了sout方法,传入了一个参数“张三”,该参数即为实参。

那么,实参值是如何传入方法的呢?这是由方法的参数传递机制来控制的。

值传递和引用传递

参数传递机制有两种:值传递和引用传递。我们先来看下程序语言中是如何定义和区分值传递和引用传递的:

值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

那么,在我们大Java中,到底是值传递还是引用传递呢?

Java中是值传递还是引用传递?

有了上面的概念,我们就可以一起来探究一下,Java中方法参数到底是值传递还是引用传递了。

先看如下代码:

public class ParamPass1 {
public static void main(String[] args) {
ParamPass1 p = new ParamPass1();
int i = 10;
System.out.println("pass方法调用前,i的值为=" + i);
p.pass(i);
System.out.println("pass方法调用后,i的值为=" + i);
} public void pass(int i) {
i *= 3;
System.out.println("pass方法中,i的值为=" + i);
}
}

上面代码中,我们在类中定义了一个pass方法,方法内部将传入的参数i的值增加至3倍,然后分别在pass方法和main方法中打印参数的值,输出结果如下:

pass方法执行前,i的值为=10
pass方法中,i的值为=30
pass方法执行后,i的值为=10

从上面运行结果来看,pass方法中,i的值是30,pass方法执行结束后,变量i的值依然是10。

可以看出,main方法里的变量i,并不是pass方法里的i,pass方法内部对i的值的修改并没有改变实际参数i的值,改变的只是pass方法中i的值(pass方法中,i=30),因为pass方法中的i只是main方法中变量i的复制品

因此同学们很容易得出结论:Java中,一个方法不可能修改一个基本数据类型的参数 ,所以是值传递

然而,结论下的还太早,因为方法参数共有两种类型:

  1. 基本数据类型
  2. 引用数据类型

前面看到的只是基本数据类型的参数,那对于引用类型的参数,又是怎么样的呢?看如下代码:

public class ParamPass2 {
public static void main(String[] args) {
ParamPass2 p = new ParamPass2(); User user = new User();
user.setName("张三");
user.setAge(18); System.out.println("pass方法调用前,user=" + user.toString());
p.pass(user);
System.out.println("pass方法调用后,user=" + user.toString());
} public void pass(User user) {
user.setName("李四");
System.out.println("pass方法中,user = " + user.toString());
}
} class User {
/**
* 姓名
*/
private String name; /**
* 年龄
*/
private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

上面代码中,定义了一个User类,在main方法中,new了一个新的User对象user,然后给user对象的成员变量赋值,pass方法中,修改了传入的user对象的属性。

运行main方法,结果如下:

pass方法调用前,user= User{name='张三', age=18}
pass方法中,user = User{name='李四', age=18}
pass方法调用后,user= User{name='李四', age=18}

经过pass方法执行后,实参的值竟然被改变了!!!那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么?

有同学可能会说:难道在Java的方法中,在传递基本数据类型的时候是值传递,在传递引用数据类型的时候是引用传递?

其实不然,Java中传递引用数据类型的时候也是值传递

为什么呢?

先给大家说一下概念中的重点:

值传递,是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递,是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

总结下两者的区别:

值传递 引用传递
根本区别 会创建副本 不会创建副本
所以 函数中无法改变原始对象 函数中可以改变原始对象

敲黑板:复制的是参数的引用(地址值),并不是引用指向的存在于堆内存中的实际对象。

main方法中的user是一个引用(也就是一个指针),它保存了User对象的地址值,当把user的值赋给pass方法的user形参后,即让pass方法的user形参也保存了这个地址值,即也会引用到堆内存中的User对象。

上面代码中,之所以产生引用传递的错觉,是因为参数保存的是实际对象的地址值,你改变的只是地址值指向的堆内存中的实际对象,并没有真正改变参数,参数的地址值没有变。

下面结合生活中的场景,再来深入理解一下值传递和引用传递。

你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。

你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。

但是,不管上面哪种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?

我们在pass方法中,改变user对象的name属性的值的时候,不就是在“砸电视”么。你改变的不是那把钥匙(地址值),而是钥匙打开的房子(地址值对应的实际对象)。

那我们如何真正的改变参数呢,看如下代码:

public class ParamPass3 {
public static void main(String[] args) {
ParamPass3 p = new ParamPass3(); User user = new User();
user.setName("张三");
user.setAge(18); System.out.println("pass方法调用前,user= " + user.toString());
p.pass(user);
System.out.println("pass方法调用后,user= " + user.toString());
} public void pass(User user) {
user = new User();
user.setName("李四");
user.setAge(20);
System.out.println("pass方法中,user = " + user.toString());
}
} class User {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private int age; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

在这段代码中,pass方法中,我们真正的改变了user参数,因为它指向了一个新的地址(user = new User()),即参数的地址值改变了。运行结果如下:

pass方法调用前,user= User{name='张三', age=18}
pass方法中,user = User{name='李四', age=20}
pass方法调用后,user= User{name='张三', age=18}

从结果看出,对参数进行了修改,没有影响到实际参数。

所以说,Java中其实还是值传递的,只不过对于引用类型参数,值的内容是对象的引用。

深入理解Java中方法的参数传递机制的更多相关文章

  1. java中方法的参数传递机制

    问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个 ...

  2. 【Java基础】12、java中方法的参数传递机制

    问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个 ...

  3. java中方法的参数传递机制(值传递还是引用传递)

    看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参 ...

  4. java中方法的参数传递机制_一个对象被当作参数传递到一个方法后

    一个例子: 在Boy.java类中 在Girl.java类中      在marry方法中的this指的是这个方法所属的对象的引用,在这里指的是girl这个对象 在BoyGirlTest.java测试 ...

  5. 我的Java开发学习之旅------>Java语言中方法的参数传递机制

    实参:如果声明方法时包含来了形参声明,则调用方法时必须给这些形参指定参数值,调用方法时传给形参的参数值也被称为实参. Java的实参值是如何传入方法?这是由Java方法的参数传递机制来控制的,Java ...

  6. 深入理解为什么Java中方法内定义的内部类可以访问方法中的局部变量

    好文转载:http://blog.csdn.net/zhangjg_blog/article/details/19996629 开篇 在我的上一篇博客 深入理解Java中为什么内部类可以访问外部类的成 ...

  7. Java方法之参数传递机制

    目录 Java方法之参数传递机制 基本数据类型 引用数据类型 综合练习 总结 Java方法之参数传递机制 Java方法中如果声明了形参,在调用方法时就必须给这些形参指定参数值,实际传进去的这个值就叫做 ...

  8. 【Java】深入理解Java中的spi机制

    深入理解Java中的spi机制 SPI全名为Service Provider Interface是JDK内置的一种服务提供发现机制,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用 ...

  9. Java高频经典面试题(第一季)四:方法的参数传递机制

    考点? 方法的参数传递机制 String,包装类等对象的不可变性 方法的参数传递机制: ①形参是基本数据类型 传递数据值 ②实参是引用数据类型 传递地址值 特殊的类型:String.包装类等对象不可变 ...

随机推荐

  1. ora-12170 与 Oracle lsnrctl

    在startup 启动数据库后,使用plsql去连接数据库时, 出现ora-12170 错误:   在启动.关闭或者重启oracle监听器之前确保使用lsnrctl status命令检查oracle监 ...

  2. spawn类expect方法详解

    本文我们将介绍spawn类的基本方法expect方法,这个方法是用来匹配返回的结果,这个返回的结果是指子程序的返回结果,同时会将匹配的相关信息保存在spawn类的相关属性中. 基本属性包括三个,第一个 ...

  3. [Java多线程] volatile 关键字正确使用方法

    volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性,即多线程环境中,使用 volatile 关键字的变量仅可以保证不同线程读取变量时,可以读到最新修改的变量值,但是 ...

  4. const与define应用上该怎么取舍

    const与define应用上该怎么取舍 #define WYB 100; const float WYB = 100; define是在预编译的时候展开替换的,const是编译运行阶段使用 defi ...

  5. 驱动框架入门——以LED为例[【转】

    本文转载自;http://blog.csdn.net/oqqHuTu12345678/article/details/72783903 以下内容源于朱有鹏<物联网大讲堂>课程的学习,如有侵 ...

  6. HDU2049 不容易系列之(4)考新郎 —— 错排

    题目链接:https://vjudge.net/problem/HDU-2049 不容易系列之(4)——考新郎 Time Limit: 2000/1000 MS (Java/Others)    Me ...

  7. CSU - 1530 Gold Rush —— 二进制

    题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1530 对于一块2^n质量的gold.需要把它分成a质量和b质量(a+b=2^n),且 ...

  8. Appium java 环境配置

    一.安装node.js 下载地址:http://pan.baidu.com/s/1qYyNDm8 点击安装,next下一步就ok. 安装完成,命令行输入:npm 这样显示的话就ok了.  二.下载Ap ...

  9. selenium使用笔记(一)——selenium你该知道的

    有时候在交流群里经常会看到这样的问题,selenium能进行性能测试吗?selenium1和selenium2有什么区别等等问题,在这里谈一下自己学习和工作以后对selenium的认识.我所记录的东西 ...

  10. 关于ArcGIS动态图层空间内栅格数据,JS前端显示颜色不正确的解决方案

    ArcGIS的动态空间,可承载Table,Shp,Raster等数据. 我们的需求是,每天客户有新的卫星数据,但是不同类型,有多波段Landsat卫星数据,有Modis数据等.不定期更新到共享文件夹, ...