String---自我理解

1.JVM图

在这里插入图片描述

2.代码展示

public class TestString {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    	String s1=new String("hello);
    	String s2="hello";
       String str1="hello";
       
        System.out.println("str1的内存地址:"+System.identityHashCode(str1));
        String str2="world";
        //str1+=str2实际上是执行了str1=(new StringBuilder()).append(str2).toString();前后实际额外产生了一个StringBuilder与一个helloworld的字符串常量。
        str1+=str2;
        System.out.println("str1的值:"+str1+"  str1的内存地址"+System.identityHashCode(str1));
        String str3="123";
        // return new String(buf, true);
        String str4=str3.concat("456");
        System.out.println("str3的内存地址"+System.identityHashCode(str3));
        System.out.println("str4的内存地址"+System.identityHashCode(str4));
        String str5="ABC";
        //return new String(buf, true);
        String str6=str5.replace("A","B");
        System.out.println("str5的内存地址"+System.identityHashCode(str5));
        System.out.println("str6的内存地址"+System.identityHashCode(str6));

        String str7="hello world";
        System.out.println("str7的内存地址"+System.identityHashCode(str7));
        Field field=str7.getClass().getDeclaredField("value");
        field.setAccessible(true);
        char [] o = (char[])field.get(str7);
        o[0]='s';
        System.out.println(str7);
        System.out.println("str7修改后的内存地址"+System.identityHashCode(str7));

    }

}

在这里插入图片描述

3.程序理解

首先,堆和方法区都属于线程共享区,通过main()方法进栈。
然后再栈中定义一个对象s1,去堆中开辟一个内存空间,将内存空间的引用赋值给s1,“hello”是常量,然后去字符串常量池 查看是否有hello字符串对象,没有的话分配一个空间存放hello,并且将其空间地址存入堆中new出来的空间中。
在栈中定义一个对象s2,然后去字符串常量池中查看是否有”hello”字符串对象,有,直接把”hello”的地址赋值给s2.
s1中存的是堆中分配的空间,堆中分配的空间中存的是字符串常量池中分配空间存放”hello”的空间的地址值。而s2中之间存的是字符串常量池中分配空间存放hello”的空间的地址值

4.问题解析

4.1 equals和==

![在这里插入图片描述](https://img-blog.csdnimg.cn/20191219141554751.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwOTk1Njg=,size_16,color_FFFFFF,t_70)
String  重写了equals方法,比较的是字符串值,
==比较的是对象的内存地址
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191219141903324.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTIwOTk1Njg=,size_16,color_FFFFFF,t_70)
String 创建的对象是不可变的。因为final保证了此value数组一旦被赋值就无法改变了。

但是也不是绝对的,比如反射可以帮我们做很多坏事,改变数组的值,但是引用地址不会变。

 //创建字符串"Hello World", 并赋给引用 s
        String s = "Hello World";
        System.out.println("s = " + s); //Hello World
        //获取String类中的value字段
        Field valueFieldOfString = String.class.getDeclaredField("value");
        //改变value属性的访问权限
        valueFieldOfString.setAccessible(true);
        //获取s对象上的value属性的值
        byte[] value = (byte[]) valueFieldOfString.get(s);
        //改变value所引用的数组中的第5个字符
        value[5] = '_';
        System.out.println("s = " + s);  //Hello_World
        System.out.println("Hello World".toString());
        System.out.println(s == "Hello World"); // true
        System.out.println(s.equals("Hello World")); // true

如上方代码所示,我们定义了一个 String 变量 s ,并且赋值给它 “Hello World”,然后通过 Java 的反射机制去修改第五个字符的值,在输出结果的时候,理所当然的结果是 “Hello_World”,但是我在这个情况之上多了一手,把被修改后的值与初始值比较看看,会发生什么情况,结果一试就出现了问题,竟然与初始值相等且返回了 true .

于是,打算好好深究一下,我们知道 String 的变量是存放在 JVM 的常量池中的,同时指向 “Hello World” 的引用 s1 是 String 对象,存放在堆区,我们通过反射区修改的值并不是常量池中的 “Hello World” ,而是 堆区中的对象 s1 的值,所以我们输出 s1 的时候就是 “Hello_World” ,那么为什么初始值比较修改后的值的时候会出现 true 呢?

我们看看 System.out.println(“Hello World”.toString()); 这行代码,输出结果是 Hello_World,这是因为 “Hello World”.toString() 的时候,会去常量池中去找 "Hello World"的引用,它的引用就是 s1 ,所以输出的是 s1 的值,即 “Hello_World”,所以,到这里,我们就知道了,为什么 s == “Hello World” 和 s.equals(“Hello World”) 返回 true 了 .

String str=new String("ABC");
        System.out.println("修改前的str的内存地址1" + System.identityHashCode(str));
        Field field=str.getClass().getDeclaredField("value");
        field.setAccessible(true);
        char[] value = (char[]) field.get(str);
        value[1] = '_';
        System.out.println(str);
        System.out.println("修改后的str的内存地址1" + System.identityHashCode(str));

synchronized StringBuffer       线程安全    而StringBuffer类使用append和insert等方法改变字符串值时只是在**原有对象存储的内存地址**上进行连续操作,减少了资源的开销。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值