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", 并赋给引用 sString 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_WorldSystem.out.println("Hello World".toString());System.out.println(s == "Hello World"); // trueSystem.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等方法改变字符串值时只是在**原有对象存储的内存地址**上进行连续操作,减少了资源的开销。

String---自我理解相关推荐

  1. 关于OpenGL ES 3D 光晕如何产生的自我理解

    2019独角兽企业重金招聘Python工程师标准>>> 星空图的自我理解:可以看作一个透明的半径很大的天球,然后在其表面上绘制大小不一的点,这样便可以够成星空图. 如何产生光晕的自我 ...

  2. 关于渗透的一些思路持续更新(自我理解)

    关于渗透的一些思路持续更新(自我理解) 先回忆一下 准确的是17年才接触到渗透以及ddos 其他hei产东西就不介绍了 接触的自我感觉有点迟 从最开始我也是从阿D明小子那些很傻瓜化的软件走过来的 但是 ...

  3. str在java中什么意思_Java中String的理解

    Java中String的理解 最近在读String的源码,看了些String的文章,自己对String作了下总结记录下来. 1.String为什么是不可变的? String是final类,不可继承,其 ...

  4. 读阿里中台战略思想有感—自我理解

    读阿里中台战略思想有感-自我理解 中台的意义在于避免项目工程建设的不断"造轮子",也就是当需求来临时,全部重新建设,核心公共模块的工程代码没有被重复利用. 阿里对于中台战略思想的实 ...

  5. Sparsity Quantization 之自我理解

    Sparsity & Quantization 之自我理解 Sparsity Quantization DNN的最初的动力是以precise为中心,但随着DNN在Edge的推广,Latency ...

  6. 我的python之路一初识python的自我理解

    初识python的自我理解 关于python的自我简记 我认识的python 怎么开始python的学习和使用 我尝试过的几个python学习的常用组合 关于python的自我简记 初识python是 ...

  7. 从C# String类理解Unicode(UTF8/UTF16)

    上一篇博客:从字节理解Unicode(UTF8/UTF16).这次我将从C# code 中再一次阐述上篇博客的内容. C# 代码看UTF8 代码如下: string test = "UTF- ...

  8. 自我理解的KMP 算法 模式匹配

    首先,KMP最重要的是next 函数的设计,下面是next函数的原理 纯手写,可跳过不看,网上有比较抽象的理论,但是比较难以理解 下面是代码部分,重点在于 get_next 函数体 //KMP模式匹配 ...

  9. ULua反射原理——自我理解,有问题请斧正,谢谢!

    ULua反射原理 旧版本ULua 使用的类 实现方式 现版本的ulua(tolua) 实现方式 wrap:What ,How? What:wrap是什么 How :Wrap使用与生成 How :对于庞 ...

  10. 从自定义string类型理解右值引用

    理解右值引用 前言 问题复现 自定义string(CMyString) 遇到问题 图示理解 右值引用 什么是右值 添加右值引用参数的成员方法 结果对比 解决遗留问题 前言 在之前,我写过一篇: 通过自 ...

最新文章

  1. Spring Boot读取配置的几种方式
  2. C#为什么支持协变的参数只能用于方法的返回值?支持逆变的参数只能用于方法参数?...
  3. ASP.NET中过滤HTML字符串的两个方法
  4. ubuntu 安装星际译王词典
  5. JSR303常见参数
  6. 自己移植Asterisk1.8到OpenWRT下
  7. JWTToken超时刷新策略
  8. 最严格的身份证校验(Java版)
  9. 微信公众号网页授权代码优化过程(三)
  10. Linux安装以及固定ip
  11. 大一大学计算机应用基础,大一计算机应用基础试题
  12. linux安装db2数据库并设置开机自启动
  13. 针对自动识别大麦网滑块验证码,提出解决方案,并进行分析、总结
  14. 计算机用户文件夹怎么重命名,win10修改账户文件夹名方法_windows10用户文件夹改名怎么操作...
  15. vs2012中将图片放到resource中进行调用
  16. 长期招对日/赴日开发人才
  17. Android通知栏字体大小,Android通知栏介绍与适配总结(上篇)
  18. ubuntu 安装GPU黑屏 修改GRUB_Ubuntu 18.04 安装笔记
  19. C语言精练教程:连载中
  20. IntelliJ IDEA 破解方法

热门文章

  1. ORVIBO 精灵款升级分析
  2. 逆元的概念及求解方法
  3. 忽然想起你,你在远方还好吗!
  4. 第三方支付的流程分析与总结
  5. CDN加速是什么?具体有什么用?
  6. (Neighbourhood Components Analysis) NCA 近邻成分分析的学习
  7. 大量用户反馈 QQ 账号被盗;​AirPods Pro2 或支持查找功能;Spring Boot 2.7.1发布|极客头条
  8. C/S模型(客户/服务器模型)
  9. “加号 +” 的运算原理(详细!!!)
  10. 6英寸划片机 陆芯3252半自动单轴晶圆切割机