1. 程式人生 > 其它 >abstract類可以建立物件_C#學習筆記10--抽象類/靜態類/單例

abstract類可以建立物件_C#學習筆記10--抽象類/靜態類/單例

技術標籤:abstract類可以建立物件

一.抽象類

上一節的虛方法可以在父類中宣告並實現, 子類可以直接繼承該方法, 並且可以按照自己的意願對其進行重寫. 但是虛方法對子類沒有約束力, 子類可以選擇重寫或者不重寫. 例如父類中有一個行為, 子類們都有但是每個人具體的行為不一樣(例如英雄們的攻擊方式不同), 這樣一來子類就必須要重寫父類的該方法. 但是如果一不小心子類忘記重寫了, 那麼等到呼叫的時候呼叫的還是父類的方法, 這樣就實現不了多型. 而今天的抽象類/方法就可以很好的解決這個問題.

1.1 抽象方法

在類中, 使用 abstract 關鍵字修飾的方法為抽象方法.

1.1.2 語法

訪問修飾符 abstract 返回值 方法名(引數列表);
子類在重寫的時候加 override關鍵字即可;
// 栗子  英雄的抽象方法 -- Attack
// 這是父類--Hero
public abstract void Attack();
// 抽象方法只起到宣告的作用, 沒有實現, 所以方法名的小括號後面不跟大括號, 直接分號結束;

// 這是子類們
//Shooter 類
public override void Attack()
{
    Console.WriteLine("遠端攻擊");
}
// Mage 類
public override void Attack()
{
    Console.WriteLine("法術攻擊");
}

抽象方法和虛方法的對比:

  1. 抽象方法沒有實現(不加大括號, 直接 ';' 結尾), 虛方法有實現;
  2. 抽象方法子類必須重寫, 而虛方法子類可以不重寫;
  3. 抽象方法的實現用abstract, 虛方法用virtual 實現;
  4. 重寫都用override來重寫;
  5. 抽象方法是一種特殊的虛方法, 一旦類中有抽象方法, 則類必須宣告為抽象類, 抽象類中可以有虛方法;

1.2 抽象類

用abstract宣告的類為抽象類, 抽象類裡可以沒有抽象方法, 但是抽象方法必須宣告在抽象類中;

宣告抽象類的兩種情況:

  1. 抽象類中無抽象屬性跟方法時, 他的作用時為了避免使用它建立物件;
  2. 抽象類中包含抽象屬性或方法時, 他的作用是宣告一些抽象的屬性或方法, 交由具體派生類去實現.

1.2.1語法

訪問修飾符 abstract class 類名{程式碼段;}                          

注意事項:

  1. 抽象方法只有宣告, 沒有實現;
  2. 不能將抽象方法設定成private(子類必須要重寫! 所以肯定得先要能夠訪問);
  3. 抽象類不能被例項化(例項化就是直接建立物件);
  4. 抽象類裡可以沒有抽象方法, 但一旦有抽象方法那麼該類必須為抽象類;
  5. 子類如果也是抽象類那麼可以選擇重寫或不重寫父類的抽象方法;
  6. 父類的訪問許可權必須要高於或等於子類的訪問許可權(例如父類是internal, 子類是public 就不可以, 父親只讓同一域名下的來訪問, 結果孩子說來者皆是客, 那肯定要被打啊.)

二.靜態類

要知道如果我們想要訪問某個類中的方法或者欄位的時候我們需要建立這個類的物件, 通過物件來訪問類裡的東西. 那有時候我們並不想通過建立物件的形式訪問, 此時就需要用到一個新方法 -- 靜態成員與靜態類.

2.1 靜態成員

用關鍵字 static 修飾的成員就是靜態成員:

2.1.1 靜態欄位

建立 : 在欄位資料型別前加 關鍵字 static即可
class Hero
{
// 定義一個字串型別的靜態成員變數 Intention
    public static string Intention = "推塔";
}
訪問 類名.變數名
// Main函式
Console.WriteLine(Hero.Intention);
// 推塔

2.1.2 靜態方法

建立 : 在方法的返回值型別前用 static 修飾即可
class Hero
{
// 靜態方法
    public static void HeroIntention()
    {
        Console.WriteLine("英雄的目標是推塔!")
    }
}
呼叫 : 類名.方法名
// Main 函式
Hero.HeroIntention();
// 英雄的目標是推塔!

注意:

  1. 靜態方法裡只能呼叫所在類裡面的其他靜態成員, 不能呼叫非靜態成員; (因為類裡非靜態成員的訪問需要通過物件, 而靜態成員不需要. 那麼假如靜態方法它含有非靜態成員, 外界在呼叫該靜態方法的時候就會出錯);
  2. 訪問靜態成員和靜態方法不需要建立物件的, 可以直接訪問;
  3. static 不能與 abstract, virtual, override 一起使用; // 因為靜態類不能繼承;

2.2 靜態類

用static 修飾的類為靜態類;

如下:

	 public static class Girl
	 {
                // 常量
		public const int age = 20;
                // 靜態成員
		public static string name;
                // 靜態方法
		public static void Print()
		{
			Console.WriteLine("買買買....");
		}
           }

注意:

  1. 靜態類中只能包含靜態成員和常量
  2. 靜態類是不允許被繼承的;
  3. 所有訪問靜態類的都是指向同一空間;
  4. 被sealed修飾的類不能被繼承;

