老生常谈--Java值传递和引用传递

起因

前两天面试被问到了这个问题,虽然之前老早就了解过这个问题,但是并没有深入了解,所以面试的时候一下子慌了,菜是原罪,今天菜鸡来补补基础知识。

其实这个问题一直是被讨论的,常见的三种说法就是,1,Java 值传递引用传递都有,2,只有值传递,3只有引用传递,今天查了很多资料,我发现这个问题并不是随随便便就能说清楚。

先说传参

方法的参数可以分为实参和形参,实参是指被调用时传入的实际的值,在方法调用前就已经初始化完毕。而形参是指方法中用来“承接”实参的参数,它是在这个方法里有效,即作用域。方法执行结束之后即被销毁。

OK,接下来说所谓的值和引用。

Java数据类型

1,基本数据类型,这种是Java中最小粒度的数据类型,包括:byte,short,int,long,float,double,char,boolean。

2,引用类型:是编程语言中存放实际内容所在地址的一种数据类型。一般是类,接口,数组。

在这里不得不说一下内存区域划分:

在Java文件经过编译器之后,转化成为class文件,之后JVM将class文件通过类加载加载到运行时数据区来存储程序运行时需要用到的数据和相关信息。

运行时数据区

1,虚拟机栈,它是线程私有的,所以是线程隔离的,栈里面存储的是栈帧,每一个栈帧表示被调用的一个方法,方法调用的过程对应着栈帧从入栈到出栈的过程,栈帧中存储的是方法运行需要的信息,它是用来支持方法调用和方法执行的数据结构:

(1),局部变量表(方法中的局部变量,变量为基本类型时,直接存储值,为引用类型时,存储指向具体对象的引用)

(2),方法出口地址(方法执行完返回的地址)

(3),操作数栈(JVM被称为基于栈的执行引擎)

(4),指向常量池的引用

(5),一些附加信息。

2,堆,是所有线程共享的,用来存储对象和数组,在JVM中,只有一个堆。

3,方法区:是一块所有线程共享的内存逻辑区域,在JVM这个中也只有一个方法区,用来存储一些线程可共享的内容,同时它也是线程安全的。方法区存储的信息有:

类的全路径名,类的直接超类的全限定名,类的访问修饰符,类的类型,类的直接接口的全限定名的有序列表,常量池等。

4,本地方法栈:本地方法栈类似于虚拟机栈,也是线程私有,不同的是,虚拟机栈是为Java方法服务,而本地方法栈是为native方法服务。

5,程序计数器,也是线程私有,字节码解释器工作就是通过改变程序计数器里的值来选取下一条执行的字节码指令,分支循环等都需要它来实现。

数据是如何存储的?

JVM 在运行过程中,涉及内存分配有:堆,栈,静态方法区,常量区。内存分配的策略有:堆式,栈式,静态。

基本数据类型的存储:

1,基本数据类型的局部变量以及他的值都是存储在栈上,也就是虚拟机栈的栈帧中。

如:int a = 30;事实上这个过程分为两步,第一步是创建一个age变量, 第二步是先查找栈中是否有30这个值,如果有,则直接将age指向它,没有则开辟一块内存来存储30.

再将age指向它。对它再赋值也是相同的道理,如age = 50,先查找栈中是否有50,有则指向,没有则开辟一块空间存50,再将age指向它。所以足以见之,基本数据类型的赋值是不改变自身数据的,而是像上面那样先查找栈中是否存在,有则指向它,没有则开辟一块内存来装下新的值。

2,基本数据类型的成员变量

与上面的不同的是,它存放在堆里,因为它跟随着一个实例对象,跟实例对象的生命周期相同。

3,基本数据类型的静态变量,与上面都不同,静态变量和它的值都存储在运行时常量区,它是跟随类加载的,随类的消失而消失。

引用数据类型的存储:

引用数据类型分为两部分,一部分是变量,存在栈中,用来存储实际内容的地址,同时在堆中有一块内存来存实际内容。

所谓值传递和引用

值传递

在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时的形参接收到的实际上是实参的副本,在方法体内的任何操作,都是对副本的操作,对实参不影响。如:

public class Main {

    public static void main(String[] args)  {
        int a = 10;
        function(a);
        System.out.println(a);
    }
    private static void function(int a){
        a = 50;
    }
}

输出结果:10

结合之前的铺垫,首先JVM会将其中的main()压入虚拟机栈,用一个栈帧存储,即为当前栈帧,栈帧存储着方法的局部变量表,操作栈,方法出口地址等。之后执行到function方法时,JVM也创建为它创建一个
栈帧,用来存放function的相关信息,因此a的值是在function的栈帧中,而它的值是从实参复制得来的,而再方法体内部进行赋值的时候,也不会去改变main所在的栈帧中的a的值,而是在当前的栈帧中,对值进行修改。而栈帧之间是隔离的,所以不会对实参产生影响。

引用传递

引用传递和值传递的不同点在于,调用方法的时候,实参的地址通过方法传给形参,所以对实参的操作会影响本身的内容,但是但是,这里面还需要分情况。如:

public class Main {
    public static void main(String[] args)  {
        StringBuilder a = new StringBuilder("10");
        function(a);
        System.out.println(a);
    }
    private static void function(StringBuilder str){
        str.append("20");
    }
}

