java中按值传递_Java中为什么只有按值传递

Java中为什么只有按值传递

Java 中只有按值传递

"Java 中只有按值传递",初看到这几个字有点不敢相信,无数次通过函数改变过对象,无数次跟同事说 Java 在传对象的时候是按引用传递。后来细细想想,之所以以为 Java 传对象是按引用传递是因为其中有很多概念都没理清楚,与 C++ 中的搞混了。从 C++ 转 Java 的时候将 C++ 中的知识点映射到 Java 没错,这有利于 C++ 转 Java 的人更快的学习 Java。但一旦映射错误就很容易形成固定思维。

在 C++ 和 Java 中都有引用的概念,但他们完全不是同一个东西。Java 中的引用更类似 C++ 的指针,C++ 的引用在 Java 中并无对应概念。在 C++ 中有按值传递、按指针传递和按引用传递三种,而在 Java 中没有 C++ 引用和指针的概念,所以只有按值传递一说。

为了更好的说明 Java 中只有按值传递,先来看看 Java 的数据类型,Java 的数据类型分为基本数据类型和引用类型,其中:

1、基本类型包括 byte/short/int/long/float/double/char/boolean 八种,基本类型在内存中地址中保存的即本身的值,其一般都在栈上分配。

2、引用类型指向一个对象,它与 C++ 的指针非常相似。但 C++ 的指针可以指向基本类型和类对象,而 Java 的引用只能指向类(枚举、接口等)对象。Java 中对象本身在堆上分配,而引用类型在栈上分配,其内存地址中保存的是对象在堆中的地址。两种类型在内存中的布局如下:

?

上图可以一目了然的看出基本类型与引用类型的区别,基本类型数据即本身,引用类型仅仅是引用(就是使用dup指令复制一个引用的值,但是它们都指向同一个内存地址)。来看下基本类型和引用类型在参数传递中所表现的不同的地方:

1 classMyInteger{

2 intvalue;

3 }

4

5 publicclassTestReference{

6

7 publicstaticvoidchangeBasic(intarg){

8 arg=2;

9 }

10

11 publicstaticvoidchangeReference(MyIntegerarg){

12 arg.value=2;

13 }

14

15 publicstaticvoidmain(String[]args){

16

17 intbasicTypeA=1;

18

19 MyIntegerreferenceTypeA=newMyInteger();

20 referenceTypeA.value=1;

21

22 System.out.println("调用changeBasic之前basicTypeA的值"+basicTypeA);

23 changeBasic(basicTypeA);

24 System.out.println("调用changeBasic之后basicTypeA的值"+basicTypeA);

25

26 System.out.println("调用changeReference之前referenceTypeA的值"+referenceTypeA.value);

27 changeReference(referenceTypeA);

28 System.out.println("调用changeReference之后referenceTypeA的值"+referenceTypeA.value);

29 }

30 }

运行结果如下:

可以看出基本类型 int 的变量 basicTypeA 在 changeBasic 调用后值并没有发生改变,而引用类型 MyInteger 的变量 referenceTypeA 在调用 changeReference 后发生了改变。这里就比较容易误导读者以为:Java 基本类型是按值传递而引用类型是按引用传递(暂且这么定义)。

其实不然,按值传递的意思想必大家都知道:传递的是值的的拷贝,比如上面代码中的调用 changeBasic(basicTypeA) 时,arg 是 basicTypeA 的一个拷贝,所以无论对 arg 做任何操作都不影响 basicTypeA 变量本身。而调用 changeReference(referenceTypeA) ,arg 也是 referenceTypeA 的一个拷贝,但是由于 arg 和 referenceTypeA 都是引用类型且他们指向同一对象,所以通过 arg 修改对象,referenceTypeA 也能看到。两种类型变量在内存中调用过程如下:

?

所以可以看出无论是基本类型还是引用类型,都是按值传递。只是由于它们在内存中所表示的内容不同,最后表现出来的结果也有所不同。同理,在 C++ 中的按值传递、按指针传递和按引用传递理论上都可以归为按值传递(其实这个归类在学 C++ 的时候就归纳出来了,只是后来反而忘了)。

对"引用"进行传递的坑

Java 的引用类似于 C++ 的指针,但是 C++ 的对象传递提供了对象本身直接传递和指针传递两种方式(引用方式不谈),而 Java 对象只有对引用进行传递这一种,不存在直接将对象本身进行传递。

1、对象本身进行传递的好处是传递的都是对象的拷贝,在函数中对拷贝的对象做任何修改都不会改变原对象。但是如果对象非常大,而且调用很频繁会影响性能。

2、对象的引用(或者指针)传递的好处是只需要拷贝一个引用(或者指针)大小的数据即可,且可以在调用的函数中改变原对象内容。

在 C++ 中以上(1)和(2)程序员自己还可以选择,Java 里面直接只有第 2 种方式,凡事有利有弊,有时候我们并不想在函数中改变原对象的内容,这里我就踩过一个坑,有个对象通过管道传递的流程如下:

