内容稍多,可直接看第4点的讨论结果

前言

在涉及到传递参数给方法时,容易出现一些参数传递错误的问题,这就涉及到了参数的传递问题,必须搞清楚:参数是如何传递到方法中的?一般来说,参数的传递可以分为两种:值传递和引用传递。

所谓值传递,就是方法中的形参获得的是实参的值,而引用传递,就是说方法中的形参获得的是实参的引用(地址)。

参数的传递其实类似于一个赋值操作,所以接下来,先讨论值和地址的问题,再讨论赋值操作,最后才进行参数传递的讨论。

1.基本概念

  首先要搞清楚一个概念,即:变量中储存的内容是什么。

  变量类型分为基本数据类型和引用类型:

    基本数据类型的变量直接存储的是变量的值,如int i=10;  i中存储的为变量的值。

    引用类型的变量存储的则是实际对象的地址,如Dog myDog = new Dog();  myDog中存储的仅仅是地址(也可以称为引用)。

2.赋值操作

  需要明确赋值操作的含义。

  赋值操作“=”包含两个意思:1.放弃了原有的值或引用;2.得到了 = 右侧变量的值或引用。

    基本类型:= 操作代表完整复制了变量的值。例如:int a=b;  a仅获取了b的值,两者在此之后并无任何关系。

    引用类型:= 操作代表复制了变量的引用。 例如:Dog aDog=bDog;  aDog复制了bDog的引用,两个变量都指向同一个Dog对象。

  一个简单的例子:

public class Test {
public static void main(String[] args) {
int x=10;
int y=x;
x=20;
System.out.println("x:"+x);
System.out.println("y:"+y);
Dog aDog=new Dog();
aDog.name="阿黄";
aDog.age=1;
Dog bDog=aDog;
aDog.name="旺财";//改变aDog的名字
bDog.age=2;//改变bDog的年龄
System.out.println("aDog:"+aDog.name+","+aDog.age);
System.out.println("bDog:"+bDog.name+","+bDog.age);
}
} class Dog {
String name;
int age;
}

  

x:20
y:10
aDog:旺财,2
bDog:旺财,2

输出结果

  由结果可以看出,对于基本数据类型,在y=x后,y仅仅是获得了x的值,两者不再有关系了,因此输出结果不同;

  对于引用类型,在bDog=aDog后,bDog获取了aDog的地址,两个变量都是指的同一条狗。虽然分别变化了aDog的名字和bDog的年龄,但其实变的都是同一个对象,所以输出结果相同。

3.方法中的参数传递讨论

  方法中参数传递的本质即为赋值操作。

/*第一个例子:基本类型*/
void foo(int value) {
value = 100;
}
foo(num); // num 没有被改变

  例一分析:参数传递,相当于执行了value=num,value获得了num的值,这之后value与num无关,所以num没有改变。

/*第二个例子:没有提供改变自身方法的引用类型*/
void foo(String text) {
text = "windows";
}
foo(str); // str 也没有被改变

  例二分析:参数传递后,text与str都存储了相同的地址,指向同一个对象;但之后text重新进行了赋值操作text= "windows",text放弃了原有的引用而指向了新的对象(见上述赋值操作的两重意思),而str仍为原有的引用,所以str没有改变。

/*第三个例子:提供了改变自身方法的引用类型*/
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
builder.append("4");
}
foo(sb); // sb 被改变了,变成了"iphone4"。

  例三分析:参数传递后,sb与builder指向同一个对象,builder.append()仅对该对象进行了增加操作,sb和builder仍指向同一对象,所以sb变为了"iphone4"。

  参数传递后:

  执行builder.append("4")后:

/*第四个例子:提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符。*/
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
builder = new StringBuilder("ipad");
}
foo(sb); // sb 没有被改变,还是 "iphone"。  

  例四分析:参数传递后,sb与builder指向同一个对象,但之后builder重新进行了赋值操作builder = new StringBuilder("ipad"),builder指向了一个新的对象,而sb仍为指向原来的对象,所以sb没有改变。

  参数传递后:

  执行builder = new StringBuilder("ipad")后:

