JAVA的String的传值和传地址问题
2016-03-10 17:36
351 查看
JAVA的String原来只要值被修改,就会生成新对象,哈希值改变。与StringBuffer的区别在这里也有一出。转载:
http://www.2cto.com/kf/201407/314194.html
关于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
;
56
57 for (int i = offset, j = 0; i < end; i++, j++) {
58 int c = codePoints[i];
59
afd6
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 }
这两种方式中String变量的值都发生了改变,但是,其底层还是创建了一个新的String对象[忽略涉及字符串在缓冲池创建对象的部分],然后返回引用给当前句柄。
为什么通过这两种方式可以去改变String变量的值呢?
暂时还想不太清楚,欢迎大家补充;
可以对比python的列表和元组,元组是不可变的,即元组的对象不可以删除,不可以增加;
如果该元组的一个元素为列表类型,那么,可以修改这个列表的内容,当然,列表对象还是当前这个列表对象。
http://www.2cto.com/kf/201407/314194.html
关于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
;
56
57 for (int i = offset, j = 0; i < end; i++, j++) {
58 int c = codePoints[i];
59
afd6
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 }
这两种方式中String变量的值都发生了改变,但是,其底层还是创建了一个新的String对象[忽略涉及字符串在缓冲池创建对象的部分],然后返回引用给当前句柄。
为什么通过这两种方式可以去改变String变量的值呢?
暂时还想不太清楚,欢迎大家补充;
可以对比python的列表和元组,元组是不可变的,即元组的对象不可以删除,不可以增加;
如果该元组的一个元素为列表类型,那么,可以修改这个列表的内容,当然,列表对象还是当前这个列表对象。
相关文章推荐
- java实现观察者模式
- Java多线程UDP客户端与服务器间使用Socket通信
- Java 交换两个变量值
- Spring 注解学习手札(八)补遗——@ExceptionHandler
- 第七届蓝桥杯部分练习题答案(Java)
- 【J2EE】:Java EE应用的组件
- Myeclipse离线安装svn插件
- Zookeeper的集群配置和Java测试程序
- JAVA高级程序设计学习计划
- springbooot之freemarker
- ROS使用IDE Eclipse
- java.util.concurrent包 (备忘)
- java 内存溢出的 解决方法!~
- ecplise建java工程有红色叹号
- Spring Hibernate Transaction示例
- Java概述和开发环境
- Spring、Spring MVC、MyBatis整合文件配置详解
- java解压zip文件实例
- Java SE技术概览 - Jave SE Platform at a Glance
- java四种内部类