?

funcA 与 funcB 是两个不同的人负责的,一次升级后 funcB 的负责人发现在函数中获取的对象 X 内容不对,一开始还以为是传递对象 X 的接口出现了错误便是一顿排查,知道最后才发现对象 X 是在升级后在 funcA 中被修改了,浪费了不少时间。当然这个架构的流程设计的不合理是主要原因(只需要在分发的时候讲对象 X 做手动拷贝即可避免上述问题),但是不不影响我们抛出 Java 只能对引用进行传递的弊端。

在调用链较长、各种 for/while 循环中很容易就犯了上述错误,解决方案当然就是手动拷贝对象,Java 中拷贝对象有以下两种方式:

1、实现Cloneable接口并重写Object类中的clone()方法。

2、实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

其中 2 能避免深浅拷贝的问题,但调用比较耗时。1 也能避免深浅拷贝但是需要自己手动去写相应的代码,如果嵌套较深,代码将非常复杂。至于深浅拷贝的问题可以自行百度,其本质还是因为只是将对象的引用进行了传递而导致的一些问题。

转自:https://www.cnblogs.com/zhuwbox/p/10392970.html

Java中为什么只有按值传递 相关文章

Java多线程加法计算--Java识别静态验证码和动态验证码

Java多线程加法计算 Java识别静态验证码和动态验证码 写了一个简单java工具类,用于验证码点阵打印+自动识别。为了提升识别精度和程序性能,此工具类是针对特定类型的验证码的,若要用于其他类型的验证码识别,需要做相应调整。 文章分两部分演示了此java工

JAVA入门0506:安装开发环境HelloWorld详解

JAVA开发环境搭建 JDK下载与安装 配置环境变量 JDK目录介绍 HelloWorld 及 简单语法规则 Notepad++安装和使用 JDK下载和安装 JDK8 JDK8 卸载JDK 删除环境变量中JAVA_HOME的目录下文件 并删除JAVA_HOME环境变量 和 PATH中的JAVA相关变量 运行 cmd - java - ve

JAVA-面对对象

这篇笔记只是个人的一些见解加上网络上的一些资料写的,可能再某些地方会有问题,请见谅。 面对对象 两个基本概念:类和对象三大特征:封装,继承,多态其它关键字:static,final,this,super 基本概念 类 类的概念:类就好像一个模板,它描述一类对象的行

JavaSE——03方法详解

Java方法详解 方法重载 重载就是在一个类中,有相同的函数名字,但形参不同的函数 重载规则: 方法名必须相同 参数列表必须相同(个数不同、或类型不同、参数排列顺序不同等) 方法返回类型可以相同也可以不同 仅仅返回类型不同不足以成为方法的重载 public

深入浅出Java线程池:使用篇

前言 很高兴遇见你~ 借助于很多强大的框架,现在我们已经很少直接去管理线程,框架的内部都会为我们自动维护一个线程池。例如我们使用最多的okHttp以及他的封装框架Retrofit,线程封装框架RxJava和kotlin协程等等。为了更好地使用这些框架,则必须了解他的实

Loading in《The Java Virtual Machine Specification Java SE 7 Edition》

java的类加载机制 一.类加载是什么 类加载过程即是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。 举个通俗点的例子来说,JVM在执行某段代码时,遇到了class A, 然而此时内存中并没有class A的相关信息,于是JVM就会到

Java 设置Excel条件格式(高亮条件值、应用单元格值/公式/数据条等类型)C# 创建Excel气泡图

C# 创建Excel气泡图 气泡图(Bubble Chart)是可用于展示三个变量之间的关系。通过绘制x 值, y 值和大小值即可确定图表中气泡的坐标及大

java面向对象三大特征

java面向对象三大特征 封装 利用抽象数据类型将数据和基于数据的操作封装在一起,数据被保护在抽象数据类型的内部,经可能隐藏内部的细节,只保留一些对外的的接口,用户无需知道对象内部的细节,但是可以通过对象对外提供的接口来访问该对象 以下Person类封

Okio源码分析它补充了 java.io 和 java.nio 的不足,使访问、存储和处理数据更加容易

Okio源码分析它补充了 java.io 和 java.nio 的不足,使访问、存储和处理数据更加容易 okio 的文档说明:https://square.github.io/okio/。本文代码介绍基于版本 1.17.4。 概述 Okio 作为 Okhttp 底层 io 库,它补充了 java.io 和 java.nio 的不足,使访问、

Java-JDBC-预编译+批处理

Java-JDBC-预编译+批处理 什么是批处理:批处理就是将一个SQL语句集合发给数据库执行,也就是发送一批SQL给数据库,而不是一条一条得发送给数据库执行,这样可以大大减少访问数据库次数,从而提高SQL执行效率; 预编译( preparedStatement )+批处理的优点 优点:语

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值