4.值传递与引用传递讨论结果

  在上述讨论中,可以知道,参数传递相当于赋值操作,

    对于基本数据类型,方法中的形参获取了实参的值,即可以理解为值传递

    对于引用类型,方法中的形参获取的是实参的引用,两者指向同一对象,即可以理解为引用传递

  值传递中,形参的改变不影响实参;引用传递中,形参让对象改变,相当于让实参也发生了改变。

  也有人认为,Java中的参数传递只有值传递,我觉得应该是把“引用传递”认为是引用的地址值的传递,所以统称为值传递。(知识还很浅薄,只是个人理解)

5.再来两个例子

  基本数据类型比较简单,不再赘述。引用类型的例子再来两个,读者可自行分析

public class Test {
private void test1(A a) {
a.age = 20;
System.out.println("test1方法中的age=" + a.age);
}
public static void main(String[] args) {
Test t = new Test();
A a = new A();
a.age = 10;
t.test1(a);
System.out.println("main方法中的age=" + a.age);
}
}
class A {
public int age = 0;
}

 

test1方法中的age=20
main方法中的age=20

test1结果

public class Test {
private void test2(A a) {
a = new A();// 新加的一行
a.age = 20;
System.out.println("test2方法中的age=" + a.age);
}
public static void main(String[] args) {
Test t = new Test();
A a = new A();
a.age = 10;
t.test2(a);
System.out.println("main方法中的age=" + a.age);
}
}
class A {
public int age = 0;
}

  

test2方法中的age=20
main方法中的age=10

test2结果

  提示:test2中新加的一行使形参重新指向了一个新的对象,所以之后操作就和主程序中的实参无关了。

 6.String类型和数组的参数传递

  String类型和数组的参数传递比较容易让人困惑,后面又看到相关的文章,这里再讨论一下

public class Test {
public void arrayPassTest(String s, String[] ss) {
s = "bad";
ss[0] = "bbb";
}
public static void main(String[] args) {
String s1 = new String("good");
String[] ss1 = { "aaa" }; // string数组,只有一个元素
Test test = new Test();
test.arrayPassTest(s1, ss1);
System.out.println(s1 + ss1[0]);
}
}

  

goodbbb 

输出结果

对于String类型,

  很多人将String类型的参数传递理解为值传递(类似于基本数据类型),但其实String 类型的传递与其他引用类型相同,仍然是引用传递,也即是地址传递。

  既然是引用传递,那为什么s1不随s改变呢?在方法arrayPassTest()中,s="bad"其实相当于是s=new String("bad"),s指向了一个新的String对象,与实参s1不再是同一个对象了,所以s1不变。(明白new String(),将String类型和其他引用类型一样理解就可以了。)

对于数组,

  数组也是一种引用类型,ss和ss1指的是同一个数组对象,ss[0]="bbb"只是将该数组对象中的第一个元素改变了,并没有新的数组对象产生。ss和ss1指向的数组对象始终都是同一个,所以ss1发生了改变。

7.参考来源

  《Head First Java》

  Java 到底是值传递还是引用传递?

  主要参考的是这两个回答:12

  【Java基础】Java:按值传递还是按引用传递详细解说

  java之传递String类型的参数

本文内容是根据参考内容 结合自己的个人理解写的,如有错误,请多多指正。

