关于堆中字符串的存放
string s1="123" string s2="123" string s1="456"
此时s1输出为456 而s2仍然为123
因为在使用 String str = "字符串" 的方式来创建String变量的时候,那么String的值便会存储在String常量池中,在我们以这种方式创建String变量时,编译器会先判断你这个内容有没有已经在常量池出现过了,如果已经出现过,那么就不会再在常量池中使用空间来存放一个相同的内容,这个内容只会固定有一个引用,所以在创造相同内容的String的时候,他们的引用都是相同的。又有一种情况:一开始A和B内容相同,就是说s1与s2的引用都相同时,此时将s1的内容更改,那么s1的内容在常量池中就会使用另一块空间,那么相应的s1的引用也会改变,而s2的引用并不会改变,因为s2此时还是存储的原来的内容。我们可以来看简易的图解:
栈区、堆区、常量区、静态区、代码区 ,static的变量放在静态区
在C#中,内存管理是由.NET运行时(CLR - Common Language Runtime)负责的。CLR将内存分为几个不同的区域,用于存储不同类型的数据。以下是这些区域的简要说明:
-
栈区(Stack):
-
栈是一种后进先出(LIFO - Last In, First Out)的数据结构。
-
局部变量和方法调用时的参数通常存储在栈上。
-
栈的大小是有限的,并且在线程创建时分配。
-
-
堆区(Heap):
-
堆是用于动态内存分配的内存区域。
-
对象通常是在堆上分配的。
-
.NET运行时的垃圾回收器负责管理堆内存,自动回收不再使用的对象。
-
-
常量区(Read-Only Data Section):
-
常量数据存储在只读数据区,这些数据在程序的整个生命周期内都不会改变。
-
-
静态区(Static Data Section):
-
静态变量和类变量存储在静态区。
-
静态变量的生命周期与应用程序域的生命周期相同,它们在第一次使用时初始化,并在应用程序域卸载时销毁。
-
-
代码区(Code Section):
-
包含编译后的程序代码,即中间语言(MSIL - Microsoft Intermediate Language)代码。
-
代码区是只读的,存储了方法的IL代码和元数据。
-
-
JIT编译后的代码:
-
虽然不是传统意义上的内存区域,但.NET运行时的JIT(Just-In-Time)编译器会将IL代码编译成本地机器代码,这些代码在执行时存储在内存中。
-
-
全局常量区:
-
有些实现可能还有一个全局常量区,用于存储全局常量。
-
-
线程局部存储(Thread Local Storage, TLS):
-
用于存储线程特有的数据,比如线程的堆栈底和线程的异常处理上下文。
-
关于 static
变量,它们确实存储在静态区。每个 static
变量都属于一个类或结构,而不是类的实例。因此,它们在程序集加载时初始化,并在程序集卸载时释放。由于 static
变量与任何特定实例无关,它们在整个应用程序域中是唯一的。
修饰符介绍
在C#中,修饰符(Modifiers)用于定义类、方法、属性、字段等成员的访问级别和行为。以下是一些常用的修饰符:
-
访问修饰符:
-
public
:成员可以被任何其他代码访问。 -
private
:成员只能在其定义的类内部访问。 -
protected
:成员只能在其定义的类或派生类中访问。 -
internal
:成员只能在同一个程序集(Assembly)中访问。 -
protected internal
:成员可以在同一个程序集或派生类中访问。
-
-
非访问修饰符:
-
static
:表示该成员属于类本身,而不是类的实例。 -
const
:表示一个常量,其值在编译时已知,并且在运行时不可更改。 -
readonly
:表示一个字段可以在声明时或构造函数中被赋值,但之后不可更改。 -
abstract
:用于声明抽象类或抽象方法,抽象方法必须在派生类中被重写。 -
sealed
:用于阻止继承类进一步重写方法。 -
override
:用于重写基类中的虚方法或属性。 -
virtual
:用于声明一个方法或属性,该方法或属性可以在派生类中被重写。 -
event
:用于声明事件,通常与委托一起使用。 -
new
:用于隐藏继承类中的同名成员。 -
volatile
:指示字段可能被多个线程同时访问,要求编译器和运行时确保对该字段的读写操作的原子性。 -
async
:用于声明异步方法,允许使用await
关键字。 -
unsafe
:指示包含指针操作的代码块,只能在unsafe
上下文中使用。
-
-
接口实现修饰符:
-
当实现接口成员时,可以使用
public
或private
修饰符来指定实现的可访问性。
-
-
参数修饰符:
-
ref
:表示方法调用时传递的参数是按引用传递的。 -
out
:类似于ref
,但调用者不需要在调用前初始化参数。 -
params
:表示方法可以接收不定数量的参数,这些参数被封装在数组中。
-
-
特性修饰符(Attribute Modifiers):
-
用于定义特性(Attribute)的适用范围,如
AttributeUsage
。
-
方法重载和重写
方法重载(Overloading)
方法重载是指在同一个类中可以有多个同名的方法,但它们的参数列表必须不同。参数列表的不同可以是参数的数量不同,或者是参数的类型不同。
特点:
-
方法名相同。
-
参数列表不同(参数数量、类型、顺序)。
-
返回类型可以相同也可以不同。
-
重载方法在编译时进行解析。
方法重写(Overriding)
方法重写是指在派生类(子类)中重写基类中的虚方法(virtual)或抽象方法(abstract)。它允许派生类提供自己的实现方式。
特点:
-
基类方法必须被声明为
virtual
、abstract
或override
。 -
派生类使用
override
关键字来重写方法。 -
重写方法的访问级别不能比基类方法更严格。
-
返回类型必须与被重写的方法一致,或者为基方法返回类型的子类型。