本文只是个人总结见解,勿喷

首先肯定的是string是引用类型

string s_a = "yhc";

string s_b = s_a;

if(s_a.Equals(s_b))

Console.WriteLine("相同?");

else

Console.WriteLine("不相同");

输出是“相同”,让s_b=s_a,本质是让s_b指向了yhc在堆上的存储位置,此时s_a和s_b都指向了yhc在堆上的存储位置。

理论上说如果修改s_a的值,例如s_a=”wq”,b也会跟着变化,但是实际上不是的,在c#里面,如果修改其中一个string对象的值比如s_a,系统会为s_a再重新申请一块内存用来存放修改后的s_a的值wq,而b依然指向于a曾今的内存位置。

这就是为什么在如果要频繁对string操作的时候建议用stringbuilder,因为stringbuilder类似c++中的标准库string,是可以动态增加内存大小的,而c#中的string是固定大小,你string一个对象就给你分配一个固定大小的内存空间,这个对象一旦创建就无法更改,如果你要往里面增加内容,我就必须新建一个string对象,把值拷贝进来,然后把之前的string对象删除,频繁操作string会带来效率的降低,毕竟创建对象,分配内存,删除对象这个过程需要耗时耗资源。

我们可以通过比较内存地址来断定这一结论(ReferenceEquals静态方法用于比较对象的引用是否一致,而不是简单的值),如果s_a和s_b指向的是同一地址,这里的比较应该返回相同
s_a =
"wq";
if (ReferenceEquals(s_a,s_b))

Console.WriteLine("相同");

else

Console.WriteLine("不相同");

答案很明显,会输出“不相同”,这说明系统为s_a重新分配了新地址的内存空间,当然你也许会说为什么不用==,因为string的= =被重写后,其实比较的还是string的值,(当然如果你一定要用= =也是可以的,只不过需要装箱成object类型,让==不再为string的特例重写比较,即if(((object)s_a)==( (object)s_b))),但是= =在比较其他对象的时候可不是这样了,这是string这个引用类型表现出值类型特征的一点,其实还有一个地方也表现了string类型的值类型特征表现,就是在string类型作为参数传递的时候,传递的是string对象的引用地址,但是在传递过去的方法里面修改string对象的值却不影响本体,这是因为系统根据传送过来的地址重新构造分配了一个全新的string对象,比如调用方法 void fun(string a);并把s_a传递作为参数过来的时候,a会被系统重新分配一个地址,而不是指向s_a的地址,这是引用类型的一个特例。比如A是一个普通类,比如
A b=new A();
b.name=”yhc”;
fun(b);

//代码运行到此,如果输出,此时输出a的name值:为wq
void fun(A a)

{

//代码运行到此,如果输出,此时输出a的name值:为yhc

a.name=”wq”;//因为参数传递的是b的引用,对a的操作就是对b的操作

//代码运行到此,如果输出,此时输出a的name值:为wq

}

但是如果是这样:

A b=new A();
b.name=”yhc”;
fun(b);

//代码运行到此,如果输出,此时输出a的name值:为yhc
void fun(A a)

{

//代码运行到此,如果输出,此时输出a的name值:为yhc

a=new
A();

a.name=”wq”;

//代码运行到此,如果输出,此时输出a的name值:为wq

}

在执行玩fun(b)后的输出,就不再是wq了,因为在方法fun内部,我们把a指向了一个新建立在堆上的对象A,这个new对象在离开函数方法后会被GC垃圾回收机制回收,什么时候来回收就不知道了,当然如果是建立在堆栈上的资源会被立刻释放,但是关于建立在堆上的对象的GC回收也有特例,就是那些非托管对象,比如什么SqlConnection数据库连接、文件句柄、网络连接什么的,这些对象在方法内部new后,离开方法后不会被GC回收。

