关于Java 值传递深度分析
首先说观点:java只有值传递没有引用传递
然后再来看看值传递与引用传递两者的定义
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
这里牢记值传递中将实际参数复制一份。
然后就是对于参数类型:值类型 和 引用类型。
结合起来理解就是:值类型传递,java是将其值内容复制一份给形参;对于引用类型传递,java是将其地址复制一份给形参。
下面结合实例深入理解为什么java只有值传递
package 字符串;
public class 值传递 {
public static void main(String[] args)
{
String str1="abc";
updateStr1(str1);
System.out.println("main函数中"+str1);
}
public static void updateStr1(String str1)
{
str1="cba"; //<注解>
System.out.println("调用函数中"+str1);
}
}
结果:
在这里我们能够清晰看到我们传递的是String类型的对象即(引用类型),并且在调用函数中我们修改了str1为cba,如果是引用传递那么我们在主函数打印则应该是cba,
但是很遗憾我们在主函数中仍然打印出来的是abc。所以我们可以说java是值传递类型了吗,答案是不完全的。
接下来再看这一段代码:
package 字符串;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public class 值传递2 {
public static void main(String[] args)
{
person p1=new person();
p1.setAge(10);
System.out.println("我在主函数里对p1的年龄属性赋值为"+p1.getAge());
setage(p1);
System.out.println("我再从主函数里获取P1的年龄属性"+p1.getAge());
}
public static void setage(person p1)
{
p1.setAge(18); //不是我们对它的地址进行了操作,而是我们对它地址的内容进行了操作
System.out.println("我在调用函数里对p1的年龄属性重新赋值为"+p1.getAge());
}
}
结果:
咦,怎么回事这次也是传递的对象(引用类型),为什么这次我们对年龄这个字段的修改在主函数同步了呢?
别急,下面我们先来分析这两个例子。
首先第一个类型的例子中,我们传递的是String类型的变量,它是一个特殊的类型的引用变量。
(不可变字符串:编译器可让字符串共享,即将各种字符串存放于公共存储池中,字符串变量是指向其中相应位置 --出自《Java核心技术 卷1》)
出于这句话的理解就是每个字符串都对应一个地址:我们例一中是将str1的地址复制给了我们的形参str1,并且形参中str1的地址进行了改变指向了“cba”的地址。所以说在主函数中的str1的地址仍然指向的是“abc”所对应的地址。
所以说对于String类型的变量,我们对于给它重新赋值不是改变了它的内容,而是改变了它指向字符串的位置。这也就解释了为什么java中String类型是不可变类型。
而在我们例二中,我们将p1的地址复制给了我们形参中的p1,此时他们都指向的内存中一块相同的地址这里存放着相同内容,所以我们在调用函数对这个地址中的内容进行修改时就会同步到我们主函数中的p1。所以这个并不意味着这个是引用传递。
好吧,那怎么才能解释好Java确实是值传递呢(上面String类型例子是特殊的引用类型不方便解释)
下面我们通过这个例子说明:
package 字符串;
public class person {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} }
public class 值传递3 {
public static void main(String[] args) {
person p1=new person();
person p2=new person();
p1.setAge(10);
p2.setAge(18);
System.out.println("我在主函数里对p1的年龄属性赋值为"+p1.getAge());
System.out.println("我在主函数里对p2的年龄属性赋值为"+p2.getAge());
swap(p1,p2);
System.out.println("************我是主函数里的分割线***************");
//我再在主函数里分别对p1,p2获取他们的年龄,若为引用传递则p1的年龄应该为18,p2为10.
System.out.println("我在主函数里获取p1的年龄"+p1.getAge());
System.out.println("我在主函数里获取p1的年龄"+p2.getAge());
}
public static void swap(person p1,person p2)
{
System.out.println("************我是调用函数里的分割线***************");
person temp=new person();
temp=p1;
p1=p2;
p2=temp;
System.out.println("我在调用函数里交换了p1和p2指向的地址");
System.out.println("我在调用函数里对p1的年龄属性赋值为"+p1.getAge());
System.out.println("我在调用函数里对p2的年龄属性赋值为"+p2.getAge()); } }
结果:
看到没,这就是充分说明Java是值传递的例子。在这个例子中我们依然传递的是person类的对象p1,p2(引用类型),他们将各自的地址复制一份到了形参p1、p2。
然后我们在调用函数中交换了他们的地址,确实在调用函数中他们的age属性发生交换。但是再当我们在主函数获取他们的age时,如果是引用传递则应该p1的age为18,p2的age为10,
和我们在调用函数中打印结果一致。但是,很遗憾在主函数中他们的值仍然是p1(10),p2(18)。所以这也充分印证了java是值传递。
那么什么是引用传递呢?我们把代码放入C#看看。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace 值传递or引用传递
{
public class person
{
private int age;
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
} }
class Program
{
static void Main(string[] args)
{
person p1 = new person();
person p2 = new person();
person p3 = new person();
p1.setAge(10);
p2.setAge(18);
p3.setAge(15);
Console.WriteLine("我在主函数里对p1的年龄属性赋值为" + p1.getAge());
Console.WriteLine("我在主函数里对p2的年龄属性赋值为" + p2.getAge());
Console.WriteLine("我在主函数里对p3的年龄属性赋值为" + p3.getAge());
swap(ref p1,ref p2,p3);
Console.WriteLine("************我是主函数里的分割线***************");
//我再在主函数里分别对p1,p2获取他们的年龄,若为引用传递则p1的年龄应该为18,p2为10.
Console.WriteLine("我在主函数里获取p1的年龄" + p1.getAge());
Console.WriteLine("我在主函数里获取p2的年龄" + p2.getAge());
Console.WriteLine("我在主函数里获取p3的年龄" + p3.getAge());
}
public static void swap(ref person p1,ref person p2, person p3)
{
Console.WriteLine("************我是调用函数里的分割线***************");
person temp = new person();
temp = p1;
p1 = p2;
p2 = temp;
p3.setAge(20);
Console.WriteLine("我在调用函数里交换了p1和p2指向的地址");
Console.WriteLine("我在调用函数里对p1交换地址后年龄为" + p1.getAge());
Console.WriteLine("我在调用函数里对p2交换地址后年龄为" + p2.getAge());
Console.WriteLine("我在调用函数里修改p3年龄为" + p3.getAge()); }
}
}
结果:
请注意在C#中如果我们要实现引用传递,请加上关键字ref,否则,它执行的原理仍然与我们java中执行的机制一样,即拷贝一份地址给形参。
如果你还有点晕,不妨我们来看看下面两张图。


