全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

java String 可变性的分析

前言

这两天在看Java面试相关的一些问题,很偶然也很幸运的看到了下面这篇文章。

https://www./article/105448.htm

这篇文章的作者有一系列关于Java深入学习的文章,很值得一看,个人觉得非常好,很有收获。

起因

正如我们所理解的,通过

String hello = "Hello World!";

String xx = new String("Hello World!");

得到的字符串对象是不一样的,new方式是在堆空间中创建的,而直接的字符串则是先被放到常量池中。如果有新的与之一样的对象被创建,则直接让这个新对象引用常量池中的这个地址即可。

这样的好处就是可以最大限度的节省内存空间。

而使用new方式创建的则就不一样了,只要是用了new创建字符串,就会在堆空间中开辟出一块内存,然后返回这个内存地址的引用。所以这样创建的对象,即使内容一致,也不会是指向同一个内存地址。

下面用几个简单的代码做下测试。

/**
*字符串中对于内容和地址的判定可以用下面两种方式,但侧重点不一样。
*/
equals // 判断 两个字符串的内容是否一致
==  // 判断两个字符串的内存地址是否一致

且看下面的代码:

public static void simple() {
  String s1 = "Hello World!";
  String s2 = "Hello World!";
  String s3 = new String("Hello World!");
  String s4 = new String("Hello World!");

  // 下面开始比较引用和内容的比较
  System.out.println("字符串赋值方式:");
  System.out.println(s1==s2);
  System.out.println(s1.equals(s2));

  System.out.println("\n字符串赋值方式和new方式:");
  System.out.println(s1==s3);
  System.out.println(s1.equals(s3));

  System.out.println("\nnew 方式:");
  System.out.println(s3==s4);
  System.out.println(s3.equals(s4));
 }

得到的结果如下:

字符串赋值方式:
true
true

字符串赋值方式和new方式:
false
true

new 方式:
false
true

结果却是和我们所说的那样。

深入源码

不出所料,String确实是“不可变的”,每次改变底层其实都是创建了一个心的字符串对象,然后赋予了新值。

为什么会这样呢?我们也许可以在源码中找到真相。

哦,原来Java对于String类只是维护了一个final类型的字符数组啊。怪不得赋值之后就不能改变了呢。

但是也许你会有疑问,咦,不对啊,“我经常使用String的什么replace方法改变字符串的内容啊。你这则么解释呢?”

其实答案还是那样,它真的没变,我们并没有看到事情的真相,相信看完下面的源码,你就明白了。

/**
  * Returns a string resulting from replacing all occurrences of
  * {@code oldChar} in this string with {@code newChar}.
  * <p>
  * If the character {@code oldChar} does not occur in the
  * character sequence represented by this {@code String} object,
  * then a reference to this {@code String} object is returned.
  * Otherwise, a {@code String} object is returned that
  * represents a character sequence identical to the character sequence
  * represented by this {@code String} object, except that every
  * occurrence of {@code oldChar} is replaced by an occurrence
  * of {@code newChar}.
  * <p>
  * Examples:
  * <blockquote><pre>
  * "mesquite in your cellar".replace('e', 'o')
  *   returns "mosquito in your collar"
  * "the war of baronets".replace('r', 'y')
  *   returns "the way of bayonets"
  * "sparring with a purple porpoise".replace('p', 't')
  *   returns "starring with a turtle tortoise"
  * "JonL".replace('q', 'x') returns "JonL" (no change)
  * </pre></blockquote>
  *
  * @param oldChar the old character.
  * @param newChar the new character.
  * @return a string derived from this string by replacing every
  *   occurrence of {@code oldChar} with {@code newChar}.
  */
 public String replace(char oldChar, char newChar) {
  if (oldChar != newChar) {
   int len = value.length;
   int i = -1;
   char[] val = value; /* avoid getfield opcode */

   while (++i < len) {
    if (val[i] == oldChar) {
     break;
    }
   }
   if (i < len) {
    char buf[] = new char[len];
    for (int j = 0; j < i; j++) {
     buf[j] = val[j];
    }
    while (i < len) {
     char c = val[i];
     buf[i] = (c == oldChar) ? newChar : c;
     i++;
    }
    return new String(buf, true);
   }
  }
  return this;
 }

源码中很明确的使用了

new String(buf, true);

的方式返回给调用者新对象了。

真的不可变吗?

读到上面的内容,其实基本上已经够了。但是了解一下更深层次的内容,相信对我们以后编程来说会更好。

源码中清楚的使用char[] value来盛装外界的字符串数据。也就是说字符串对象的不可变的特性,其实是源自value数组的final特性。

那么我们可以这么想,我们不改变String的内容,而是转过头来改变value数组的内容(可以通过反射的方式来修改String对象中的private属性的value),结果会怎样呢?

答案是真的会变哦。

可以先看下下面的代码

