String — 深入理解String

String类型是我们常用的基础数据类型之一,他是一个特殊的引用类型,在.NET里面算是少有的异类,那么特殊在什么地方呢?接下来就让深入的了解String

一、特殊之处

(1)、创建特殊性:String对象不能使用newobj指令创建,而是ldstr指令创建,在实现机制上,CLR给了特殊的照顾优化内存

(2)、应用上,String类型表现为值类型,但在内存中,String类型表现为引用类型,存储在托管堆里面

(3)、两次创建内容相同的String对象可以指向相同的地址

(4)、String类是跨应用程序域的,可以在不同的应用程序域中访问到同一String对象

(5)、String类是密封类,不能被继承

(6)、因为字符串驻留是进程级别的,所以可以跨AppDomin存在,即同一个字符串对象可以在不同的应用程序域被访问,突破了AppDomin的隔离,其原因还是字符串的恒定性,因为是不可变的,所以没必要在隔离

二、字符串的恒定性

string对象和其他类型最大的区别就是它的恒定性,指的是:字符串创建后,系统会在托管堆上开辟一块连续空间,我们无法通过人为的方式去修改它,所有对string类型操作的,实际上都是返回了一个新的string,其本身不产生任何差别。

当然这样有利也有弊:利:保证原string对象的稳定性,不会出现线程同步问题。

弊:创建新的字符串对象,会消耗性能和内存。所以CLR使用了一项技术来解决这个问题——字符串驻留

三、字符串驻留

先看代码

string s1 = "小红";
string s2 = "小" + "红";
string s3 = "小";
string s4 = s3 + "红";
Console.WriteLine(ReferenceEquals(s1, s2));//ReferenceEquals():是Object的一个方法,比较引用是否相等,不要用于值类型比较Console.WriteLine(ReferenceEquals(s1, s4));

执行结果:

答案是不是有点出乎意料?

按理说,s1和s2是两个不同的string对象,为什么引用会相同呢?看结果就算是相同的,那么s4的值也是"小红",为什么和s1又不一样了?

好的,带着我们的问题,让我们首先来了解下什么叫做字符串驻留机制:

对于相同的字符串,CLR不会为期重新分配内存空间,而是公用同一内存地址。

那么在内部CLR是怎么实现这一功能的?

CLR内部维护了一个hash table 来管理其创建的大部分string对象。其中key是string本身,而value为分配给对应的string的内存地址。如下如所示

现在我们在来回过头看一下那两个结果,为什么是true和false

第一个结果:s1和s2,在创建s1的时候,哈希表内的key并没有"小红"这个值,所以当JIT编译的时候,会在这个哈希表的key里面添加一个"小红",然后把s1在内存中的地址方法value里面,当创建s2的时候,JIT又在这个哈希表内进行查找,然后发现key里面已经有了这个值,所以,就把key-Value对应的value赋值给了s2,所以说他们两个的引用是相同的,那么为什么第二个结果是false呢?

第二个原因:虽然s4的结果最后也是"小红",但是在创建的过程中,发生了变化,s4是动态生成的字符串,这样的字符串是不会被添加到哈希表中进行维护的,所以返回为false。

这样,我们就能很容易的解释上述代码的原因了。

补充:对于动态生成的字符串,没有添加到CLR的哈希表中导致字符串驻留失效的,我们可以通过两个静态的string方法,手工启用字符串驻留机制

string s1="小红";
string s3="小";
//s4 = string.Intern(s3+"红");  //true
s4 = string.IsInterned(s3+"红");  //true
Console.WriteLine(ReferenceEquals(s1, s4));

public static String Intern(String str);

//如果暂存了 str,则返回系统对其的引用;否则返回对值为 str 的字符串的新引用。

public static String IsInterned(String str);

//此方法在暂存池中查找 str。 如果已经将 str 放入暂存池中,则返回对此实例的引用;否则返回 null。

两者的区别:

同样,如果我们不想使用字符串驻留机制,可以使用string.copy()来解除这一限定

string s1 = "小红";
string s2 = string.Copy(s1);
Console.WriteLine(ReferenceEquals(s1, s2));  //false

