String类型函数传递问题

问题

  • 以前没有注意过的一个问题, 最近在使用String类型作为函数入参的时候, 发现函数内对于String类型的改变并不会影响到外层调用对象本身;

结论 (先说结论)

  • 这个问题根本不存在 (属于是自己把自己绕进去了);
  • String类型与普通的java对象一样, 只不过是用final修饰的不可变对象 (具体看String类型的源码与相关介绍);

测试数据(为什么会有这个问题, 来源于以下操作)

  • 发现String (其实Integer, Long... 等等这些类型也会这样)函数传递修改后, 对象的值并没有被改变;
  • 主要是因为String类型与Integer...等等这些类型的赋值方式迷惑了我们, 不需要通过"new"关键字和反射也可以构建对象;
  • 比如: Integer a = 123; 这种操作, 让我们误当作基本类型赋值(实际上这个问题比较基础, 但有时候也会迷糊);
  • 以下是对上述的操作案例;
  1. package timer;
  2. /**
  3. * @author liwangcai E-mail:1252376504@qq.com
  4. */
  5. public class StringDemo {
  6. public static void changeString(String tmp) {
  7. //此处操作具有一定的迷惑行为, 通常情况下只有基本数据类型才会这么操作;
  8. //但是对与String类型, 相当于新建了一个对象或者是拿到了常量池中的对象;
  9. tmp = "new";
  10. //打印通过函数传递进来的tmp参数的地址
  11. System.out.println(System.identityHashCode(tmp));
  12. }
  13. public static void changeInteger(Integer tmp) {
  14. //此处操作和String类型一样
  15. tmp = 199;
  16. //打印通过函数传递进来的tmp参数的地址
  17. System.out.println(System.identityHashCode(tmp));
  18. }
  19. public static void changeOther(StringDemo stringDemo) {
  20. System.out.println(System.identityHashCode(stringDemo));
  21. }
  22. public static void main(String[] args) {
  23. String tmp = "old";
  24. //打印原始的tmp
  25. System.out.println(tmp);
  26. //打印原始的tmp对象地址
  27. System.out.println(System.identityHashCode(tmp));
  28. changeString(tmp);
  29. //打印函数调用后的tmp值
  30. System.out.println(tmp);
  31. //出于好奇也测试了一下Integer类型
  32. Integer iTmp = 1;
  33. //打印原始的iTmp的值
  34. System.out.println(iTmp);
  35. //打印原始的iTmp对象地址
  36. System.out.println(System.identityHashCode(iTmp));
  37. changeInteger(iTmp);
  38. //打印函数调用后iTmp的值
  39. System.out.println(iTmp);
  40. //对于普通的java对象
  41. StringDemo stringDemo = new StringDemo();
  42. //打印当前对象的地址
  43. System.out.println(System.identityHashCode(stringDemo));
  44. changeOther(stringDemo);
  45. }
  46. }
  47. //执行结果
  48. old
  49. 685325104
  50. 685325104
  51. 460141958
  52. old
  53. 1
  54. 1163157884
  55. 1163157884
  56. 1956725890
  57. 1
  58. 356573597
  59. 356573597

以上测试结果得到的结果分析

  • 上面的这个操作实际上就是迷惑所在,在这里单独把它列出来看一下
  1. Integer a;
  2. //此处实际上是java的自动装箱, 相当于调用了valueOf函数
  3. // 实际上是new了一个Integer对象出来,或者将另一个Integer对象直接赋值(可以去看一下Integer的源码)
  4. a = 123;
  5. //基础类型的直接赋值
  6. int b;
  7. b = 123;
  8. //与Integer类似,String类型也有常量池, 相当于缓存, 此处不是重点;
  9. // 相当于new了一个对象出来, 或将另一个String对象直接赋值;
  10. String c;
  11. c = "abc"

普通对象操作对比

  1. /**
  2. * @author liwangcai E-mail:1252376504@qq.com
  3. */
  4. public class StringTest {
  5. public static void changeUser(User user) {
  6. user = new User("zhang san", 24);
  7. }
  8. public static void main(String[] args) {
  9. User user = new User("li si", 25);
  10. System.out.println(user);
  11. changeUser(user);
  12. System.out.println(user);
  13. }
  14. @Data
  15. @AllArgsConstructor
  16. @ToString
  17. static class User {
  18. private String name;
  19. private Integer age;
  20. }
  21. }
  22. //执行结果, 可以发现对象的值并没有改变
  23. StringTest.User(name=li si, age=25)
  24. StringTest.User(name=li si, age=25)
  25. Process finished with exit code 0

为什么new一个对象并不会改对象值?

  • 主要是因为在Java中函数传递只有值传递 (不是本博客重点, 不展开描述)

如果想要改变String的值正确的操作姿势?

  • String对象是普通的Java对象, 不过是被final修饰了;
  • 实际上String对象的值存在其内部的char value[]数组中,如果想要改变String的值应该区修改这个数组的数据;
  • 不过上述数组是使用final修饰的, 所以如果使用jdk中的String类, 那么String的值是无法被修改的;

如果要改变String的值应该怎么做?

  • 实现自己的String类型, 内部存储char[]数组不设置为final类型就可以了;
  1. /**
  2. * @author liwangcai E-mail:1252376504@qq.com
  3. */
  4. public class MyStringTest {
  5. public static void main(String[] args) {
  6. MyString myString = new MyString(new char[]{'a', 'b'});
  7. System.out.println(myString);
  8. changeMyString(myString);
  9. System.out.println(myString);
  10. }
  11. private static void changeMyString(MyString myString) {
  12. myString.setValue(new char[]{'c', 'd'});
  13. }
  14. @ToString
  15. @AllArgsConstructor
  16. @Data
  17. static class MyString {
  18. char[] value;
  19. }
  20. }
  21. //测试结果
  22. //可以发现值改变了
  23. MyStringTest.MyString(value=[a, b])
  24. MyStringTest.MyString(value=[c, d])
  25. Process finished with exit code 0

