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

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. Golang 高阶函数

    定义 高阶函数是接收函数作为参数或返回函数作为输出的函数. 高阶函数是对其他函数进行操作的函数,要么将它们作为参数,要么返回它们. 举例 函数作为参数 package main import &quo ...

  2. RESTFul是一种风格

    只要符合RESTFul风格的,都可以叫做使用了RESTFul架构,一般的网站里传数据,都是用的?a=1&b=2...如果是RESTFul风格的话,就会是/a/1/b/2..类似于这样的方式来传 ...

  3. Promise与async/await与Generator

    Promise是什么: Promise是异步微任务(process.nextTick.Promise.then() catch() finally()等),用于解决异步多层嵌套回调的问题(回调地狱-- ...

  4. Docker将镜像文件发布到私服库

    上一篇已经介绍如何将镜像发布到阿里云:https://www.cnblogs.com/sportsky/p/16246394.html 一.创建新镜像文件 1.创建容器并在容器内创建文件 在根目录下创 ...

  5. Blazor和Vue对比学习(基础1.4):事件和子传父

    Blazor和Vue的组件事件,都使用事件订阅者模式.相对于上一章的组件属性,需要多绕一个弯,无论Blazor还是Vue,都是入门的第一个难点.要突破这个难点,一是要熟悉事件订阅模式<其实不难& ...

  6. 每日一题20180401-Linux

    一.题目 1.1 在mysql命令行临时开启自动补全 1.2 通过shell脚本打印乘法口诀表 二.答案 2.1 # auto-rehash:读取表信息和列信息,可以在连上终端后开启tab补齐功能 # ...

  7. 个人冲刺(六)——体温上报app(二阶段)

    冲刺任务:完成主页面功能 MainActivity.java package com.example.helloworld; import android.app.AlertDialog; impor ...

  8. pip下载更改为清华镜像

    step1: + 在user(用户)下新建一文件夹再在该文件夹下新建pip.ini文件 + 例如:user/pip/pip.ini + tips:如果未打开在查看里的隐藏扩展名记得打开 step2: ...

  9. 关于基础RMQ——ST算法

    RMQ,Range Maximum/Minimum Query,顾名思义,就是询问某个区间内的最大值或最小值,今天我主要记录的是其求解方法--ST算法 相对于线段树,它的运行速度会快很多,可以做到O( ...

  10. js 动画补间 Tween

    1 /* RunningList (触发过程中可以安全的删除自己) 2 如果触发过程中删除(回调函数中删除正在遍历的数组), 不仅 len 没有变(遍历前定义的len没有变, 真实的len随之减少), ...