上面说的跑远里,我们回到之前,当然有人会说用ReferenceEquals来比较不够深刻和准确性,因为s_a改为wq后,值也发生了变化,用ReferenceEquals比较无法判断是值不同还是引用不同,于是我们如下修改:按照string修改后系统会自动为其分配新的内存空间的原理我们把s_a修改成yhc,即虽然修改了,但是值还是保持原来的yhc,只为了测试这个修改操作有没有重新分配内存空间

s_a = "yhc";
if (ReferenceEquals(s_a,s_b))

Console.WriteLine("相同");

else

Console.WriteLine("不相同");
我们都以为输出会是“不相同”,但是,但是结果让我们失望,系统输出“相同”,原因是微软的CLR使用了优化技术,叫做字符串驻留技术,这个技术的原理大家可能都知道,就是CLR初始化的时候会创建一个内部散列表,表为键值形式,键为字符串,比如这里的yhc,值为yhc在堆内存上存储的地址,初始化的时候散列表肯定为空了,即时编译器(JIT)编译时先去散列表找字符串yhc,第一次找没找到yhc,其就会在堆上开辟空间来存放yhc,然后把存放的地址和yhc这个字符串存储在散列表中,然后第二次又去找,就是这里的修改s_a的值为yhc,发现在散列表的键中找到了yhc这个值,于是就不再分配内存来存放修改后的yhc,而是让s_a依然指向之前这个yhc在堆上存放的空间,到此也就是说,你修改后的s_a=“yhc”,虽然修改了,但是其值没有变,还是yhc,所以被编译器的优化机制优化了,所以这个测试还是测试不出来我们要的效果。

上面测试缺陷的原理我们知道了,但是我们差点忘了我们到底是要测试什么,我们要测试的是s_a或者s_b修改后,系统为其重新分配内存空间,我们要比较修改后两者的空间地址是否一致,于是总结上面教训如下操作:

s_a=string.Copy(s_b)
if (ReferenceEquals(s_a,s_b))

Console.WriteLine("相同");

else

Console.WriteLine("不相同");

这里,s_a=string.Copy(s_b),string.Copy方法是创建一个与指定的s_b具有相同值的 System.String 的实例。注意是新实例,用这个方法则编译器不会做上面的字符串驻留技术优化,即此时s_a再次被修改了,修改的值是拷贝了s_b的的值(s_b的值为yhc),即此时s_a的值也是yhc,这时候系统输出“不相同”了,这就验证了我们string对象被修改后,系统会为他重新分配内存空间。

完整测试代码如下:
测试很简单,但是深究很多
string s_a = "yhc";

string s_b = s_a;

s_a
= string.Copy(s_b);

if (ReferenceEquals(s_a,s_b))

Console.WriteLine("相同");

else

Console.WriteLine("不相同");

Console.Read();

