关于String的一个疑问

先看一段代码:

public class JavaTest {
 
    public static void changeStr(String str){
        str="welcome";
    }
    public static void main(String[] args) {
 
        String str="1234";
        changeStr(str);
        System.out.println(str);
    }
}

String不是引用类型吗?为什么输出来的还是1234?

针对 @喵了个咪 的回答,贴段代码:

    public static void change(List list){
        list.add("welcome");
    }
    public static void main(String[] args) {
        String str="1234";
        List<String >list = new ArrayList<>();
        list.add(str);
        change(list);
        assert  list.size() == 2;
    }
阅读 5k
9 个回答

String 是不可变的,所以你在函数体内部做的改变不可能修改这个变量本身地址的值,而是在另一个地址新建了一个String 类型的变量,而你获取不到这个新建的变量的地址,函数执行完毕这个变量就可能会被回收。

String str = "asd";
public static void change(String data){
    data = "123";
}

可以这样想,str是一个变量,指向内存地址A。
当调用 -change- 方法的时候,str将内存地址A传递给变量data
在change方法内,进行赋值的时候,java会重新申请一块内存空间去存放数值 "123",内存地址为B,并将data的内存地址修改为B
方法执行结束
注意方法外的变量str的指向的内存地址A是没有改变的,内存地址A指向的值还是为"asd"。

下面举个例子:

public class Main1 {
    public static void main(String[] args) {
        Demo demo = new Demo();
        demo.i = 123;
        System.out.println("方法外 : " + demo.hashCode());
        change(demo);
        System.out.println(demo.i);
        change2(demo);
        System.out.println(demo.i);
    }
    
    public static void change(Demo data) {
        System.out.println("方法内前 : " + data.hashCode());
        data = new Demo();
        System.out.println("方法内后 : " + data.hashCode());
        data.i = 321;
    }

    /*
     * 这样则是针对内存地址的修改
     */
    public static void change2(Demo data) {
        data.i = 321;
        System.out.println("方法内 : " + data.hashCode());
    }
}

class Demo {
    public int i;
}

结果:

方法外 : 366712642
方法内前 : 366712642
方法内后 : 1829164700
123
方法内 : 366712642
321

表达的不是太清楚。。。希望例子能表达清楚一些

java调用方法时传递的是值类型,不是引用类型。话说你也可以把地址打出来瞧一瞧。

上述代码中String是按值传递的,所以在另一个被调用方法中changeStr,其实参和main方法中的str变量是两个地址不同的变量。所以,如题。

举个栗子:
  String str1 = "a";
  String str2 = "a";
  System.out.println(str1==str2); //true
  str1 = "b";
  System.out.println(str1 + "," + str2); //b,a
  System.out.println(str1==str2); //false
现象:
赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象.当我们改变str1的值的时候,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。
why?

String类被设计成为不可改变(immutable)的类。如果你要改变其值,那么JVM在运行时就会根据新值悄悄创建了一个新对象,然后将这个对象的地址返回给原来类的引用,这个创建过程虽说是完全自动进行的.

so:
我推断,你打印出changeStr 方法中str改变后地址应该不等于main中str的地址.

新手上路,请多包涵

1.String是被final修饰的,也就是说,一个地址值始终对应一个值;

String str="1234";//创建String对象"1234"; 地址值为a
changeStr(str);//地址值a传入
str="welcome";//(1)创建String对象"welcome";(2)局部变量str地址值为b;(3)此方法结束,地址值b无引用,gc...
System.out.println(str);//又回到调用方法,此时的变量str地址值还是a,a对应的值就是"1234";</p>
 
 //地址值传递;Demo并没有被final修饰
public class Test04 {
public static void main(String[] args) {
    Demo demo = new Demo();
    demo.str="aaa";
    System.out.println(demo);
    changeStr(demo);
    System.out.println(demo);
    System.out.println(demo.str);
}
public static void changeStr(Demo demo) {
    System.out.println(demo);
    demo.str = "welcome";//重点是这儿:(1)创建String对象"welcome";(2)修改了传入demo对象的str属性所对应的地址值;
    System.out.println(demo);
} }
class Demo {
    public String str;
}
 

控制台输出:


gethub.com.Demo@15db9742
gethub.com.Demo@15db9742
gethub.com.Demo@15db9742
gethub.com.Demo@15db9742
welcome

引用类型地址值始终没变,这是不是你想要的结果呢?

2.方法内变量保存的是引用类型的地址值,并通过地址值寻值;

public class Test05 {
public static void main(String[] args) {
    Demo demo = new Demo();
    demo.strs="aaa";
    System.out.println("1-"+demo);
    changeStr(demo);
    System.out.println("5-"+demo);
    System.out.println("6-"+demo.strs);
}

public static void changeStr(Demo demo) {
    System.out.println("2-"+demo);
    Demo demo2 = new Demo();
    demo = demo2;
    demo.strs = "welcome";
    System.out.println("3-"+demo);
    System.out.println("4-"+demo.strs);
}
}

final class  Demo {
    public String strs;
}

看结果:

1-gehub.com.Demo@15db9742
2-gehub.com.Demo@15db9742
3-gehub.com.Demo@6d06d69c//在这儿方法内将传入的demo的地址改为demo2的地址值;
4-welcome
5-gehub.com.Demo@15db9742//这儿输出的还是demo的地址值
6-aaa

此例子看出方法内的变量存储引用类型的地址值,掉用完其他方法,还是会按照调用之前的地址值寻值;
来喷我:

首先关于String,在java语言规范里面规定了除了8种基础类型外其他的都是引用类型。
关于造成题主的这个问题是String这个类是一个不可变的类,也就是说String构造的字符串在常量池中是不可以修改的。
假设在main方法里面的那个str的内存地址是0x01;
传入方法之后:形参指向内存地址 0x01 ,赋值语句执行后修改方法体的str的引用;但是从头到尾并没有修改main方法体中的引用,方法体的str只是拷贝了一份main中str,而修改是在拷贝这里再加上string的不可变,自然就不会改变了。
图片描述

图片写错了,是执行赋值语句之后指向内存地址0x01的引用不存在了


稍微改下题主的代码:
测试代码:

public class String_Refer_Test {
    public static void ChangeStr(String str){
        str="WelcomeChangeStr";
    }
    public static void ChangeStr_1(AnotherClass ano){
        ano.str="WelcomeChangeStr_1";
    }
    public static void ChangeStr_2(String str){
        str="WelcomeChanger_2";
    }
    public static void main(String[] args){
        String str="1234";
        ChangeStr(str);
        System.out.println(str);
        AnotherClass anotherClass =new AnotherClass("1234");
        ChangeStr_1(anotherClass);
        System.out.println(anotherClass.str);
        ChangeStr_2(anotherClass.str);
        System.out.println(anotherClass.str);
    }
}
class AnotherClass{
    public  String str;
    public AnotherClass(String str){
        this.str=str;
    }
}

输出结果:
1234
WelcomeChangeStr_1
WelcomeChangeStr_1

可以发现第二个方法和第三个方法中,第二个方法改变了String的值,但是第三个方法却没有改变,是因为第二个方法提供了修改了自身的引用的语句,anotherclass存在方法区的str变量被修改了,但是anotherclass是可以变的类,所以修改在堆中分配的内存地址没改变,不过方法区的str引用变了而已
图片描述

其次其实编译下ChangeStr()方法发现也就三句:

0 ldc #2 <WelcomeChangeStr>
2 astore_0
3 return

根本没有提供改变自身值的字节码,也就不可能修改本身的值

新手上路,请多包涵

可以将 String 理解为 一个特殊的常量噢!

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题