Java传参
1、 如果参数是基本数据类型(int、long等),传值。方法内部改变参数值,外部值不变。
2、 如果参数是对象类型,传地址。方法内部改变对象值,外部对象值改变。但是,如果方法内部调用new重新构建参数对象,外部对象仍然记录构造前的对象值。(在不同函数中,所有改String的改变可能都要重构String,或者说所有String都是传值的,在这点上,相当于String为基本类了)
Java参数传值还是传引用
参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递。写它是为了揭穿普遍存在的一种神话,即认为 Java 应用程序按引用传递参数,以避免因依赖“按引用传递”这一行为而导致的常见编程错误。
对此节选的某些反馈意见认为,我把这一问题搞糊涂了,或者将它完全搞错了。许多不同意我的读者用 C++ 语言作为例子。因此,在此栏目中我将使用 C++ 和 Java 应用程序进一步阐明一些事实。
要点
读完所有的评论以后,问题终于明白了,考试吧提示: 至少在一个主要问题上产生了混淆。因为对象是按引用传递的。对象确实是按引用传递的;节选与这没有冲突。节选中说所有参数都是按值 -- 另一个参数 -- 传递的。下面的说法是正确的:在 Java 应用程序中永远不会传递对象,而只传递对象引用。因此是按引用传递对象。但重要的是要区分参数是如何传递的,这才是该节选的意图。Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数。参数可以是对象引用,而 Java 应用程序是按值传递对象引用的。
C++ 和 Java 应用程序中的参数传递
Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型。当作为参数传递给一个方法时,处理这两种类型的方式是相同的。两种类型都是按值传递的;没有一种按引用传递。这是一个重要特性,正如随后的代码示例所示的那样。
在继续讨论之前,定义按值传递和按引用传递这两个术语是重要的。按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本。因此,如果函数修改了该参数,调用代码中的原始值也随之改变。
上面的这些是很重要的,请大家注意以下几点结论,这些都是我认为的上面的文章中的精华和最终的结论:
1、对象是按引用传递的
2、Java 应用程序有且仅有的一种参数传递机制,即按值传递
3、按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本
4、按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本
首先考试吧来看看第一点:对象是按引用传递的
确实,这一点我想大家没有任何疑问,例如:
class Test01
{
public static void main(String[] args)
{
StringBuffer s= new StringBuffer("good");
StringBuffer s2=s;
s2.append(" afternoon.");
System.out.println(s);
}
}
对象s和s2指向的是内存中的同一个地址因此指向的也是同一个对象。
如何解释“对象是按引用传递的”的呢?
这里的意思是进行对象赋值操作是传递的是对象的引用,因此对象是按引用传递的,有问题吗?
程序运行的输出是:
good afternoon.
这说明s2和s是同一个对象。
这里有一点要澄清的是,这里的传对象其实也是传值,因为对象就是一个指针,这个赋值是指针之间的赋值,因此在java中就将它说成了传引用。(引用是什么?不就是地址吗?地址是什么,不过就是一个整数值)
再看看下面的例子:
class Test02
{
public static void main(String[] args)
{
int i=5;
int i2=i;
i2=6;
System.out.println(i);
}
}
程序的结果是什么?5!!!
这说明什么,原始数据类型是按值传递的,这个按值传递也是指的是进行赋值时的行为。
下一个问题:Java 应用程序有且仅有的一种参数传递机制,即按值传递
class Test03
{
public static void main(String[] args)
{
StringBuffer s= new StringBuffer("good");
StringBuffer s2=new StringBuffer("bad");
test(s,s2);
System.out.println(s);//9
System.out.println(s2);//10
}
static void test(StringBuffer s,StringBuffer s2) {
System.out.println(s);//1
System.out.println(s2);//2
s2=s;//3
s=new StringBuffer("new");//4
System.out.println(s);//5
System.out.println(s2);//6
s.append("hah");//7
s2.append("hah");//8
}
}
程序的输出是:
good
bad
new
good
goodhah
bad
考试吧提示: 为什么输出是这样的?
这里需要强调的是“参数传递机制”,它是与赋值语句时的传递机制的不同。
我们看到1,2处的输出与我们的预计是完全匹配的
3将s2指向s,4将s指向一个新的对象
因此5的输出打印的是新创建的对象的内容,而6打印的原来的s的内容
7和8两个地方修改对象内容,但是9和10的输出为什么是那样的呢?
Java 应用程序有且仅有的一种参数传递机制,即按值传递。
至此,我想总结一下我对这个问题的最后的看法和我认为可以帮助大家理解的一种方法:
我们可以将java中的对象理解为c/c++中的指针
例如在c/c++中:
int *p;
print(p);//1
*p=5;
print(*p);//2
1打印的结果是什么,一个16进制的地址,2打印的结果是什么?5,也就是指针指向的内容。
即使在c/c++中,这个指针其实也是一个32位的整数,我们可以理解我一个long型的值。
而在java中一个对象s是什么,同样也是一个指针,也是一个int型的整数(对于JVM而言),我们在直接使用(即s2=s这样的情况,但是对于System.out.print(s)这种情况例外,因为它实际上被晃猄ystem.out.print(s.toString()))对象时它是一个int的整数,这个可以同时解释赋值的传引用和传参数时的传值(在这两种情况下都是直接使用),而我们在s.XXX这样的情况下时s其实就是c/c++中的*s这样的使用了。这种在不同的使用情况下出现不同的结果是java为我们做的一种简化,但是对于c/c++程序员可能是一种误导。java中有很多中这种根据上下文进行自动识别和处理的情况,下面是一个有点极端的情况:
class t
{
public static String t="t";
public static void main(String[] args)
{
t t =new t();
t.t();
}
static void t() {
System.out.println(t);
}
}
(关于根据上下文自动识别的内容,有兴趣的人以后可以看看我们翻译的《java规则》)
1、对象是按引用传递的
2、Java 应用程序有且仅有的一种参数传递机制,即按值传递
3、按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本
4、按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本
三句话总结一下:
1.对象就是传引用
2.原始类型就是传值
3.String类型因为没有提供自身修改的函数,每次操作都是新生成一个String对象,所以要特殊对待。可以认为是传值。
==========================================================================
public class Test03 {
public static void stringUpd(String str) {
str = str.replace("j", "l");
System.out.println(str);
}
public static void stringBufferUpd(StringBuffer bf) {
bf.append("c");
System.out.println(bf);
}
public static void main(String[] args) {
/**
* 對於基本類型和字符串(特殊)是傳值
*
* 輸出lava,java
*/
String s1 = new String("java");
stringUpd(s1);
System.out.println(s1);
/**
* 對於對象而言,傳的是引用,而引用指向的是同一個對象
*
* 輸出javac,javac
*/
StringBuffer bb = new StringBuffer("java");
stringBufferUpd(bb);
System.out.println(bb);
}
}
解析:就像光到底是波还是粒子的问题一样众说纷纭,对于Java参数是传值还是传引用的问题,也有很多错误的理解和认识。我们首先要搞清楚一点就是:不管Java参数的类型是什么,一律传递参数的副本。对此,thinking in Java一书给出的经典解释是When you’re passing primitives into a method, you get a distinct copy of the primitive. When you’re passing a reference into a method, you get a copy of the reference.(如果Java是传值,那么传递的是值的副本;如果Java是传引用,那么传递的是引用的副本。)
在Java中,变量分为以下两类:
① 对于基本类型变量(int、long、double、float、byte、boolean、char),Java是传值的副本。(这里Java和C++相同)
② 对于一切对象型变量,Java都是传引用的副本。其实传引用副本的实质就是复制指向地址的指针,只不过Java不像C++中有显著的*和&符号。(这里Java和C++不同,在C++中,当参数是引用类型时,传递的是真实引用而不是引用副本)
需要注意的是:String类型也是对象型变量,所以它必然是传引用副本。不要因为String在Java里面非常易于使用,而且不需要new,就被蒙蔽而把String当做基本变量类型。只不过String是一个非可变类,使得其传值还是传引用显得没什么区别。
对基本类型而言,传值就是把自己复制一份传递,即使自己的副本变了,自己也不变。而对于对象类型而言,它传的引用副本(类似于C++中的指针)指向自己的地址,而不是自己实际值的副本。为什么要这么做呢?因为对象类型是放在堆里的,一方面,速度相对于基本类型比较慢,另一方面,对象类型本身比较大,如果采用重新复制对象值的办法,浪费内存且速度又慢。就像你要张三(张三相当于函数)打开仓库并检查库里面的货物(仓库相当于地址),有必要新建一座仓库(并放入相同货物)给张三么? 没有必要,你只需要把钥匙(引用)复制一把寄给张三就可以了,张三会拿备用钥匙(引用副本,但是有时效性,函数结束,钥匙销毁)打开仓库。
在这里提一下,很多经典书籍包括thinking in Java都是这样解释的:“不管是基本类型还是对象类型,都是传值。”这种说法也不能算错,因为它们把引用副本也当做是一种“值”。但是笔者认为:传值和传引用本来就是两个不同的内容,没必要把两者弄在一起,弄在一起反而更不易理解。
Java传参的更多相关文章
- Java传参那些事!
刚刚学习java传参的时候很纠结,也非常的不理解!课本上的“按值传递”和“按址传递”搞的自己是一头雾水,后来写的项目多了,自然就明白了! 现在写传参几乎就是条件反射一般——“秒成”,分享当初自己为此写 ...
- Java传参都是传引用变量的副本
最近做练习时碰到一个问题,Java到底是怎样传参的,经过查资料与实验,我发现Java传参都是传引用变量的副本值. 1 Java中的引用变量 1.1 字面值引用变量:即基本数据类型的引用变量 ,如 in ...
- java 传参方式--值传递还是引用传递
java 传参方式--值传递还是引用传递 参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递.写它是为了揭穿普遍存在的一种神话,即认为 Java 应用程序按引用 ...
- 使用java传参调用exe并且获取程序进度和返回结果的一种方法
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在某个项目中需要考虑使用java后台调用由C#编写的切图程序( ...
- 深入分析java传参
概述 java中的参数传递问题可以根据参数的类型大致可以分为三类:传递基本类型,传递String类型,传递引用类型,至于最终是否可以归纳为值传递和引用传递,根据每个人的理解不同,答案不同,此 ...
- Java传参-基本数据类型和引用数据类型作为参数的区别(值传递)
java中的方法可以传递参数,参数的传递方法就是值传递. 参数有形参和实参,定义方法时写的参数叫形参,真正调用方法时,传递的参数叫实参. 调用方法时,会把实参传递给形参,方法内部其实是在使用形参. 所 ...
- String的按值传递,java传参都是传值
java中对象作为参数传递给一个方法,到底是值传递,还是引用传递? String和int参数传递是按值传递还是引用传递? 一道面试题目,String的传递: public String change( ...
- java传参问题
参考链接:https://www.cnblogs.com/linkstar/p/5951141.html public class Example { String testString = publ ...
- Java传参:值传递 or 引用传递 ?
刚开始学Java的时候一度以为:基本数据类型是值传递,引用类型是引用传递.新人很容易在这两个概念上面被搞糊涂,后来看了Hollis的文章才明白了Java中只有值传递. 接下来我能用简单明了的方式来说明 ...
随机推荐
- HDU 2216 Game III(BFS)
Game III Problem Description Zjt and Sara will take part in a game, named Game III. Zjt and Sara wil ...
- php基础的第一天 任务点滴,event对象方法概括 ing....
day1 任务 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <t ...
- Openjudge-计算概论(A)-简单算术表达式求值
描述: 两位正整数的简单算术运算(只考虑整数运算),算术运算为: +,加法运算:-,减法运算:*,乘法运算:/,整除运算:%,取余运算. 算术表达式的格式为(运算符前后可能有空格):运算数 运算符 运 ...
- css3字体
1.定义个性化字体 @font-face{ font-family:字体名字; src:字体地址,可以多写几个用逗号隔开兼容浏览器 } div{ font-family:字体名字/*使用字体*/ } ...
- tableview 详解I
在开发iphone的应用时基本上都要用到UITableView,这里讲解一下UITableView的使用方法及代理的调用情况 UITableView使用详解 - (void)viewDidLoad { ...
- js继承实现
JS实现继承可以分为:对象冒充和原型链继承 其中对象冒充又包括:临时变量,call 和 apply 临时变量方法: function Person(name,sex){ this.name = nam ...
- js添加div
有这样一段div布局 <div class="clearfix"> <p class="left c"> <s ...
- 一些常见warning的原因和解决方法
在入职三周后,终于赶齐了接手项目落下两个月的项目,有了一些自己的空闲时间对项目进行整理.主要整理包括类目的整合,从原来一个系统文件夹下几百个文件整改为以MVC设计思想为原则的分文件夹整理类目,井然有序 ...
- [Python] spides
摘要:本文将介绍用Python进行的爬虫的各种练习以及涉及到的各种知识,包括Http协议,cookie等等 工具 Fiddler Python默认不用代理,所以fiddler不能截取它的包.如果想用用 ...
- android之服务service
service的概念: 官方定义:在后台长时间的操作,没有用户界面,不与用户进行交互,在默认的情况下,service运行在应用程序进程的主线程中,如果需要在Service中处理一些网络连接等耗时操作, ...