2.3 靜態構造方法

靜態構造方法是C#的一個新特性, 其實很少會用到它, 不過當我們想初始化一些靜態變數的時候就可以用到它;

	 public static class Girl
	 {
		public const int age = 20;
		public static string name;
		public static void Print()
		{
			Console.WriteLine("買買買....");
		}

                // 用構造方法初始化一下欄位
                // 要注意, 靜態構造是不能夠手動呼叫的, 所以不能加訪問修飾符!
		static Girl()
		{
			name = "張三"
		}
	 }

補充 : 結構體中的靜態構造

    public struct StudentInfo
    {
        public static int age;
        public string name;
        public void Run()
        {
            Console.WriteLine("Runing");
        }
        static StudentInfo()
        {
            Console.WriteLine("我是結構體的靜態構造");
        }

        public StudentInfo(string name , int age1)
        {
            this.name = name;
            age = age1;
        }

先看一下結果:

a80c3ae1e1404bc303e7f5eade7209ef.png
可以看出當使用顯式宣告的構造方法時會呼叫靜態構造;

7f8a5e2aeb5e6534d658163dc327a359.png
第一次呼叫結構體方法的時候也會呼叫靜態構造;

9cf66ddf4f605ffc3ea9cf2557cf9397.png
第一次訪問靜態成員的時候也會呼叫靜態構造;

總結一下, 結構體什麼時候呼叫靜態構造 :

  1. 使用顯式宣告的構造方法;
  2. 第一次呼叫結構體方法的時候;
  3. 第一次訪問靜態成員的時候;

注意:

  1. 靜態構造不能有返回值不能有引數;
  2. 靜態成員不能訪問例項成員;
  3. 靜態構造不能手動呼叫;
  4. 一個類中只能有一個靜態構造, 即靜態構造不能有過載;
  5. 靜態構造只能呼叫一次, 在第一次建立物件或者是第一次訪問靜態成員時呼叫.
  6. 靜態構造要先於普通構造方法;
  7. 靜態構造先呼叫自己的再呼叫父親的;

四.單列(重點!)

在遊戲開發中, 經常會有這樣一些特殊的類, 必須保證這些類在遊戲中只存在一個例項, 才能保證他們的邏輯正確性以及良好的效率;

單例模式就可以很好的實現, 今天主要記錄單例的三個模式:飽漢, 餓漢以及多執行緒安全模式.

單例的飽漢模式

一共兩步:

1> 把預設構造設定成 private, 即不允許通過例項化的方式建立物件;
2> 在內部定義一個靜態的例項;
	//飽漢模式
	class Kai : Hero
	{
               // 第二步 在內部定義一個靜態例項 就可以它的唯一性;
		public static Kai instance = new Kai();

		//第一步 將構造方法設定成private
		private Kai()
		{

		}

		public override void AtkFunction()
		{
		
		}

		public override void BeAtkFunction()
		{
			this.hp -= 100;
			Console.WriteLine("剩餘血量{0}",this.hp);
		}

	}

飽漢模式有一個缺點, 不管我們需不需要這個物件, 它都會被創建出來, 而且會一直存在佔著記憶體空間知道程式結束;於是餓漢就來了;

餓漢模式:

餓漢模式的思想是先宣告一個物件, 當需要用到的時候再例項化;
	public class Equip
	{
                // 先不例項化, 這裡欄位也要設定成私有的, 外界通過屬性來訪問. 封裝一下;
		private static Equip instance;

                // 通過屬性來例項化
		public static Equip Instance
		{

			get
			{
                                // 判斷是必須的, 要保證唯一;
                                // 如果當前沒有該物件就建立物件, 如果有的話就返回當前物件;
				if (instance == null)
		         ***   {
					instance = new Equip();
				}
				return instance;
			}
		}
                // 還是要將構造方法設定成私有的, 讓外界無法建立該物件;
		private Equip()
		{

		}

	}

餓漢模式已經可以滿足我們的需求了, 但是在多執行緒的時候, 有很小的概率在餓漢模式中會創建出兩個物件來(假設A執行緒走到了"***"處, B 執行緒在 "***" 上面, 某一時刻 A 停在了 " ***", B 執行緒開始走, 到 if 語句判斷為空, 建立物件. 然後 切換給A 執行緒, 這個時候A 往下也建立物件. 這樣就會發生衝突了). 雖然概率小但是還是要預防, 於是多執行緒安全模式來了.

多執行緒安全模式:

多執行緒安全模式在餓漢模式下進行一些小改動就可以, 新增給系統一個資源(鑰匙), 然後在建立物件的語句前加一個鎖, 只有拿到鑰匙的 才可以被允許執行鎖裡面的語句, 其他的只能在門外面等到資源被釋放拿到這個資源才可以.

	public class Equip
	{
		private static Equip instance;
                // 資源
		private static object obj = new object();

		public static Equip Instance
		{

			get
			{
                                // 鎖
				lock (obj)
				{
					if (instance == null)
					{
						instance = new Equip();
					}
				}
				return instance;

			}

		}

		private Equip()
		{

		}



	}

=======================================================

思維導圖

1c1c6270374e7447d814d5836689dd32.png