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 字符串;
  

  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的值传递深度分析就到这里。欢迎大家一起讨论。(可以打脸/哈哈)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

(0)

相关推荐

  • 详解Java引用类型的参数也是值传递

    简述 调用方法的时候,有需要传参数的情况.在Java中,参数的类型有基本类型和引用类型两种. 一开始听到一个说法,Java没有引用传递,但是一直没有太多的思考在上面,直到前不久玩数组的时候,突然间发现把数组引用变量作为参数传递到一个方法当中进行操作之后,再去访问原数组,尽然改变了.于是乎,就想到了之前在C++里面学过的引用传递,突然有一种错愕的感觉,就查了一些资料,探究当Java引用类型变量作为参数传递给方法的时候,到底是值传递还是引用传递. 结论:如果将Java引用类型变量作为参数传递给方法,

  • Java中值传递和引用传递的区别

    在Java中参数的传递主要有两种:值传递和参数传递: 下面是对两种传递方式在内存上的分析: 一:值传递 解释:实参传递给形参的是值  形参和实参在内存上是两个独立的变量 对形参做任何修改不会影响实参 代码示例如下: package arrayDemo; public class Demo1 { public static void main(String[] args) { int b =20; change(b);// 实参 实际上的参数 System.out.println(b); } pu

  • 深入了解为什么Java中只有值传递?

    1.为什么 Java 中只有值传递? 首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语.按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址.一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值. 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式. Java程序设计语言总是采用按值调用.也就是说,方法得到的是所有参数值的一个拷贝,也就

  • Java中值传递的深度分析

    前言 首先说观点:java只有值传递没有引用传递 然后再来看看值传递与引用传递两者的定义 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数. 引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数. 这里牢记值传递中将实际参数复制一份. 然后就是对于参数类型:值类型 和 引用类型. 结合起来理解就是:值类型传递,java

  • Java中对HashMap的深度分析

    在Java的世界里,无论类还是各种数据,其结构的处理是整个程序的逻辑以及性能的关键.由于本人接触了一个有关性能与逻辑同时并存的问题,于是就开始研究这方面的问题.找遍了大大小小的论坛,也把<Java 虚拟机规范>,<apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector>,和<Thinking in Java>翻了也找不到很好的答案,于是一气之下把JDK的 src 解压出来研究,扩然开朗,遂写此文,跟大家分享感

  • 详解java的值传递、地址传递、引用传递

    详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前的理解,java中基本数据类型是值传递,对象是地址(引用)传递.给大家看个例子: public class ObjectTrans { public static void main(String[] args) { String name = "123"; SChange(name);

  • java 中volatile和lock原理分析

    java 中volatile和lock原理分析 volatile和lock是Java中用于线程协同同步的两种机制. Volatile volatile是Java中的一个关键字,它的作用有 保证变量的可见性 防止重排序 保证64位变量(long,double)的原子性读写 volatile在Java语言规范中规定的是 The Java programming language allows threads to access shared variables (§17.1). As a rule,

  • JAVA中值类型和引用类型的区别

     1. Java中值类型和引用类型的不同? [定义] 引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值, 那么调用这个方法是传入的变量的值也将改变.值类型表示复制一个当前变量传给方法, 当你在这个方法中改变这个变量的值时,最初生命的变量的值不会变.通俗说法: 值类型就是现金,要用直接用:引用类型是存折,要用还得先去银行取现.----(摘自网上) [值类型] 也就是基本数据类型 基本数据类型常被称为四类八种 四类:  1,整型 2,浮点型 3

  • Java中值类型和引用类型详解

    我们都知道java是一种面向对象的编程语言,但是在实际意义上java并不是纯面向对象,因为面向对象的意义就是万物皆对象,那么如果说int类型的变量也是一个对象的话,那么我们应该能用"."这个东西访问到它的属性或者是方法,例如: import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { List<Integer>

  • java 中Buffer源码的分析

    java 中Buffer源码的分析 Buffer Buffer的类图如下: 除了Boolean,其他基本数据类型都有对应的Buffer,但是只有ByteBuffer才能和Channel交互.只有ByteBuffer才能产生Direct的buffer,其他数据类型的Buffer只能产生Heap类型的Buffer.ByteBuffer可以产生其他数据类型的视图Buffer,如果ByteBuffer本身是Direct的,则产生的各视图Buffer也是Direct的. Direct和Heap类型Buff

  • C语言中 值传递和指针传递实例详解

    C语言中 值传递和指针传递实例详解 在C语言中,函数的参数和返回值的传递方式有两种:值传递和指针传递. 值传递和指针传递初学者总会有一种朦胧的感觉,所以建议把指针传递的概念摸透,才能熟练应用. 值传递示例:x其实是n的一份临时拷贝,所以并不会改变n的值. #include <stdio.h> #include <windows.h> void Fun(int x) { x = 1; } int main() { int n = 2; Fun(n); printf("%d\

  • java 中RandomAccess接口源码分析

    java 中RandomAccess接口源码分析 RandomAccess是一个接口,位于java.util包中. 这个接口的作用注释写的很清楚了: /** * Marker interface used by <tt>List</tt> implementations to indicate that * they support fast (generally constant time) random access. The primary * purpose of this

随机推荐