为了方便大家理解把图画成这样,然后关于java的值传递深度分析就到这里。欢迎大家一起讨论。(可以打脸/哈哈)
关于Java 值传递深度分析的更多相关文章
- JAVA CAS原理深度分析 volatile,偏向锁,轻量级锁
JAVA CAS原理深度分析 http://blog.csdn.net/hsuxu/article/details/9467651 偏向锁,轻量级锁 https://blog.csdn.net/zqz ...
- String 不变性以及 Java 值传递和引用传递
String 不变性以及 Java 值传递和引用传递 public class Example { String str = new String("good"); char[] ...
- 老生常谈--Java值传递和引用传递
起因 前两天面试被问到了这个问题,虽然之前老早就了解过这个问题,但是并没有深入了解,所以面试的时候一下子慌了,菜是原罪,今天菜鸡来补补基础知识. 其实这个问题一直是被讨论的,常见的三种说法就是,1,J ...
- Java值传递和引用传递详细解说
前天在做系统的时候被Java中参数传递问题卡了一下,回头查阅了相关的资料,对参数传递问题有了新的了解和掌握,但是有个问题感觉还是很模糊,就是 Java中到底是否只存在值传递,因为在查阅资料时,经常看到 ...
- 关于Java函数传参以及参数在函数内部改变的问题——JAVA值传递与引用最浅显的说明!
看了很多关于阐述JAVA传参到底是值传递还是引用的问题,有些说得很肤浅让人感觉似懂非懂的感觉,但是好像又能解决一些问题,然后就止步了.还有一些则是,讲得很深奥,看着好像很有道理的样子,但是其实还是没怎 ...
- 理解java值传递与引用传递
1.基本类型和引用类型在内存中的保存 Java中数据类型分为两大类,基本类型和对象类型.相应的,变量也有两种类型:基本类型和引用类型.基本类型的变量保存原始值,即它代表的值就是数值本身:而引用类型的变 ...
- 06-02 Java值传递、数据加密
值传递: /* 思考题1:看程序写结果,然后分析为什么是这个样子的.并画图讲解.最后总结Java中参数传递规律. Java中的参数传递问题: 基本类型:形式参数的改变对实际参数没有影响.基本类型传递的 ...
- JAVA 值传递
Java里方法的参数传递方式只有一种:值传递 值传递:当系统开始执行方法时,系统为形参执行初始化,就是把实参变量的值赋给方法的形参变量,方法的操作的并不是实际的实参变量 引用型变量:系统复制的是变量, ...
- java 值传递和引用传递。
java中 基本类型的参数传递是值传递,即前后两个方法的变量不相干,被调方法参数值改变不影响调用方法的传参数值. 引用数据类型的参数传递是 传递的是参数的地址.即被调方法修改参数值会,调用方法里会跟着 ...
随机推荐
- 自定义的Spring Boot starter如何设置自动配置注解
本文首发于个人网站: 在Spring Boot实战之定制自己的starter一文最后提到,触发Spring Boot的配置过程有两种方法: spring.factories:由Spring Boot触 ...
- idea的各种牛逼插件
Translation : 一款免费的解决对话框的翻译插件 MyBatisCodeHelper-Pro: 功能类似mybatis plugin,我的文章有破解的教程 地址:破解
- css过渡transition属性
一.CSS3 过渡 (一).CSS3过渡简介 CSS3过渡是元素从一种样式逐渐改变为另一种的效果. 实现过渡效果的两个要件: 规定把效果添加到哪个 CSS 属性上 规定效果的时长 定义动画的规则 过渡 ...
- mixin 扩展类的装饰
1,步骤: 1,自定义装饰器 # 我们自定义的装饰器: def my_decorator_1(func): def wrapper(request, *args, **kwargs): print(' ...
- 一个基于Net Core3.0的WPF框架Hello World实例
目录 一个基于Net Core3.0的WPF框架Hello World实例 1.创建WPF解决方案 1.1 创建Net Core版本的WPF工程 1.2 指定项目名称,路径,解决方案名称 2. 依赖库 ...
- [Nginx]Ubuntu18.04 安装配置Nginx1.13.6+FastDFS
一. 安装fastdfs依赖包 1. 解压缩libfastcommon-master.zip 2. 进入到libfastcommon-master的目录中 3. 执行 ./make.sh 4. 执行 ...
- My Eclipse 配置
设置编码格式 点击Windows → preference → WorkSpace 设置编码格式 点击Windows → preference → General →Appearence → Colo ...
- CSPS模拟 51
蒟蒻由于仍然苟活在$1jf$,不得不接受省选题的吊打$QWQ$ 蒟蒻由于拿了大神们不屑打的弱智暴力,而大神们$T3$的各种快速变换没调出来,所以拿到辽人生第一个$1jf$黄名 既侥幸又$kx$ T1 ...
- 五角场之殇。曾与张江、漕河泾、紫竹齐名。如今,上海四大IT科技园是否还在?
五角场.张江.漕河泾.紫竹并称为上海四大 IT 科技园.张江与漕河泾有着最多的国内互联网公司,以及部分的外企.随着国内互联网公司的崛起,张江与漕河泾名声远扬,不仅在上海IT圈人尽皆知,在全国范围也是小 ...
- NOIP模拟测试2-5
该补一下以前挖的坑了 先总结一下 第二次 T1 搜索+剪枝 #include<cstdio> #include<iostream> #define ll long long u ...