字符串常量的相关的翻译

    我认为理解这个问题的关键点就在于理解String对象和它的内容之间的关系,内容也就是String对象的私有成员,一个char数组(保存了字符串的编码,也就是String类中的的`value`成员,其类型为 char[] )。String类是一个简单的封装,它包装了char数组 并 提供了一些有限的方法来修改数组,同时还会保持该数组的不变性(即不修改数组本身,而是对修改产生新的对象),同时String对象还会记住数组的哪一部分是被实际使用到的(见下方)。这一切意味着你可以有两个不同的String对象(相当轻量级的)来指向同一个char数组。(PS:个人批注,除了基本类型外,其它的类型均是存放引用,数组也是一样的,所以String对象内的char数组,其实是说 String对象内的char数组的引用)。

   我将会给你看几个例子,以及每个String对象的hashCode()和 以及它们内部char数组的hashCode() ,然后我还会贴出 java -c -verbose的输出,以及展示我所提供的测试类的常量池。请不要把常量池和字面量池混淆,它们有些许的不同。

预先准备

   为了达到我们的测试目的,我创建了一个用于解开String对象包装的实用方法:

   

rivate int showInternalCharArrayHashCode(String s) {
    final Field value = String.class.getDeclaredField("value");
    value.setAccessible(true);
    return value.get(s).hashCode();
}

  这个方法将会打印出char数组的hashCode,有效的帮助我们理解特定的字符串是否指向了同样的char数组。请不要着急,耐心往下看。

两个字符串字面量存在于同一个class

String one = "abc";
String two = "abc";
    顺带一提,如果你简单的写出"ab" + "c",java编译器将会在编译期进行串联,生成的最终代码将会与上面生成的一毛一样,当然这种情况也只限于编译器知道你的所有字符串的情况下才是等效的。(ps:个人批注,例如 "a" + 变量b + "c " 中有不确定的变量b,因此是无法自动串联在一起的)

类常量池

   每个class文件有一个自己的常量池 ,常量池维护了一个在代码中多次出现的常量的集合。它包含了经常出现的字符串,数值,方法名,以及其它的内容。
   这里贴出上面的示例代码的常量池内容 :
   
const #2 = String   #38;    //  abc
//...
const #38 = Asciz   abc;
  重要的是要注意的是字符串常量对象(#2)和字符串指向的Unicode编码文本“abc”(#38)之间的区别。 

字节码

  这里是生成的字节码。 请注意,一个和两个引用都分配了相同的#2常量,指向“abc”字符串:
ldc #2; //String abc
astore_1    //one
ldc #2; //String abc
astore_2    //two

输出

  我为每一条测试语句使用如下的打印输出:
System.out.println(showInternalCharArrayHashCode(one));
System.out.println(showInternalCharArrayHashCode(two));
System.out.println(System.identityHashCode(one));
System.out.println(System.identityHashCode(two));

没有发生任何惊喜,这两对输出是相等的:
23583040
23583040
8918249
8918249

这也就意味着不仅仅是每个对象指向了相同的char数组所以它们相等,还意味着变量 one 和 two 是两个相同的引用,因此 one == two 的结果必然会是true, 很显然,如果变量 one 和 变量two 指向了同一个String对象,那么 one.value 和 two.value 也一定会相等。

字面量 以及 new String()

   现在这个例子我们将会调整成我们想要看到的样子 —— 一个 普通字面量 以及 一个new String 将使用相同的字面量,那么它将会如何工作?
  
String one = "abc";
String two = new String("abc");
   实际上这个"abc"字面量在源码当中被使用了两次,这将会给你些许暗示。

类常量池
  
    与上面的没什么区别

字节码
  
ldc #2; //String abc
astore_1    //one

new #3; //class java/lang/String
dup
ldc #2; //String abc
invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
astore_2    //two
  这里一定要注意看,第一个对象的创建与先前所展示的一毛一样,没有任何不一样的地方,它只是获取了常量池当中已被创建的String (#2) 的一个 常量引用。然而第二个对象的创建则通过常规的构造函数进行。但是!第一个String对象将作为参数传入第二个对象的构造函数,你可以理解为:
String two = new String(one);
输出
    这次的输出有些令人惊讶,第二对表示String对象的引用输出是可以理解的 —— 因为我们创建了两个String对象,一个String对象是在常量池为我们创建的(PS:个人批注,我理解为所谓的 运行时常量池,就是把常量池中的部分东西实例化后 所存放的地方 ,例如 字符串字面量实例化成一个String对象,而该对象的引用则被虚拟机所持有,并在代码需要该常量String的时候 赋值给代码中使用到的地方 ),而第二个String对象则是我们手工进行创建的。但是为什么,但为什么 第一对输出 表明这两个字符串对象指向相同的char [] 数组?!
41771
41771
8388097
16585653
  然而当你看了String(String)构造函数的源码后你就会对这一切不感冒(非常的容易理解):
public String(String original) {
    this.offset = original.offset;
    this.count = original.count;
    this.value = original.value;
}
看到了吗?当你基于一个已存在的String对象 来创建一个新的String对象的时候,将会重复使用char 数组,String对象是不会变的,在不涉及对String对象发起改变的时候,根本不会拷贝内存,只是简单的将引用指向而已。

运行时修改 以及 intern()

   接下来来看看修改时会发生什么情况:
  
String one = "abc";
String two = "?abc".substring(1);  //also two = "abc"
   其实 java 编译器并不够聪明,所以也不会在编译期间算不出第二句的值,因此你会看到如下的情况:

类常量池
 
  我们看到了两个常量字符串引用引向了两个不同的常量字面量:
  
const #2 = String   #44;    //  abc
const #3 = String   #45;    //  ?abc
const #44 = Asciz   abc;
const #45 = Asciz   ?abc;
字节码

ldc #2; //String abc
astore_1    //one

ldc #3; //String ?abc
iconst_1
invokevirtual   #4; //Method String.substring:(I)Ljava/lang/String;
astore_2    //two


第一个字符串同往常一样。第二个字符串通过"?abc"进行创建,并调用了substring(1)。

输出

   没有什么特别的地方 —— 我们有两个不同的字符串,指向了不同的 char[] 数组:
27379847
7615385
8388097
16585653

嗯,两个char数组并非是不同的,equals() 方法仍然会返回true。我们现在有了同一个字符串的两个副本。

现在我们做一件事情,运行如下代码:
two = two.intern();
执行后 再看看 hashCode。 不仅仅是 one 和 two 指向了相同的 char数组,就连它们两个也是相同的引用了!
11108810
11108810
15184449
15184449
这意味着one.equals(two) 和 one == two 会通过测试。于此同时 我们还节省了部分的内存空间,因为字符串 "abc" char数组 现在只在内存中有一份拷贝 (另一个将会因为没有引用而被垃圾回收器回收)



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值