文档章节

关于Java 值传递深度分析

o
 osc_gu9d45li
发布于 2019/04/07 14:34
字数 1939
阅读 7
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

首先说观点: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 字符串;
  
  public class person {
   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的值传递深度分析就到这里。欢迎大家一起讨论。(可以打脸/哈哈)

 

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
Flappy Bird(安卓版)逆向分析(一)

更改每过一关的增长分数 反编译的步骤就不介绍了,我们直接来看反编译得到的文件夹 方法1:在smali目录下,我们看到org/andengine/,可以知晓游戏是由andengine引擎开发的。打开/res/raw/at...

enimey
2014/03/04
5.9K
18
我的架构演化笔记 功能1: 基本的用户注册

“咚咚”,一阵急促的敲门声, 我从睡梦中惊醒,我靠,这才几点,谁这么早, 开门一看,原来我的小表弟放暑假了,来南京玩,顺便说跟我后面学习一个网站是怎么做出来的。 于是有了下面的一段...

强子哥哥
2014/05/31
976
3
Nutch学习笔记4-Nutch 1.7 的 索引篇 ElasticSearch

上一篇讲解了爬取和分析的流程,很重要的收获就是: 解析过程中,会根据页面的ContentType获得一系列的注册解析器, 依次调用每个解析器,当其中一个解析成功后就返回,否则继续执行下一个解...

强子哥哥
2014/06/26
712
0
程序猿媛一:Android滑动翻页+区域点击事件

滑动翻页+区域点击事件 ViewPager+GrideView 声明:博文为原创,文章内容为,效果展示,思路阐述,及代码片段。文尾附注源码获取途径。 转载请保留原文出处“http://my.oschina.net/gluoyer...

花佟林雨月
2013/11/09
4.1K
1

没有更多内容

加载失败,请刷新页面

加载更多

Vim清除最后一个搜索突出显示 - Vim clear last search highlighting

问题: Want to improve this post? 想要改善这篇文章吗? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. 提供此问题......

技术盛宴
今天
23
0
原子属性和非原子属性有什么区别? - What's the difference between the atomic and nonatomic attributes?

问题: What do atomic and nonatomic mean in property declarations? 属性声明中atomic和nonatomic是什么意思? @property(nonatomic, retain) UITextField *userName;@property(atomic, ......

fyin1314
今天
7
0
马化腾每天刷 Leetcode?代码你打算写到几岁?

本文作者:o****0 前几天,一张未证真伪的截图流传,图中显示马化腾几乎每天都会在 Leetcode 上提交代码。 截图还贴出一个 Leetcode 账户地址。该地址的头像已从马化腾的照片换成腾讯 logo,...

百度开发者中心
前天
13
0
滴滴 3000+ Kylin Cube 背后的实践经验揭秘

本次分享主要有三个部分:Kylin 在滴滴的整体应用、架构的实践经验、滴滴全局字典最新版本的实现以及 Kylin 最新实时 OLAP 探索经验分享。 Kylin 在滴滴的应用&架构 Kylin 在滴滴的三类应用场...

浪尖聊大数据
昨天
9
0
ssh“权限太开放”错误 - ssh “permissions are too open” error

问题: I had a problem with my mac where I couldn't save any kind of file on the disk anymore. 我的Mac出现问题,无法再在磁盘上保存任何类型的文件。 I had to reboot OSX lion and r......

javail
今天
19
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部