关于Java 值传递 深度分析

简介: 关于Java 值传递 深度分析首先说观点:java只有值传递没有引用传递然后再来看看值传递与引用传递两者的定义值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

关于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的值传递深度分析就到这里。欢迎大家一起讨论。(可以打脸/哈哈)
原文地址https://www.cnblogs.com/kunming97/p/10665287.html

相关文章
|
14天前
|
安全 Java API
精通 Java 后台开发:案例分析与实践
【4月更文挑战第5天】本文旨在帮助读者掌握 Java 后台开发,通过电子商务系统案例探讨数据库设计、RESTful API、安全性和性能优化。使用 Spring 框架简化开发,Spring Security 保障安全,缓存技术提升性能。实践部分强调版本控制、单元测试、CI/CD 和代码规范的重要性,助力开发者提升技能,应对挑战。
|
14天前
|
Java 调度
Java中常见锁的分类及概念分析
Java中常见锁的分类及概念分析
19 0
|
14天前
|
Java
Java中ReentrantLock中tryLock()方法加锁分析
Java中ReentrantLock中tryLock()方法加锁分析
16 0
|
14天前
|
监控 算法 Java
【深度挖掘Java性能调优】「底层技术原理体系」深入探索Java服务器性能监控Metrics框架的实现原理分析(Gauge和Histogram篇)
【深度挖掘Java性能调优】「底层技术原理体系」深入探索Java服务器性能监控Metrics框架的实现原理分析(Gauge和Histogram篇)
42 0
|
8天前
|
监控 Java 开发者
Java一分钟之-Java性能分析与调优:JProfiler, VisualVM等工具
【5月更文挑战第21天】本文介绍了Java性能优化的两个利器——JProfiler和VisualVM。JProfiler通过CPU Profiler、内存分析器和线程视图帮助解决过度CPU使用、内存泄漏和线程阻塞问题;VisualVM则聚焦于GC行为调整和类加载优化,以减少内存压力和提高应用性能。使用这些工具进行定期性能检查,是提升Java应用效率的关键。
27 0
|
14天前
|
缓存 Java
Java中循环创建String对象的内存管理分析
Java中循环创建String对象的内存管理分析
31 2
|
5天前
|
存储 分布式计算 Java
深入探究JAVA编程语言:概念、应用与实例分析
**JAVA**是广泛应用的高级编程语言,以其易学性、跨平台能力和高效的性能著称。它采用面向对象编程,强调封装、继承和多态,且具备平台无关性、内置安全性和多线程支持。JAVA广泛应用于Web开发(如JSP、Servlet)、移动应用(Android开发)、大数据处理(Hadoop、Spark)和桌面应用。通过一个计算两数之和的简单示例,展示了JAVA的易读性和面向对象特性,帮助读者理解JAVA在实际开发中的运用。
|
5天前
|
算法 搜索推荐 Java
Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!
Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!
7 0
|
14天前
|
Java 关系型数据库 MySQL
【Java Spring开源项目】新蜂(NeeBee)商城项目运行、分析、总结
【Java Spring开源项目】新蜂(NeeBee)商城项目运行、分析、总结
167 4
|
14天前
|
Java
【Java多线程】分析线程加锁导致的死锁问题以及解决方案
【Java多线程】分析线程加锁导致的死锁问题以及解决方案
28 1