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

首先肯定的是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. Python输出中文到文件时的字符编码问题

    今天在使用Python的GUI平台wxPython时,写了一个只有打开.编辑.保存功能的简易笔记本,代码如下: #coding:utf-8 import wx def load(event): f = ...

  2. Python 多版本共存之pyenv

    经常遇到这样的情况: 系统自带的 Python 是 2.6,自己需要 Python 2.7 中的某些特性: 系统自带的 Python 是 2.x,自己需要 Python 3.x: 此时需要在系统中安装 ...

  3. (二)js选择结构

    1.js的执行顺序. a)    一般按照书写的顺序来执行. b)    另外一种是通过判断然后执行下一项语句. 注:一般讲js语句写在body内容的最后来执行. 2.js的结构 a)    顺序结构 ...

  4. 转载:将STM32的标准库编译成lib使用【图文】

    from:http://www.cnblogs.com/zyqgold/p/3189719.html 百度上边也有不少关于lib文件的文章,恰巧看到该博文,感觉该博文的条理清晰,步骤明确,故复制到这个 ...

  5. CodeForces - 662A:Gambling Nim (求有多少个子集其异或为S)(占位)

    As you know, the game of "Nim" is played with n piles of stones, where the i-th pile initi ...

  6. win2003从组策略关闭端口(445/135/137/138/139/3389等)教程

    一些恶劣的病毒会从某些端口入侵计算机,因此关闭某些用不到的而又具有高风险的端口就显得很有必要,是服务器管理员要做的基本的安全防范.本文将介绍win2003系统在组策略关闭某一个端口的教程,文章以关闭4 ...

  7. 51nod 1089 最长回文子串 V2(Manacher算法)

    回文串是指aba.abba.cccbccc.aaaa这种左右对称的字符串. 输入一个字符串Str,输出Str里最长回文子串的长度. 收起   输入 输入Str(Str的长度 <= 100000) ...

  8. SSH使用总结(xml配置)

    beans.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="htt ...

  9. 【spring源码学习】springMVC之映射,拦截器解析,请求数据注入解析,DispatcherServlet执行过程

    [一]springMVC之url和bean映射原理和源码解析 映射基本过程 (1)springMVC配置映射,需要在xml配置文件中配置<mvc:annotation-driven >  ...

  10. 几个ADB常用命令

    http://blog.163.com/ymguan@yeah/blog/static/14007287220133149477594/ 1. 显示当前运行的全部模拟器:    adb devices ...