private static void deep() throws NoSuchFieldException, IllegalAccessException {
  String hello = "Hello World!";
  String xx = new String("Hello World!");
  String yy = "Hello World!";

  /**
   * 判断字符串是否相等,默认以内存引用为标准
   */
  System.out.println(hello == xx);
  System.out.println(hello == yy);
  System.out.println(xx == yy);

  // 查看hello, xx, yy 三者所指向的value数组的真实位置
  Field hello_field = hello.getClass().getDeclaredField("value");
  hello_field.setAccessible(true);
  char[] hello_value = (char[]) hello_field.get(hello);
  System.out.println( hello_field.get(hello));

  Field xx_field = xx.getClass().getDeclaredField("value");
  xx_field.setAccessible(true);
  char[] xx_value = (char[]) xx_field.get(xx);
  System.out.println(xx_field.get(xx));

  Field yy_field = yy.getClass().getDeclaredField("value");
  yy_field.setAccessible(true);
  char[] yy_value = (char[]) yy_field.get(yy);
  System.out.println(yy_field.get(yy));
  /**
   * 经过反射获取到这三个字符串对象的最底层的引用数组value,发现如果一开始内容一致的话,java底层会将创建的字符串对象指向同一个字符数组
   * 
   */

  // 通过反射修改字符串引用的value数组
  Field field = hello.getClass().getDeclaredField("value");
  field.setAccessible(true);
  char[] value = (char[]) field.get(hello);
  System.out.println(value);
  value[5] = '^';
  System.out.println(value);

  // 验证xx是否被改变
  System.out.println(xx);
 }

结果呢?

false
true
false
[C@6d06d69c
[C@6d06d69c
[C@6d06d69c
Hello World!
Hello^World!
Hello^World!

真的改变了。

而我们也可以发现,hello,xx, yy最终都指向了内存中的同一个value字符数组。这也说明了Java在底层做了足够强的优化处理。

当创建了一个字符串对象时,底层会对应一个盛装了相应内容的字符数组;此时如果又来了一个同样的字符串,对于value数组直接获取刚才的那个引用即可。(相信我们都知道,在Java中数组其实也是一个对象类型的数据,这样既不难理解了)。

不管是字符串直接引用方式,还是new一个新的字符串的方式,结果都是一样的。它们内部的字符数组都会指向内存中同一个“对象”(value字符数组)。

总结

稍微有点乱,但是从这点我们也可以看出String的不可变性其实仍旧是对外界而言的。在最底层,Java把这一切都给透明化了。我们只需要知道String对象有这点特性,就够了。

其他的,日常应用来说,还是按照String对象不可变来使用即可。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# java  # String  # 可变性的分析  # Sting  # 不可变吗?java  # String可变  # JAVA不可变类(immutable)机制与String的不可变性(推荐)  # 这篇文章  # 池中  # 都是  # 最底层  # 几个  # 有一  # 改变了  # 是在  # 你就  # 却是  # 你会  # 则是  # 两种  # 可以用  # 很有  # 只需  # 其他的  # 会在  # 是从  # 我们可以 


相关文章: 重庆市网站制作公司,重庆招聘网站哪个好?  建站之星后台管理:高效配置与模板优化提升用户体验  如何通过.red域名打造高辨识度品牌网站?  建站之星安装路径如何正确选择及配置?  北京建设网站制作公司,北京古代建筑博物馆预约官网?  完全自定义免费建站平台:主题模板在线生成一站式服务  建站之星安装步骤有哪些常见问题?  东莞市网站制作公司有哪些,东莞找工作用什么网站好?  c# F# 的 MailboxProcessor 和 C# 的 Actor 模型  为什么Go需要go mod文件_Go go mod文件作用说明  建站之星代理如何获取技术支持?  如何获取上海专业网站定制建站电话?  如何使用Golang安装API文档生成工具_快速生成接口文档  如何用花生壳三步快速搭建专属网站?  网站设计制作公司地址,网站建设比较好的公司都有哪些?  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  建站之星导航如何优化提升用户体验?  如何选择高效便捷的WAP商城建站系统?  动图在线制作网站有哪些,滑动动图图集怎么做?  简历在线制作网站免费版,如何创建个人简历?  ,如何利用word制作宣传手册?  建站之星安装失败:服务器环境不兼容?  深圳网站制作案例,网页的相关名词有哪些?  如何挑选最适合建站的高性能VPS主机?  广州建站公司哪家好?十大优质服务商推荐  湖北网站制作公司有哪些,湖北清能集团官网?  如何在橙子建站上传落地页?操作指南详解  海南网站制作公司有哪些,海口网是哪家的?  如何通过NAT技术实现内网高效建站?  视频网站制作教程,怎么样制作优酷网的小视频?  网站制作的软件有哪些,制作微信公众号除了秀米还有哪些比较好用的平台?  专业网站设计制作公司,如何制作一个企业网站,建设网站的基本步骤有哪些?  如何有效防御Web建站篡改攻击?  淘宝制作网站有哪些,淘宝网官网主页?  网站网页制作专业公司,怎样制作自己的网页?  定制建站如何定义?其核心优势是什么?  我的世界制作壁纸网站下载,手机怎么换我的世界壁纸?  制作网站哪家好,cc、.co、.cm哪个域名更适合做网站?  c# 在高并发下使用反射发射(Reflection.Emit)的性能  如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?  北京网站制作网页,网站升级改版需要多久?  子杰智能建站系统|零代码开发与AI生成SEO优化指南  c# await 一个已经完成的Task会发生什么  如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?  如何在企业微信快速生成手机电脑官网?  如何选择CMS系统实现快速建站与SEO优化?  昆明网站制作哪家好,昆明公租房申请网上登录入口?  高性能网站服务器配置指南:安全稳定与高效建站核心方案  jQuery 常见小例汇总  如何在Mac上搭建Golang开发环境_使用Homebrew安装和管理Go版本 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。