Java中String类型的参数传递问题的解析

一、引入示例

public class StringAsParamOfMethodDemo {

	public static void main(String[] args) {
		StringAsParamOfMethodDemo sapm = new StringAsParamOfMethodDemo();
		sapm.testA();
		} 

	private void testA() {
		String originalStr = "original";
		System.out.println("=======Test A Begin:=======");
		System.out.println("1.The outer String:\n" + originalStr);
		simpleChangeString(originalStr);
	    System.out.println("3.The outer String after inner change:\n" + originalStr);
	    System.out.println("=======Test A End.=======");
	    System.out.println();
	    } 

	public void simpleChangeString(String original){
		original = original + " is changed!";
		System.out.println("2.The changed inner String:\n" + original);
	}

这段代码的逻辑是这样的:先赋值一个String类型的局部变量,然后把这个变量作为参数送进一个方法中,在这个方法中改变该变量的值。

编译运行之后,发现输出结果是这样的:

=======Test A Begin:=======

1.The outer String:

original

2.The changed inner String:

original is changed!

3.The outer String after inner change:

original

=======Test A End.=======

结果表明在方法内部对String类型的变量的重新赋值操作并没有对这个变量的原型产生任何影响。

好了,这个示例的逻辑和运行结果都展示清楚了,接下来我们来对这个小程序进行分析。在这之前我们先来回顾下Java中所谓的“传值”和“传引用”问题。

二、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("1.The outer String: " + originalStr);
		    changeNewString(originalStr);
		    System.out.println("3.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("2.The changed inner String: " + original);
    }

运行结果是:

======= Test B Begin =======

1.The outer String: original

2.The changed inner String: original is changed!

3.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 testB() {
		    String originalStr = new String("original");
		    System.out.println("======= Test B Begin =======");
		    System.out.println("1.The outer String: " + originalStr);
		    changeNewString(originalStr);
		    System.out.println("3.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("2.The changed inner String: " + original);
    }

结果如下:

=========Test C Begin=========

1.The outer String: original

2.The changed inner String: original is changed!

3.The outer String after inner change: original

=========Test C End=========

这证明了问题并不是出在这,又一个解释在实践论据下夭折了。

那到底是什么原因导致了这种状况呢?

  五、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在这种情况下的表现是相同的。

Integer例子如下:

private void testD() {
        Integer originalInt = new Integer(123);
        System.out.println("=========Test D Begin=========");
        System.out.println("1.The outer Integer: " + originalInt);
        changeIntWithMethod(originalInt);
        System.out.println("3.The outer Integer after inner change: " + originalInt);
        System.out.println("=========Test D End=========");
        System.out.println();
      }

    private static void changeIntWithMethod(Integer original) {
	    original += 456;
	    System.out.println("2.The changed inner Integer: " + original);
    }

结果为:

=========Test D Begin=========

1.The outer Integer: 123

2.The changed inner Integer: 579

3.The outer Integer after inner change: 123

=========Test D End=========

时间: 2024-08-05 18:10:51

Java中String类型的参数传递问题的解析的相关文章

(转)Java中String类型的参数传递问题

这篇文章主要介绍了简单谈谈Java中String类型的参数传递问题的相关资料,需要的朋友可以参考下 提要:本文从实现原理的角度上阐述和剖析了:在Java语言中,以 String 作为类型的变量在作为方法参数时所表现出的“非对象”的特性. 一.最开始的示例 写代码最重要的就是实践,不经过反复试验而得出的说辞只能说是凭空遐想罢了.所以,在本文中首先以一个简单示例来抛出核心话题: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public clas

Java中String类型详解

这篇博客是我一直想总结的,这两天一直比较忙,先上传下照片吧,过后有时间再弄成正常的. 本文主要是对Java中String类型的总结,包括其在JVM中是怎么存储的...

java中String类型转换方法

integer to String : int i = 42;String str = Integer.toString(i);orString str = "" + idouble to String :String str = Double.toString(i);long to String :String str = Long.toString(l);float to String :String str = Float.toString(f);String to intege

Java中String类型细节

Java中String类型细节 一 . String两种初始化方式 1 . String str1= "abc";//String类特有的创建字符对象的方式,更高效 在字符串缓冲区中检测"abc"是否存在 若存在则不重复创建,将地址赋值给str1. 若不存在,则在字符串缓冲区中创建对象并赋地址给str1. 2 . String str1= new String( "abc"); //构造函数初始化  或者  char [] ch={'a','b'

java中String类型

String类型是字符串类型.. 字符串一旦创建不可以在改变."abc"字符串对象一旦创建,不可以再改成"abcd" 提升字符串的访问效率:在程序中使用了"缓存"技术.所以在java中所有使用"双引号"括起来的字符串都会在"字符串常量池"中创建一份.字符串常量池在方法区中被存储. 在程序执行过程中,如果用到某个字符串,例如:"abc"那么程序救护在字符串常量池中去搜索该字符串,如果没有找

Java中String类型的数据比较

在Java中如果想比较两个字符串是否相等,可以使用string1==string2 或string1.equal(string2)来比较. 但是,第一种方法过于局限.例如, String string1="string": String string2="string": 此时,string1==string2的返回值为True. 但是,如果 String string1=new String("string"): String string2=

关于Java中String类型的问题

简单来说,"=="是用来检测俩引用是不是指向内存中的同一个对象,而equals()方法则检测的是两个对象的值是否相等.只要你项检测俩字符串是不是相等的,你就必须得用equals()方法. 如果你知道"字符串保留(string intern)"的概念那就更好了. 2. 为什么安全敏感的字符串信息用char[]会比String对象更好? String对象是不可变的就意味着直到垃圾回收器过来清扫之前它们都不会发生变化的.用数组的话,就可以很明确的修改它任何位置的字符元素.

Java中String类型的不可变性和驻留池

一 基本概念 可变类和不可变类(Mutable and Immutable Objects)的初步定义: 可变类:当获得这个类的一个实例引用时,可以改变这个实例的内容. 不可变类:不可变类的实例一但创建,其内在成员变量的值就不能被修改.其中String类就是不可变类的经典应用. 二 例子 package cn.xy.test; public class StringTest{ /**  * a的值在编译时就被确定下来,故其值"xy"被放入String的驻留池(驻留池在堆中)并被a指向.

好程序员Java教程分享Java中String类型的10个问题

如何比较两个字符串?用"="还是equals简单来说,"=="是用来检测俩引用是不是指向内存中的同一个对象,而equals()方法则检测的是两个对象的值是否相等.只要你想检测俩字符串是不是相等的,你就必须得用equals()方法. 如果你知道"字符串保留(string intern)"的概念那就更好了. 为什么安全敏感的字符串信息用char[]会比String对象更好?String对象是不可变的就意味着直到垃圾回收器过来清扫之前它们都不会发生变化