一、常见的错误认识

错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。
错误理解二:Java是引用传递。
错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

1.1、实参与形参是什么

形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”。

简单举个例子:

  1. public static void main(String[] args) {
  2. ParamTest pt = new ParamTest();
  3. pt.sout("Hello"); // 实际参数为 Hello
  4. }
  5. public void sout(String name) { // 形式参数为 name
  6. System.out.println(name);
  7. }

实际参数是调用有参方法的时候真正传递的内容,而形式参数是用于接收实参内容的参数。

1.2、值传递与引用传递

上面提到了,当我们调用一个有参函数的时候,会把实际参数传递给形式参数。但是,在程序语言中,这个传递过程中有两种情况,即值传递和引用传递。我们来看下程序语言中是如何定义和区分值传递和引用传递的。

  • 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
  • 引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。

Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。

二、案例

1.1、int Demo

  1. public class Main {
  2. public static void main(String[] args) {
  3. int i = 10;
  4. test(i);
  5. System.out.println(i==10); // true
  6. }
  7. static void test(int i) {
  8. i = 20;
  9. }
  10. }

1.2、String Demo

没有提供改变自身方法的引用类型

  1. public class Main {
  2. public static void main(String[] args) {
  3. String str = "hello";
  4. test(str);
  5. System.out.println(str == "hello"); // true
  6. }
  7. static void test(String str) {
  8. str = "world";
  9. }
  10. }

1.3、StringBuilder Demo

提供了改变自身方法的引用类型

  1. public class main {
  2. public static void main(String[] args) {
  3. StringBuilder sb = new StringBuilder("hello");
  4. test(sb);
  5. System.out.println(sb.toString() == "hello"); // false
  6. System.out.println(sb.toString() == "hello world"); // false
  7. System.out.println(sb.toString().equals("hello world")); // true
  8. }
  9. static void test(StringBuilder sb) {
  10. sb.append(" world");
  11. }
  12. }

1.4、StringBuilder Demo

提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符。

  1. public class main {
  2. public static void main(String[] args) {
  3. StringBuilder sb = new StringBuilder("hello");
  4. test(sb);
  5. System.out.println(sb.toString() == "hello"); // false
  6. System.out.println(sb.toString() == "world"); // false
  7. System.out.println(sb.toString().equals("hello")); // true
  8. }
  9. static void test(StringBuilder sb) {
  10. sb = new StringBuilder("world");
  11. }
  12. }

1.5、StringBuilder Demo

试图在方法内将对象赋值为 null

  1. public class Main {
  2. public static void main(String[] args) {
  3. StringBuilder sb = new StringBuilder("hello");
  4. test(sb);
  5. System.out.println(sb == null); // false
  6. System.out.println(sb.toString().equals("hello")); // true
  7. System.out.println(sb.toString() == "hello"); // false
  8. String str = new String("hello");
  9. System.out.println(str == sb.toString()); // false
  10. }
  11. static void test(StringBuilder sb) {
  12. sb = null;
  13. }
  14. }

1.6、StringBuilder Demo

试图在方法内使用赋值运算符初始化对象

public class Main {
    public static void main(String[] args) {
        StringBuilder sb = null;
        test(sb);

        System.out.println(sb == null); // true
    }

    static void test(StringBuilder sb) {
        sb = new StringBuilder("hello");
    }
}

二、总结

Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。

值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。

REF

https://www.zhihu.com/question/31203609