| ||||
1.可以實現代碼的復用 2.符合人思考的方式 | ||||
1.類的定義:用class關鍵修飾的叫做類 2.對象的定義:類名定義的數據類型 | ||||
1.由對象歸納為類,是歸納對象共性的過程 2.在類的基礎上,將狀態和行為實體化為對象的過程為實例化 | ||||
| ||||
| ||||
1.3創建對象 | ||||
namespace Temp { class Program { static void Main(string[] args) { Class1 c = new Class1(); } } class BaseClass { int z = 3; public BaseClass() { MethodA(); } public virtual void MethodA() { Console.WriteLine("BaseClass.MethodA"); } } class Class1 : BaseClass { int x = 1; int y; public Class1() { y = 2; } public override void MethodA() { Console.WriteLine(x + y); } } } 以上是一個簡單的繼承層次結構。不要使用VS測試,腦子分析一下最終輸出了什么? 分析過程中腦子存在任何疑問的同學,請馬上動手測試一下吧,在Main方法中設個斷點單步跟蹤一下。 這里描述一下單步調試的整個過程: 黃色光標進入Class1類時跳入了第一句int x = 1; 黃色光標跳過第二句int y 指向 Class1的構造函數; 在執行構造函數的代碼塊之前跳入了父類,黃色光標指向父類BaseClass 的 int z = 3 語句; 黃色光標指向BaseClass 的構造函數; 黃色光標指向構造函數內的MethodA() 調用; 黃色光標跳向子類Class1 重寫的方法MethodA(); 查看兩個字段發現x=1, y=0; 黃色光標指向Console 語句; 黃色光標從父類構造函數的MethodA() 調用中跳出; 黃色光標從父類構造函數跳出,并再次指向子類構造函數,執行完其中的代碼塊; 直至執行完畢。 這里說明了幾個順序問題: 對于直接賦值的字段的賦值步驟是在構造函數之前執行,子類字段的賦值是在父類的字段賦值之前; 對于字段的內存分配、初始化等步驟是在我們所能看到的黃色光標進入Class1類的步驟之前; 執行構造函數前會首先執行父類的構造函數; 執行構造函數時 CLR已能識別方法的覆寫情況,表明方法的加載過程是在對字段的賦值步驟之前; int類型的字段在分配內存、初始化階段已默認賦了0 值(僅限字段中的int,方法中的int變量并非如此)。 總結:當執行new語句時,發生了以下幾件事情(更細的情形本文暫不探討): 為字段分配內存并進行初始化; 如果類是第一次加載(即系統中從未創建類的其它對象,或者曾經創建但因不再有引用而被GC 全部回收),則 Copy其實例方法至方法表; 為類中需要直接賦值的字段賦值; 執行構造函數。 | ||||
| ||||
1,變量的作用域? 解析: namespace CSharp { public class TestScope { public TestScope() { //sA在TestScope()方法內部有效. string[] sA = new string[5] { "H", "e", "l", "l", "o" }; //塊作用域:s6只在foreach循環內部有效。 foreach (string s6 in sA) { Console.WriteLine(s6); } //在這里不能引用s6 //Console.WriteLine(s6); //同樣不能重新定義s6 //string s6 = ""; } } } 2.成員變量和局部變量的定義和區別? 解析: 成員變量:作為類的成員而存在,直接存在于類中。所有類的成員變 量可以通過this來引用。 局部變量:作為方法或語句塊的成員而存在,存在于方法的參數列表和方法定義中。 1.成員變量可以被public,protect,private,static等修飾符修飾,而局部變量不能被控制修飾符及static修飾;兩者都可以定義成final型。 2.成員變量存儲在堆,局部變量存儲在棧。局 部變量的作用域僅限于定義它的方法,在該方法的外部無法訪問它。成員變量的作用域在整個類內部都是可見的,所有成員方法都可以使用它。如果訪問權限允許, 還可以在類的外部使用成員變量。 3.局部變量的生存周期與方法的執行期相同。 當方法執行到定義局部變量的語句時,局部變量被創建;執行到它所在的作用域的最后一條語句時,局部變量被銷毀。類的成員變量,如果是實例成員變量,它和對 象的生存期相同。而靜態成員變量的生存期是整個程序運行期。 4.成員變量有默認值,基本類型的默認值為0,復合類型的默認值為null。(被final修飾且沒有static的必須顯式賦值),局部變量不會自動賦值,所以局 部變量在定義后先要賦初值,然后才能使用。 5.局部變量可以和成員變量 同名,且在使用時,局部變量具有更高的優先級。 | ||||
1.值類型 解析:值類型源于System.ValueType家族,每個值類型的對象dou都有一個獨立的內存域用于保護自己的值,值類型數據所在的內存區域成為棧(Stack) 2.引用類型、 解析:引用類型源于System.Object家族 | ||||
| ||||
| ||||
| ||||
Net的類型可以分為值類型和引用類型,值類型通常分配在線程堆棧上,并且不包含任何指向實例數據的指針。引用類型實例分配在托管堆上,變量保存了實例數據的內存引用。 1.值類型變量只是進行數據復制,創建一個同值新對象,而引用變量的賦值僅僅是把對象的引用的指針賦給變量,使得變量引用與對象共享同一個內存地址。 2.繼承結構的區別,引用類型一般都有繼承性。但由于值類型是密封的,因此值類型不能作為其它任何類型的基類,但是可以單繼承或者是多繼承接口。另一個區別是值類型都繼承自System。ValueType類,而引用類型則不會繼承自System。ValueType類。 3.內存分配的區別:值類型通常分配在棧上,它的變量直接包含變量的實例,使用效率相對比較高。而引用類型分配在托管堆上,引用類型的變量通常包含一個指向實例的指針,變量通過指針來引用實例。 | ||||
| ||||
1.用于創建對象和調用構造函數。 例如: Class1 obj = new Class1(); 2.可用于創建匿名類型的實例: var query = from cust in customers select new {Name = cust.Name, Address = cust.PrimaryAddress}; 3.還用于調用值類型的默認構造函數。 例如: int i = new int(); | ||||
用類型在內存中的部署 解析: 當創建一個應用類型變量時: object reference = new object(); 關鍵字new將在托管堆上分配內存空間,并返回一個該內存空間的地址。左邊的reference位于棧上,是一個引用,存儲著一個內存地址;而這個地址指向的內存(位于托管堆)里存儲著其內容(一個System.Object的實例)。下面為了方便,簡稱引用類型部署在托管推上。 考慮數組: int[] reference = new int[100]; 根據定義,數組都是引用類型,所以int數組當然是引用類型(即reference.GetType().IsValueType為false)。 而int數組的元素都是int,根據定義,int是值類型(即reference[i].GetType().IsValueType為true)。那么引用類型數組中的值類型元素究竟位于棧還是堆? 如果用WinDbg去看reference[i]在內存中的具體位置,就會發現它們并不在棧上,而是在托管堆上。 實際上,對于數組: TestType[] testTypes = new TestType[100]; 如果TestType是值類型,則會一次在托管堆上為100個值類型的元素分配存儲空間,并自動初始化這100個元素,將這100個元素存儲到這塊內存里。 如果TestType是引用類型,則會先在托管堆為testTypes分配一次空間,并且這時不會自動初始化任何元素(即testTypes[i]均為null)。等到以后有代碼初始化某個元素的時候,這個引用類型元素的存儲空間才會被分配在托管堆上。 | ||||
1.值類型 C#的所有值類型均隱式派生自System.ValueType: 結構體:struct(直接派生于System.ValueType); 數值類型: 整型:sbyte(System.SByte的別名),short(System.Int16),int(System.Int32),long(System.Int64),byte(System.Byte),ushort(System.UInt16),uint(System.UInt32),ulong(System.UInt64),char(System.Char); 浮點型:float(System.Single),double(System.Double); 用于財務計算的高精度decimal型:decimal(System.Decimal)。 bool型:bool(System.Boolean的別名); 用戶定義的結構體(派生于System.ValueType)。 枚舉:enum(派生于System.Enum); 可空類型(派生于System.Nullable<T>泛型結構體,T?實際上是System.Nullable<T>的別名)。 每種值類型均有一個隱式的默認構造函數來初始化該類型的默認值。例如: int i = new int(); 等價于: Int32 i = new Int32(); 等價于: int i = 0; 等價于: Int32 i = 0; 使用new運算符時,將調用特定類型的默認構造函數并對變量賦以默認值。在上例中,默認構造函數將值0賦給了i。MSDN上有完整的默認值表。 關于int和Int32的細節,在我的另一篇文章中有詳細解釋:《理解C#中的System.Int32和int》。 所有的值類型都是密封(seal)的,所以無法派生出新的值類型。 值得注意的是,System.ValueType直接派生于System.Object。即System.ValueType本身是一個類類型,而不是值類型。其關鍵在于ValueType重寫了Equals()方法,從而對值類型按照實例的值來比較,而不是引用地址來比較。 可以用Type.IsValueType屬性來判斷一個類型是否為值類型: TestType testType = new TestType (); if (testTypetype.GetType().IsValueType) { Console.WriteLine("{0} is value type.", testType.ToString()); } 2.引用類型 C#有以下一些引用類型: 數組(派生于System.Array) 用戶用定義的以下類型: 類:class(派生于System.Object); 接口:interface(接口不是一個“東西”,所以不存在派生于何處的問題。Anders在《C# Programming Language》中說,接口只是表示一種約定[contract]); 委托:delegate(派生于System.Delegate)。 object(System.Object的別名); 字符串:string(System.String的別名)。 可以看出: 引用類型與值類型相同的是,結構體也可以實現接口; 引用類型可以派生出新的類型,而值類型不能; 引用類型可以包含null值,值類型不能(可空類型功能允許將null 賦給值類型); 引用類型變量的賦值只復制對對象的引用,而不復制對象本身。而將一個值類型變量賦給另一個值類型變量時,將復制包含的值。 對于最后一條,經常混淆的是string。我曾經在一本書的一個早期版本上看到String變量比string變量效率高;我還經常聽說String是引用類型,string是值類型,等等。例如: string s1 = "Hello, "; string s2 = "world!"; string s3 = s1 + s2;//s3 is "Hello, world!" 這確實看起來像一個值類型的賦值。再如: string s1 = "a"; string s2 = s1; s1 = "b";//s2 is still "a" 改變s1的值對s2沒有影響。這更使string看起來像值類型。實際上,這是運算符重載的結果,當s1被改變時,.NET在托管堆上為s1重新分配了內存。這樣的目的,是為了將做為引用類型的string實現為通常語義下的字符串。 | ||||
無論是指類型的變量或是類類型的變量,其存儲單元都是在棧中分配的,唯一不同的是類類型的變量實際上存儲的是該類對象的指針,相當于vc6中的CType*,只是在.net平臺的語言中將指針的概念屏蔽掉了。我們都知道棧的一大特點就是LIFO(后進先出),這恰好與作用域的特點相對應(在作用域的嵌套層次中,越深層次的作用域,其變量的優先級越高)。因此,再出了“}”后,無論是值類型還是類類型的變量(對象指針)都會被立即釋放(值得注意的是:該指針所指向的托管堆中的對象并未被釋放,正等待GC的回收)。.NET中的棧空間是不歸GC管理的,GC僅管理托管堆。 我想就我的理解簡要說明一下: 1、GC只收集托管堆中的對象。 2、所有值類型的變量都在棧中分配,超出作用域后立即釋放棧空間,這一點與VC6完全 一樣。 3、區別類類型的變量和類的對象,這是兩個不同的概念。類類型的變量實際上是該類對 象的指針變量。如C#中的定義CType myType;與VC6中的定義CType* myType;是完全一 樣的,只是.net語言將*號隱藏了。與VC6相同,必須用new 關鍵字來構造一個對象, 如(C#):CType myType=new CType();其實這一條語句有兩次內存分配,一次是為類類 型變量myType在棧中分配空間(指針類型所占的空間,對32位系統分配32位,64位 系統則分配64位,在同一個系統中,所有指針類型所占的內存空間都是一樣的,而 不管該類型的指針所指向的是何種類型的對象),另一次是在托管堆(GC所管理的 堆)中構造一個CType類型的對象并將該對象的起始地址賦給變量myType。正因為如 此才造成了在同一個作用域中聲明的類類型的變量和該類型的對象的生存期不一樣。 | ||||
GC就是垃圾回收器,一般來說系統會自動檢測不會使用的對象或變量進行內存的釋放,不需要手動調用,用Collect()就是強制進行垃圾回收,使內存得到及時的釋放,讓程序效率更高. 給個例子:使用Optimized 設置對第 2代對象進行垃圾回收。 using System; class Program { static void Main(string[] args) { GC.Collect(2, GCCollectionMode.Optimized); } } 詳細解釋 https://msdn.microsoft.com/zh-cn/library/system.gc.aspx | ||||
| ||||
| ||||
c# 中方法簽名 指的是? 方法簽名由方法名稱和一個參數列表(方法的參數順序和類型)組成。 注意:方法的簽名并不包括方法的返回值。雖然每個重載方法可以有不同的返回類型,單返回類型并不足以區分所條用的是哪個方法。 在C#中,同一個類中的兩個或兩個以上的方法可以有不同的名字,只要他們的參數聲明不同即可。在這種情況下,該方法就被稱為重載(overload),這個過程稱為方法重載(method overloading)。方法重載是C#最有用的特性之一。 當一個方法被調用時,C#用方法簽名確定調用哪一個方法。因此,每個重載方法的參數列表必須是不同的。雖然每個重載方法可以有不同的返回類型,單返回類型并不足以區分所條用的是哪個方法。當C#調用一個重載方法時,參數與條用參數相匹配的方法被執行。 重寫(override)是指,派生類對基類的方法的實現進一步改進。 不能重寫非虛方法或靜態方法。重寫的基方法必須是virtual、abstract 或 override的。為什么 override也可以重寫呢?因為基類中的override實際上是對基類的基類進行的重寫,由于繼承可傳遞,所以也可以對基類中override的方法進行重寫。 override聲明不能更改 virtual方法的可訪問性。override方法和 virtual方法必須具有相同的訪問級別修飾符。 不能使用修飾符new、static、virtual 或 abstract來修改 override 方法。 重寫屬性聲明必須指定與繼承屬性完全相同的訪問修飾符、類型和名稱,并且被重寫的屬性必須是virtual、abstract 或 override 的。 | ||||
| ||||
示例代碼: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace RefDifferenceToOut 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 //這里做為ref類型的參數必須初始化。ref類型參數傳入未初始化變量會報錯。 13 string refTestStr = string.Empty; 14 15 //作為out類型參數可以不初始化,因為out類型參數是在函數內部賦值。 16 string outTestStr = string.Empty; 17 18 Program p = new Program(); 19 20 Console.WriteLine("默認空值輸出:"); 21 Console.WriteLine(p.UseRef(ref refTestStr)); 22 Console.WriteLine(p.USEOut(out outTestStr)); 23 Console.WriteLine("refTestStr:" + refTestStr); 24 Console.WriteLine("outTestStr:" + outTestStr); 25 Console.WriteLine("-------------------"); 26 27 refTestStr = "1"; 28 outTestStr = "2"; 29 30 Console.WriteLine("從新賦值:refTestStr = /"1/";outTestStr = /"2/";"); 31 Console.WriteLine(p.UseRef(ref refTestStr)); 32 Console.WriteLine(p.UseOut(out outTestStr)); 33 Console.WriteLine("refTestStr:" + refTestStr); 34 Console.WriteLine("outTestStr:" + outTestStr); 35 Console.WriteLine("-------------------"); 36 37 38 refTestStr = "3"; 39 outTestStr = "4"; 40 Console.WriteLine("從新賦值:refTestStr = /"3/";outTestStr = /"4/";"); 41 Console.WriteLine(p.UseRef(ref refTestStr)); 42 Console.WriteLine(p.UseOut(out outTestStr)); 43 Console.WriteLine("refTestStr:" + refTestStr); 44 Console.WriteLine("outTestStr:" + outTestStr); 45 Console.WriteLine("-------------------"); 46 47 48 Console.WriteLine("--------params-------"); 49 p.param("str_a", "2", "3", "4"); 50 51 52 } 53 54 public string UseRef(ref string refTest) 55 { 56 return refTest += "rrrrrref"; 57 } 58 59 public string UseOut(out string outTestStr) 60 { 61 //在這里需要給outTest從新賦值 62 outTestStr = "0"; 63 outTestStr += "oooooout"; 64 return outTestStr; 65 } 66 67 ///< summary> 68 /// params參數練習。 69 ///< /summary> 70 ///< param name="a">同是string參數</param> 71 ///< param name="list">string類型列表參數</param> 72 public void param(string a,params string[] list) 73 { 74 Console.WriteLine(list.ToString()); 75 76 int i = 0; 77 78 Console.WriteLine(a); 79 foreach (string s in list) 80 { 81 Console.WriteLine(i++ +"_"+ s); 82 } 83 } 84 } 85 } 86 輸出結果: 默認空值輸出: rrrrrref 0oooooout refTestStr:rrrrrref outTestStr:0oooooout ------------------- 從新賦值:refTestStr = "1";outTestStr = "2"; 1rrrrrref 0oooooout refTestStr:1rrrrrref outTestStr:0oooooout ------------------- 從新賦值:refTestStr = "3";outTestStr = "4"; 3rrrrrref 0oooooout refTestStr:3rrrrrref outTestStr:0oooooout ------------------- --------params------- System.String[] str_a 0_2 1_3 2_4 總結: ref和out都對函數參數采用引用傳遞形式——不管是值類型參數還是引用類型參數,并且定義函數和調用函數時都必須顯示生命該參數為ref/out形式。兩者都可以使函數傳回多個結果。 兩者區別: 兩種參數類型的設計思想不同,ref的目的在于將值類型參數當作引用型參數傳遞到函數,是函數的輸入參數,并且在函數內部的任何改變也都將影響函數外部該參數的值;而out的目的在于獲取函數的返回值,是輸出參數,由函數內部計算得到的值再回傳到函數外部,因此必須在函數內部對該參數賦值,這將沖掉函數外部的任何賦值,使得函數外部賦值毫無意義。 表現為: 1、out必須在函數體內初始化,這使得在外面初始化變得沒意義。也就是說,out型的參數在函數體內不能得到外面傳進來的初始值。 2、ref必須在函數體外初始化。 3、兩者在函數體內的任何修改都將影響到函數體外面。 | ||||
同一個類中定義多個方法名稱相同、參數列表(參數個數、參數類型)不同的方法,稱為方法重載 | ||||
| ||||
1.方法名與類名相同 2.沒有返回值類型 3.主要完成對象的初始化工作 | ||||
1.無參構造 2.帶參構造 3.隱式構造 | ||||
實例化對象,初始化數據的 | ||||
| ||||
其實就是使用this來實現的。看一下例子就會明白的了。 class Class1 { public Class1() { //Code 1 } public Class1(string s):this() { //Code 2 } public Class1(int i, string j) : this(j) { //Code 3 } } | ||||
this,只能在類的定義代碼中使用,代表的是自己,凡是用this的地方,其實都是可以省略的。 而像下面這種this的使用,是用作擴展方法的第一個參數的修飾符,這樣不會和參數的name重復不清 public Employee(string name, string alias) { // Use this to qualify the fields, name and alias: this.name = name; this.alias = alias; } | ||||
方法名與類名相同 沒有返回值 | ||||
任何對象都會自動調用ToString() 這是編譯器的一個機制 | ||||
http://m.survivalescaperooms.com/ywqu/category/223486.html | ||||
http://developer.51cto.com/art/201007/210830.htm 在UML建模中UML類圖的java代碼表現 UML類圖元素 1.類(Classes) 類包含3個組成部分。第一個是Java中定義的類名。第二個是屬性(attributes)。第三個是該類提供的方法。 屬性和操作之前可附加一個可見性修飾符。加號(+)表示具有公共可見性。減號(-)表示私有可見性。#號表示受保護的可見性。省略這些修飾符表示具有package(包)級別的可見性。如果屬性或操作具有下劃線,表明它是靜態的。在操作中,可同時列出它接受的參數,以及返回類型,如下圖所示: 2.包(Package) UML類圖中包是一種常規用途的組合機制。UML中的一個包直接對應于Java中的一個包。在Java中,一個包可能含有其他包、類或者同時含有這兩者。進行建模時,你通常擁有邏輯性的包,它主要用于對你的模型進行組織。你還會擁有物理性的包,它直接轉換成系統中的Java包。每個包的名稱對這個包進行了惟一性的標識。 3.接口(Interface) 接口是一系列操作的集合,它指定了一個類所提供的服務。它直接對應于Java中的一個接口類型。接口既可用下面的那個圖標來表示(上面一個圓圈符號,圓圈符號下面是接口名,中間是直線,直線下面是方法名),也可由附加了<<interface>>的一個標準類來表示。通常,根據接口在類圖上的樣子,就能知道與其他類的關系。 UML類圖關系: 1.依賴(Dependency) 實體之間一個“使用”關系暗示一個實體的規范發生變化后,可能影響依賴于它的其他實例。更具體地說,它可轉換為對不在實例作用域內的一個類或對象的任何類型的引用。其中包括一個局部變量,對通過方法調用而獲得的一個對象的引用(如下例所示),或者對一個類的靜態方法的引用(同時不存在那個類的一個實例)。也可利用“依賴”來表示包和包之間的關系。由于包中含有類,所以你可根據那些包中的各個類之間的關系,表示出包和包的關系。 2.關聯(Association) 實體之間的一個結構化關系表明對象是相互連接的。箭頭是可選的,它用于指定導航能力。如果沒有箭頭,暗示是一種雙向的導航能力。在Java中,關聯轉換為一個實例作用域的變量,就像圖E的“Java”區域所展示的代碼那樣。可為一個關聯附加其他修飾符。多重性(Multiplicity)修飾符暗示著實例之間的關系。在示范代碼中,Employee可以有0個或更多的TimeCard對象。但是,每個TimeCard只從屬于單獨一個Employee。 3.聚合(Aggregation) UML類圖中聚合是關聯的一種形式,代表兩個類之間的整體/局部關系。聚合暗示著整體在概念上處于比局部更高的一個級別,而關聯暗示兩個類在概念上位于相同的級別。聚合也轉換成Java中的一個實例作用域變量。 關聯和聚合的區別純粹是概念上的,而且嚴格反映在語義上。聚合還暗示著實例圖中不存在回路。換言之,只能是一種單向關系。 4.合成(Composition) 合成是聚合的一種特殊形式,暗示“局部”在“整體”內部的生存期職責。合成也是非共享的。所以,雖然局部不一定要隨整體的銷毀而被銷毀,但整體要么負責保持局部的存活狀態,要么負責將其銷毀。 局部不可與其他整體共享。但是,整體可將所有權轉交給另一個對象,后者隨即將承擔生存期職責。Employee和TimeCard的關系或許更適合表示成“合成”,而不是表示成“關聯”。 5.泛化(Generalization) UML類圖中泛化表示一個更泛化的元素和一個更具體的元素之間的關系。泛化是用于對繼承進行建模的UML元素。在Java中,用extends關鍵字來直接表示這種關系。 6.實現(Realization) 實例關系指定兩個實體之間的一個合同。換言之,一個實體定義一個合同,而另一個實體保證履行該合同。對Java應用程序進行建模時,實現關系可直接用implements關鍵字來表示。 | ||||
新聞熱點
疑難解答