Java按值调用和按引用调用的区别

Posted SwapEnd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java按值调用和按引用调用的区别相关的知识,希望对你有一定的参考价值。

  Java中函数参数传递的方式分为按值调用和按引用调用两种。这两者之间主要区别在于原始变量或对象是否能够被修改。

  • 按值调用:当将一个基本数据类型(例如,int、char等)作为参数传给函数时,实际上传递了该变量的副本而不是真正意义上的“指针” 或 “引用”。因此,在方法内部对形参进行更改并不能影响到外面实参所代表原有存储空间内容。

  以下示例展示了通过方法尝试更改Java int型输入数值计算结果:

public class Main 
    public static void main(String[] args) 
        int number = 5;
        System.out.println("Before method call, the value of \'number\' is: " + number);
        
        changeNumber(number);
        
        System.out.println("After method call, the value of \'number\' is still unchanged. It\'s: " + number);    
    
    
    private static void changeNumber(int num)
      num *= 2; // 对num执行操作,并无法同步更新旧元素位置指向新地址信息;
      

输出如下:

Before method call, the value of \'number\' is: 5
After method call, the value of \'numberisechsunchanged.Itis:5.

  从运行结果来看,即使我们在changeNumber() 方法里做出了处理,num 的最终句柄仍然与其初始赋予权限声明那样指向同一个内存地址。原因在于:

  Java会为基本数据类型分配一块连续的物理空间来储存该变量,所以当将其传递给函数时实际上是复制了这个值并且放到另外位置中去。

  • 按引用调用 :而对数组、对象或集合等非基元素变量进行操作时我们通常能感观察指针与内容副本不易区别开,此种情况下更倾向称之为“按照引入方式”,意思就如你通过建立某些事务属相关联从而能直接地访问似得。(注意: Java 中没有真正的指针)

  以下是使用自定义类作为参数(即非固定位数整型)进行方法取反示例:

package Test;

public class Test 
  public static void main(String[] args) throws Exception       
      // 创建Number封装主体数字 
       final Number numberHolder = new Number(5);
        
        System.out.println("Before method call, the value is: " + numberHolder.getValue());
        
// 对象形式输入进changeNum() 方法先后经历:
         //number 句柄拷贝;
         //新创建MyInteger对象(heap 推断可知),初始化value属性和根据new产生返回校验号码推算出myIntHandle 地址,并覆盖控制信息部分新内存;
         //将MyInteger对象的句柄 myIntHandle 传递给形参nun,并完成数据类型一致化;
 
      changeNumber(numberHolder);
      
        System.out.println("After method call, the value has been changed to: " + numberHolder.getValue());
    
    
   private static void changeNumber(Number num)
       final int temp = -num.value;
       
// 下面操作即为对原有引用所指向地址空间进行修改:
       num.value = -temp;     
     


class Number
    int value;

    public int getValue() 
        return value;
    

    public Number(int n) 
        this.value = n;
    

输出如下:

Before method call, the value is: 5
After method call, the value has been changed to: 5

  从运行结果来看,changeNumber() 方法中虽然仅采取了简单的整型数值计算和复合赋值方式处理入层次数字体系信息,但是在这个过程中它依旧成功地改变了输入参数 numberHolder 所固定“连续物理授位”的内容。

  总之:按照调用者与被调函数声明要求不同程序允许我们以两种主流方案去实现相关预期功能,需要具体情况而分。

Java的按值传递和按引用传递解说

在网上看到的一个帖子解释Java的按值传递和按引用传递,感觉挺全面,就转过来,以供以后学习参考:


1:按值传递是什么
  指的是在方法调用时,传递的参数是按值的拷贝传递。示例如下:

public class TempTest {

  private void test1(int a){
  //做点事情
  }

  public static void main(String[] args) {

    TempTest t = new TempTest();
    int a = 3;
    t.test1(a);//这里传递的参数a就是按值传递
  }
}

  按值传递重要特点:传递的是值的拷贝,也就是说传递后就互不相关了。

  示例如下:

public class TempTest {

  private void test1(int a){

    a = 5;
    System.out.println("test1方法中的a="+a);
  }

  public static void main(String[] args) {

    TempTest t = new TempTest();
    int a = 3;
    t.test1(a);//传递后,test1方法对变量值的改变不影响这里的a
    System.out.println(”main方法中的a=”+a);

  }

}

  

 

运行结果是:

test1方法中的a=5
main方法中的a=3

 

2:按引用传递是什么
  指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。
示例如下:

public class TempTest {

  private void test1(A a){

  }

  public static void main(String[] args) {

    TempTest t = new TempTest();
    A a = new A();
    t.test1(a); //这里传递的参数a就是按引用传递

  }

}