总结

  • 对于String,Integer类型 "=" 操作符号迷惑了我们, 实际上一开始提出的问题并不存在;
  • 如果要修改同一个对象, 需要修改其内的成员;

String类型函数传递问题的更多相关文章

  1. string的函数的学习

    1.string类型的构造函数和对象的定义 string s3 : 把string s2 拷贝的 s3 string s4 : 把数组首地址或者字符串首地址strArr 从0开始截取到第n个字母 st ...

  2. String类型和包装类型作为参数传递时,是属于值传递还是引用传递呢?

    原理知识: 如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的.如果在函数中改变了副本的 值不会改变原始的值. 如果参数类型是引用类型,那 ...

  3. 对String值不可变的理解以及String类型的引用传递问题

    今天复习java时,突然注意到了一句以前没有注意过的一句话,String 是final修饰的,其值是不可变的.当时看的一脸懵逼,String str = "abc"; str = ...

  4. Delphi中返回类型为string的函数的一个陷阱(不是很懂)

    如果类的一个成员函数的返回值是string类型,需要注意一个问题 其返回值可能是错误的 例如函数的实现如下 function GetString( s: string ): string;begin  ...

  5. php中函数 isset(), empty(), is_null() 的区别,boolean类型和string类型的false判断

    php中函数 isset(), empty(), is_null() 的区别,boolean类型和string类型的false判断 实际需求:把sphinx返回的结果放到ssdb缓存里,要考虑到sph ...

  6. String类型_static成员_动态内存分配_拷贝构造函数_const关键字_友元函数与友元类

    1:String类型 #include <iostream> using namespace std; int main() { //初始化方法 string s1 = "hel ...

  7. printf()函数不能直接输出string类型

    因为string不是c语言的内置数据,所以直接printf输出string类型的是办不到的. 要这样输出: printf("%s\n",a.c_str()); 举例: #inclu ...

  8. string类型常用函数

    一个字符串就是一个string类型数据,此类型变量我们可以把它看作一个只读数组,其元素是char变量,在这里我们来说下string类型的常用命令. 1.TocharArray():将此实例中的字符复制 ...

  9. [STL]string类型的getline函数

    3.cin.getline() 实际是cin.getline(接收字符串到m,接收个数n,结束字符).接收一个字符串,可以接收空格等,最后一个字符为‘\0’.结束符可以通过设置第三个参数自己设置,默认 ...

随机推荐

  1. [AcWIng 799] 最长连续不重复子序列

    点击查看代码 #include<iostream> using namespace std; const int N = 1e5 + 10; int a[N], s[N]; int mai ...

  2. 1 Mybatis动态SQL

    Mybatis动态SQL 1. 注解开发 ​ 我们也可以使用注解的形式来进行开发,用注解来替换掉xml. 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从 ...

  3. Redis GEO 地理位置

    目录 GEO指令 GEOADD GEODIST GEOPOP GEOHASH GEORADIUS GEORADIUSBYMEMBER 指令补充 删除操作 避免单集合数量过多 存储原理 GEOADD存储 ...

  4. Redis设计与实现2.2:数据持久化

    数据持久化 这是<Redis设计与实现>系列的文章,系列导航:Redis设计与实现笔记 RDB持久化 RDB 持久化功能所生成的 RDB 文件是一个经过压缩的二进制文件,通过该文件可以还原 ...

  5. 152-技巧-Power Query 快速合并文件夹中表格之自定义函数 TableXlsxCsv

    152-技巧-Power Query 快速合并文件夹中表格之自定义函数 TableXlsxCsv 附件下载地址:https://jiaopengzi.com/2602.html 一.背景 在我们使用 ...

  6. 【Axure】母版引发事件

    引发事件是指你将母版中某一元件的事件从母版中提升出来,以使其在页面的级别可用. 通过引发事件,可以对在不同页面上母版实例的同一个元件设置不同的交互. 设置引发事件 打开一个母版: 选择其中一个组件: ...

  7. 碎碎念软件研发02:敏捷之Scrum

    一.什么是 Scrum 1.1 Scrum 定义 Scrum 是敏捷开发方法之一,它使用比较广泛. 敏捷的其它开发方法还有 XP(极限编程).FDD(特性驱动开发).Crystal(水晶方法).TDD ...

  8. CF1682D Circular Spanning Tree

    题意: 构造题,节点1~n顺时针排列成圆形,告诉你每个点度数奇偶性,让你构造一棵树,树边不相交. 思路: 因为每条边给总度数贡献2,因此如果度数为1的点有奇数个,直接输出no.显然0个度数为1的,也输 ...

  9. 【原创】项目五w1r3s.v1.0

    实战记录 1.nmap信息枚举 1)C段扫描 nmap -sP 192.168.186.0/24 2)扫描全端口信息 nmap -p- 192.168.186.143 3)扫描版本信息 nmap -p ...

  10. Linux系列之linux访问windows文件

    Linux永久挂载windows共享文件 Linux系统必须安装samba-client Linux服务器必须能访问到Windows的共享文件服务的(445端口) 1.Windows共享文件 2.测试 ...