C#OOP CH04 深入类的方法
本章目标:
- 理解并会编写类的构造函数
- 会实现方法重载
- 理解类之间的通信
1. 构造函数
构造函数是类中一种特殊的方法,也叫构造方法,或者构造器,或者构造体,函数名与类名相同,不返回任何值,也不能用void修饰,主要完成对象的初始化工作。
构造函数在每次实例化类时 通过new关键字调用
1.1 无参构造函数:
默认情况下,系统会分配给类分配一个无参构造函数,并且没有方法体,用来对成员变量进行初始化操作。
可以自定义一个无参构造函数,在方法体内对类的属性进行初始化操作。
无参构造函数的语法为:
访问修饰符 类名()
{
//方法体
}
1.2 带参构造函数:
语法为: 参数列表一般用来给类的属性赋值.
访问修饰符 类名(参数列表)
{
//方法体
}
注意:
-
当我们创建一个类时,系统将自动给类分配一个默认的无参构造函数
而一旦定义了构造函数 系统将不再自动分配构造函数。
结构与枚举没有构造函数
1.3 析构函数
析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。
以C#语言为例,析构函数名也应与类名相同,只是在函数名前面加一个波浪符
~
,例如~
stud( ),以区别于构造函数。它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。
语法:
~ 类名()
{
//方法体
}
2. 方法重载(overload)
同一个类中 方法名相同 参数列表(参数个数,参数类型)不同 称为方法重载。
注意 方法重载与返回值类型无关
2.1 构造方法重载
举例如下:
public SE()
{
}
public SE(int id)
{
this.ID = id;
}
public SE(int id,String name)
{
this.ID = id;
this.Name = name;
}
// 注意 多个重载的构造函数之间 可以使用 this关键字调用
// 语法为:
public SE(int id,String name) : this(id)
{
this.Name = name;
}
> 补充访问修饰符:
C#中的访问修饰符 以及权限
public: 公共访问,不受任何限制
private: 私有访问,只限于本类成员访问,子类和实例都访问不到,类中成员没写修饰符,默认为private
protected: 受保护的,只能在本类或是子类中访问,实例不能访问
internal: 内部访问,只限于在此项目中访问,其他都访问不到 如果类没写访问修饰符,默认为internal
3. 扩展
3.1 软件设计模式之单例模式:
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。
通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例(例如 Windows系统中的回收站)
所以单例模式的要点有三个:
一是 :某个类只能有一个实例;
二是:它必须自行创建这个实例;
三是:它必须自行向整个系统提供这个实例。
设计核心:将构造器私有化,对外提供公有的获取该类实例的方法
3.2 懒汉模式:
所谓懒汉模式,正如其名,比较懒,在类加载的时候并不创建自身实例,采用延迟加载的方式,只有在运行时调用了相关的方法才会被动创建自身的实例。
代码演示如下:
/// <summary>
/// 单例模式之懒汉模式
/// </summary>
public sealed class Singleton
{
private static Singleton single;
private Singleton() {}
public static Singleton GetInstance()
{
if(null == single)
{
single = new Singleton();
}
return single;
}
}
使用Main方法测试并发情况下,懒汉模式的线程安全问题,代码如下:
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
new Thread(()=>
{
Console.WriteLine(Singleton.GetInstance().GetHashCode());
}).Start();
}
Console.ReadLine();
}
控制台输出如图所示:
从输出结果分析,很明显 , 在多线程下无法正常工作,这也是致命的缺陷。
现在对上面的示例进行修改,因为最简单的方法就是考虑同步,这里采用synchronized实现。
/// <summary>
/// 单例模式之懒汉模式
/// </summary>
public sealed class Singleton
{
private static Singleton single;
private static readonly Object obj = new object();
private Singleton() {}
public static Singleton GetInstance()
{
if(null == single)
{
lock (obj)//解决线程并发时 系统创建多个实例的问题
{
if (null == single)
single = new Singleton();
}
}
return single;
}
}
以上写法能够在多线程并发环境中很好地工作,并且看起来它也具备了延迟加载的特性。但是很遗憾,这种处理方式效率不高,可以说95%以上的情况都不需要同步。
那么对于线程安全问题,还有另一种解决方式,即饿汉模式。
3.3 饿汉模式:
饿汉模式在类加载的时候就已经完成了初始化操作,所以类加载较慢,但是获取对象的速度很快。由于饿汉模式在类初始化时就完成了实例化,所以它是不存在线程安全问题的。修改以上的代码:
/// <summary>
/// 单例模式之饿汉模式
/// </summary>
public sealed class Singleton
{
//饿汉式
private static Singleton single = new Singleton();
private Singleton() { }
public static Singleton GetInstance()
{
return single;
}
}
上述代码中修改了GetInstance()方法,直接返回single,而此实例在类加载时就已经自行实例化了。这种方式基于classloader机制,有效避免了多线程的同步问题。
但是,由于导致类加载的原因比较多,而此时单例类ConfigManager在类加载时就实例化,显然没有达到延迟加载的效果。
现在可以对比一下两种方式:
懒汉模式,在类加载时不创建实例,因此类加载速度快,但是运行时获取对象的速度较慢,具备延迟加载的特性,但是又存在线程不安全的问题。
饿汉模式在类加载时就完成初始化,所以类加载较慢,但是获取对象的速度很快。
懒汉模式是“时间换空间”,饿汉模式是“空间换时间”,因为一开始就创建了实例,所以每次使用时直接返回该实例就好了。
在实际开发场景中,实例化单例类很消耗资源,我们希望它可以延迟加载,显然饿汉模式并不能实现。那么我们应该怎么处理呢?
要想让饿汉模式同时具备延迟加载的特性,可以搭配静态内部类进行改造实现。
改造代码如下:
/// <summary>
/// 单例模式之饿汉模式
/// </summary>
public sealed class Singleton
{
//饿汉式
private Singleton() { }
//使用静态内部类 解决饿汉模式的延迟加载问题
private static class Single
{
internal static readonly Singleton sing = new Singleton();
}
public static Singleton GetInstance()
{
retunr Single.sing;
}
}
总结:
对于单例模式的使用,不管是懒汉模式,饿汉模式,或者静态内部类的方式,根据具体的业务需求而定。反正要遵守一个原则:
在整个程序运行期间,有且仅有一个实例。
若违背这一点,那么即使设计的天花乱坠,也不是单例模式的实现。