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

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

例子引入:

 package com.cnblog.GDUTtiantian.String;

 /**
  * @author GDUTtiantian
  */
 public class JavaString {

     public static void change(String name){
         //修改name的值
         name = "ChangedName";
     }

     public static void main(String[] args) {
         String name = "GDUTtiantian";        

         change(name);

         System.out.println(name);

     }

 }

运行结果:

1 GDUTtiantian

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

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

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

 public final class String
     implements java.io.Serializable, Comparable<String>, CharSequence {
     /** The value is used for character storage. */
     private final char value[];

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

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

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

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

String和StringBuffer的比较:

 package com.cnblog.GDUTtiantian.String;

 /**
  * @author GDUTtiantian
  *
  * String, StringBuffer 在传参过程中的哈希值比较
  */
 public class JavaString4 {

     public static void change(String str) {
         System.out.println("形参的哈希值:" + str.hashCode());

         str = "newString";//修改了形参的值
         System.out.println("修改后:" + str.hashCode());
     }

     public static void change(StringBuffer sb) {
         System.out.println("形参的哈希值:" + sb.hashCode());
         sb.append("newStringBuffer");//修改了形参的值
         System.out.println("修改后:" + sb.hashCode());
     }

     public static void main(String[] args) {
         String str = new String("GDUTtiantian");
         StringBuffer sb = new StringBuffer("tiantian");

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

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

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

 }

运行结果:

 修改前:-501451264
 形参的哈希值:-501451264
 修改后:-595706415

 ----------------------------

 修改前:1036782095
 形参的哈希值:1036782095
 修改后:1036782095

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

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

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

     /**
      * Allocates a new {@code String} that contains characters from a subarray
      * of the <a href="Character.html#unicode">Unicode code point</a> array
      * argument.  The {@code offset} argument is the index of the first code
      * point of the subarray and the {@code count} argument specifies the
      * length of the subarray.  The contents of the subarray are converted to
      * {@code char}s; subsequent modification of the {@code int} array does not
      * affect the newly created string.
      *
      * @param  codePoints
      *         Array that is the source of Unicode code points
      *
      * @param  offset
      *         The initial offset
      *
      * @param  count
      *         The length
      *
      * @throws  IllegalArgumentException
      *          If any invalid Unicode code point is found in {@code
      *          codePoints}
      *
      * @throws  IndexOutOfBoundsException
      *          If the {@code offset} and {@code count} arguments index
      *          characters outside the bounds of the {@code codePoints} array
      *
      * @since  1.5
      */
     public String(int[] codePoints, int offset, int count) {
         if (offset < 0) {
             throw new StringIndexOutOfBoundsException(offset);
         }
         if (count < 0) {
             throw new StringIndexOutOfBoundsException(count);
         }
         // Note: offset or count might be near -1>>>1.
         if (offset > codePoints.length - count) {
             throw new StringIndexOutOfBoundsException(offset + count);
         }

         final int end = offset + count;

         // Pass 1: Compute precise size of char[]
         int n = count;
         for (int i = offset; i < end; i++) {
             int c = codePoints[i];
             if (Character.isBmpCodePoint(c))
                 continue;
             else if (Character.isValidCodePoint(c))
                 n++;
             else throw new IllegalArgumentException(Integer.toString(c));
         }

         // Pass 2: Allocate and fill in char[]
         final char[] v = new char[n];

         for (int i = offset, j = 0; i < end; i++, j++) {
             int c = codePoints[i];
             if (Character.isBmpCodePoint(c))
                 v[j] = (char)c;
             else
                 Character.toSurrogates(c, v, j++);
         }

         this.value = v;
     }

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

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

 #include<stdio.h>

 //形参中数组退化为指针了
 //这里s是指向array数组的指针
 void go(char * s){

     s = "JavaString";//指针指向另一个空间,"JavaString"字符串的首地址
     printf("s:%s#\n", s);
 }

 //形参中数组退化为指针了
 void change(char * s){

     s[] = 'c';
     s[] = 'h';
     s[] = 'a';
     s[] = 'n';
     s[] = 'g';
     s[] = 'e';
     s[] = '\0';
 }

 int main(){
     ] = "GDUTtiantian";

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

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

     ;
 }

第7行 : s = "JavaString";

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

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

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

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

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

 s:JavaString#
 array:GDUTtiantian#
 array:change#

 Process returned  (0x0)   execution time : 0.140 s
 Press any key to continue.

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

用数组实现

 package com.cnblog.GDUTtiantian.String;

 /**
  * @author GDUTtiantian
  */
 public class JavaString2 {

     public static void change(String[] name){
         //修改name的值
         name[0] = "ChangedName";
     }

     public static void main(String[] args) {
         String[] name = {"GDUTtiantian"};        

         change(name);

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

     }

 }

运行结果:

1 ChangedName

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

 package com.cnblog.GDUTtiantian.String;

 /**
  * @author GDUTtiantian
  */

 class NewString {
     private String value;

     public NewString(String str){
         value = str;
     }

     public String getValue() {
         return value;
     }

     public void setValue(String value) {
         this.value = value;
     }

     @Override
     public String toString() {
         return getValue();
     }
 }

 public class JavaString3 {
     private static NewString newName = new NewString("ChangedName");

     public static void change(NewString name){
         //修改name的值
         name.setValue(newName.getValue());
     }

     public static void main(String[] args) {
         NewString name = new NewString("GDUTtiantian");        

         change(name);

         System.out.println(name);

     }

 }

运行结果:

1 ChangedName

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

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

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

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

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

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

我的邮箱:zone.technology.exchange@gmail.com

JAVA的String的传值和传地址问题的更多相关文章

  1. 一段代码让你秒懂java方法究竟是传值还是传地址

    先看看代码以及执行结果: 凝视写得非常清楚了.我就不多说了. 我说说我的结论.事实上在java中没有传值还是传址的概念,java仅仅有引用的概念.引用类似传址.只是是一个变量名中保存着对象的地址,地址 ...

  2. python中传值和传地址问题

    在python中,还没有对这个知识点有一个详细的定义,很模糊的说明了,通过下面代码,可以观察出来,什么时候传的是值,什么时候传的是地址 有时候会发现自己的数据发生变化,可能就是这个原因,python的 ...

  3. C/C++中传值和传地址(引用)

    C/C++中参数传递有两种方式,传值或传地址(传引用),通常我们要在被调用函数中改变一个变量的值就需要传地址调用方式,例如: void swap_by_value(int a, int b) { in ...

  4. c语言 参数传值和传地址

    static void TestCharP(char **p) { char *q = "ssssss"; *p=q; } static void TestCharP1(char ...

  5. java中函数传值和传地址的问题

    记录一下这个难过的双休,2019.3.16-2019.3.17,16号上午字节跳动笔试,四道题只做出1道半,输入输出搞的半死,第三题类似于leetcode上的分糖问题,数组初始化的时候全部赋为0了,要 ...

  6. Java经典问题:传值与传引用?

    转自:http://developer.51cto.com/art/201104/254715.htm Java到底是传值还是传引用?相信很少有人能完全回答正确.通常的说法是:对于基本数据类型(整型. ...

  7. Java方法传递参数传值还是传址的问题

    这几天重构项目代码遇到一个疑问:可不可以在方法A中定义一个boolean变量b为false,然后A调用方法C把b传递到C方法中经过一些列业务判断后修改为true,C执行结束后A方法中b的值还是原来的f ...

  8. java集合中的传值和传引用

    在学习java集合过程中发现了传值和传引用的区别: 我们来看下面两句话 ●java集合就像一种容器,我们可以把多个对象(实际上是对象的引用),丢进该容器.(来自疯狂java讲义) ●当使用Iterat ...

  9. C语言:传值,传地址

    形参:形式参数实参:实际参数 传值: 把实参的值复制给形参, 修改函数内的形参,不会影响实参. 传地址: 指针传值,形参为指向实参地址的指针 当对形参的指向操作时,相当于对实参本身进行的操作 #inc ...

随机推荐

  1. CSS隐藏元素的几种方法

    使用CSS隐藏元素的方法很多,在这里简单总结一下: 1.display:none display:none 应该是最常用的一种隐藏元素的方法,使用该方法隐藏的元素脱离文档流不占据空间,不会被浏览器解析 ...

  2. 3dsMax脚本插件开发之路

    经过这两个月的努力,RDF2.1的升级开发已经基本完成,只待过些天正式发布.所以现在总算有时间思考,来整理一下自己的思路,以及今后的方向. 回顾当初,1.0是纯Maxscript编写的,一机一码的方式 ...

  3. 自定义UITableViewCell

    随着日常的使用,系统提供的cell已经不能满足开发的需要,因为系统提供的是单一的,所以 这就引来了自定义cell的出现,可以根据 自己的需要来布局各个控件所处的位置.不同位置显示不同的控件. 创建一个 ...

  4. Python自动化 【第二篇】:Python基础-列表、元组、字典

    本节内容 模块初识 .pyc简介 数据类型初识 数据运算 列表.元组操作 字符串操作 字典操作 集合操作 字符编码与转码 一.模块初识 Python的强大之处在于他有非常丰富和强大的标准库和第三方库, ...

  5. Provisional headers are shown,本地测试成功,服务器运行却失败

    基于MVC的项目 具体情况是一个页面在访问的时候进不了首页,但详细页面却可以进去 下面说说解决方法和思路,以便找出问题所在 第一:把服务器代码下载到本地运行,代码是否出错,出错了,问题找到了,没出错接 ...

  6. delphi 10 seattle 安卓服务开发(二)

    关于delphi 10 移动服务开发的几张图

  7. ORACLE 分组之后容易被忽略的bug

    COL_2 COL_321       3123       31 如上表数据 前台显示显示需要把COL_2的21和23转换成中文 ‘整机’ 最开始如下编写 SELECT t.col_3, CASE ...

  8. linux 的 samba 实现共享文件夹

    samba Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件,由服务器及客户端程序构成. service XXX start 失败多是权限不够,使用管理员权限尝试

  9. map,list

    ---恢复内容开始--- Map<String, List> map=new HashMap<String,List>() HashMap可以理解成是一对对数据的集合我暂时把L ...

  10. HDU 4578 Transformation (线段树区间多种更新)

    http://acm.hdu.edu.cn/showproblem.php?pid=4578 题目大意:对于一个给定序列,序列内所有数的初始值为0,有4种操作.1:区间(x, y)内的所有数字全部加上 ...