终于明白了!java只有值传递,而没有引用传递

一、总结在前

jvm中每个方法占用栈内存中一个独立的栈帧,方法之间互相调用传参时,传递的都是各自栈帧中存储的参数值的拷贝副本,而这个栈中的参数值:
① 有时候存储的是“我们见到的值”(基础数据类型)
② 而有时候存储的是“引用”,该引用指向了堆内存中存储的“我们见到的值”(其他类型如list、数组、对象)

而不是栈帧之间的“参数值”直接指向的另一个栈帧。所以都是值传递(拷贝复制),没有引用传递(直接指向)。

二、正文

参数类型:

  • 形参:方法被调用时需要传递进来参数,例如:public void run(int a)中的int a ,它只有run方法被调用间a才有意义,也就是被分配内存空间,在方法执行完毕后,方法出栈即被销毁释放内存空间,也就不存在了。
  • 实参:方法被调用时传递进来的实际值,它在方法被调用前就被初始化,并且在方法被调用时传入。例:成员变量和局部变量(方法内的局部变量除外)

值传递和引用传递:

  • 值传递:在方法被调用时,实参通过把他的内容副本传入方法内部,此时形参接收的内容是实参的拷贝。因此在方法内对实参的任何操作,都仅仅是对这个内容的副本进行操作,不影响原初始值的内容。值传递传递的是一个真实的内容副本,对副本的操作不影响原内容,也就是形参再怎么变化,也不影响实参对应的内容。在jvm内存中的说法也就是传递的堆地址。
  • 引用传递:“引用”也就是指向真实内容的地址值。在方法调用时,实参的地址通过放法调用传递给相应的形参,在方法体内,形参和实参指向同一块内存地址,对形参的操作会影响原来的内容。即传递的是栈地址。

java中不存在引用传递

看如下代码(基本类型参数的值传递):

public class Zcd {
	public static void main(String[] args) {
		int a=12;
		System.out.println("调用过程前a的值"+a);
		change(a);
		System.out.println("调用过程后a的值"+a);
	}
 
	private static void change(int a) {
		// TODO Auto-generated method stub
		a=100;
		System.out.println("调用过程中a的值"+a);
	}
}

输出结果:

 

我们发现值传递和我们上面所说一致,它是把实参复制一份通过形参传给方法,而这个方法改变的只是副本,无法对原来的数值改变。

如下图:值传递的jvm内存图,因为int a是局部变量,它只在方法区内它不占用堆内存也不在方法区内存放,方法出栈数据就会丢失,所以change方法结束后它会出栈,而且赋的值也根本不会占用内存,副本的值也不会对原数据产生改变。这就是基本数据类型的值传递

 

看如下代码(引用类型参数的值传递):

public class Valtransfer {
	public static void main(String[] args) {
		String name=new String("123");
		System.out.println(name);
		System.out.println("调用过程前堆地址"+name.hashCode());
		change(name);
		System.out.println(name);
		System.out.println("调用过程后堆地址"+name.hashCode());
	}
 
	private static void change(String name) {
		// TODO Auto-generated method stub
		name="lhs";
		System.out.println("调用过程中堆地址"+name.hashCode());
		System.out.println(name);
	}
}

结果:

 

我们发现值传递和我们上面所说不一致,String比较特殊,它的源代码是由final修饰的,你无法改变他原始的值,它是把实参复制一份通过形参传给方法,而这个方法改变的只是副本,并且和其他引用类型参数不同它改变了它的堆地址,所以也无法对原来的数值改变。

string源代码如下图

 

如下图:值传递的jvm内存图,change方法结束后它会出栈,又因为string源代码是final修饰,我们知道final修饰的引用类型固定的是一个堆地址,所以复制的那一份应该是开辟了新的堆地址,副本的值并不会对原数据产生改变。

 

那我们看这个代码:

public class Valtransfer {
	public static void main(String[] args) {
		StringBuffer a = new  StringBuffer("A");
		StringBuffer b = new  StringBuffer("B");
		change(a, b);
		System.out.println(a+","+b);
	}
	
	public static void change(StringBuffer x,StringBuffer y) {
		x.append(y);
		y = x;
	}
}

结果是:

会发现它会对原数据进行行了改变,那他就是引用传递吧?并不是,他还是值传递 ,java没有引用传递。

StringBuffer的append方法会对数据所在的内存进行字符串拼接,从而避免内存资源浪费,提高效率,从而导致了对原数据的改变。

还有一种看如下代码(引用类型参数的值传递)

public class Valtransfer {
	static String nameString;
	static int age;
	public Valtransfer(String name,int age) {
		this.nameString=name;
		this.age=age;
	}
	public static void main(String[] args) {
		Valtransfer a=new Valtransfer("qcby",26);
		System.out.println("调用过程前"+a.nameString);
		getName(a.nameString);
		System.out.println("调用后"+a.nameString);
	}
	
	public static void getName(String name) {
		nameString="gs";
		System.out.println("调用过程中"+nameString);
	}
}

结果为:

这时就会疑问,这总会是引用传递了吧?并不是,这是引用类型参数的值传递 。

我们看jvm内存图:

getName方法入栈后改变了静态常量池中的内容,之后它出栈,所以导致了内容的改变。它还是复制了一份副本,他们堆地址相同,栈地址不同。我们所说的对副本的更改对原本没有影响是指在入栈后方法内的数据,而对指向的堆和方法区内的更改是会对原数据进行更改的,这就是造成java有引用传递争论的原因,结论是没有值传递。

还有一种看如下代码(引用类型参数的值传递)

public class Valtransfer {
	String nameString;
	int age;
	public Valtransfer(String name,int age) {
		this.nameString=name;
		this.age=age;
	}
	public static void main(String[] args) {
		Valtransfer a=new Valtransfer("qcby",26);
		System.out.println("调用过程前"+a.nameString);
		System.out.println("调用过程前堆地址"+a.hashCode());
		getName(a);
		System.out.println("调用后"+a.nameString);
		System.out.println("调用过程后堆地址"+a.hashCode());
	}
	
	public static void getName(Valtransfer a) {
		a.nameString="gs";
		System.out.println("调用过程中"+a.nameString);
		System.out.println("调用过程中堆地址"+a.hashCode());
	}
}

有人就又会疑问了,形参里的是对象,那传递的应该是引用本身,这肯定是引用传递,我确实这样疑惑过,但仔细想想,其实还是值传递,因为他们指向同一个堆,所以改变堆内内容当然对原本有影响了,我们所说的没有影响是栈中的原数据,这里是对象a,副本a根本对原实参a无法造成影响,因为a是对象,所以你对对象a中数据的更改跟a没有关系,因为堆中的数据时共享的,很多人都会理解错这一点。 

结果如下图:

 

 还有这种值传递,如下图

 我们说的复制的堆地址上面形参中的jvm图是这样copy的:

上面我把多种值传递情况都列了出来,希望可以解决大家的疑惑 。

 

总结:java没有引用传递,值传递复制的是堆地址,是拷贝了一个副本。

