String类型函数传递问题
问题
- 以前没有注意过的一个问题, 最近在使用String类型作为函数入参的时候, 发现函数内对于String类型的改变并不会影响到外层调用对象本身;
结论 (先说结论)
- 这个问题根本不存在 (属于是自己把自己绕进去了);
- String类型与普通的java对象一样, 只不过是用final修饰的不可变对象 (具体看String类型的源码与相关介绍);
测试数据(为什么会有这个问题, 来源于以下操作)
- 发现String (其实Integer, Long... 等等这些类型也会这样)函数传递修改后, 对象的值并没有被改变;
- 主要是因为String类型与Integer...等等这些类型的赋值方式迷惑了我们, 不需要通过"new"关键字和反射也可以构建对象;
- 比如: Integer a = 123; 这种操作, 让我们误当作基本类型赋值(实际上这个问题比较基础, 但有时候也会迷糊);
- 以下是对上述的操作案例;
package timer; /** * @author liwangcai E-mail:1252376504@qq.com */ public class StringDemo { public static void changeString(String tmp) { //此处操作具有一定的迷惑行为, 通常情况下只有基本数据类型才会这么操作; //但是对与String类型, 相当于新建了一个对象或者是拿到了常量池中的对象; tmp = "new"; //打印通过函数传递进来的tmp参数的地址 System.out.println(System.identityHashCode(tmp)); } public static void changeInteger(Integer tmp) { //此处操作和String类型一样 tmp = 199; //打印通过函数传递进来的tmp参数的地址 System.out.println(System.identityHashCode(tmp)); } public static void changeOther(StringDemo stringDemo) { System.out.println(System.identityHashCode(stringDemo)); } public static void main(String[] args) { String tmp = "old"; //打印原始的tmp System.out.println(tmp); //打印原始的tmp对象地址 System.out.println(System.identityHashCode(tmp)); changeString(tmp); //打印函数调用后的tmp值 System.out.println(tmp); //出于好奇也测试了一下Integer类型 Integer iTmp = 1; //打印原始的iTmp的值 System.out.println(iTmp); //打印原始的iTmp对象地址 System.out.println(System.identityHashCode(iTmp)); changeInteger(iTmp); //打印函数调用后iTmp的值 System.out.println(iTmp); //对于普通的java对象 StringDemo stringDemo = new StringDemo(); //打印当前对象的地址 System.out.println(System.identityHashCode(stringDemo)); changeOther(stringDemo); } } //执行结果 old 685325104 685325104 460141958 old 1 1163157884 1163157884 1956725890 1 356573597 356573597
以上测试结果得到的结果分析
- 上面的这个操作实际上就是迷惑所在,在这里单独把它列出来看一下
Integer a; //此处实际上是java的自动装箱, 相当于调用了valueOf函数 // 实际上是new了一个Integer对象出来,或者将另一个Integer对象直接赋值(可以去看一下Integer的源码) a = 123; //基础类型的直接赋值 int b; b = 123; //与Integer类似,String类型也有常量池, 相当于缓存, 此处不是重点; // 相当于new了一个对象出来, 或将另一个String对象直接赋值; String c; c = "abc"
普通对象操作对比
/** * @author liwangcai E-mail:1252376504@qq.com */ public class StringTest { public static void changeUser(User user) { user = new User("zhang san", 24); } public static void main(String[] args) { User user = new User("li si", 25); System.out.println(user); changeUser(user); System.out.println(user); } @Data @AllArgsConstructor @ToString static class User { private String name; private Integer age; } } //执行结果, 可以发现对象的值并没有改变 StringTest.User(name=li si, age=25) StringTest.User(name=li si, age=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类型就可以了;
/** * @author liwangcai E-mail:1252376504@qq.com */ public class MyStringTest { public static void main(String[] args) { MyString myString = new MyString(new char[]{'a', 'b'}); System.out.println(myString); changeMyString(myString); System.out.println(myString); } private static void changeMyString(MyString myString) { myString.setValue(new char[]{'c', 'd'}); } @ToString @AllArgsConstructor @Data static class MyString { char[] value; } } //测试结果 //可以发现值改变了 MyStringTest.MyString(value=[a, b]) MyStringTest.MyString(value=[c, d]) Process finished with exit code 0
总结
- 对于String,Integer类型 "=" 操作符号迷惑了我们, 实际上一开始提出的问题并不存在;
- 如果要修改同一个对象, 需要修改其内的成员;