【Java】 参数的传递:值传递与引用传递讨论的更多相关文章

  1. java只有值传递,不存在引用传递

    今天,我在一本面试书上看到了关于java的一个参数传递的问题: 写道 java中对象作为参数传递给一个方法,到底是值传递,还是引用传递? 我毫无疑问的回答:“引用传递!”,并且还觉得自己对java的这 ...

  2. Java语言对对象采用的是引用传递还是按值传递?

    按值调用表示方法接收的是调用者提供的值:而按引用调用表示方法接收的是调用者提供的变量地址:一个方法可以修改传递引用所对应的变量值, 而不能修改传递值调用所对应的变量值: Java语言对对象采用的是引用 ...

  3. 如何形象简单地理解java中只有值传递,而没有引用传递?

    首先,java中只有值传递,没有引用传递.可以说是"传递的引用(地址)",而不能说是"按引用传递". 按值传递意味着当将一个参数传递给一个函数时,函数接收的是原 ...

  4. Java的参数传递是「值传递」还是「引用传递」?

    关于Java传参时是引用传递还是值传递,一直是一个讨论比较多的话题. 有人说Java中只有值传递,也有人说值传递和引用传递都是存在的,比较容易让人产生疑问. 关于值传递和引用传递其实需要分情况看待. ...

  5. 【C/C++】引用&的含义/语法/作为函数参数/函数返回值/本质/常量引用

    含义 引用不产生副本,只是给原变量起了别名. 对引用变量的操作就是对原变量的操作. 基本语法 数据类型 &别名 = 原名 e.g. int a = 10; int &b = a; // ...

  6. js中所有函数的参数(按值和按引用)都是按值传递的,怎么理解?

    我觉着我可能对这块有点误解,所以单独开个博说下自己的理解,当然是研究后的正解了. 1,参数传递是基本类型,看个例子: function addTen(num){ num += 10; return n ...

  7. C#引用传递

    学过C#的人都知道,通过值或通过引用,值类型和引用类型都可以作为方法参数传递.在C#中,不管是值类型或者是引用类型,所有方法参数在默认情况下是通过值传递的. 1)通过值传递值类型 在通过值传递作为方法 ...

  8. C#引用传递[转]

    学过C#的人都知道,通过值或通过引用,值类型和引用类型都可以作为方法参数传递.在C#中,不管是值类型或者是引用类型,所有方法参数在默认情况下是通过值传递的. 1)通过值传递值类型 在通过值传递作为方法 ...

  9. Java调用函数传递参数到底是值传递还是引用传递

    今天翻看微信上有关Java技术的公众号时,看到了一篇关于Java中值传递的问题,文章讨论了在Java中调用函数进行传参的时候到底是值传递还是引用传递这个面试时会问到的问题.之前也接触过类似的问题,但只 ...

随机推荐

  1. HDU 3032 multi-sg 打表找规律

    普通NIM规则加上一条可以分解为两堆,标准的Multi-SG游戏 一般Multi-SG就是根据拓扑图计算SG函数,这题打表后还能发现规律 sg(1)=1 sg(2)=2 sg(3)=mex{0,1,2 ...

  2. 用户管理_组管理_权限管理.ziw

    2017年1月10日, 星期二 用户管理_组管理_权限管理 用户管理: useradd, userdel, usermod, passwd, chsh, chfn, finger, id, chage ...

  3. HBase基本概念

    HBase是什么 HBase构建在 HDFS 之上的分布式列式键值存储系统.HBase内部管理的文件全部存储在HDFS中. HBase VS HDFS HDFS适合批处理场景 不支持数据随机查找 不适 ...

  4. git提示error setting certificate verify locations解决办法

    先打开git bash窗口 执行命令: git config --system http.sslcainfo "C:\Program Files (x86)\git\bin\curl-ca- ...

  5. MapReduce (hive表SequenceFile的结果做输入)、MultipleOutputs和Reduce端迭代iterable的一些说明

    很长时间以来一直写hive,嵌套脚本.偶尔写UDF.  最近用Hive的dynamic partition和多路插入做一些事情,很遗憾的结果是非常不稳定,有时能成功,有时失败.(可能是因为hive版本 ...

  6. Mongodb开启远程连接并认证

    环境: Mongodb版本:3.4.6 步骤: 1.  mongo创建管理员: 在mongo shell下: use admin db.createUser( { user: "testus ...

  7. 推荐一款超级漂亮的HTML5 CSS3的图片轮播器

    最近在学习HTML5和CSS3,印象最深的是CSS3的动画功能,不仅有浏览器原生支持,执行效率高,而且免去在js中自己管理timer. 本来想写一个图片轮播器练练手,结果在网上发现一个国人写的开源的图 ...

  8. 微服务深入浅出(10)-- Docker

    概念 1.Docker引擎 一个运行在服务器上的后台进程 2.Docker客户端 分为两种:CLI和RestAPI,与Docker引擎交互 3.Docker镜像 类似于我们使用的光盘,将程序打包到Do ...

  9. EOJ Monthly 2019.2 (based on February Selection) D.进制转换

    题目链接: https://acm.ecnu.edu.cn/contest/140/problem/D/ 题目: 思路: 我们知道一个数在某一个进制k下末尾零的个数x就是这个数整除kx,这题要求刚好末 ...

  10. UNIX环境高级编程 第14章 高级I/O

    这一章涉及很多概念和函数,包括:非阻塞I/O.记录锁.I/O复用.异步I/O.readv和writev函数以及内存映射. 非阻塞I/O 在Unix中,可以将系统调用分为两种,一种是“低速”系统调用,另 ...