string的深入理解的更多相关文章

  1. java常用类,包装类,String类的理解和创建对象以及StringBuilder和StringBuffer之间的区别联系

    一.包装类的分类: 1.黄色部分的父类为Number 继承关系: Boolean Character 其他六个基本数据类型 2.装箱和拆箱 理解:一个例子,其他的都相同 装箱:Integer inte ...

  2. 从Java String实例来理解ANSI、Unicode、BMP、UTF等编码概念

    转(http://www.codeceo.com/article/java-string-ansi-unicode-bmp-utf.html#0-tsina-1-10971-397232819ff9a ...

  3. public static void main(String[] args){}函数理解

    主函数的一般写法如下: public static void main(String[] args){…} 下面分别解释这些关键字的作用: (1)public关键字,这个好理解,声明主函数为publi ...

  4. Java String 常量池理解

    String:字符串常量池 作为最基础的引用数据类型,Java 设计者为 String 提供了字符串常量池以提高其性能,那么字符串常量池的具体原理是什么,我们带着以下三个问题,去理解字符串常量池: 字 ...

  5. Java中char和String 的深入理解 - 字符编码

    开篇 https://blog.csdn.net/weixin_37703598/article/details/80679376 我们并不是在写代码,我们只是将自己的思想通过代码表达出来! 1 将思 ...

  6. String类型的理解

    引用:https://www.cnblogs.com/binyue/p/3862276.html java语言中: 变量除了八大基本数据类型(byte,short,int,long,boolean,f ...

  7. String Buffer和String Builder(String类深入理解)

      String在Java里面JDK1.8后它属于一个特殊的类,在创建一个String基本对象的时候,String会向“ 字符串常量池(String constant pool)” 进行检索是否有该数 ...

  8. String源码理解之indexOf函数

    1前言 不多说,直接上源码 2源码 我自己的理解,可能表述不清,多看几遍,不行就debug跟一遍代码自然就懂了. /** * Code shared by String and StringBuffe ...

  9. RegExp.exec和String.match深入理解

    今天在重新阅读<JavaScript权威指南>的RegExp和String的时候,看到了2个比较容易混淆的函数:RegExp的exec和String的match 这2个函数都是从指定的字符 ...

  10. 对 String 字符串的理解

    1.通过构造方法创建的字符串对象和直接赋值方式创建的字符串对象区别? 通过构造方法创建字符串对象是在堆内存. 直接赋值方式创建对象是在方法区的常量池. ==: 基本数据类型,比较的是基本数据类型的值是 ...

随机推荐

  1. UICollectionView-网格视图

    1. UICollectionView 网格视图,除了提供与UITableView同样的功能显示一组顺序显示的数据,还支持多列的布局. 2. UICollectionView 使用 > 设置Co ...

  2. Vue: webpack js basic structure

    vue webpack所用基础包: nom install vue vue-loader webpack webpack-cli webpack-dev-server vue-template-com ...

  3. L134

    这种成功和后来的研究(表明记忆本身并不是先天决定的)使爱立信总结到,记忆的行为与其说是一种习得的行为不如说是一种先天的行为. 这点我们不清楚-构思物体和找出数字模型的能力,回答问题(最好的诗人和哲学家 ...

  4. slab机制总结篇

    一: slab是为了解决内部碎片提出的,还是外部碎片? 为了解决内部碎片. 内部碎片的产生:因为所有的内存分配必须起始于可被 4.8 或 16 整除(视处理器体系结构而定)的地址或者因为MMU的分页机 ...

  5. Android以root起一个process[shell脚本的方法]

    有时候我们写的app要用uid=0的方式启动一个process,framework层和app层是做不到的,只有通过写脚本,利用am来实现.下面是具体步骤: 1.创建一个包含Main()方法Java p ...

  6. 前端之jQuery02

    文档操作 重点:创建标签,jQuery里面没有这个方法 内部(子标签) 添加到指定元素内部后面 $(A).append(B): // B作为A的最后一个儿子元素:(把B追加到A) $(A).appen ...

  7. rhel7+apache+c cgi+动态域名实现web访问

    1. 申请动态域名/安装no-ip客户端 https://blog.csdn.net/lee244868149/article/details/44095835 2. yum安装httpd 两种方法安 ...

  8. Adobe Fireworks CS6是一款集网页图片设计、制作与编辑为一体的专业软件

    Adobe Fireworks CS6是一款集网页图片设计.制作与编辑为一体的专业软件,它不仅可以轻松制作出各种动感的Gif.动态按钮.动态翻转等网络图片,还可以轻松实现大图切割,让网页加载的图片显示 ...

  9. Oracle Sql Developer 连接oracle

    PL/Sql 初次使用需要配置文件内容,对于我这种Oracle新手来说各种配置有点凌乱,所以果断选择Sql Developer. 选择它是因为初次使用的时候它不用想PL/Sql那样配置文件,而只需要添 ...

  10. HTML`CSS_网站页面不同浏览器兼容性问题解决

    目前,最为流行的浏览器共有五个:分别是ie,Edge浏览器(属于微软),火狐,谷歌(chrome)Safari和Opera五大浏览器. Trident内核:IE ,360,,猎豹,百度: Gecko内 ...