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

首先肯定的是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. Elasticsearch 文档专用

    ES安装等操作 http://blog.csdn.net/cnweike/article/details/33736429 https://www.elastic.co/guide/cn/elasti ...

  2. 开源项目Universal Image Loader for Android 说明文档 (1) 简介

     When developing applications for Android, one often facesthe problem of displaying some graphical ...

  3. JAVA并发全景图1.1版本

    感谢微信群"Spring Boot那些事"兄弟们的热心整理和总结

  4. pgrep 和 pkill 使用小记

    在停止指定进程时,经常使用如下命令: kill `ps aux | grep -w program_name | grep -v grep | awk '{print $2}'` 使用 pgrep 和 ...

  5. Java中最常见的十道面试题

    第一,谈谈final, finally, finalize的区别. final?修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承.因此一个类不能既被声明为 ...

  6. (一)java概述

    1.Java1995年斯坦福大学网络公司推出的一门高级语言.一种面向网络,完全的面向对象,完全可靠跨平台的语言.      java:一种面向对象的高级语言           将现实生活中的事物以及 ...

  7. linux基础【文件夹含义】

    linux文件目录是一个树状的目录 bin -->可执行文件 boot-->操作系统引导文件,系统内核,启动信息 dev -->device,设备信息,计算机硬件设备信息 etc - ...

  8. 21天学通C++_Day2

    继续学习,今天满课,相对学习内容较少,下面罗列内容: 0.常量 ▪字面常量: ▪使用关键字const声明的常量,const double Pi = 22.0/7; //后面有分号,跟定义变量一样 ▪使 ...

  9. Sublime Text 3下Emmet使用技巧

    链接:http://jingyan.baidu.com/article/92255446a87900851648f4d6.html

  10. django中的locale()函数

    就是可以将函数中的变量与其对应的值,自动包裹成字典传到静态页面 参考链接:http://www.jb51.net/article/69558.htm