我们所说的对副本的更改对原本没有影响是指在入栈后方法内的数据,而对指向的堆和方法区内的更改是会对原数据进行更改的,这就是造成java有引用传递争论的原因

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介 本书主要是冲着实际应用而来的,共分11章。在刚开始的第一章就详细地讲解了Java开发环境的搭建、反编译工具的使用、JDK文档资料的查阅,Java程序的编译、运行过程。在第二章中,全面地讲解Java的基本语法知识,对基本语法的讲解也不是泛泛而谈,而是在其中贯穿各种实际应用中的巧妙用法和注意事项。在第三章和第四章中,透彻系统地讲解了面向对象的思想和应用。在以后的章节中,用通俗易懂的手法,紧密联系实际应用的方式,深入浅出地讲解了多线程,常用Java类,Java中的I/O(输入输出)编程,GUI与Applet,网络编程等方面的知识。 本书许多内容都来源于程序员圈子里的非正式交流,或源于某些成功的案例与作者的经验、心得,但这些东西对新手来说,是很难自学到的。作者从事了多年的软件开发和培训教学,非常清楚那些容易使新手困惑的问题,在学习过程中会碰到的拦路虎,作者结合了多年实际开发与教学经验,收集了众多学员在学习中常提到的问题,对平时讲课的内容进行了精心整理。读者从本书中不仅可以学习到Java本身方面的知识,还能学到了许多编程思想和实际操作手法,仿佛老手就在你面前进行现场演示一样。本书不仅全面的介绍了Java语言本身,最重要还交会读者去掌握编程思想,找到编程感觉,而不是死记硬背语言本身,书中涉及到的应用问题分析,远远超了一个Java程序员在学习和应用Java过程中所有可能碰到的问题。 本书不仅讲概念,讲怎么做,还告诉读者为什么;不仅讲操作技能,还贯穿一些系统的理论,这样读者才不至于不明不白,或是似乎明白,但不知道具体该怎么干。本书一步步引导读者深入,使读者轻松愉快、兴趣盎然、水到渠成、潜移默化地掌握Java编程及许多其他的软件开发思想。 本书语言流畅,内容翔实,分析透彻,是一本适合广大计算机编程爱好者的优秀读物。本书结构合理,图文并茂,实用性强,适合于广大有经验的开发人员来迅速转换到Java语言,对广大初学计算机编程语言的爱好者来说,这本书就是非常好的切入点。本书基本理论知识完备,又紧密联系实际开发,也非常适合作为大专院校师生的教学与学习用书,将给广大师生带来一种革命性的教学方式与学习思路,令人耳目一新。 这不是一本参考资料和Java百科全书,不是什么"宝典"和"大全",但却可以让新手变为老手,相信学完此书,再看任何以前看不懂的Java书都会显得非常轻松。即使是很有经验的老手,也能从本书中有巨大收益。如果你想非常轻松就精通Java编程,并期望学完便能参加实际的开发工作,本书就是你非常好的一个选择。 目录: 第1章 Java开发前奏 1.1 Java虚拟机及Java的跨平台原理 1.2 Java开发环境的搭建 1.3 体验Java编程的过程 1.4 classpath的设置 1.5 有效利用Java的文档帮助 1.6 JVM(虚拟机)的运行过程 1.7 垃圾回收器 1.8 反编译工具的介绍 第2章 Java编程基础 2.1 Java基本语法格式 2.2 变量及变量的作用域 2.3 函数与函数的重载 2.4 Java中的运算符 2.5 程序的流程控制 2.6 数组 第3章 面向对象(上) 3.1 面向对象的概念 3.2 类与对象 3.3 构造函数 3.4 this引用句柄 3.5 与垃圾回收有关的知识 3.6 函数的参数传递 3.7 Static关键字 3.8 内部类 3.9 使用Java的文档注释 第4章 面向对象(下) 4.1 类的继承 4.2 抽象类与接口 4.3 对象的多态性 4.4 异常 4.5 包 4.6 访问控制 4.7 使用jar文件 第5章 多线程 5.1 如何创建与理解线程 5.2 多线程的同步 5.3 线程间的通信 5.4 线程生命的控制 第6章 Java API 6.1 理解API的概念 6.2 工具软件的介绍与使用 6.3 String类和StringBuffer类 6.4 基本数据类型的对象包装类 6.5 集合类 6.6 Hashtable与Properties类 6.7 System类与Runtime类 6.8 Date与Calendar,DateFormat类 6.9 Math与Random类 6.10 学习API的方法 第7章 IO/输入输出 7.1 File类 7.2 RandomAccessFile类 7.3 节点流 7.4 过滤流与包装类 7.5 IO中的高级应用 第8章 图形用户界面GUI(一) 8.1 初识AWT 8.2 AWT线程 8.3 AWT事件处理 8.4 GUI组件上的图形操作 第9章 图形用户界面GUI(二) 9.1 常用AWT组件 9.2 布局管理器 9.3 Swing 第10章 Applet 10.1 浏览器怎样显示网页 10.2 浏览器处理网页脚本代码的过程 10.3 浏览器怎么处理Applet 10.4 Applet类及其方法 10.5 一个显示动画的Applet的程序 10.6 关于Java的一些细节 10.7 验证Applet对象在客户端如何存在 第11章 网络编程 11.1 网络编程的基础知识 11.2 Java编写UDP网络程序 11.3 Java编写TCP网络程序

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值