String作为方法参数传递 与 引用传递

String作为方法参数传递

String 和 StringBuffer的区别见这里:

http://wenku.baidu.com/view/bb670f2abd64783e09122bcd.html

一、 最开始的示例
写代码最重要的就是实践,不经过反复试验而得出的说辞只能说是凭空遐想罢了。所以,在本文中首先以一个简单示例来抛出核心话题:

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的赋值方式和其参数传值问题并没有直接联系。总之,这种解释并不是正解。
四、
关于String参数传递问题的曲解之二——“=”变值与方法变值
又有些朋友认为,变值不同步的问题是处在改变值的方式上。
这 种说法认为:“在Java
中,改变参数的值有两种情况,第一种,使用赋值号“=”直接进行赋值使其改变;第二种,对于某些对象的引用,通过一定途径对其成员数据进行改变,如通过对
象的本身的方法。对于第一种情况,其改变不会影响到被传入该参数变量的方法以外的数据,或者直接说源数据。而第二种方法,则相反,会影响到源数据——因为
引用指示的对象没有变,对其成员数据进行改变则实质上是改变的该对象。”
这种方式听起来似乎有些…,我们还是用老办法,编写demo,做个小试验,代码如下:

private
void testC() {
String originalStr = new
String("original");
System.out.println("Test C
Begin:");
System.out.println("The outer String: " +
originalStr);
changeStrWithMethod(originalStr);
System.out.println("The
outer String after inner change: " + originalStr);
System.out.println("Test C
End.");
System.out.println();
}

private static void
changeStrWithMethod(String original) {
original = original.concat(" is
changed!");
System.out.println("The changed inner String: " +
original);
}

结果如下:

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

怎么样,这证明了问题并不是出在这,又一个解释在实践论据下夭折了。
那到底是什么原因导致了这种状况呢?
好了,不卖关子了,下面说下我的解释。

五、
String参数传递问题的症结所在
其实,要想真正理解一个类或者一个API/框架的最直接的方法就是看源码。
下面我们来看看new出String对象的那小段代码(String类中),也就是String类的构造函数:

public
String(String original) {
int size = original.count;
char[] originalValue
= original.value;
char[] v;
if (originalValue.length > size) {
//
The array representing the String is bigger than the new
// String itself.
Perhaps this constructor is being called
// in order to trim the baggage, so
make a copy of the array.
int off = original.offset;
v =
Arrays.copyOfRange(originalValue, off, off+size);
} else {
// The array
representing the String is the same
// size as the String, so no point in
making a copy.
v = originalValue;
}
this.offset = 0;
this.count =
size;
this.value = v;
}


许你注意到了里面的char[],这说明对String的存储实际上通过char[]来实现的。怎么样?其实就是一层窗户纸。不知道大家还记不记得在 Java
API中定义的那些基本类型的包装类。比如Integer是int包装类、Float是float的包装类等等。对这些包装类的值操作实际上都是通过对其
对应的基本类型操作而实现的。是不是有所感悟了?对,String就相当于是char[]的包装类。包装类的特质之一就是在对其值进行操作时会体现出其对
应的基本类型的性质。在参数传递时,包装类就是如此体现的。所以,对于String在这种情况下的展现结果的解释就自然而然得出了。同样
的,Integer、Float等这些包装类和String在这种情况下的表现是相同的,具体的分析在这里就省略了,有兴趣的朋友可以自己做做试验。
这也就是为什么若组串操作是通过不同方法来实现的时候,推荐大家使用StringBuffer的真正原因了。至于StringBuffer为什么不会表现出String这种现象,大家再看看的StringBuffer的实现就会明白了,在此也不再赘述了。
六、
写在最后
由此String类型的参数传递问题的原理也就展现出来了。其实可以看出,只要分析方式正确,思考终究得出正确结论的。
正确分析方法的基础有二:
1、
多实践:手千万不要犯懒,实践必会出真知。
2、
基于原理:搞清楚程序逻辑的最直接最简单的方式就是看源码,这毋庸置疑。
只要基于这两个基础进行分析,在很多情况下会达到事半功倍的效果。这算是经验之谈吧,也算是分析程序的“捷径”方式之一。

时间: 2025-01-12 03:21:32

String作为方法参数传递 与 引用传递的相关文章

Python参数传递(引用传递和值传递)

参考:https://blog.csdn.net/icessunt/article/details/80919188 python中的函数参数既支持按值调用,也支持按引用调用. python中的变量是对象引用: 变量存储的值是内存地址.当函数被调用的时候,解释器会查看传入的变量(内存地址)指的那个指的类型,如果是一个可变类型的值,就按照引用传递变量:如果是一个非可变类型的值,就考虑按照值传递变量. 可变类型:字典dict,列表list,集合set 不可变类型:字符串srt,整数int,元组tup

一道 Java 方法传值面试题——Java方法传值的值传递概念和效果 + Integer 缓存机制 + 反射修改 private final 域

原题代码如下: 1 public void test1() { 2 int a = 1, b = 2; 3 System.out.println("before: a=" + a + ", b=" + b); 4 swap1(a, b); 5 System.out.println("after: a=" + a + ", b=" + b); 6 } 7 8 private void swap1(int i1, int i2)

Java基础_0310:引用传递

引用传递 引用传递是Java之中最让初学者费解的概念,而在实际的开发之中,引用传递又有着非常重要的作用: 引用传递的核心在于同一块堆内存空间被不同的栈内存所指向: 范例:第一道引用传递范例 class Message { private int num = 10; // 定义int基本类型的属性 /** * 本类没有提供无参构造方法,而是提供有参构造,可以接收num属性的内容 * @param num 接收num属性的内容 */ public Message(int num) { this.nu

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

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

由String作为方法参数,引起的值传递,引用传递,及StringBuffer

原文引用: http://www.cnblogs.com/zuoxiaolong/p/lang1.html http://www.cnblogs.com/clara/archive/2011/09/17/2179493.html http://xueliang1yi.blog.163.com/blog/static/11455701620121140330271/ 一. 最开始的示例写代码最重要的就是实践,不经过反复试验而得出的说辞只能说是凭空遐想罢了.所以,在本文中首先以一个简单示例来抛出核心

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

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

Java中String是不是引用传递?

本文转自:http://www.jcodecraeer.com/a/chengxusheji/java/2012/0805/340.html 编者语:书上都说string是引用类型,但事实上我所看到的string和所谓的值类型没有什么区别,但通过看以下的文章,明白了: 1.string a="abc";之后,如果a="xy",则是a并没有改变内存中已经存在的"abc",而是又创建了另外一个实例.实际上相当于:string a=new String

Java String 值传递 VS 引用传递

在Java代码中,如果方法参数中有String类型,那么这个参数是值传递还是方法传递呢? 基本类型容易理解,对象类型也容易理解,可偏偏String这个对象类型让人有些疑惑.别着急,看完你就清晰了. 其实,我们大可不必纠结于文字游戏,在Java中宣称只有值传递,而在C++程序员来看要区分为值传递和引用/指针传递,实质上是一致的. 1. 对于基本类型,如int.char,大家都能理解,属于值传递,一个方法中对于基本类型的修改,不会反馈到方法调用者. public static void main(S

JAVA随笔篇二(深入分析JAVA简单类型、String和对象的值传递和引用传递)

关于JAVA的值传递和引用传递,翻看了很多资料和博客,感觉大多数讲的很乱,都是自己明白了之后就不讲了的样子,终于算是比较理解这几个概念了,下面做一个总结. 1.简单类型的参数传递 Java方法的参数是简单类型的时候,是按值传递的 (pass by value).下面举一个经典的swap函数: 无法交换值的方法: package TestTransferPack; public class TestTransfer { public static void main(String[] args)