为什么大家都说Java中只有值传递?

最近跟Java中的值传递和引用传递杠上了,一度怀疑人生。查了很多资料,加上自己的理解,终于搞清楚了,什么是值传递和引用传递。也搞明白了,为什么大家都说Java只有值传递,没有引用传递。原来,我一直以来的认知都是错误的。。。

首先,需要了解一些概念性的东西。

形参与实参:

形参,是指在定义函数时使用的参数,目的是用于接收调用该函数时传入的参数。简单理解,就是所有函数(即方法)的参数都是形参。

实参,是指调用函数时,传递给函数的参数。

public static void main(String[] args) {
    int num = 3;
    printVal(num); //这里num是实参
}

private static void printVal(int num) {
    num = 5; //这里num就是形参
}

值传递和引用传递

值传递:是指在调用函数时,将实际参数复制一份传递给函数,这样在函数中修改参数时,不会影响到实际参数。其实,就是在说值传递时,只会改变形参,不会改变实参。

引用传递:是指在调用函数时,将实际参数的地址传递给函数,这样在函数中对参数的修改,将影响到实际参数。

这里,需要特别强调的是,千万不要以为传递的参数是值就是值传递,传递的是引用就是引用传递。也不要以为传递的参数是基本数据类型就是值传递,传递的是对象就是引用传递。 这是大错特错的。以前的我,一直都是这样认为的,现在想来真是太天真了。判断是值传递还是引用传递的标准,和传递参数的类型是没有一毛钱关系的。

下面三种情况,基本上可以涵盖所有情况的参数类型。

当传递的参数是基本数据类型时:

public class TestNum {
    public static void main(String[] args) {
        int num = 3;
        System.out.println("修改前的num值:"+num);
        changeValue(num);
        System.out.println("修改后的num值:"+num);
    }

    private static void changeValue(int num) {
        num = 5;
        System.out.println("形参num值:"+num);
    }
}

打印结果:

修改前的num值:3
形参num值:5
修改后的num值:3

可以发现,传递基本数据类型时,在函数中修改的仅仅是形参,对实参的值的没有影响。

需要明白一点,值传递不是简单的把实参传递给形参,而是,实参建立了一个副本,然后把副本传递给了形参。下面用图来说明一下参数传递的过程:

图中num是实参,然后创建了一个副本temp,把它传递个形参value,修改value值对实参num没有任何影响。

传递类型是引用类型时:

public class User {
    private int age;
    private String name;
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public User() {
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
public class TestUser {
    public static void main(String[] args) {
        User user = new User(18, "zhangsan");
        System.out.println("修改对象前:"+user);
        changeUser(user);
        System.out.println("修改对象后:"+user);
    }

    private static void changeUser(User user) {
        user.setAge(20);
        user.setName("lisi");
    }
}

打印结果:

修改对象前:User{age=18, name='zhangsan'}
修改对象后:User{age=20, name='lisi'}

可以发现,传过去的user对象,属性值被改变了。由于,user对象存放在堆里边,其引用存放在栈里边,其参数传递图如下:

user是对象的引用,为实参,然后创建一个副本temp,把它传递给形参user1。但是,他们实际操作的都是堆内存中的同一个User对象。因此,对象内容的修改也会体现到实参user上。

传递类型是String类型(Integer等基本类型的包装类等同)

public class TestStr {
    public static void main(String[] args) {
        String str = new String("zhangsan");
        System.out.println("字符串修改前:"+str);
        changeStr(str);
        System.out.println("字符串修改后:"+str);
    }

    private static void changeStr(String str) {
        str = "lisi";
    }
}

打印结果:

字符串修改前:zhangsan
字符串修改后:zhangsan

咦,看到这是不是感觉有点困惑。按照第二种情况,传递参数是引用类型时,不是可以修改对象内容吗,String也是引用类型,为什么在这又不变了呢?

再次强调一下,传递参数是引用类型,并不代表就是引用传递,其实它还是值传递。此时的 lisi 和上边的 zhangsan 根本不是同一个对象。画图理解下:

图中,str是对象 zhangsan 的引用,为实参,然后创建了一个副本temp,把它传递给了形参str1。此时,创建了一个新的对象 lisi ,形参str1指向这个对象,但是原来的实参str还是指向zhangsan。因此,形参内容的修改并不会影响到实参内容。所以,两次打印结果都是zhangsan。

第三种情况和第二种情况虽然传递的都是引用类型变量,但是处理方式却不一样。第三种情况是创建了一个新的对象,然后把形参指向新对象,而第二种情况并没有创建新对象,操作的还是同一个对象。如果把上边changeUser方法稍作改变,你就会理解:

private static void changeUser(User user) {
    //添加一行代码,创建新的User对象
    user = new User();
    user.setAge(20);
    user.setName("lisi");
}

运行以上代码,你就会惊奇的发现,最终打印修改前和修改后的内容是一模一样的。
这种情况,就等同于第三种情况。因为,这里的形参和实参引用所指向的对象是不同的对象。因此,修改形参对象内容并不会影响实参内容。

修改对象前:User{age=18, name='zhangsan'}
修改对象后:User{age=18, name='zhangsan'}

总结:

从以上三个例子中,我们就能理解了,为什么Java中只有值传递,并没有引用传递。值传递,不论传递的参数类型是值类型还是引用类型,都会在调用栈上创建一个形参的副本。不同的是,对于值类型来说,复制的就是整个原始值的复制。而对于引用类型来说,由于在调用栈中只存储对象的引用,因此复制的只是这个引用,而不是原始对象。

最后,再次强调一下,传递参数是引用类型,或者说是对象时,并不代表它就是引用传递。引用传递不是用来形容参数的类型的,不要被“引用”这个词本身迷惑了。这就如同我们生活中说的地瓜不是瓜,而是红薯一样。

  1. 参数传递时,是拷贝实参的副本,然后传递给形参。(值传递)
  2. 在函数中,只有修改了实参所指向的对象内容,才会影响到实参。以上第三种情况修改的实际上只是形参所指向的对象,因此不会影响实参。

原文地址:https://www.cnblogs.com/starry-skys/p/12168971.html

时间: 2025-01-11 11:11:03

为什么大家都说Java中只有值传递?的相关文章

Java中的值传递与“引用传递”

首先,Java没有 引用传递 这么一说. Java只有值传递,传递的都是值,基本数据类型传递的是值,引用类型传递的是地址值. 我来理一下这其中犹如米线跟米面绞在一起了,再跟粉丝混在一起的关系. 好的,我们来看第一个例子: public static void main(String[] args) { int a = 1; int b = 4; System.out.println("Before : a=" + a + "," + "b=" +

为什么说Java中只有值传递----说服自己

在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了.如果你有以下想法,那么你有必要好好阅读本文. 错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递.如果是个引用,就是引用传递. 错误理解二:Java是引用传递. 错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递. 实参与形参 我们都知道,在Java中定义方法的时候是可以定义参数的.比如Java中的main方法: public static void main(String[] ar

java中的值传递和引用传递有什么区别呀?

值传递: (形式参数类型是基本数据类型和String):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值. 引用传递: (形式参数类型是引用数据类型参数除去String):也称为传地址.方法调用时,实际参数是对象(或数组),这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法执行中形式参数

浅析java中的值传递与引用传递

public class Test { public static void main(String[] args) { String s = new String("aaa"); change(s); System.out.println(s); StringBuilder sb = new StringBuilder("111"); change(sb); System.out.println(sb); } static void change(String s

Java中的值传递

1.先比较下字符串的比较 == 代表全等于 值和地址(存放地址) 全部相等于. equals 值等于== 和 equals的区别 列如下面的 如果name1==name2是等于的 然而name1==name3 结果 不是不等于的,因为生成了新的地址 如果 name1.equals(name3) 这个比较只要求值的比较,输出结果是相等的. String name1="zhangsan"; String name2="zhangsan"; String name3=ne

一道笔试题来理顺Java中的值传递和引用传递

前段时间参加了一场面试,其中有一道引用传递的题,因为当时并没有考虑清楚所以做错了. 现在来复盘一下,题目如下: private static void change(StringBuffer str11, StringBuffer str12) { str12 = str11; str11 = new StringBuffer("new world"); str12.append("new world");} public static void main(Stri

JAVA中只有值传递

今天,我在一本面试书上看到了关于java的一个参数传递的问题: 写道 java中对象作为参数传递给一个方法,到底是值传递,还是引用传递? 我毫无疑问的回答:"引用传递!",并且还觉得自己对java的这一特性很是熟悉! 结果发现,我错了! 答案是: 值传递!Java中只有按值传递,没有按引用传递! 回家后我就迫不及待地查询了这个问题,觉得自己对java这么基础的问题都搞错实在太丢人! 综合网上的描述,我大概了解了是怎么回事,现在整理如下,如有不对之处望大神提出! 先来看一个作为程序员都熟

Java 中的值传递和参数传递

Java中没有指针,所以也没有引用传递了,仅仅有值传递不过可以通过对象的方式来实现引用传递 类似java没有多继承 但可以用多次implements 接口实现多继承的功能 值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参 数的值. 引用传递:也称为传地址.方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值. Jav

JAVA中的值传递和引用传递问题

这是个老生常谈的问题了,引起过无数争论,但可以说一直没有一个令人满意的回答. 有人总结过: 对象是按引用传递的 Java 应用程序有且仅有的一种参数传递机制,即按值传递 按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本 按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本 简单总结: 对象就是传引用(引用地址的拷贝,实质也是传值) 原始类型就是传值 String等immutable类型因为没有提供自身修改的函数,每次操作都是新生成一个