###JAVA中的包装类型传参(Integer,String等对基本数据类型包装类),本质还是等同于值传递(即传递参数副本)。引用类型中传参除去JAVA基本类型的包装类。  String为例,Stringbuffer和StringBuild就符合引用传参特征!###

 

更新观点: 本身还是String传参是地址传递,由于string类型final类型不可改变,所以赋值为新的字符串时,改变引用地址而已。


public class StringAsParamOfMethodDemo { 

    

        public static void main(String[] args) { 

             StringAsParamOfMethodDemo StringAsParamOfMethodDemo =    

                     new StringAsParamOfMethodDemo(); 

             StringAsParamOfMethodDemo.testA(); 

        } 

    

        private void testA() { 

             String originalStr = "original"; 

             System.out.println("Test A Begin:"); 

             System.out.println("The outer String: " + originalStr); 

             simpleChangeString(originalStr); 

             System.out.println("The outer String after inner change: " + originalStr); 

             System.out.println("Test A End."); 

             System.out.println(); 

        } 

    

        public void simpleChangeString(String original) { 

             original = original + " is changed!"; 

             System.out.println("The changed inner String: " + original); 

        } 

    

}

这段代码的逻辑是这样的:先赋值一个String类型的局部变量,然后把这个变量作为参数送进一个方法中,在这个方法中改变该变量的值。编译运行之后,发现输出结果是这样的:

 

Test A Begin:

The outer String: original

The changed inner String: original is changed!

The outer String after inner change: original

Test A End.

 

这个结果表明在方法内部对String类型的变量的重新赋值操作并没有对这个变量的原型产生任何影响。好了,这个示例的逻辑和运行结果都展示清楚了,接下来我们来对这个小程序进行分析。在这之前我们先来回顾下Java中所谓的“传值”和“传引用”问题。

 

二、         Java中的“传值”和“传引用”问题

许多初学Java的程序员都在这个问题上有所思索,那是因为这是所谓的“C语言的传值和传指针问题”在Java语言上同类表现。

最后得出的结论是:

在Java中,当基本类型作为参数传入方法时,无论该参数在方法内怎样被改变,外部的变量原型总是不变的,代码类似上面的示例:

int number = 0; 

changeNumber(number) {number++}; //改变送进的int变量 

System.out.println(number); //这时number依然为0
StringBuffer strBuf = new StringBuffer(“original”); 

changeStringBuffer(strBuf) {strbuf.apend(“ is changed!”)} //改变送进的StringBuffer变量 

System.out.println(strBuf); //这时strBuf的值就变为了original is changed!

这种特性就叫做“引用传递”,也叫做传址,即方法操作参数变量时是拷贝了变量的引用,而后通过引用找到变量(在这里是对象)的真正地址,并对其进行操作。当该方法结束后,方法内部的那个参数变量随之消失。但是要知道这个变量只是对象的一个引用而已,它只是指向了对象所在的真实地址,而非对象本身,所以它的消失并不会带来什么负面影响。回头来看原型变量,原型变量本质上也是那个对象的一个引用(和参数变量是一样一样的),当初对参数变量所指对象的改变就根本就是对原型变量所指对象的改变。所以原型变量所代表的对象就这样被改变了,而且这种改变被保存了下来。

 

         了解了这个经典问题,很多细心的读者肯定会立刻提出新的疑问:“可是String类型在Java语言中属于基本类型啊!它在方法中的改变为什么没有被保存下来呢!”的确,这是个问题,而且这个新疑问几乎推翻了那个经典问题的全部结论。真是这样么?好,现在我们就来继续分析。

 

三、    关于String参数传递问题的曲解之一——直接赋值与对象赋值

String类型的变量作为参数时怎么会像基本类型变量那样以传值方式传递呢?关于这个问题,有些朋友给出过解释,但可惜并不正确。

一种解释就是,对String类型的变量赋值时并没有new出对象,而是直接用字符串赋值,所以Java就把这个String类型的变量当作基本类型看待了。即,应该String str = new String(“original”);,而不是String str = “original”;。这是问题所在么?我们来为先前的示例稍微改造下,运行之后看看结果就知道了。改造后的代码如下:

private void testB() { 

             String originalStr = new String("original"); 

             System.out.println("Test B Begin:"); 

             System.out.println("The outer String: " + originalStr); 

             changeNewString(originalStr); 

             System.out.println("The outer String after inner change: " + originalStr); 

             System.out.println("Test B End:"); 

             System.out.println(); 

             } 

    

        public void changeNewString(String original) { 

             original = new String(original + " is changed!"); 

             System.out.println("The changed inner String: " + original); 

             }

我们来看看这次运行结果是怎么样的:

 

Test B Begin:
The outer String: original
The changed inner String: original is changed!
The outer String after inner change: original
Test B End.

 

实践证明,这种说法是错的。

实际上,字符串直接赋值和用new出的对象赋值的区别仅仅在于存储方式不同。

简单说明下:

字符串直接赋值时,String类型的变量所引用的值是存储在类的常量池中的。因为”original”本身是个字符串常量,另一方面String是个不可变类型,所以这个String类型的变量相当于是对一个常量的引用。这种情况下,变量的内存空间大小是在编译期就已经确定的。

而new对象的方式是将”original”存储到String对象的内存空间中,而这个存储动作是在运行期进行的。在这种情况下,Java并不是把”original”这个字符串当作常量对待的,因为这时它是作为创建String对象的参数出现的。

所以对String的赋值方式和其参数传值问题并没有直接联系。总之,这种解释并不是正解。