1.类与对象的关系
类是对一类事务的统称,是抽象的,不能拿来直接使用,比如汽车,没有具体指哪一辆汽车
对象是一个具体存在的,看的见,摸得着的,可以拿来直接使用,比如我家的那辆刚刚买的新汽车,就是具体的对象
对象是根据类这个模板创建出来的,类里面有哪些特性或者功能对象里面也有,多不得,少不得
2.new 一个对象做了哪些事情?
Person person = new Person();new是一个运算符
(1)在内存中开辟一块合适大小的空间
(2)在这个空间里创建对象
(3)调用这个对象的构造函数
(4)返回这个空间的引用地址
3.访问修饰符
类的访问修饰符只有public和internal,内部类的访问修饰符可以为private
4.属性
属性的本质是一个get和一个set方法,而set方法中定义了一个参数名称为value,所以我们可以在set方法中访问value
属性就是对字段的封装
属性本身不存值,值是存在属性封装的对应的字段中
只要类中的字段要被外界访问,就需要把这个字段封装为属性
5.构造函数
(1)构造函数访问修饰符一般情况下是public,没有返回值,方法的名称与类名相同
(2)构造函数在创建这个对象的时候被自动调用,程序员无法手动调用
(3)构造函数有什么用?如果我们希望在创建这个类的对象的同时需要执行一些代码,我们就可以将这些代码写到构造函数中
(4)this关键字代表当前对象,当前运行在内存中的那一个对象,this关键字可以调用其他的构造函数
(5)构造函数调用构造函数:
public Dog(int age,string name):this(age)
{
}
public Dog(int age)
{
}
调用的时候:Dog dog = new Dog(2,"旺财");程序会先调用Dog(int age)构造函数,然后在调用Dog(int age,string name)构造函数
(6)隐式构造函数:如果程序员没有为类手动添加构造函数,C#编译器在编译的时候会自动添加一个无参数的构造函数
如果添加了构造函数,C#编译器在编译的时候则不会添加
6.常量和只读变量
(1)const修饰的数据叫做常量,
常量一旦声明常量的值就不能改变,因为C#编译器在编译的时候声明常量的那句代码不见了,在使用常量的地方就用常量的值替换了
比如
const string name = "小明";
Console.WriteLine(name);
编译之后变为
Console.WriteLine("小明");所以不能赋值
readonly修饰的变量只读变量
readonly string name = "小明";
public Person()
{
}
编译后
readonly string name ;
public Person()
{
this.name = "小明";
}
const和readonly的区别:
const不能修改值,readonly只能在构造函数中修改值
const在编译的时候就要确定值,readonly在运行时要确定值
7.枚举
枚举是一个值类型,每一个枚举成员都对应了一个整型的数值,这个数值默认是从0开始递增
枚举和int类型转换:int a = (int)Direction.South; int i = 2; Direction d = (Direction)i;
枚举和string类型转换:string str = "South"; Direction d = (Direction)Enum.Parse(typeof(Direction),str,true); //true表示忽略
大小写
枚举值所对应的数值默认是int类型的,可以在枚举的名字后面加一个冒号来指定这个数值的类型,只能是整型的(byte,short。。。)
如:
enum Direction:byte
{
}
好处:限定变量的取值范围,在编码过程中不容易出错
8.结构struct
(1)结构中可以定义字段、属性、方法、构造函数,也可以通过new关键字来创建对象
(2)无参数的构造函数无论如何C#编译器都会自动生成,所以我们不能显示的声明无参数的构造函数
(3)在构造函数中必须要为结构体的所有的字段赋值
(4)在构造函数中为属性赋值,不认为是对字段赋值,因为属性不一定是去操作字段
(5)结构是一个值类型,在传递结构变量的时候,会将结构对象里面的每一个字段复制一份,然后拷贝到新的对象中
(6)不能定义自动属性,因为自动属性会生成一个字段,而这个字段必须要求在构造函数中,而我们又不知道这个字段
(7)声明结构体对象可以不new关键字,但是这个时候结构体对象的字段没有初始值,因为没有调用构造函数,而构造函数中必须为字段赋
值,所以通过new关键字创建的结构体对象的字段就有默认值。
(8)当我们要表示一个轻量级的对象的时候,就可以定位结构以提高速度。根据传值的影响来选择,希望传引用就定义为类,希望传拷贝就
定义为结构
9.垃圾回收
托管代码:被CLR管理的代码
非托管代码:不被CLR管理的代码
应用程序域:当。net应用程序在CLR中运行的时候,CLR会创建一个单独的应用程序域,让。net应用程序在应用程序域中运行
分配在栈空间的变量一旦执行完其所在的作用域(即"{ }"中),这个变量就会CLR立即回收
分配在堆里面的对象没有任何对象引用的时候,这个对象就被标记为“垃圾对象”,等待垃圾回收期回收。
GC会定时的清理堆空间中的垃圾对象,GC清理垃圾对象的频率程序员无法决定,由CLR自动控制
当一个对象被标记为“垃圾对象”的时候,这个对象不一定立即被回收
垃圾回收运行机制:
垃圾回收会根据应用程序的运行情况来使用合适的算法,其中一种算法如下:
CLR会预先开辟一块空间,当创建对象的时候会放在第0代的内存中,当第0代的空间满的时候,GC开始回收第0代,不被标记垃圾的对象会移
动到第1代内存中,然后第0代空间变为空,当再次创建对象的时候会存储在第0代空间,一直这样重复,第1代空间不满的时候不会回收。当
第1代空间满的时候,会垃圾回收第1代,然后把老的对象移动到第1代,新对象存储在第0代。第1代到第2代是和第0代到第1代一样,当三代
都满的时候会报异常说内存溢出
GC.GetGeneration(p)获取指定对象是属于第几代,总共有3代,从0开始,0,1,2
GC.Collect();立即让垃圾回收器对所有的代进行垃圾回收
GC.Collect(0);表示对第0代回收GC.Collect(1);表示对第0代和第1代回收
Person p = new Person();
GC.GetGeneration(p);值为0
GC.Collect();
GC.GetGeneration(p);值为1
GC.Collect();
GC.GetGeneration(p);值为2
GC.Collect();
GC.GetGeneration(p);值为2
10.析构函数
析构函数不能有访问修饰符,不能有参数,所有不能被继承或重载,在对象被垃圾回收器回收的时候,析构函数被GC自动调用,当没有垃圾
对象的时候不会被调用,
执行一些清理善后的操作
~Person()
{
}
11.静态成员
(1)在这个类第一次被加载的时候,这个类下面所有的静态成员会被加载,静态成员是属于类的,实例成员是属于对象的
(2)静态成员只能被创建一次,所以静态成员只有一份,实例成员有多少个对象就创建多少次
(3)静态成员被创建在静态存储区中,一旦创建直到程序退出才会被回收
(4)什么时候定义为静态的?变量需要被共享的时候,方法需要被反复调用的时候
(5)静态方法中不能直接调用实例成员,因为静态方法被调用的时候,对象有可能没有创建
(6)this/base关键字在静态方法中不能使用,因为有可能对象不存在
(7)在实例方法中可以调用静态成员,因为这个时候静态成员肯定存在
静态成员和实例成员区别:
(1)生命周期不同,静态成员在程序退出时才会被回收,实例成员则在该对象被垃圾回收器回收时才会被回收
(2)存储的位置不同,静态成员存储在静态存储区中,实例成员存储在堆的对象里面
12.静态类
(1)静态类中只能声明静态成员
(2)静态类中不能有实例构造函数
(3)静态类中不能被实例化,因为没有实例成员,实例化无意义
(4)静态类不能被继承,因为本质是一个抽象的密封类,所以不能被继承也不能被实例化
(5)如果一个类下面的所有成员都需要被共享,那么就可以把这个类定义为静态类
(6)不能声明一个静态类的变量,比如静态类Person p;
静态构造函数:非静态类也可以有静态构造函数
静态类的成员第一次被访问之前就会执行静态构造函数
静态构造函数只被执行一次