class A{

  public int age = 0;

}

 

3:按引用传递的重要特点
  传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。
  示例如下:

public class TempTest {

  private void test1(A a){

    a.age = 20;
    System.out.println("test1方法中的age="+a.age);

}

  public static void main(String[] args) {

    TempTest t = new TempTest();
    A a = new A();
    a.age = 10;
    t.test1(a);
    System.out.println(”main方法中的age=”+a.age);

  }

}

class A{

  public int age = 0;

}

  

运行结果如下:

test1方法中的age=20
main方法中的age=20

  

4:理解按引用传递的过程——内存分配示意图
  要想正确理解按引用传递的过程,就必须学会理解内存分配的过程,内存分配示意图可以辅助我们去理解这个过程。
用上面的例子来进行分析:
(1):运行开始,运行第13行,创建了一个A的实例,内存分配示意如下:

 技术分享图片

(2):运行第14行,是修改A实例里面的age的值,运行后内存分配示意如下:

技术分享图片

(3):运行第15行,是把main方法中的变量a所引用的内存空间地址,按引用传递给test1方法中的a变量。请注意:这两个a变量是完全不同的,不要被名称相同所蒙蔽。
内存分配示意如下:

 技术分享图片

由于是按引用传递,也就是传递的是内存空间的地址,所以传递完成后形成的新的内存示意图如下: 

技术分享图片

也就是说:是两个变量都指向同一个空间。

(4):运行第5行,为test1方法中的变量a指向的A实例的age进行赋值,完成后形成的新的内存示意图如下:

技术分享图片

 

此时A实例的age值的变化是由test1方法引起的
(5):运行第6行,根据此时的内存示意图,输出test1方法中的age=20
(6):运行第16行,根据此时的内存示意图,输出main方法中的age=20


5:对上述例子的改变
  理解了上面的例子,可能有人会问,那么能不能让按照引用传递的值,相互不影响呢?就是test1方法里面的修改不影响到main方法里面呢?
  方法是在test1方法里面新new一个实例就可以了。改变成下面的例子,其中第3行为新加的:

 

public class TempTest {

  private void test1(A a){

  a = new A();//新加的一行
  a.age = 20;
  System.out.println("test1方法中的age="+a.age);

  }

  public static void main(String[] args) {

    TempTest t = new TempTest();
    A a = new A();
    a.age = 10;
    t.test1(a);
    System.out.println(”main方法中的age=”+a.age);

  }

}

class A{

  public int age = 0;

}

  

运行结果为:

test1方法中的age=20
main方法中的age=10

  

为什么这次的运行结果和前面的例子不一样呢,还是使用内存示意图来理解一下
6:再次理解按引用传递
(1):运行开始,运行第14行,创建了一个A的实例,内存分配示意如下:

 技术分享图片

(2):运行第15行,是修改A实例里面的age的值,运行后内存分配示意如下: 

技术分享图片


(3):运行第16行,是把main方法中的变量a所引用的内存空间地址,按引用传递给test1方法中的a变量。请注意:这两个a变量是完全不同的,不要被名称相同所蒙蔽。
内存分配示意如下:

技术分享图片

 

由于是按引用传递,也就是传递的是内存空间的地址,所以传递完成后形成的新的内存示意图如下: 

技术分享图片

 

也就是说:是两个变量都指向同一个空间。


(4):运行第5行,为test1方法中的变量a重新生成了新的A实例的,完成后形成的新的内存示意图如下:

技术分享图片

 


(5):运行第6行,为test1方法中的变量a指向的新的A实例的age进行赋值,完成后形成的新的内存示意图如下:

技术分享图片

注意:这个时候test1方法中的变量a的age被改变,而main方法中的是没有改变的。

(6):运行第7行,根据此时的内存示意图,输出test1方法中的age=20
(7):运行第17行,根据此时的内存示意图,输出main方法中的age=10
7:说明
(1):“在Java里面参数传递都是按值传递”这句话的意思是:按值传递是传递的值的拷贝,按引用传递其实传递的是引用的地址值,所以统称按值传递。
(2):在Java里面只有基本类型和按照下面这种定义方式的String是按值传递,其它的都是按引用传递。就是直接使用双引号定义字符串方式:String str = “Java私塾”;


原文链接:https://blog.csdn.net/zzp_403184692/article/details/8184751



























以上是关于Java按值调用和按引用调用的区别的主要内容,如果未能解决你的问题,请参考以下文章

Java的按值传递和按引用传递解说

(转)Java:按值传递和按引用传递详细解说

VB 参数传递:按值传递和按地址传递

C/C++按值传递和按地址传递

java中方法传值小知识解析

按引用传递然后复制和按值传递在功能上是不是不同?