public static String Copy(String str);

特别指出:字符串驻留进程级别的,可以跨应用程序域(AppDomain)而存在,也就是不同的类里面相同的字符串值同样是同一个引用。垃圾回收不能释放哈希表中的引用字符串对象,只有进程结束这些对象才会被释放

时间: 2024-10-25 20:50:29

String — 深入理解String的相关文章

Java常量字符串String理解 String理解

以前关于String的理解仅限于三点:1.String 是final类,不可继承2.String 类比较字符串相等时时不能用“ == ”,只能用  "equals" 3.String  类不可更改 String 使用非常方便,因此一般涉及字符串时都用该类进行字符串处理至于String类的类在机制,则极少去探究. 直到读到下面这个例子. class X{     public static String strX="hello";}class Y{   public 

Java提高篇——理解String 及 String.intern() 在实际中的应用

1. 首先String不属于8种基本数据类型,String是一个对象.   因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. new String()和new String("")都是申明一个新的空字符串,是空串不是null: 3. String str="kvill":  String str=new String ("kvill");的区别: 在这里,我们不谈堆,也不谈

理解String不可变

问题的引入: java中只有值传递,没有引用传递,这里有两种情况,一种是传对象,一种是传基础类型,传对象其实是传对象的引用, http://guhanjie.iteye.com/blog/1683637这篇文章描述了一些情况: 基础类型传递的时候,直接把内存里面真正的值传递过去,而在对象传递的时候,是把对象的引用传递过去. 那么问题来了,为什么String作为对象,再方法调用的时候却跟基础类型调用的效果是一样的. 给出代码: public class Example { String str =

关于 String 自我理解

String 的一些认识: String对象是不可变,所以使用 final 修饰 字符串拼接,合理利用 StringBuilder(线程非安全),StringBuffer 线程安全 常用方法就不详细介绍 ■构造函数 public final class String implements java.io.Serializable, Comparable<String>, CharSequence { 实现 java.io.Serializable 接口,支持序列化 实现Comparable 接

深入理解String类

1.String str = "eee" 和String str = new String("eee")的区别 先看一小段代码, 1 public static void main(String[] args) { 2 String str1 = "eee"; 3 String str2 = "eee"; 4 String str3 = new String("eee"); 5 System.out.pri

c#基础系列2---深入理解 String

"大菜":源于自己刚踏入猿途混沌时起,自我感觉不是一般的菜,因而得名"大菜",于自身共勉. 扩展阅读:深入理解值类型和引用类型 基本概念 string(严格来说应该是System.String) 类型是我们日常coding中用的最多的类型之一.那什么是String呢?^ ~ ^ String是一个不可变的连续16位的Unicode代码值的集合,它直接派生自System.Object类型. 与之对应的还有一个不常用的安全字符串类型System.Security.Sec

Java基础3:深入理解String及包装类

Java基础3:深入理解String及包装类 String的连接 @Testpublic void contact () {    //1连接方式    String s1 = "a";    String s2 = "a";    String s3 = "a" + s2;    String s4 = "a" + "a";    String s5 = s1 + s2;    //表达式只有常量时,编译

Java中String的理解

Java中String的理解 最近在读String的源码,看了些String的文章,自己对String作了下总结记录下来. 1.String为什么是不可变的? String是final类,不可继承,其方法也不可被覆盖,避免从子类操纵父类属性:String的值保存在private final char[]数组中,本质是一个字符数组,私有则外部不可访问和修改,final引用则引用(或说引用的值)不变.引用可以简单地认为是堆上对象的首地址.String内部的private int hash,缓存has

几张图轻松理解String.intern()

在翻<深入理解Java虚拟机>的书时,又看到了2-7的 String.intern()返回引用的测试. 其实要搞明白String.intern(),我总结了下面几条规则: 一.new String都是在堆上创建字符串对象.当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用. 二.通过字面量赋值创建字符串(如:String str=”twm”)时,会先在常量池中查找是否存在相同的字符串,若存在,则将栈中的引用直接指向该字符串:若