输出:1020

public class Main {
    public static void main(String[] args)  {
        StringBuilder a = new StringBuilder("10");
        function(a);
        System.out.println(a);
    }
    private static void function(StringBuilder str){
        str = new StringBuilder("20");
    }
}

输出:10

这是为何呢,之前有说,变量是存储在栈,实际内容存储在堆,而造成这里不同的原因就是,Java中的赋值的操作(=)都不是将自身的内容进行改变,而是先检查有没有已经存在的并且等于它的值,有则指向

它,没有则另外创建一个,再指向它,所以在第二段代码里,str=new StringBuilder("20")并没有修改原有的自身的内容而是指向了一个新对象,也就是形参的str和实参a只想了不同的地址,自然对形参没有影响。而在第一段代码中,str.append();是对指向的内存进行操作,而指向的内存也就是实参引用所指向的内存,故有影响。

所以我觉得这里可以做个总结,那就是Java之间并没有引用传递,无论基本数据类型还是引用类型,传的都是值,都是实参的副本,但是不同的是,基本数据类型是实参内容的复制品,而引用类型也是,但是赋值的内容是地址罢了,即实参和形参指向一个同一块内存(对象),形参变化是否会改变实参取决于操作是否是对指向的这块内存进行直接修改。如果是赋值操作这种,那么形参操作对实参就没有影响。

原文地址:https://www.cnblogs.com/Yintianhao/p/12335584.html

时间: 2024-10-10 13:54:33

老生常谈--Java值传递和引用传递的相关文章

Java中的值传递和引用传递

解释 1.Java中有没有引用传递? 答:Java中只有按值传递,没有按引用传递! 2.当一个对象被当作参数传递到一个方法中后,在此方法中可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递. Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是相对传址而言).如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的,如果在函数中改变了副本

java中参数传递--值传递,引用传递

java中的参数传递--值传递.引用传递 参数是按值而不是按引用传递的说明 Java 应用程序有且仅有的一种参数传递机制,即按值传递. 在 Java 应用程序中永远不会传递对象,而只传递对象引用.因此是按引用传递对象.Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数.参数可以是对象引用,而 Java 应用程序是按值传递对象引用的. Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型.当作为参数传递给一个方法时,处理这两种类型的方式是相同的.两

java中值传递和引用传递

本来今天刚学习的内容,然后去其他博客看了下,发现都吵起来了,就是名字原因,有的说java有值传递和引用传递,有的说引用传递本质就是值传递,我管你杂说的,只要自己理解好,代码知道运行结果就好了. 我用自己的话,自己的理解来解释下,反正都是自己写着玩,自己看的 值传递:传递的值,这个值以后怎么改变,源值不会发生改变的. 引用传递:将对象的引用地址传递过去,如果值发生改变,那么源值也发生改变. 代码如下: 值传递: public class Test1 { public static void mai

第002弹:Java 中的值传递和引用传递

在 Java 的代码开发过程中,为了尽可能提高方法的复用性,明确方法的作用,同时防止一个方法内部过于臃肿的问题,往往会创建许多方法,那么不可避免地会涉及到参数传递的问题.通常来说,我们将 Java 中的参数传递分为两种:值传递和引用传递. 值传递:参数在进入方法时,将入参深度复制一个副本,在方法内部操作的是入参的副本,在方法执行完毕之后,外部的入参没有发生任何变化. 引用传递:在方法内部操作的是参数本身,对入参做出的修改会保留到方法的外部. 那么在 Java 中,哪些情况属于值传递,哪些情况属于

java中方法的参数传递机制(值传递还是引用传递)

看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参数.当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本.指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的. 以下是从其他文章里转的,只为加深理解 public class TempTest { private void te

Java中,函数参数的传递,是值传递还是引用传递

当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递. 可以这么理解:有一个Person(name="S"),作为参数传给一个方法,在此方法中将此Person重新设置name=“B”,那么有一下结论 ①这个Person还是这个Person,一直都是他,但是他的名字被改了,不管是原来的引用还是方法里的引用,引用的都是一个对象,所以,所有引用的值都变了 ②如果在方法中重新new了一个Person赋给参数,

java — 值传递和引用传递

在 Java 应用程序中永远不会传递对象,而只传递对象引用.因此是按引用传递对象.Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数.参数可以是对象引用,而 Java 应用程序是按值传递对象引用的. Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型.当作为参数传递给一个方法时,处理这两种类型的方式是相同的.两种类型都是按值传递的:没有一种按引用传递. java实际上只有值传递,没有真正意义上的引用传递. 按值传递意味着当将一个参数传递给一个函

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

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

Java值传递和引用传递

1.Java到底是值传递还是引用传递呢? 对于java中的值传递和引用传递一直颇受争论,<Core Java>的作者,以及JAVA的创造者James Gosling都认为当一个对象或引用类型变量被当作参数传递时,也是值传递,这个值就是对象的引用,因此JAVA中只有值传递,没有引用传递.还有一种说法是引用可以看作是对象的别名,当对象被当作参数传递给方法时,传递的是对象的引用,因此是引用传递.而<Thinking in Java>一书的作者Bruce Eckel则站在了中立的立场上.