JAVA的String的传值和传地址问题

 

关于Java的String类型,可能你会碰到这种情况,将String类型的变量传到一个函数,在这个函数中修改变量的值,但是,实参的值并没有发生改变。

 

Java中String的传值/传地址问题:

例子引入:

 1 package com.cnblog.GDUTtiantian.String;

 2 

 3 /**

 4  * @author GDUTtiantian

 5  */

 6 public class JavaString {

 7 

 8     public static void change(String name){

 9         //修改name的值

10         name = "ChangedName";

11     }

12     

13     

14     public static void main(String[] args) {

15         String name = "GDUTtiantian";        

16         

17         change(name);

18         

19         System.out.println(name);

20         

21     }

22     

23 }

运行结果:

 1 GDUTtiantian 

 

为什么结果不是"ChangedName"呢?

String类的底层实现是用一个字符数组去实现的,就像Integer类,底层也是对int进行封装[装箱和拆箱]。

看String类的修饰部分(源码):

1 public final class String

2     implements java.io.Serializable, Comparable<String>, CharSequence {

3     /** The value is used for character storage. */

4     private final char value[];

注意,String类加了final关键字,所以不能被继承。

第4行是字符串底层的存储结构:字符数组。

String的内容不能被动态地修改,因为底层是字符数组实现的,数组的大小是在初始化时决定的;

如果可以修改,新的字符串长度比原来数组大,那么就会造成数组越界。

 

String和StringBuffer的比较:

 1 package com.cnblog.GDUTtiantian.String;

 2 

 3 /**

 4  * @author GDUTtiantian

 5  * 

 6  * String, StringBuffer 在传参过程中的哈希值比较

 7  */

 8 public class JavaString4 {

 9     

10     

11     public static void change(String str) {

12         System.out.println("形参的哈希值:" + str.hashCode());

13         

14         str = "newString";//修改了形参的值

15         System.out.println("修改后:" + str.hashCode());

16     }

17     

18     public static void change(StringBuffer sb) {

19         System.out.println("形参的哈希值:" + sb.hashCode());

20         sb.append("newStringBuffer");//修改了形参的值

21         System.out.println("修改后:" + sb.hashCode());

22     }

23     

24 

25     public static void main(String[] args) {

26         String str = new String("GDUTtiantian");

27         StringBuffer sb = new StringBuffer("tiantian");

28         

29         System.out.println("修改前:" + str.hashCode());

30         change(str);

31         

32         System.out.println("\n----------------------------\n");

33         

34         System.out.println("修改前:" + sb.hashCode());

35         change(sb);

36     }

37     

38 }

运行结果:

1 修改前:-501451264

2 形参的哈希值:-501451264

3 修改后:-595706415

4 

5 ----------------------------

6 

7 修改前:1036782095

8 形参的哈希值:1036782095

9 修改后:1036782095

 实参String变量传给形参,是传一个地址过去,并没有重新创建一个对象,StringBuffer变量也是这么做;

但是,在修改形参的值后,String变量的哈希值发生了改变,StringBuffer变量的哈希没有发生改变,即String变量指向了一个新建的对象。

 

看看JDK中String类的一段源码(String类的一个构造方法):

 1     /**

 2      * Allocates a new {@code String} that contains characters from a subarray

 3      * of the <a href="Character.html#unicode">Unicode code point</a> array

 4      * argument.  The {@code offset} argument is the index of the first code

 5      * point of the subarray and the {@code count} argument specifies the

 6      * length of the subarray.  The contents of the subarray are converted to

 7      * {@code char}s; subsequent modification of the {@code int} array does not

 8      * affect the newly created string.

 9      *

10      * @param  codePoints

11      *         Array that is the source of Unicode code points

12      *

13      * @param  offset

14      *         The initial offset

15      *

16      * @param  count

17      *         The length

18      *

19      * @throws  IllegalArgumentException

20      *          If any invalid Unicode code point is found in {@code

21      *          codePoints}

22      *

23      * @throws  IndexOutOfBoundsException

24      *          If the {@code offset} and {@code count} arguments index

25      *          characters outside the bounds of the {@code codePoints} array

26      *

27      * @since  1.5

28      */

29     public String(int[] codePoints, int offset, int count) {

30         if (offset < 0) {

31             throw new StringIndexOutOfBoundsException(offset);

32         }

33         if (count < 0) {

34             throw new StringIndexOutOfBoundsException(count);

35         }

36         // Note: offset or count might be near -1>>>1.

37         if (offset > codePoints.length - count) {

38             throw new StringIndexOutOfBoundsException(offset + count);

39         }

40 

41         final int end = offset + count;

42 

43         // Pass 1: Compute precise size of char[]

44         int n = count;

45         for (int i = offset; i < end; i++) {

46             int c = codePoints[i];

47             if (Character.isBmpCodePoint(c))

48                 continue;

49             else if (Character.isValidCodePoint(c))

50                 n++;

51             else throw new IllegalArgumentException(Integer.toString(c));

52         }

53 

54         // Pass 2: Allocate and fill in char[]

55         final char[] v = new char[n];

56 

57         for (int i = offset, j = 0; i < end; i++, j++) {

58             int c = codePoints[i];

59             if (Character.isBmpCodePoint(c))

60                 v[j] = (char)c;

61             else

62                 Character.toSurrogates(c, v, j++);

63         }

64 

65         this.value = v;

66     }

代码57行开始,就是对字符数组进行复制。

 

这里,用C/C++中的字符串/数组/指针的引用做比较:

 1 #include<stdio.h>

 2 

 3 //形参中数组退化为指针了

 4 //这里s是指向array数组的指针

 5 void go(char * s){

 6 

 7     s = "JavaString";//指针指向另一个空间,"JavaString"字符串的首地址

 8     printf("s:%s#\n", s);

 9 }

10 

11 //形参中数组退化为指针了

12 void change(char * s){

13 

14     s[0] = 'c';

15     s[1] = 'h';

16     s[2] = 'a';

17     s[3] = 'n';

18     s[4] = 'g';

19     s[5] = 'e';

20     s[6] = '\0';

21 }

22 

23 int main(){

24     char array[100] = "GDUTtiantian";

25 

26     go(array);

27     printf("array:%s#\n", array);

28 

29     change(array);

30     printf("array:%s#\n", array);

31 

32     return 0;

33 }

第7行 : s = "JavaString";

这一行比较重要,s是指向main()函数中array数组的首地址的指针,然后在第7行,s指向另外一个字符串的首地址;

这里和String变量在形参中的改变有相似之处。

第14行: s[0] = 'c';

这里的s也是指向main()函数中array数组的首地址的指针,然后把array数组的第一个字符修改为'c'.

 

运行结果[在CodeBlock编译运行]:

1 s:JavaString#

2 array:GDUTtiantian#

3 array:change#

4 

5 Process returned 0 (0x0)   execution time : 0.140 s

6 Press any key to continue.

 

 

Java实现字符串的值修改可以有两种方式:

用数组实现

 1 package com.cnblog.GDUTtiantian.String;

 2 

 3 /**

 4  * @author GDUTtiantian

 5  */

 6 public class JavaString2 {

 7 

 8     public static void change(String[] name){

 9         //修改name的值

10         name[0] = "ChangedName";

11     }

12     

13     

14     public static void main(String[] args) {

15         String[] name = {"GDUTtiantian"};        

16         

17         change(name);

18         

19         System.out.println(name[0]);

20         

21     }

22     

23 }

运行结果:

 1 ChangedName 

 

将String设置为新建类型的一个成员变量

 1 package com.cnblog.GDUTtiantian.String;

 2 

 3 /**

 4  * @author GDUTtiantian

 5  */

 6 

 7 class NewString {

 8     private String value;

 9     

10     public NewString(String str){

11         value = str;

12     }

13     

14     public String getValue() {

15         return value;

16     }

17     

18     public void setValue(String value) {

19         this.value = value;

20     }

21     

22     

23     @Override

24     public String toString() {

25         return getValue();

26     }

27 }

28 

29 public class JavaString3 {

30     private static NewString newName = new NewString("ChangedName");

31 

32     public static void change(NewString name){

33         //修改name的值

34         name.setValue(newName.getValue());

35     }

36     

37     

38     public static void main(String[] args) {

39         NewString name = new NewString("GDUTtiantian");        

40         

41         change(name);

42         

43         System.out.println(name);

44         

45     }

46     

47 }

 

运行结果:

 1 ChangedName 

 

这两种方式中String变量的值都发生了改变,但是,其底层还是创建了一个新的String对象[忽略涉及字符串在缓冲池创建对象的部分],然后返回引用给当前句柄。

 为什么通过这两种方式可以去改变String变量的值呢?

 暂时还想不太清楚,欢迎大家补充;

可以对比python的列表和元组,元组是不可变的,即元组的对象不可以删除,不可以增加;

如果该元组的一个元素为列表类型,那么,可以修改这个列表的内容,当然,列表对象还是当前这个列表对象。

 

 

欢迎讨论交流, 我的主页:http://www.cnblogs.com/GDUT/

                    我的邮箱:[email protected]

 

 

 

 

 

 

 

 

你可能感兴趣的:(String)