转自:http://hi.baidu.com/cpuramdisk/item/84a75b5e78007013da163535
【yasi】关于“Java方法中传进去一个对象,为什么不能改变这个对象”的问题,在网上搜了很多中英文的资料,就下面这篇帖子讲的最清楚透彻。果断收录。
java方法中传值和传引用的问题是个基本问题,但是也有很多人一时弄不清。
(一)基本数据类型:传值,方法不会改变实参的值。
public class TestFun {
public static void testInt(int i){
i=5;
}
public static void main(String[] args) {
int a=0 ;
TestFun.testInt(a);
System.out.println("a="+a);
}
}
程序执行结果:a=0 。
(二)对象类型参数:传引用,方法体内改变形参引用,不会改变实参的引用,但有可能改变实参对象的属性值。
======【yasi】这里加入对此的个人理解======
这里说“对于对象类型参数,传入的是引用”,不妥。个人觉得这里传入的仍然是值,只不过是“对象引用的副本”,即把对象引用的值,而不是对象引用的引用传进去了。这样说太绕了,下面举个例子。
public void Foo(String s) {
s = new String("Hello");
}
#include <iostream>
using namespace std;
class CObj {
public:
CObj() {x = 0;}
CObj(int x) {this->x = x;}
public:
int x;
};
void Foo1(CObj * pObj) {
pObj = new CObj(200);
}
void Foo2(CObj * &pObj) {
pObj = new CObj(200);
}
int main(int argc, char * argv[]) {
CObj * pObj1 = new CObj(100);
cout << pObj1->x << endl;
Foo1(pObj1);
cout << pObj1->x << endl;
Foo2(pObj1);
cout << pObj1->x << endl;
return 0;
}
运行结果如下:
100
100
200
void Foo1(CObj * pObj) {
CObj * pTmp = pObj;
pTmp = new CObj(200);
}
可见,Foo1() 函数外面的 pObj1在Foo() 函数里是不会被改变的。这就说明了为什么下面的代码不能改变传入的String引用了。
public void Foo(String s) {
s = new String("Hello");
}
其实,打个比方,这段代码就相当于(当然不是很贴切,因为s本身就已经是一个local变量了):
public void Foo(String s) {
String tmp = s;
tmp = new String("Hello");
}
相比之下,C++代码中的 Foo2() 函数,就能改变传入的 pObj 对象本身,因为传入的指针本身被重置了!
void Foo2(CObj * &pObj) {
pObj = new CObj(200);
}
上面各种方式的比较:
C++的传入引用貌似方便,但是常常要看到函数实现的内部,才知道传入的参数究竟在函数中是只读使用的,还是有写操作的。相比之下,Java基本上就是通过函数返回值传出结果,比C++要清晰的多。因此,在用Java时,尽量不要像C++的方式靠,而应尽量利用函数的返回值。比如,如果要同时返回两个String则可以返回一个List<String>;又比如,如果要将字符串“Jack_London_1234”中的字符串"London"和数字1234识别并返回,可以返回一个List<Object>,第一个元素是String "London",第二个元素是Integer 1234,然后分别做类型转换,转换成String类型和Integer类型。
PS
如果String类提供一个函数,比如void assign(String input),该函数将input的内容赋给当前String对象(注意是堆上的对象实体),那么像下面这样的函数也是可以通过s参数返回“hello”的。之所以String类没有提供这样的函数,可能就是编写者不希望使用者像C++那样去用参数吧。
public void Foo3(String s) {
s.assign("hello");
}
==================
举两个例子:
(1)方法体内改变形参引用,但不会改变实参引用 ,实参值不变。
public class TestFun2 {
public static void testStr(String str){
str="hello";//型参指向字符串 “hello”
}
public static void main(String[] args) {
String s="1" ;
TestFun2.testStr(s);
System.out.println("s="+s); //实参s引用没变,值也不变
}
}
执行结果打印:s=1
(2)方法体内,通过引用改变了实际参数对象的内容,注意是“内容”,引用还是不变的。
import java.util.HashMap;
import java.util.Map;
public class TestFun3 {
public static void testMap(Map map){
map.put("key2","value2");//通过引用,改变了实参的内容
}
public static void main(String[] args) {
Map map = new HashMap();
map.put("key1", "value1");
new TestFun3().testMap(map);
System.out.println("map size:"+map.size()); //map内容变化了
}
}
执行结果,打印:map size:2 。可见在方法testMap()内改变了实参的内容。
(3)第二个例子是拿map举例的,还有经常涉及的是 StringBuffer :
public class TestFun4 {
public static void testStringBuffer(StringBuffer sb){
sb.append("java");//改变了实参的内容
}
public static void main(String[] args) {
StringBuffer sb= new StringBuffer("my ");
new TestFun4().testStringBuffer(sb);
System.out.println("sb="+sb.toString());//内容变化了
}
}
执行结果,打印:sb=my java 。
所以比较参数是String和StringBuffer 的两个例子就会理解什么是“改变实参对象内容”了。
总结:
第一:java方法基本数据类型是传值,对象类型传引用,这是千真万确的。
第二:当参数是对象时,无论方法体内进行了何种操作,都不会改变实参对象的引用。
第三:当参数是对象时,只有在方法内部改变了对象的内容时,才会改变实参对象内容。
java程序的函数调用到底是传值呢还是传参呢?这可是个难缠的问题,如果搞不清楚还是挺容易出错的:P对于这个问题,最经典的解释莫过于“java函数是传值的,java函数传递的参数是对象的引用”
这两句话好像初听上去有点绕,不过意思倒是表达得蛮精确的。我看到过几个解释这个问题的例子,不过个人感觉看过例子之后还是只知道是什么不知道为什么,停留在照猫画虎的水平上还是挺容易出问题的。所以举例子之前,先从jvm的实现原理上有个了解应当是不无裨益的。jvm的结构图前一阵子贴到blog上了,那可是从“深入java虚拟机”这本巨牛的书上看来的,绝对有权威性。从jvm的结构图上可以看出来,jvm在实现的时候将属于它的内存分为五部分,其中程序代码(严格的说应当是字节码)是放在java栈的栈帧中,而对象是从堆中分配的,堆这个东西我看可以理解成“对象池”。程序和程序中需要用到的对象放在两个相对独立的区域中,那么程序怎么使用对象呢?答案是程序中真正使用对象的地方其实只是声明了一个对象的引用,也就是把堆中分配了的相应对象的地址放到引用中,栈和堆之间就是通过一个一个的引用来联系的。引用嘛,我理解就是一个指针常量,指针常量又是个什么东西呢?说白了,就是一个无符号整数,这个整数所表达的是引用对象的地址。好了,这下清楚了,不管是基本类型变量(int,float,double什么的)还是对象,相应的内存地址中存放的都是一个数(无符号整数,整数,浮点数等)。传递参数的时候传递的就是相应内存地址中的数,所以说“java函数是传值的”。当然,这个数对于基本类型和对象类型来说意义是不一样的,对于基本类型这个数就是其值本身,传递值的结果就是,改变新的变量的值不影响旧的变量的值;而对于对象来说这个数是它的地址,传递这个值就相当于传递了真实对象的引用,传递了引用或者说是地址的结果就是变化会全局可见,所以又可以说“java函数传递的参数是对象的引用”。唔,松口气啦。经过上面这一小堆讨论,不难理解为什么java在传递参数时对于基本类型和对象表现不同:)现在开始举例了,举网上搜来的例子,看看是不是比原来没有上面的解释的时候好理解一点?
public class TestRef
{显示原理获取/创建矢量要素→设置矢量要素显示风格→加载矢量要素对象到图层添加方法GeoJSON,可以描述点、线、面等矢量要素类型L.geoJSON().addTo(map)——geoJSON Extends FeatureGrouphttps://leafletjs.com/reference-1.6.0.html#geojson点对象MarkerL.marker([lat, lng]).addTo(map)通过设置Icon的属性信息,规定点对象的风格样式,例如图片来源、大小
图像增广通过对训练图像做一系列随机改变,来产生相似但又不同的训练样本,从而扩大训练数据集的规模。图像增广的另一种解释是,随机改变训练样本可以降低模型对某些属性的依赖,从而提高模型的泛化能力。我们可以对图像进行不同方式的裁剪,使感兴趣的物体出现在不同位置,从而减轻模型对物体出现位置的依赖性。我们也可以调整亮度、色彩等因素来降低模型对色彩的敏感度。1翻转和裁剪,2变化颜色(亮度、对比度、饱...
幸运九宫格抽奖码,九宫格抽奖系统源码php+mysql,微信抽奖系统1.本程序在PHP5.3~PHP5.6环境完美运行2.后台访问地址域名/admin/index.phpadminadmin1.导入1776.sql到数据库2.修改db.php填写数据库相关信息。
DDS:Direct Digital Synthesis,是指信号发生器。DDS信号发生器采用直接数字频率合成(Direct Digital Synthesis,简称DDS)技术,把信号发生器的频率稳定度、准确度提高到与基准频率相同的水平,并且可以在很宽的频率范围内进行精细的频率调节。采用这种方法设计的信号源可工作于调制状态,可对输出电平进行调节,也可输出各种波形。DDS原理:我这里介绍的是相位截断DDS。我们从图中可以看出其原理主要由4部分构成,相位增量,相位累加器,相位量化.
npm 5 was released today,其中一项新功能包括通过创建 package-lock.json 文件进行确定性安装。这个文件应该保存在源代码管理中吗?我假设它类似于 yarn.lock 和 composer.lock,两者都应该保存在源代码管理中。
Java基础:(面向对象,变量,操作符,接口与继承,类和对象)Java基础知识点这里面向对象变量操作符系列类和对象接口与继承Java中级:(异常处理,输入输出,集合框架泛型,多线程,数据库,图形界面,网络编程)Java中级知识异常处理文件操作集合框架泛型多线程图形界面前端部分:HTMLCSSJavaScript更多前端知识J2EE实践项目:仿天猫J2EE...
随着视频内容的爆发,如今的移动用户已经不再满足现有的上网速度,希望电信商能够提供更快的数据传输速度、以及更可靠的网络服务,而下一代通信技术5G就是在此背景下诞生的。虽然现在5G还处在规划阶段,但是整个行业都在共同努力,希望能够确定5G的最终形态。但是这个行业的所有参与者都必定同意这一点:随着移动用户数量及其需求的上升,5G必须比如今的蜂窝网络基站更快地...
如果你画的曲线图没有只有点,没有线连接,一定是你的数据有问题,中间有空数据或者有空格数据,完整图如下:在每个点上单击选中右键选择properties选择independent,也就是无关联,然后设置每个点的颜色和线条的颜色在状态栏的windows中选择refresh刷新图形,图形可以放大缩小,如果需要图形,请直接截图就行tick和increment是设置显示的步长,可以试着修改...
我认为问题在于,您试图将文件结果加载到downloadmsg中,但这不起作用,因为.load()只将结果加载为html…而不是二进制数据或其他编码。一种可能有效的方法是在HTML中创建隐藏的iframe,如下所示:然后,将iframe的attr设置为querystring:$("#secretIFrame").attr("src","myphpscript.php?option1=apple&am...
Spring循环依赖什么是Spring的循环依赖?循环依赖会存在哪些问题?示例:AService依赖BService; BService依赖[email protected] class AService { // @Autowired public BService bService;}@Servicepublic class BSer...
结论:1.用KVC改变只读属性的值;2.若禁止KVC方式修改只读属性的值,可在对应类重写类方法// 该方法默认返回YES。 即在不存在满足条件的存取方法时,允许直接访问属性对应的实例变量+ (BOOL)accessInstanceVariablesDirectly { return NO;}分析:1.被readonly修饰的属性,是没有创建sette...
刚接触数据仓库是在我的第一份实习工作——数据中心数据管理系统开发,它是一个B/S架构的应用,与一般的项目不同的是,系统是以数据仓库来进行数据存取的,这是我第一次听说数仓这个词,感觉它庞大而且神秘,不知道从何入手,对数据仓库有一种敬畏之心,后来经过慢慢的学习和使用,发现其实它在应用开发中的使用方法跟传统关系数据库没什么区别,无非就是普通的SQL查询以及JDBC连接。所以数仓的使用不是本文的重点,我们...