• String类型函数传递问题


    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类型 "=" 操作符号迷惑了我们, 实际上一开始提出的问题并不存在;
    • 如果要修改同一个对象, 需要修改其内的成员;
  • 相关阅读:
    Windows server 安装和配置PostgreSQL 14
    vue中使用base64编码上传文件或者图片,以及base64编码的图片在img标签中使用
    广州穗雅医院【口腔健康揭秘】影响口腔白斑的4大因素
    分布式电源接入对配电网的影响matlab程序(IEEE9节点系统算例)
    Java编码规范--OOP规约
    react频繁使用的js(input防抖请求、节流)
    C++11(lambda表达式)
    Rust错误处理简介
    C++入门5 名字空间|new|虚拟地址
    如何在响应头中防治xss
  • 原文地址:https://www.cnblogs.com/liwangcai/p/16485511.html