1. C#和java有什麼不同
C#(C-Sharp)是Microsoft的新編程語言,被譽為「C/C++家族中第一種面向組件的語言」。然而,不管它自己宣稱的是什麼,許多人認為C#更像是Java的一種克隆,或者是Microsoft用來替代Java的產品。事實是否是這樣的呢?
本文的比較結果表明,C#不止是Java的同胞那麼簡單。如果你是一個Java開發者,想要學習C#或者了解更多有關C#的知識,那麼本文就是你必須把最初10分鍾投入於其中的所在。
一、C#、C++和Java
C#的語言規范由Microsoft的Anders Hejlsberg與Scott Wiltamuth編寫。在當前Microsoft天花亂墜的宣傳中,對C#和C++、Java作一番比較總是很有趣的。考慮到當前IT媒體的輿論傾向,如果你早就知道C#更接近Java而不是C++,事情也不值得大驚小怪。對於剛剛加入這場討論的讀者,下面的表1讓你自己作出判斷。顯然,結論應該是:Java和C#雖然不是孿生子,但C#最主要的特色卻更接近Java而不是C++。
表1:比較C#、C++和Java最重要的功能
功能 C# C++ Java
繼承 允許繼承單個類,允許實現多個介面 允許從多個類繼承 允許繼承單個類,允許實現多個介面
介面實現 通過「interface」關鍵詞 通過抽象類 通過「interface」關鍵詞
內存管理 由運行時環境管理,使用垃圾收集器 需要手工管理 由運行時環境管理,使用垃圾收集器
指針 支持,但只在很少使用的非安全模式下才支持。通常以引用取代指針 支持,一種很常用的功能。 完全不支持。代之以引用。
源代碼編譯後的形式 .NET中間語言(IL) 可執行代碼 位元組碼
單一的公共基類 是 否 是
異常處理 異常處理 返回錯誤 異常處理。
了解表1總結的重要語言功能之後,請繼續往下閱讀,了解C#和Java的一些重要區別。
二、語言規范的比較
2.1、簡單數據類型
簡單數據類型(Primitive)在C#中稱為值類型,C#預定義的簡單數據類型比Java多。例如,C#有unit,即無符號整數。表2列出了所有C#的預定義數據類型:
表2:C#中的值類型
類型 說明
object 所有類型的最終極的基類
string 字元串類型;字元串是一個Unicode字元的序列
sbyte 8位帶符號整數
short 16位帶符號整數
int 32位帶符號整數
long 64位帶符號整數
byte 8位無符號整數
ushort 16位無符號整數
uint 32位無符號整數
ulong 64位無符號整數
float 單精度浮點數類型
double 雙精度浮點數類型
bool 布爾類型;bool值或者是true,或者是false
char 字元類型;一個char值即是一個Unicode字元
decimal 有28位有效數字的高精度小數類型
2.2、常量
忘掉Java中的static final修飾符。在C#中,常量可以用const關鍵詞聲明。
public const int x = 55;
此外,C#的設計者還增加了readonly關鍵詞。如果編譯器編譯時未能確定常量值,你可以使用readonly關鍵詞。readonly域只能通過初始化器或類的構造函數設置。
2.3、公用類的入口點
在Java中,公用類的入口點是一個名為main的公用靜態方法。main方法的參數是String對象數組,它沒有返回值。在C#中,main方法變成了公用靜態方法Main(大寫的M),Main方法的參數也是一個String對象數組,而且也沒有返回值,如下面的原型聲明所示:
public static void Main(String[] args)
但是,C#的Main方法不局限於此。如果不向Main方法傳遞任何參數,你可以使用上述Main方法的一個重載版本,即不帶參數列表的版本。也就是說,下面的Main方法也是一個合法的入口點:
public static void Main()
另外,如果你認為有必要的話,Main方法還可以返回一個int。例如,下面代碼中的Main方法返回1:
using System;
public class Hello {
public static int Main() {
Console.WriteLine("Done");
return 1;
}
}
與此相對,在Java中重載main方法是不合法的。
2.4、switch語句
在Java中,switch語句只能處理整數。但C#中的switch語句不同,它還能夠處理字元變數。請考慮下面用switch語句處理字元串變數的C#代碼:
using System;
public class Hello {
public static void Main(String[] args) {
switch (args[0]) {
case "老闆":
Console.WriteLine("早上好!我們隨時准備為您效勞!");
break;
case "雇員":
Console.WriteLine("早上好!你可以開始工作了!");
break;
default:
Console.WriteLine("早上好!祝你好運!");
break;
}
}
}
與Java中的switch不同,C#的switch語句要求每一個case塊或者在塊的末尾提供一個break語句,或者用goto轉到switch內的其他case標簽。
2.5、foreach語句
foreach語句枚舉集合中的各個元素,為集合中的每一個元素執行一次代碼塊。請參見下面的例子。
using System;
public class Hello {
public static void Main(String[] args) {
foreach (String arg in args)
Console.WriteLine(arg);
}
}
如果在運行這個執行文件的時候指定了參數,比如「Hello Peter Kevin Richard」,則程序的輸出將是下面幾行文字:
Peter
Kevin
Richard
2.6、C#沒有>>>移位操作符
C#支持uint和ulong之類的無符號變數類型。因此,在C#中,右移操作符(即「>>」)對於無符號變數類型和帶符號變數類型(比如int和long)的處理方式不同。右移uint和ulong丟棄低位並把空出的高位設置為零;但對於int和long類型的變數,「>>」操作符丟棄低位,同時,只有當變數值是正數時,「>>」才把空出的高位設置成零;如果「>>」操作的是一個負數,空出的高位被設置成為1。
Java中不存在無符號的變數類型。因此,我們用「>>>」操作符在右移時引入負號位;否則,使用「>>」操作符。
2.7、goto關鍵詞
Java不用goto關鍵詞。在C#中,goto允許你轉到指定的標簽。不過,C#以特別謹慎的態度對待goto,比如它不允許goto轉入到語句塊的內部。在Java中,你可以用帶標簽的語句加上break或continue取代C#中的goto。
2.8、聲明數組
在Java中,數組的聲明方法非常靈活,實際上有許多種聲明方法都屬於合法的方法。例如,下面的幾行代碼是等價的:
int[] x = { 0, 1, 2, 3 };
int x[] = { 0, 1, 2, 3 };
但在C#中,只有第一行代碼合法,[]不能放到變數名字之後。
2.9、包
在C#中,包(Package)被稱為名稱空間。把名稱空間引入C#程序的關鍵詞是「using」。例如,「using System;」這個語句引入了System名稱空間。
然而,與Java不同的是,C#允許為名稱空間或者名稱空間中的類指定別名:
using TheConsole = System.Console;
public class Hello {
public static void Main() {
TheConsole.WriteLine("使用別名");
}
}
雖然從概念上看,Java的包類似於.NET的名稱空間。然而,兩者的實現方式不同。在Java中,包的名字同時也是實際存在的實體,它決定了放置.java文件的目錄結構。在C#校�錮淼陌�吐嘸�拿�浦�涫峭耆�擲氳模�簿褪撬擔��瓶占淶拿�植換岫暈錮淼拇虯�絞講��魏斡跋臁T贑#中,每一個源代碼文件可以從屬於多個名稱空間,而且它可以容納多個公共類。
.NET中包的實體稱為程序集(Assembly)。每一個程序集包含一個manifest結構。manifest列舉程序集所包含的文件,控制哪些類型和資源被顯露到程序集之外,並把對這些類型和資源的引用映射到包含這些類型與資源的文件。程序集是自包含的,一個程序集可以放置到單一的文件之內,也可以分割成多個文件。.NET的這種封裝機制解決了DLL文件所面臨的問題,即臭名昭著的DLL Hell問題。
2.10、默認包
在Java中,java.lang包是默認的包,它無需顯式導入就已經自動包含。例如,要把一些文本輸出到控制台,你可以使用下面的代碼:
System.out.println("Hello world from Java");
C#中不存在默認的包。如果要向控制台輸出文本,你使用System名稱空間Console對象的WriteLine方法。但是,你必須顯式導入所有的類。代碼如下:
using System;
public class Hello {
public static void Main() {
Console.WriteLine("Hello world from C#");
}
}
2.11、面向對象
Java和C#都是完全面向對象的語言。在面向對象編程的三大原則方面,這兩種語言接近得不能再接近。
繼承:這兩種語言都支持類的單一繼承,但類可以實現多個介面。所有類都從一個公共的基類繼承。
封裝與可見性:無論是在Java還是C#中,你都可以決定類成員是否可見。除了C#的internal訪問修飾符之外,兩者的可見性機制非常相似。
多態性:Java和C#都支持某些形式的多態性機制,且兩者實現方法非常類似。
2.12、可訪問性
類的每個成員都有特定類型的可訪問性。C#中的訪問修飾符與Java中的基本對應,但多出了一個internal。簡而言之,C#有5種類型的可訪問性,如下所示:
public:成員可以從任何代碼訪問。
protected:成員只能從派生類訪問。
internal:成員只能從同一程序集的內部訪問。
protected internal:成員只能從同一程序集內的派生類訪問。
private:成員只能在當前類的內部訪問。
2.13、派生類
在Java中,我們用關鍵詞「extends」實現繼承。C#採用了C++的類派生語法。例如,下面的代碼顯示了如何派生父類Control從而創建出新類Button:
public class Button: Control { . . }
2.14、最終類
由於C#中不存在final關鍵詞,如果想要某個類不再被派生,你可以使用sealed關鍵詞,如下例所示:
sealed class FinalClass { . . }
2.15、介面
介面這個概念在C#和Java中非常相似。介面的關鍵詞是interface,一個介面可以擴展一個或者多個其他介面。按照慣例,介面的名字以大寫字母「I」開頭。下面的代碼是C#介面的一個例子,它與Java中的介面完全一樣:
interface IShape { void Draw(); }
擴展介面的語法與擴展類的語法一樣。例如,下例的IRectangularShape介面擴展IShape介面(即,從IShape介面派生出IRectangularShape介面)。
interface IRectangularShape: IShape { int GetWidth(); }
如果你從兩個或者兩個以上的介面派生,父介面的名字列表用逗號分隔,如下面的代碼所示:
interface INewInterface: IParent1, IParent2 { }
然而,與Java不同,C#中的介面不能包含域(Field)。
另外還要注意,在C#中,介面內的所有方法默認都是公用方法。在Java中,方法聲明可以帶有public修飾符(即使這並非必要),但在C#中,顯式為介面的方法指定public修飾符是非法的。例如,下面的C#介面將產生一個編譯錯誤。
interface IShape { public void Draw(); }
2.16、is和as操作符
C#中的is操作符與Java中的instanceof操作符一樣,兩者都可以用來測試某個對象的實例是否屬於特定的類型。在Java中沒有與C#中的as操作符等價的操作符。as操作符與is操作符非常相似,但它更富有「進取心」:如果類型正確的話,as操作符會嘗試把被測試的對象引用轉換成目標類型;否則,它把變數引用設置成null。
為正確理解as操作符,首先請考慮下面這個例子中is操作符的運用。這個例子包含一個IShape介面,以及兩個實現了IShape介面的類Rectangle和Circle。
using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
if (shape is Rectangle) {
Rectangle rectangle = (Rectangle) shape;
Console.WriteLine("Width : " + rectangle.GetWidth());
}
if (shape is Circle) {
Circle circle = (Circle) shape;
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}
編譯好代碼之後,用戶可以輸入「rectangle」或者「circle」作為Main方法的參數。如果用戶輸入的是「circle」,則shape被實例化成為一個Circle類型的對象;反之,如果用戶輸入的是「rectangle」,則shape被實例化成為Rectangle類型的對象。隨後,程序用is操作符測試shape的變數類型:如果shape是一個矩形,則shape被轉換成為Rectangle對象,我們調用它的GetWidth方法;如果shape是一個圓,則shape被轉換成為一個Circle對象,我們調用它的GetRadius方法。
如果使用as操作符,則上述代碼可以改成如下形式:
using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
Rectangle rectangle = shape as Rectangle;
if (rectangle != null) {
Console.WriteLine("Width : " + rectangle.GetWidth());
}
else {
Circle circle = shape as Circle;
if (circle != null)
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}
在上面代碼的粗體部分中,我們在沒有測試shape對象類型的情況下,就用as操作符把shape轉換成Rectangle類型的對象。如果shape正好是一個Rectangle,則shape被轉換成為Rectangle類型的對象並保存到rectangle變數,然後我們調用它的GetWidth方法。如果這種轉換失敗,則我們進行第二次嘗試。這一次,shape被轉換成為Circle類型的對象並保存到circle變數。如果shape確實是一個Circle對象,則circle現在引用了一個Circle對象,我們調用它的GetRadius方法。
2.17、庫
C#沒有自己的類庫。但是,C#共享了.NET的類庫。當然,.NET類庫也可以用於其他.NET語言,比如VB.NET或者JScript.NET。值得一提的是StringBuilder類,它是對String類的補充。StringBuilder類與Java的StringBuffer類非常相似。
2.18、垃圾收集
C++已經讓我們認識到手工管理內存是多麼缺乏效率和浪費時間。當你在C++中創建了一個對象,你就必須手工地拆除這個對象。代碼越復雜,這個任務也越困難。Java用垃圾收集器來解決這個問題,由垃圾收集器搜集不再使用的對象並釋放內存。C#同樣採用了這種方法。應該說,如果你也在開發一種新的OOP語言,追隨這條道路是一種非常自然的選擇。C#仍舊保留了C++的內存手工管理方法,它適合在速度極端重要的場合使用,而在Java中這是不允許的。
2.19、異常處理
如果你聽說C#使用與Java相似的異常處理機制,你不會為此而驚訝,對吧?在C#中,所有的異常都從一個名為Exception的類派生(聽起來很熟悉?)另外,正如在Java中一樣,你還有熟悉的try和catch語句。Exception類屬於.NET System名稱空間的一部分。
三、Java沒有的功能
C#出生在Java成熟之後,因此,C#擁有一些Java(目前)還沒有的絕妙功能也就不足為奇。
3.1、枚舉器
枚舉器即enum類型(Enumerator,或稱為計數器),它是一個相關常量的集合。精確地說,enum類型聲明為一組相關的符號常量定義了一個類型名字。例如,你可以創建一個名為Fruit(水果)的枚舉器,把它作為一個變數值的類型使用,從而把變數可能的取值范圍限制為枚舉器中出現的值。
public class Demo {
public enum Fruit {
Apple, Banana, Cherry, Durian
}
public void Process(Fruit fruit) {
switch (fruit) {
case Fruit.Apple:
...
break;
case Fruit.Banana:
...
break;
case Fruit.Cherry:
...
break;
case Fruit.Durian:
...
break;
}
}
}
在上例的Process方法中,雖然你可以用int作為myVar變數的類型,但是,使用枚舉器Fruit之後,變數的取值范圍限制到了Applet、Banana、Cherry和Durian這幾個值之內。與int相比,enum的可讀性更好,自我說明能力更強。
3.2、結構
結構(Struct)與類很相似。然而,類是作為一種引用類型在堆中創建,而結構是一種值類型,它存儲在棧中或者是嵌入式的。因此,只要謹慎運用,結構要比類快。結構可以實現介面,可以象類一樣擁有成員,但結構不支持繼承。
然而,簡單地用結構來取代類可能導致慘重損失。這是因為,結構是以值的方式傳遞,由於這種傳遞方式要把值復制到新的位置,所以傳遞一個「肥胖的」結構需要較大的開銷。而對於類,傳遞的時候只需傳遞它的引用。
下面是一個結構的例子。注意它與類非常相似,只要把單詞「struct」替換成「class」,你就得到了一個類。
struct Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
3.3、屬性
C#類除了可以擁有域(Field)之外,它還可以擁有屬性(Property)。屬性是一個與類或對象關聯的命名的特徵。屬性是域的一種自然擴展――兩者都是有類型、有名字的類成員。然而,和域不同的是,屬性不表示存儲位置;相反,屬性擁有存取器(accessor),存取器定義了讀取或者寫入屬性值時必須執行的代碼。因此,屬性提供了一種把動作和讀取、寫入對象屬性值的操作關聯起來的機制,而且它們允許屬性值通過計算得到。
在C#中,屬性通過屬性聲明語法定義。屬性聲明語法的第一部分與域聲明很相似,第二部分包括一個set過程和/或一個get過程。例如,在下面的例子中,PropertyDemo類定義了一個Prop屬性。
public class PropertyDemo {
private string prop;
public string Prop {
get {
return prop;
}
set {
prop = value;
}
}
}
如果屬性既允許讀取也允許寫入,如PropertyDemo類的Prop屬性,則它同時擁有get和set存取過程。當我們讀取屬性的值時,get存取過程被調用;當我們寫入屬性值時,set存取過程被調用。在set存取過程中,屬性的新值在一個隱含的value參數中給出。
與讀取和寫入域的方法一樣,屬性也可以用同樣的語法讀取和寫入。例如,下面的代碼實例化了一個PropertyDemo類,然後寫入、讀取它的Prop屬性。
PropertyDemo pd = new PropertyDemo();
pd.Prop = "123"; // set
string s = pd.Prop; // get
3.4、以引用方式傳遞簡單數據類型的參數
在Java中,當你把一個簡單數據類型的值作為參數傳遞給方法時,參數總是以值的方式傳遞――即,系統將為被調用的方法創建一個參數值的副本。在C#中,你可以用引用的方式傳遞一個簡單數據類型的值。此時,被調用的方法將直接使用傳遞給它的那個值――也就是說,如果在被調用方法內部修改了參數的值,則原來的變數值也隨之改變。
在C#中以引用方式傳遞值時,我們使用ref關鍵詞。例如,如果編譯並運行下面的代碼,你將在控制台上看到輸出結果16。注意i值被傳遞給ProcessNumber之後是如何被改變的。
using System;
public class PassByReference {
public static void Main(String[] args) {
int i = 8;
ProcessNumber(ref i);
Console.WriteLine(i);
}
public static void ProcessNumber(ref int j) {
j = 16;
}
}
C#中還有一個允許以引用方式傳遞參數的關鍵詞out,它與ref相似。但是,使用out時,作為參數傳遞的變數在傳遞之前不必具有已知的值。在上例中,如果整數i在傳遞給ProcessNumber方法之前沒有初始化,則代碼將出錯。如果用out來取代ref,你就可以傳遞一個未經初始化的值,如下面這個修改後的例子所示。
using System;
public class PassByReference {
public static void Main(String[] args) {
int i;
ProcessNumber(out i);
Console.WriteLine(i);
}
public static void ProcessNumber(out int j) {
j = 16;
}
}
經過修改之後,雖然i值在傳遞給ProcessNumber方法之前沒有初始化,但PassByReference類能夠順利通過編譯。
3.5、C#保留了指針
對於那些覺得自己能夠恰到好處地運用指針並樂意手工進行內存管理的開發者來說,在C#中,他們仍舊可以用既不安全也不容易使用的「古老的」指針來提高程序的性能。C#提供了支持「不安全」(unsafe)代碼的能力,這種代碼能夠直接操作指針,能夠「固定」對象以便臨時地阻止垃圾收集器移動對象。無論從開發者還是用戶的眼光來看,這種對「不安全」代碼的支持其實是一種安全功能。「不安全」的代碼必須用unsafe關鍵詞顯式地標明,因此開發者不可能在無意之中使用「不安全」的代碼。同時,C#編譯器又和執行引擎協作,保證了「不安全」的代碼不能偽裝成為安全代碼。
using System;
class UsePointer {
unsafe static void PointerDemo(byte[] arr) {
.
.
}
}
C#中的unsafe代碼適合在下列情形下使用:當速度極端重要時,或者當對象需要與現有的軟體(比如COM對象或者DLL形式的C代碼)交互時。
3.6、代理
代理(delegate)可以看作C++或者其他語言中的函數指針。然而,與函數指針不同的是,C#中的代理是面向對象的、類型安全的、可靠的。而且,函數指針只能用來引用靜態函數,但代理既能夠引用靜態方法,也能夠引用實例方法。代理用來封裝可調用方法。你可以在類裡面編寫方法並在該方法上創建代理,此後這個代理就可以被傳遞到第二個方法。這樣,第二個方法就可以調用第一個方法。
代理是從公共基類System.Delegate派生的引用類型。定義和使用代理包括三個步驟:聲明,創建實例,調用。代理用delegate聲明語法聲明。例如,一個不需要參數且沒有返回值的代理可以用如下代碼聲明:
delegate void TheDelegate();
創建代理實例的語法是:使用new關鍵詞,並引用一個實例或類方法,該方法必須符合代理指定的特徵。一旦創建了代理的實例,我們就可以用調用方法的語法調用它。
3.7、包裝和解除包裝
在面向對象的編程語言中,我們通常使用的是對象。但為了提高速度,C#也提供了簡單數據類型。因此,C#程序既包含一大堆的對象,又有大量的值。在這種環境下,讓這兩者協同工作始終是一個不可迴避的問題,你必須要有一種讓引用和值進行通信的方法。
在C#以及.NET運行時環境中,這個「通信」問題通過包裝(Boxing)和解除包裝(Unboxing)解決。包裝是一種讓值類型看起來象引用類型的處理過程。當一個值類型(簡單數據類型)被用於一個要求或者可以使用對象的場合時,包裝操作自動進行。包裝一個value-type值的步驟包括:分配一個對象實例,然後把value-type值復制到對象實例。
解除包裝所執行的動作與包裝相反,它把一個引用類型轉換成值類型。解除包裝操作的步驟包括:首先檢查並確認對象實例確實是給定value-type的一個經過包裝的值,然後從對象實例復制出值。
Java對該問題的處理方式略有不同。Java為每一種簡單數據類型提供了一個對應的類封裝器。例如,用Integer類封裝int類型,用Byte類封裝byte類型。
【結束語】本文為你比較了C#和Java。這兩種語言很相似,然而,說C#是Java的克隆或許已經大大地言過其實。面向對象、中間語言這類概念並不是什麼新東西。如果你准備設計一種面向對象的新語言,而且它必須在一個受管理的安全環境內運行,你難道不會搞出與C#差不多的東西嗎?
2. 現在學C# 好,還是Java比較好
華山論劍:C#對Java
(仙人掌工作室 2001年06月29日 17:01)
C#(C-Sharp)是Microsoft的新編程語言,被譽為「C/C++家族中第一種面向組件的語言」。然而,不管它自己宣稱的是什麼,許多人認為C#更像是Java的一種克隆,或者是Microsoft用來替代Java的產品。事實是否是這樣的呢?
本文的比較結果表明,C#不止是Java的同胞那麼簡單。如果你是一個Java開發者,想要學習C#或者了解更多有關C#的知識,那麼本文就是你必須把最初10分鍾投入於其中的所在。
一、C#、C++和Java
C#的語言規范由Microsoft的Anders Hejlsberg與Scott Wiltamuth編寫。在當前Microsoft天花亂墜的宣傳中,對C#和C++、Java作一番比較總是很有趣的。考慮到當前IT媒體的輿論傾向,如果你早就知道C#更接近Java而不是C++,事情也不值得大驚小怪。對於剛剛加入這場討論的讀者,下面的表1讓你自己作出判斷。顯然,結論應該是:Java和C#雖然不是孿生子,但C#最主要的特色卻更接近Java而不是C++。
表1:比較C#、C++和Java最重要的功能
功能 C# C++ Java
繼承 允許繼承單個類,允許實現多個介面 允許從多個類繼承 允許繼承單個類,允許實現多個介面
介面實現 通過「interface」關鍵詞 通過抽象類 通過「interface」關鍵詞
內存管理 由運行時環境管理,使用垃圾收集器 需要手工管理 由運行時環境管理,使用垃圾收集器
指針 支持,但只在很少使用的非安全模式下才支持。通常以引用取代指針 支持,一種很常用的功能。 完全不支持。代之以引用。
源代碼編譯後的形式 .NET中間語言(IL) 可執行代碼 位元組碼
單一的公共基類 是 否 是
異常處理 異常處理 返回錯誤 異常處理。
了解表1總結的重要語言功能之後,請繼續往下閱讀,了解C#和Java的一些重要區別。
二、語言規范的比較
2.1、簡單數據類型
簡單數據類型(Primitive)在C#中稱為值類型,C#預定義的簡單數據類型比Java多。例如,C#有unit,即無符號整數。表2列出了所有C#的預定義數據類型:
表2:C#中的值類型
類型 說明
object 所有類型的最終極的基類
string 字元串類型;字元串是一個Unicode字元的序列
sbyte 8位帶符號整數
short 16位帶符號整數
int 32位帶符號整數
long 64位帶符號整數
byte 8位無符號整數
ushort 16位無符號整數
uint 32位無符號整數
ulong 64位無符號整數
float 單精度浮點數類型
double 雙精度浮點數類型
bool 布爾類型;bool值或者是true,或者是false
char 字元類型;一個char值即是一個Unicode字元
decimal 有28位有效數字的高精度小數類型
2.2、常量
忘掉Java中的static final修飾符。在C#中,常量可以用const關鍵詞聲明。
public const int x = 55;
此外,C#的設計者還增加了readonly關鍵詞。如果編譯器編譯時未能確定常量值,你可以使用readonly關鍵詞。readonly域只能通過初始化器或類的構造函數設置。
2.3、公用類的入口點
在Java中,公用類的入口點是一個名為main的公用靜態方法。main方法的參數是String對象數組,它沒有返回值。在C#中,main方法變成了公用靜態方法Main(大寫的M),Main方法的參數也是一個String對象數組,而且也沒有返回值,如下面的原型聲明所示:
public static void Main(String[] args)
但是,C#的Main方法不局限於此。如果不向Main方法傳遞任何參數,你可以使用上述Main方法的一個重載版本,即不帶參數列表的版本。也就是說,下面的Main方法也是一個合法的入口點:
public static void Main()
另外,如果你認為有必要的話,Main方法還可以返回一個int。例如,下面代碼中的Main方法返回1:
using System;
public class Hello {
public static int Main() {
Console.WriteLine("Done");
return 1;
}
}
與此相對,在Java中重載main方法是不合法的。
2.4、switch語句
在Java中,switch語句只能處理整數。但C#中的switch語句不同,它還能夠處理字元變數。請考慮下面用switch語句處理字元串變數的C#代碼:
using System;
public class Hello {
public static void Main(String[] args) {
switch (args[0]) {
case "老闆":
Console.WriteLine("早上好!我們隨時准備為您效勞!");
break;
case "雇員":
Console.WriteLine("早上好!你可以開始工作了!");
break;
default:
Console.WriteLine("早上好!祝你好運!");
break;
}
}
}
與Java中的switch不同,C#的switch語句要求每一個case塊或者在塊的末尾提供一個break語句,或者用goto轉到switch內的其他case標簽。
2.5、foreach語句
foreach語句枚舉集合中的各個元素,為集合中的每一個元素執行一次代碼塊。請參見下面的例子。
using System;
public class Hello {
public static void Main(String[] args) {
foreach (String arg in args)
Console.WriteLine(arg);
}
}
如果在運行這個執行文件的時候指定了參數,比如「Hello Peter Kevin Richard」,則程序的輸出將是下面幾行文字:
Peter
Kevin
Richard
2.6、C#沒有>>>移位操作符
C#支持uint和ulong之類的無符號變數類型。因此,在C#中,右移操作符(即「>>」)對於無符號變數類型和帶符號變數類型(比如int和long)的處理方式不同。右移uint和ulong丟棄低位並把空出的高位設置為零;但對於int和long類型的變數,「>>」操作符丟棄低位,同時,只有當變數值是正數時,「>>」才把空出的高位設置成零;如果「>>」操作的是一個負數,空出的高位被設置成為1。
Java中不存在無符號的變數類型。因此,我們用「>>>」操作符在右移時引入負號位;否則,使用「>>」操作符。
2.7、goto關鍵詞
Java不用goto關鍵詞。在C#中,goto允許你轉到指定的標簽。不過,C#以特別謹慎的態度對待goto,比如它不允許goto轉入到語句塊的內部。在Java中,你可以用帶標簽的語句加上break或continue取代C#中的goto。
2.8、聲明數組
在Java中,數組的聲明方法非常靈活,實際上有許多種聲明方法都屬於合法的方法。例如,下面的幾行代碼是等價的:
int[] x = { 0, 1, 2, 3 };
int x[] = { 0, 1, 2, 3 };
但在C#中,只有第一行代碼合法,[]不能放到變數名字之後。
2.9、包
在C#中,包(Package)被稱為名稱空間。把名稱空間引入C#程序的關鍵詞是「using」。例如,「using System;」這個語句引入了System名稱空間。
然而,與Java不同的是,C#允許為名稱空間或者名稱空間中的類指定別名:
using TheConsole = System.Console;
public class Hello {
public static void Main() {
TheConsole.WriteLine("使用別名");
}
}
雖然從概念上看,Java的包類似於.NET的名稱空間。然而,兩者的實現方式不同。在Java中,包的名字同時也是實際存在的實體,它決定了放置.java文件的目錄結構。在C#中,物理的包和邏輯的名稱之間是完全分離的,也就是說,名稱空間的名字不會對物理的打包方式產生任何影響。在C#中,每一個源代碼文件可以從屬於多個名稱空間,而且它可以容納多個公共類。
.NET中包的實體稱為程序集(Assembly)。每一個程序集包含一個manifest結構。manifest列舉程序集所包含的文件,控制哪些類型和資源被顯露到程序集之外,並把對這些類型和資源的引用映射到包含這些類型與資源的文件。程序集是自包含的,一個程序集可以放置到單一的文件之內,也可以分割成多個文件。.NET的這種封裝機制解決了DLL文件所面臨的問題,即臭名昭著的DLL Hell問題。
2.10、默認包
在Java中,java.lang包是默認的包,它無需顯式導入就已經自動包含。例如,要把一些文本輸出到控制台,你可以使用下面的代碼:
System.out.println("Hello world from Java");
C#中不存在默認的包。如果要向控制台輸出文本,你使用System名稱空間Console對象的WriteLine方法。但是,你必須顯式導入所有的類。代碼如下:
using System;
public class Hello {
public static void Main() {
Console.WriteLine("Hello world from C#");
}
}
2.11、面向對象
Java和C#都是完全面向對象的語言。在面向對象編程的三大原則方面,這兩種語言接近得不能再接近。
繼承:這兩種語言都支持類的單一繼承,但類可以實現多個介面。所有類都從一個公共的基類繼承。
封裝與可見性:無論是在Java還是C#中,你都可以決定類成員是否可見。除了C#的internal訪問修飾符之外,兩者的可見性機制非常相似。
多態性:Java和C#都支持某些形式的多態性機制,且兩者實現方法非常類似。
2.12、可訪問性
類的每個成員都有特定類型的可訪問性。C#中的訪問修飾符與Java中的基本對應,但多出了一個internal。簡而言之,C#有5種類型的可訪問性,如下所示:
public:成員可以從任何代碼訪問。
protected:成員只能從派生類訪問。
internal:成員只能從同一程序集的內部訪問。
protected internal:成員只能從同一程序集內的派生類訪問。
private:成員只能在當前類的內部訪問。
2.13、派生類
在Java中,我們用關鍵詞「extends」實現繼承。C#採用了C++的類派生語法。例如,下面的代碼顯示了如何派生父類Control從而創建出新類Button:
public class Button: Control { . . }
2.14、最終類
由於C#中不存在final關鍵詞,如果想要某個類不再被派生,你可以使用sealed關鍵詞,如下例所示:
sealed class FinalClass { . . }
2.15、介面
介面這個概念在C#和Java中非常相似。介面的關鍵詞是interface,一個介面可以擴展一個或者多個其他介面。按照慣例,介面的名字以大寫字母「I」開頭。下面的代碼是C#介面的一個例子,它與Java中的介面完全一樣:
interface IShape { void Draw(); }
擴展介面的語法與擴展類的語法一樣。例如,下例的IRectangularShape介面擴展IShape介面(即,從IShape介面派生出IRectangularShape介面)。
interface IRectangularShape: IShape { int GetWidth(); }
如果你從兩個或者兩個以上的介面派生,父介面的名字列表用逗號分隔,如下面的代碼所示:
interface INewInterface: IParent1, IParent2 { }
然而,與Java不同,C#中的介面不能包含域(Field)。
另外還要注意,在C#中,介面內的所有方法默認都是公用方法。在Java中,方法聲明可以帶有public修飾符(即使這並非必要),但在C#中,顯式為介面的方法指定public修飾符是非法的。例如,下面的C#介面將產生一個編譯錯誤。
interface IShape { public void Draw(); }
2.16、is和as操作符
C#中的is操作符與Java中的instanceof操作符一樣,兩者都可以用來測試某個對象的實例是否屬於特定的類型。在Java中沒有與C#中的as操作符等價的操作符。as操作符與is操作符非常相似,但它更富有「進取心」:如果類型正確的話,as操作符會嘗試把被測試的對象引用轉換成目標類型;否則,它把變數引用設置成null。
為正確理解as操作符,首先請考慮下面這個例子中is操作符的運用。這個例子包含一個IShape介面,以及兩個實現了IShape介面的類Rectangle和Circle。
using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
if (shape is Rectangle) {
Rectangle rectangle = (Rectangle) shape;
Console.WriteLine("Width : " + rectangle.GetWidth());
}
if (shape is Circle) {
Circle circle = (Circle) shape;
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}
編譯好代碼之後,用戶可以輸入「rectangle」或者「circle」作為Main方法的參數。如果用戶輸入的是「circle」,則shape被實例化成為一個Circle類型的對象;反之,如果用戶輸入的是「rectangle」,則shape被實例化成為Rectangle類型的對象。隨後,程序用is操作符測試shape的變數類型:如果shape是一個矩形,則shape被轉換成為Rectangle對象,我們調用它的GetWidth方法;如果shape是一個圓,則shape被轉換成為一個Circle對象,我們調用它的GetRadius方法。
如果使用as操作符,則上述代碼可以改成如下形式:
using System;
interface IShape {
void draw();
}
public class Rectangle: IShape {
public void draw() {
}
public int GetWidth() {
return 6;
}
}
public class Circle: IShape {
public void draw() {
}
public int GetRadius() {
return 5;
}
}
public class LetsDraw {
public static void Main(String[] args) {
IShape shape = null;
if (args[0] == "rectangle") {
shape = new Rectangle();
}
else if (args[0] == "circle") {
shape = new Circle();
}
Rectangle rectangle = shape as Rectangle;
if (rectangle != null) {
Console.WriteLine("Width : " + rectangle.GetWidth());
}
else {
Circle circle = shape as Circle;
if (circle != null)
Console.WriteLine("Radius : " + circle.GetRadius());
}
}
}
在上面代碼的粗體部分中,我們在沒有測試shape對象類型的情況下,就用as操作符把shape轉換成Rectangle類型的對象。如果shape正好是一個Rectangle,則shape被轉換成為Rectangle類型的對象並保存到rectangle變數,然後我們調用它的GetWidth方法。如果這種轉換失敗,則我們進行第二次嘗試。這一次,shape被轉換成為Circle類型的對象並保存到circle變數。如果shape確實是一個Circle對象,則circle現在引用了一個Circle對象,我們調用它的GetRadius方法。
2.17、庫
C#沒有自己的類庫。但是,C#共享了.NET的類庫。當然,.NET類庫也可以用於其他.NET語言,比如VB.NET或者JScript.NET。值得一提的是StringBuilder類,它是對String類的補充。StringBuilder類與Java的StringBuffer類非常相似。
2.18、垃圾收集
C++已經讓我們認識到手工管理內存是多麼缺乏效率和浪費時間。當你在C++中創建了一個對象,你就必須手工地拆除這個對象。代碼越復雜,這個任務也越困難。Java用垃圾收集器來解決這個問題,由垃圾收集器搜集不再使用的對象並釋放內存。C#同樣採用了這種方法。應該說,如果你也在開發一種新的OOP語言,追隨這條道路是一種非常自然的選擇。C#仍舊保留了C++的內存手工管理方法,它適合在速度極端重要的場合使用,而在Java中這是不允許的。
2.19、異常處理
如果你聽說C#使用與Java相似的異常處理機制,你不會為此而驚訝,對吧?在C#中,所有的異常都從一個名為Exception的類派生(聽起來很熟悉?)另外,正如在Java中一樣,你還有熟悉的try和catch語句。Exception類屬於.NET System名稱空間的一部分。
三、Java沒有的功能
C#出生在Java成熟之後,因此,C#擁有一些Java(目前)還沒有的絕妙功能也就不足為奇。
3.1、枚舉器
枚舉器即enum類型(Enumerator,或稱為計數器),它是一個相關常量的集合。精確地說,enum類型聲明為一組相關的符號常量定義了一個類型名字。例如,你可以創建一個名為Fruit(水果)的枚舉器,把它作為一個變數值的類型使用,從而把變數可能的取值范圍限制為枚舉器中出現的值。
public class Demo {
public enum Fruit {
Apple, Banana, Cherry, Durian
}
public void Process(Fruit fruit) {
switch (fruit) {
case Fruit.Apple:
...
break;
case Fruit.Banana:
...
break;
case Fruit.Cherry:
...
break;
case Fruit.Durian:
...
break;
}
}
}
在上例的Process方法中,雖然你可以用int作為myVar變數的類型,但是,使用枚舉器Fruit之後,變數的取值范圍限制到了Applet、Banana、Cherry和Durian這幾個值之內。與int相比,enum的可讀性更好,自我說明能力更強。
3.2、結構
結構(Struct)與類很相似。然而,類是作為一種引用類型在堆中創建,而結構是一種值類型,它存儲在棧中或者是嵌入式的。因此,只要謹慎運用,結構要比類快。結構可以實現介面,可以象類一樣擁有成員,但結構不支持繼承。
然而,簡單地用結構來取代類可能導致慘重損失。這是因為,結構是以值的方式傳遞,由於這種傳遞方式要把值復制到新的位置,所以傳遞一個「肥胖的」結構需要較大的開銷。而對於類,傳遞的時候只需傳遞它的引用。
下面是一個結構的例子。注意它與類非常相似,只要把單詞「struct」替換成「class」,你就得到了一個類。
struct Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
3.3、屬性
C#類除了可以擁有域(Field)之外,它還可以擁有屬性(Property)。屬性是一個與類或對象關聯的命名的特徵。屬性是域的一種自然擴展――兩者都是有類型、有名字的類成員。然而,和域不同的是,屬性不表示存儲位置;相反,屬性擁有存取器(accessor),存取器定義了讀取或者寫入屬性值時必須執行的代碼。因此,屬性提供了一種把動作和讀取、寫入對象屬性值的操作關聯起來的機制,而且它們允許屬性值通過計算得到。
在C#中,屬性通過屬性聲明語法定義。屬性聲明語法的第一部分與域聲明很相似,第二部分包括一個set過程和/或一個get過程。例如,在下面的例子中,PropertyDemo類定義了一個Prop屬性。
public class PropertyDemo {
private string prop;
public string Prop {
get {
return prop;
}
set {
prop = value;
}
}
}
如果屬性既允許讀取也允許寫入,如PropertyDemo類的Prop屬性,則它同時擁有get和set存取過程。當我們讀取屬性的值時,get存取過程被調用;當我們寫入屬性值時,set存取過程被調用。在set存取過程中,屬性的新值在一個隱含的value參數中給出。
與讀取和寫入域的方法一樣,屬性也可以用同樣的語法讀取和寫入。例如,下面的代碼實例化了一個PropertyDemo類,然後寫入、讀取它的Prop屬性。
PropertyDemo pd = new PropertyDemo();
pd.Prop = "123"; // set
string s = pd.Prop; // get
3.4、以引用方式傳遞簡單數據類型的參數
在Java中,當你把一個簡單數據類型的值作為參數傳遞給方法時,參數總是以值的方式傳遞――即,系統將為被調用的方法創建一個參數值的副本。在C#中,你可以用引用的方式傳遞一個簡單數據類型的值。此時,被調用的方法將直接使用傳遞給它的那個值――也就是說,如果在被調用方法內部修改了參數的值,則原來的變數值也隨之改變。
在C#中以引用方式傳遞值時,我們使用ref關鍵詞。例如,如果編譯並運行下面的代碼,你將在控制台上看到輸出結果16。注意i值被傳遞給ProcessNumber之後是如何被改變的。
using System;
public class PassByReference {
public static void Main(String[] args) {
int i = 8;
ProcessNumber(ref i);
Console.WriteLine(i);
}
public static void ProcessNumber(ref int j) {
j = 16;
}
}
C#中還有一個允許以引用方式傳遞參數的關鍵詞out,它與ref相似。但是,使用out時,作為參數傳遞的變數在傳遞之前不必具有已知的值。在上例中,如果整數i在傳遞給ProcessNumber方法之前沒有初始化,則代碼將出錯。如果用out來取代ref,你就可以傳遞一個未經初始化的值,如下面這個修改後的例子所示。
using System;
public class PassByReference {
public static void Main(String[] args) {
int i;
ProcessNumber(out i);
Console.WriteLine(i);
}
public static void ProcessNumber(out int j) {
j = 16;
}
}
經過修改之後,雖然i值在傳遞給ProcessNumber方法之前沒有初始化,但PassByReference類能夠順利通過編譯。
3.5、C#保留了指針
對於那些覺得自己能夠恰到好處地運用指針並樂意手工進行內存管理的開發者來說,在C#中,他們仍舊可以用既不安全也不容易使用的「古老的」指針來提高程序的性能。C#提供了支持「不安全」(unsafe)代碼的能力,這種代碼能夠直接操作指針,能夠「固定」對象以便臨時地阻止垃圾收集器移動對象。無論從開發者還是用戶的眼光來看,這種對「不安全」代碼的支持其實是一種安全功能。「不安全」的代碼必須用unsafe關鍵詞顯式地標明,因此開發者不可能在無意之中使用「不安全」的代碼。同時,C#編譯器又和執行引擎協作,保證了「不安全」的代碼不能偽裝成為安全代碼。
using System;
class UsePointer {
unsafe static void PointerDemo(byte[] arr) {
.
.
}
}
C#中的unsafe代碼適合在下列情形下使用:當速度極端重要時,或者當對象需要與現有的軟體(比如COM對象或者DLL形式的C代碼)交互時。
3.6、代理
代理(delegate)可以看作C++或者其他語言中的函數指針。然而,與函數指針不同的是,C#中的代理是面向對象的、類型安全的、可靠的。而且,函數指針只能用來引用靜態函數,但代理既能夠引用靜態方法,也能夠引用實例方法。代理用來封裝可調用方法。你可以在類裡面編寫方法並在該方法上創建代理,此後這個代理就可以被傳遞到第二個方法。這樣,第二個方法就可以調用第一個方法。
代理是從公共基類System.Delegate派生的引用類型。定義和使用代理包括三個步驟:聲明,創建實例,調用。代理用delegate聲明語法聲明。例如,一個不需要參數且沒有返回值的代理可以用如下代碼聲明:
delegate void TheDelegate();
創建代理實例的語法是:使用new關鍵詞,並引用一個實例或類方法,該方法必須符合代理指定的特徵。一旦創建了代理的實例,我們就可以用調用方法的語法調用它。
3.7、包裝和解除包裝
在面向對象的編程語言中,我們通常使用的是對象。但為了提高速度,C#也提供了簡單數據類型。因此,C#程序既包含一大堆的對象,又有大量的值。在這種環境下,讓這兩者協同工作始終是一個不可迴避的問題,你必須要有一種讓引用和值進行通信的方法。
在C#以及.NET運行時環境中,這個「通信」問題通過包裝(Boxing)和解除包裝(Unboxing)解決。包裝是一種讓值類型看起來象引用類型的處理過程。當一個值類型(簡單數據類型)被用於一個要求或者可以使用對象的場合時,包裝操作自動進行。包裝一個value-type值的步驟包括:分配一個對象實例,然後把value-type值復制到對象實例。
解除包裝所執行的動作與包裝相反,它把一個引用類型轉換成值類型。解除包裝操作的步驟包括:首先檢查並確認對象實例確實是給定value-type的一個經過包裝的值,然後從對象實例復制出值。
Java對該問題的處理方式略有不同。Java為每一種簡單數據類型提供了一個對應的類封裝器。例如,用Integer類封裝int類型,用Byte類封裝byte類型。
【結束語】本文為你比較了C#和Java。這兩種語言很相似,然而,說C#是Java的克隆或許已經大大地言過其實。面向對象、中間語言這類概念並不是什麼新東西。如果你准備設計一種面向對象的新語言,而且它必須在一個受管理的安全環境內運行,你難道不會搞出與C#差不多的東西嗎?
3. 微信谷歌驗證器打不開怎麼處理
摘要 如果我們下載的谷歌二次驗證器是不能使用的,那麼可以直接利用微信小程序來進行操作,這樣就可以直接綁定好我們的微信,處理起來也是比較容易的。我們可以先注冊好自己的賬號,然後再進行驗證碼方面的確定。不管是成為會員還是直接使用二次驗證碼都是可以的,但是一般來講直接成為會員要更好一些,這樣至少是後期找回秘鑰等也是要更加容易一些。
4. 線程池中空閑的線程處於什麼狀態
一:阻塞狀態,線程並沒有銷毀,也沒有得到CPU時間片執行;
源碼追蹤:
for (;;) {
...
workQueue.take();
...
}
public E take()...{
...
while (count.get() == 0) { / /這里就是任務隊列中的消息數量
notEmpty.await();
}
...
}
public final void await()...{
...
LockSupport.park(this);
...
}
繼續往下:
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
U.park(false, 0L);
setBlocker(t, null);
}
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
//線程調用該方法,線程將一直阻塞直到超時,或者是中斷條件出現。
public native void park(boolean isAbsolute, long time);
上面就是java11線程池中阻塞的源碼追蹤;
二.對比object的wait()方法:
@FastNative
public final native void wait(long timeout, int nanos) throws InterruptedException;
還有Thread的sleep() 方法:
@FastNative
private static native void sleep(Object lock, long millis, int nanos)throws...;
可見,線程池中使用的阻塞方式並不是Object中的wait(),也不是Thread.sleep() ;
這3個方法最終實現都是通過c&c++實現的native方法.
三.在<<Java虛擬機(第二版)>>中,對線程狀態有以下介紹:
12.4.3狀態轉換
Java語言定義了5種線程狀態,在任意一個時間點,一個線程只能有且只有其中的一種
狀態,這5種狀態分別如下。
1)新建(New):創建後尚未啟動的線程處於這種狀態。
2)運行(Runable):Runable包括了操作系統線程狀態中的Running和Ready,也就是處於此
狀態的線程有可能正在執行,也有可能正在等待著CPU為它分配執行時間。
3)無限期等待(Waiting):處於這種狀態的線程不會被分配CPU執行時間,它們要等待被
其他線程顯式地喚醒。以下方法會讓線程陷入無限期的等待狀態:
●沒有設置Timeout參數的Object.wait()方法。
●沒有設置Timeout參數的Thread.join()方法。
●LockSupport.park()方法。
4)限期等待(Timed Waiting):處於這種狀態的線程也不會被分配CPU執行時間,不過無
須等待被其他線程顯式地喚醒,在一定時間之後它們會由系統自動喚醒。以下方法會讓線程
進入限期等待狀態:
●Thread.sleep()方法。
●設置了Timeout參數的Object.wait()方法。
●設置了Timeout參數的Thread.join()方法。
●LockSupport.parkNanos()方法。
●LockSupport.parkUntil()方法。
5)阻塞(Blocked):線程被阻塞了,「阻塞狀態」與「等待狀態」的區別是:「阻塞狀態」在等
待著獲取到一個排他鎖,這個事件將在另外一個線程放棄這個鎖的時候發生;而「等待狀
態」則是在等待一段時間,或者喚醒動作的發生。在程序等待進入同步區域的時候,線程將
進入這種狀態。
結束(Terminated):已終止線程的線程狀態,線程已經結束執行。
5. 你在閱讀源代碼或設計文檔時,看到哪些驚艷的技巧
作為一個寫Java c++的人,通過OOP進入骨頭,我真的很佩服別人寫的C代碼。無論它是使用函數指針作為結構的參數來復制OOP,還是純粹的程序化,都是書面的,所有函數都可以重入,它們不會相互影響,每次看到它們,你都會感到震驚。
上次偶然間看到的設計,本想和這位大神好好交流的,但是沒有見到真人。最近在寫一個RDB(Redis的mp文件)逆序列化的東西 看到一些作者節約存儲空間的方式 比如 大多數情況下 key或value的長度都能維持在60左右 所以作者用來存儲內容長度的位元組默認只用6bit 另外2bit做標記 如果超出6bit能表達的長度 則用14bit表達 如果14bit表達不了 則索性用32bit表達 佔用位元組分別是 1、2、5(第一個位元組用來標記接下來4個位元組是實際長度) 根據內容的不同 存儲的方式也不一樣 比如普通的String類型 如果裡面的字元全部都是數字 他會轉換成整型去存 內容有一定長度後 進行lzf壓縮 Redis的rdb還是相對簡單的 沒辦法想像寫mysql mp文件的逆序列化的人有多蛋疼。
6. 我的電腦里的java程序是不是重復了系統是64位的,那個後綴是24的是不是可以卸載掉呢謝謝
、 public、 internal 修飾符的訪問許可權。
答 . private : 私有成員, 在類的內部才可以訪問。
protected : 保護成員,該類內部和繼承類中可以訪問。
public : 公共成員,完全公開,沒有訪問限制。
internal: 在同一命名空間內可以訪問。
2 .列舉ASP.NET 頁面之間傳遞值的幾種方式。
答. 1.使用QueryString, 如....?id=1; response. Redirect()....
2.使用Session變數
3.使用Server.Transfer
4.C#中的委託是什麼?事件是不是一種委託?
答 : 委託可以把一個方法作為參數代入另一個方法。
委託可以理解為指向一個函數的引用。
是,是一種特殊的委託
5.override與重載的區別
答 :override 與重載的區別。重載是方法的名稱相同。參數或參數類型不同,進行多
次重載以適應不同的需要
Override 是進行基類中函數的重寫。為了適應需要。
6.如果在一個B/S結構的系統中需要傳遞變數值,但是又不能使用Session、
Cookie、Application,您有幾種方法進行處理?
答 : this.Server.Transfer
9.描述一下C#中索引器的實現過程,是否只能根據數字進行索引?
答:不是。可以用任意類型。
11.用.net做B/S結構的系統,您是用幾層結構來開發,每一層之間的關系以及為
什麼要這樣分層?
答:一般為3層
數據訪問層,業務層,表示層。
數據訪問層對資料庫進行增刪查改。
業務層一般分為二層,業務表觀層實現與表示層的溝通,業務規則層實現用戶密
碼的安全等。
表示層為了與用戶交互例如用戶添加表單。
優點: 分工明確,條理清晰,易於調試,而且具有可擴展性。
缺點: 增加成本。
13.什麼叫應用程序域?
答:應用程序域可以理解為一種輕量級進程。起到安全的作用。佔用資源小。
14.CTS、CLS、CLR分別作何解釋?
答:CTS:通用語言系統。CLS:通用語言規范。CLR:公共語言運行庫。
15.什麼是裝箱和拆箱?
答:從值類型介面轉換到引用類型裝箱。從引用類型轉換到值類型拆箱。
16.什麼是受管制的代碼?
答:unsafe:非託管代碼。不經過CLR運行。
17.什麼是強類型系統?
答:RTTI:類型識別系統。
18.net中讀寫資料庫需要用到那些類?他們的作用?
答:DataSet:數據存儲器。
DataCommand:執行語句命令。
DataAdapter:數據的集合,用語填充。
19.ASP.net的身份驗證方式有哪些?分別是什麼原理?
答:10。Windwos(默認)用IIS...From(窗體)用帳戶....Passport(密鑰)
20.什麼是Code-Behind技術?
答:代碼後植。
21.在.net中,配件的意思是?
答:程序集。(中間語言,源數據,資源,裝配清單)
22.常用的調用WebService的方法有哪些?
答:1.使用WSDL.exe命令行工具。
2.使用VS.NET中的Add Web Reference菜單選項
23..net Remoting 的工作原理是什麼?
答:伺服器端向客戶端發送一個進程編號,一個程序域編號,以確定對象的位置
。
24.在C#中,string str = null 與 string str = 「」 請盡量使用文字或圖
象說明其中的區別。
答:string str = null 是不給他分配內存空間,而string str = "" 給它分配
長度為空字元串的內存空間。
25.請詳述在dotnet中類(class)與結構(struct)的異同?
答:Class可以被實例化,屬於引用類型,是分配在內存的堆上的,Struct屬於值類
型,是分配在內存的棧上的.
27.分析以下代碼,完成填空
string strTmp = "abcdefg某某某";
int i= System.Text.Encoding.Default.GetBytes(strTmp).Length;
int j= strTmp.Length;
以上代碼執行完後,i= j=
答:i=13,j=10
28.SQLSERVER伺服器中,給定表 table1 中有兩個欄位 ID、LastUpdateDate,
ID表示更新的事務號, LastUpdateDate表示更新時的伺服器時間,請使用一句
SQL語句獲得最後更新的事務號
答:Select ID FROM table1 Where LastUpdateDate = (Select MAX
(LastUpdateDate) FROM table1)
30.簡要談一下您對微軟.NET 構架下remoting和webservice兩項技術的理解以及
實際中的應用。
答:WS主要是可利用HTTP,穿透防火牆。而Remoting可以利用TCP/IP,二進制傳
送提高效率。
31.公司要求開發一個繼承System.Windows.Forms.ListView類的組件,要求達到
以下的特殊功能:點擊ListView各列列頭時,能按照點擊列的每行值進行重排視
圖中的所有行 (排序的方式如DataGrid相似)。根據您的知識,請簡要談一下您的
思路
答:根據點擊的列頭,包該列的ID取出,按照該ID排序後,在給綁定到ListView中。
33.寫出一條Sql語句:取出表A中第31到第40記錄(SQLServer,以自動增長的ID
作為主鍵,注意:ID可能不是連續的。
答:解1: select top 10 * from A where id not in (select top 30 id
from A)
解2: select top 10 * from A where id >(select max(id) from (select
top 30 id from A )as A)
34.面向對象的語言具有________性、_________性、________性
答:封裝、繼承、多態。
35.能用foreach遍歷訪問的對象需要實現 ________________介面或聲明
________________方法的類型。
答:IEnumerable 、 GetEnumerator。
36.GC是什麼? 為什麼要有GC?
答:GC是垃圾收集器。程序員不用擔心內存管理,因為垃圾收集器會自動進行管
理。要請求垃圾收集,可以調用下面的方法之一:
System.gc()
Runtime.getRuntime().gc()
37.String s = new String("xyz");創建了幾個String Object?
答:兩個對象,一個是「xyx」,一個是指向「xyx」的引用對象s。
39.啟動一個線程是用run()還是start()?
答:啟動一個線程是調用start()方法,使線程所代表的虛擬處理機處於可運行狀
態,這意味著它可以由JVM調度並執行。這並不意味著線程就會立即運行。run()
方法可以產生必須退出的標志來停止一個線程。
40.介面是否可繼承介面? 抽象類是否可實現(implements)介面? 抽象類是否可
繼承實體類(concrete class)?
答:介面可以繼承介面。抽象類可以實現(implements)介面,抽象類是否可繼承
實體類,但前提是實體類必須有明確的構造函數。
41.構造器Constructor是否可被override?
答:構造器Constructor不能被繼承,因此不能重寫Overriding,但可以被重載
Overloading。
42.是否可以繼承String類?
答:String類是final類故不可以繼承。
43.try 里的code會
不會被執行,什麼時候被執行,在return前還是後?
答:會執行,在return前執行。
44.兩個對象值相同(x.equals(y) == true),但卻可有不同的hash code,這句
話對不對?
答:不對,有相同的hash code。
45.swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
答:switch(expr1)中,expr1是一個整數表達式。因此傳遞給 switch 和
case 語句的參數應該是 int、 short、 char 或者 byte。long,string 都不
能作用於swtich。
47.當一個線程進入一個對象的一個synchronized方法後,其它線程是否可進入此
對象的其它方法?
不能,一個對象的一個synchronized方法只能由一個線程訪問。
48.abstract的method是否可同時是static,是否可同時是native,是否可同時是
synchronized?
答:都不能。
49.List, Set, Map是否繼承自Collection介面?
答:List,Set是Map不是
50.Set里的元素是不能重復的,那麼用什麼方法來區分重復與否呢? 是用==還是
equals()? 它們有何區別?
答:Set里的元素是不能重復的,那麼用iterator()方法來區分重復與否。
equals()是判讀兩個Set是否相等。
equals()和==方法決定引用值是否指向同一對象equals()在類中被覆蓋,為的是
當兩個分離的對象的內容和類型相配的話,返回真值。
51.數組有沒有length()這個方法? String有沒有length()這個方法?
答:數組沒有length()這個方法,有length的屬性。String有有length()這個方
法。
52.sleep() 和 wait() 有什麼區別?
答:sleep()方法是使線程停止一段時間的方法。在sleep 時間間隔期滿後,線程
不一定立即恢復執行。這是因為在那個時刻,其它線程可能正在運行而且沒有被
調度為放棄執行,除非(a)「醒來」的線程具有更高的優先順序
(b)正在運行的線程因為其它原因而阻塞。
wait()是線程交互時,如果線程對一個同步對象x 發出一個wait()調用,該線程
會暫停執行,被調對象進入等待狀態,直到被喚醒或等待時間到。
53.short s1 = 1; s1 = s1 + 1;有什麼錯? short s1 = 1; s1 += 1;有什麼錯
?
答:short s1 = 1; s1 = s1 + 1;有錯,s1是short型,s1+1是int型,不能顯式
轉化為short型。可修改為s1 =(short)(s1 + 1) 。short s1 = 1; s1 += 1正
確。
54.談談final, finally, finalize的區別。
答:
final—修飾符(關鍵字)如果一個類被聲明為final,意味著它不能再派生出新
的子類,不能作為父類被繼承。因此 一個類不能既被聲明為 abstract的,又被
聲明為final的。將變數或方法聲明為final,可以保證它們在使用中 不被改變。
被聲明為final的變數必須在聲明時給定初值,而在以後的引用中只能讀取,不可
修改。被聲明為 final的方法也同樣只能使用,不能重載
finally—再異常處理時提供 finally 塊來執行任何清除操作。如果拋出一個異
常,那麼相匹配的 catch 子句就會 執行,然後控制就會進入 finally 塊(如果
有的話)。
finalize—方法名。Java 技術允許使用 finalize() 方法在垃圾收集器將對象
從內存中清除出去之前做必要的清理 工作。這個方法是由垃圾收集器在確定這個
對象沒有被引用時對這個對象調用的。它是在 Object 類中定義的 ,因此所有的
類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者執行其他清理工
作。finalize() 方法是在垃圾收集器刪除對象之前對這個對象調用的。
55.如何處理幾十萬條並發數據?
答:用存儲過程或事務。取得最大標識的時候同時更新..注意主鍵不是自增量方
式這種方法並發的時候是不會有重復主鍵的..取得最大標識要有一個存儲過程來
獲取.
56.Session有什麼重大BUG,微軟提出了什麼方法加以解決?
答:是iis中由於有進程回收機制,系統繁忙的話Session會丟失,可以用Sate
server或SQL Server資料庫的方式存儲Session不過這種方式比較慢,而且無法
捕獲Session的END事件。
57.進程和線程的區別?
答:進程是系統進行資源分配和調度的單位;線程是CPU調度和分派的單位,一個
進程可以有多個線程,這些線程共享這個進程的資源。
58.堆和棧的區別?
答: 棧:由編譯器自動分配、釋放。在函數體中定義的變數通常在棧上。
堆:一般由程序員分配釋放。用new、malloc等分配內存函數分配得到的就是在堆
上。
59.成員變數和成員函數前加static的作用?
答:它們被稱為常成員變數和常成員函數,又稱為類成員變數和類成員函數。分
別用來反映類的狀態。比如類成員變數可以用來統計類實例的數量,類成員函數
負責這種統計的動作。
60.ASP。NET與ASP相比,主要有哪些進步?
答:asp解釋形,aspx編譯型,性能提高,有利於保護源碼。
62.請說明在.net中常用的幾種頁面間傳遞參數的方法,並說出他們的優缺點。
答:session(viewstate) 簡單,但易丟失
application 全局
cookie 簡單,但可能不支持,可能被偽造
input ttype="hidden" 簡單,可能被偽造
url參數 簡單,顯示於地址欄,長度有限
資料庫 穩定,安全,但性能相對弱
63.請指出GAC的含義?
答:全局程序集緩存。
64.向伺服器發送請求有幾種方式?
答:get,post。get一般為鏈接方式,post一般為按鈕方式。
65.DataReader與Dataset有什麼區別?
答:一個是只能向前的只讀游標,一個是內存中的表。
66.軟體開發過程一般有幾個階段?每個階段的作用?
答:需求分析,架構設計,代碼編寫,QA,部署
67.在c#中using和new這兩個關鍵字有什麼意義,請寫出你所知道的意義?using
指令 和語句 new 創建實例 new 隱藏基類中方法。
答:using 引入名稱空間或者使用非託管資源
new 新建實例或者隱藏父類方法
68.需要實現對一個字元串的處理,首先將該字元串首尾的空格去掉,如果字元串中
間還有連續空格的話,僅保留一個空格,即允許字元串中間有多個空格,但連續的空
格數不可超過一個.
答:string inputStr=" xx xx ";
inputStr=Regex.Replace(inputStr.Trim()," *"," ");
70.什麼叫做SQL注入,如何防止?請舉例說明。
答:利用sql關鍵字對網站進行攻擊。過濾關鍵字'等
71.什麼是反射?
答:動態獲取程序集信息
72.用Singleton如何寫設計模式
答:static屬性裡面new ,構造函數private
73.什麼是Application Pool?
答:Web應用,類似Thread Pool,提高並發性能。
74.什麼是虛函數?什麼是抽象函數?
答:虛函數:沒有實現的,可由子類繼承並重寫的函數。抽象函數:規定其非虛
子類必須實現的函數,必須被重寫。
75.什麼是XML?
答:XML即可擴展標記語言。eXtensible Markup Language.標記是指計算機所能
理解的信息符號,通過此種標記,計算機之間可以處理包含各種信息的文章等。
如何定義這些標記,即可以選擇國際通用的標記語言,比如HTML,也可以使用象
XML這樣由相關人士自由決定的標記語言,這就是語言的可擴展性。XML是從SGML
中簡化修改出來的。它主要用到的有XML、XSL和XPath等。
76.什麼是Web Service?UDDI?
答:Web Service便是基於網路的、分布式的模塊化組件,它執行特定的任務,遵
守具體的技術規范,這些規范使得Web Service能與其他兼容的組件進行互操作。
UDDI 的目的是為電子商務建立標准;UDDI是一套基於Web的、分布式的、為
Web Service提供的、信息注冊中心的實現標准規范,同時也包含一組使企業能將
自身提供的Web Service注冊,以使別的企業能夠發現的訪問協議的實現標准。
77.什麼是ASP.net中的用戶控制項?
答:用戶控制項一般用在內容多為靜態,或者少許會改變的情況下..用的比較大..類
似ASP中的include..但是功能要強大的多。
78.列舉一下你所了解的XML技術及其應用
答:xml用於配置,用於保存靜態數據類型.接觸XML最多的是web Services..和
config
79.ADO.net中常用的對象有哪些?分別描述一下。
答:Connection 資料庫連接對象
Command 資料庫命令
DataReader 數據讀取器
DataSet 數據集
80.什麼是code-Behind技術。
答:ASPX,RESX和CS三個後綴的文件,這個就是代碼分離.實現了HTML代碼和服務
器代碼分離.方便代碼編寫和整理.
81.什麼是SOAP,有哪些應用。
答:simple object access protocal,簡單對象接受協議.以xml為基本編碼結構
,建立在已有通信協議上(如http,不過據說ms在搞最底層的架構在tcp/ip上的
soap)的一種規范Web Service使用的協議..
82.C#中 property 與 attribute的區別,他們各有什麼用處,這種機制的好處
在哪裡?
答:一個是屬性,用於存取類的欄位,一個是特性,用來標識類,方法等的附加
性質
83.XML 與 HTML 的主要區別
答:1. XML是區分大小寫字母的,HTML不區分。
2. 在HTML中,如果上下文清楚地顯示出段落或者列表鍵在何處結尾,那麼你可以
省略</p>或者</li>之類的結束 標記。在XML中,絕對不能省略掉結束標記。
3. 在XML中,擁有單個標記而沒有匹配的結束標記的元素必須用一個 / 字元作為
結尾。這樣分析器就知道不用 查找結束標記了。
4. 在XML中,屬性值必須分裝在引號中。在HTML中,引號是可用可不用的。
5. 在HTML中,可以擁有不帶值的屬性名。在XML中,所有的屬性都必須帶有相應
的值。
84.c#中的三元運算符是?
答:?:。
85.當整數a賦值給一個object對象時,整數a將會被?
答:裝箱。
86.類成員有_____種可訪問形式?
答:this.;new Class().Method;
87.public static const int A=1;這段代碼有錯誤么?是什麼?
答:const不能用static修飾。
88.float f=-123.567F; int i=(int)f;i的值現在是_____?
答:-123。
89.委託聲明的關鍵字是______?
答:delegate.
90.用sealed修飾的類有什麼特點?
答:密封,不能繼承。
91.在Asp.net中所有的自定義用戶控制項都必須繼承自________?
答:Control。
92.在.Net中所有可序列化的類都被標記為_____?
答:[serializable]
93.在.Net託管代碼中我們不用擔心內存漏洞,這是因為有了______?
答:GC。
95.當類T只聲明了私有實例構造函數時,則在T的程序文本外部,___可以___(可
以 or 不可以)從T派生出新的類,不可以____(可以 or 不可以)直接創建T的
任何實例。
答:不可以,不可以。
96.下面這段代碼有錯誤么?
switch (i){
case(): 答://case()條件不能為空
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
fault; 答://wrong,格式不正確
CaseTwo();
break;
}
97.在.Net中,類System.Web.UI.Page 可以被繼承么?
答:可以。
98..net的錯誤處理機制是什麼?
答:.net錯誤處理機制採用try->catch->finally結構,發生錯誤時,層層上拋
,直到找到匹配的Catch為止。
99.利用operator聲明且僅聲明了==,有什麼錯誤么?
答:要同時修改Equale和GetHash() ? 重載了"==" 就必須重載 "!="
101.在.net(C# or vb.net)中如何取消一個窗體的關閉。
答:private void Form1_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
e.Cancel=true;
}
102.在.net(C# or vb.net)中,Appplication.Exit 還是 Form.Close有什麼
不同?
答:一個是退出整個應用程序,一個是關閉其中一個form。
104.某一密碼僅使用K、L、M、N、O共5個字母,密碼中的單詞從左向右排列,密
碼單詞必須遵循如下規則:
(1) 密碼單詞的最小長度是兩個字母,可以相同,也可以不同
(2) K不可能是單詞的第一個字母
(3) 如果L出現,則出現次數不止一次
(4) M不能使最後一個也不能是倒數第二個字母
(5) K出現,則N就一定出現
(6) O如果是最後一個字母,則L一定出現
問題一:下列哪一個字母可以放在LO中的O後面,形成一個3個字母的密碼單詞?
A) K B)L C) M D) N
答案:B
問題二:如果能得到的字母是K、L、M,那麼能夠形成的兩個字母長的密碼單詞的
總數是多少?
A)1個 B)3個 C)6個 D)9個
答案:A
問題三:下列哪一個是單詞密碼?
A) KLLN B) LOML C) MLLO D)NMKO
答案:C
8. 62-63=1 等式不成立,請移動一個數字(不可以移動減號和等於號),使得等
式成立,如何移動?
答案:62移動成2的6次方
106. C#中 property 與 attribute的區別,他們各有什麼用處,這種機制的好
處在哪裡?
答:attribute:自定義屬性的基類;property :類中的屬性
107.C#可否對內存進行直接的操作?
答:在.net下,.net引用了垃圾回收(GC)功能,它替代了程序員 不過在C#中,
不能直接實現Finalize方法,而是在析構函數中調用基類的Finalize()方法
108.ADO。NET相對於ADO等主要有什麼改進?
答:1:ado.net不依賴於ole db提供程序,而是使用.net託管提供的程序,2:不使
用com3:不在支持動態游標和伺服器端游 4:,可以斷開connection而保留當前數
據集可用 5:強類型轉換 6:xml支持
109.寫一個HTML頁面,實現以下功能,左鍵點擊頁面時顯示「您好」,右鍵點擊
時顯示「禁止右鍵」。並在2分鍾後自動關閉頁面。
答:<script language=javascript>
setTimeout('window.close();',3000);
function show()
{
if (window.event.button == 1)
{
alert("左");
}
else if (window.event.button == 2)
{
alert("右");
}
}
</script>
110.大概描述一下ASP。NET伺服器控制項的生命周期
答:初始化 載入視圖狀態 處理回發數據 載入 發送回發更改通知 處理回發事件
預呈現 保存狀態 呈現 處置 卸載
111.Anonymous Inner Class (匿名內部類) 是否可以extends(繼承)其它類,
是否可以implements(實現)interface(介面)?
答:不能,可以實現介面
112.Static Nested Class 和 Inner Class的不同,說得越多越好
答:Static Nested Class是被聲明為靜態(static)的內部類,它可以不依賴
於外部類實例被實例化。而通常的內部類需要在外部類實例化後才能實例化。
113.,&和&&的區別。
&是位運算符,表示按位與運算,&&是邏輯運算符,表示邏輯與(and).
114.HashMap和Hashtable的區別。
答:HashMap是Hashtable的輕量級實現(非線程安全的實現),他們都完成了
Map介面,主要區別在於HashMap允許空(null)鍵值(key),由於非線程安全,
效率上可能高於Hashtable.
116.Overloaded的方法是否可以改變返回值的類型?
答:Overloaded的方法是可以改變返回值的類型。
117.error和exception有什麼區別?
答:error 表示恢復不是不可能但很困難的情況下的一種嚴重問題。比如說內存
溢出。不可能指望程序能處理這樣的情況。
exception 表示一種設計或實現問題。也就是說,它表示如果程序運行正常,從
不會發生的情況。
118.<%# %> 和 <% %> 有什麼區別?
答:<%# %>表示綁定的數據源
<% %>是伺服器端代碼塊
119.你覺得ASP.NET 2.0(VS2005)和你以前使用的開發工具(.Net 1.0或其他
)有什麼最大的區別?你在以前的平台上使用的哪些開發思想(pattern /
architecture)可以移植到ASP.NET 2.0上 (或者已經內嵌在ASP.NET 2.0中)
答:1 ASP.NET 2.0 把一些代碼進行了封裝打包,所以相比1.0相同功能減少了很
多代碼.
2 同時支持代碼分離和頁面嵌入伺服器端代碼兩種模式,以前1.0版本,.NET提示幫
助只有在分離的代碼文件,無 法 在頁面嵌入伺服器端代碼獲得幫助提示,
3 代碼和設計界面切換的時候,2.0支持游標定位.這個我比較喜歡
4 在綁定數據,做表的分頁.Update,Delete,等操作都可以可視化操作,方便了初
學者
5 在ASP.NET中增加了40多個新的控制項,減少了工作量
120.重載與覆蓋的區別?
答:1、方法的覆蓋是子類和父類之間的關系,是垂直關系;方法的重載是同一個
類中方法之間的關系,是水平關系
2、覆蓋只能由一個方法,或只能由一對方法產生關系;方法的重載是多個方法之
間的關系。
3、覆蓋要求參數列表相同;重載要求參數列表不同。
4、覆蓋關系中,調用那個方法體,是根據對象的類型(對象對應存儲空間類型)
來決定;重載關系,是根據調 用時的實參表與形參表來選擇方法體的。
121.描述一下C#中索引器的實現過程,是否只能根據數字進行索引?
答:不是。可以用任意類型。
122.在C#中,string str = null 與 string str = " " 請盡量使用文字或圖
象說明其中的區別。
答:null是沒有空間引用的;
" " 是空間為0的字元串;
126.公司要求開發一個繼承System.Windows.Forms.ListView類的組件,要求達
到以下的特殊功能:點擊ListView各列列頭時,能按照點擊列的每行值進行重排
視圖中的所有行 (排序的方式如DataGrid相似)。根據您的知識,請簡要談一下您
的思路:
答:根據點擊的列頭,包該列的ID取出,按照該ID排序後,在給綁定到ListView中
127.什麼是WSE?目前最新的版本是多少?
答:WSE (Web Service Extension) 包來提供最新的WEB服務安全保證,目前最
新版本2.0。
130.abstract class和interface有什麼區別?
答:聲明方法的存在而不去實現它的類被叫做抽象類(abstract class),它用
於要創建一個體現某些基本行為的類,並為該類聲明方法,但不能在該類中實現
該類的情況。不能創建abstract 類的實例。然而可以創建一個變數,其類型是一
個抽象類,並讓它指向具體子類的一個實例。不能有抽象構造函數或抽象靜態方
法。Abstract 類的子類為它們父類中的所有抽象方法提供實現,否則它們也是抽
象類為。取而代之,在子類中實現該方法。知道其行為的其它類可以在類中實現
這些方法。
介面(interface)是抽象類的變體。在介面中,所有方法都是抽象的。多繼承性
可通過實現這樣的介面而獲得。介面中的所有方法都是抽象的,沒有一個有程序
體。介面只可以定義static final成員變數。介面的實現與子類相似,除了該實
現類不能從介面定義中繼承行為。當類實現特殊介面時,它定義(即將程序體給
予)所有這種介面的方法。然後,它可以在實現了該介面的類的任何對象上調用
介面的方法。由於有抽象類,它允許使用介面名作為引用變數的類型。通常的動
態聯編將生效。引用可以轉換到介面類型或從介面類型轉換,instanceof 運算符
可以用來決定某對象的類是否實現了介面。
7. java並發包源碼怎麼讀
1. 各種同步控制工具的使用
1.1 ReentrantLock
ReentrantLock感覺上是synchronized的增強版,synchronized的特點是使用簡單,一切交給JVM去處理,但是功能上是比較薄弱的。在JDK1.5之前,ReentrantLock的性能要好於synchronized,由於對JVM進行了優化,現在的JDK版本中,兩者性能是不相上下的。如果是簡單的實現,不要刻意去使用ReentrantLock。
相比於synchronized,ReentrantLock在功能上更加豐富,它具有可重入、可中斷、可限時、公平鎖等特點。
首先我們通過一個例子來說明ReentrantLock最初步的用法:
package test;
import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i = 0;
@Override public void run() { for (int j = 0; j < 10000000; j++)
{ lock.lock(); try
{
i++;
} finally
{ lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
有兩個線程都對i進行++操作,為了保證線程安全,使用了ReentrantLock,從用法上可以看出,與synchronized相比,ReentrantLock就稍微復雜一點。因為必須在finally中進行解鎖操作,如果不在finally解鎖,有可能代碼出現異常鎖沒被釋放,而synchronized是由JVM來釋放鎖。
那麼ReentrantLock到底有哪些優秀的特點呢?
1.1.1 可重入
單線程可以重復進入,但要重復退出
lock.lock();
lock.lock();try{
i++;
}
finally{
lock.unlock();
lock.unlock();
}
由於ReentrantLock是重入鎖,所以可以反復得到相同的一把鎖,它有一個與鎖相關的獲取計數器,如果擁有鎖的某個線程再次得到鎖,那麼獲取計數器就加1,然後鎖需要被釋放兩次才能獲得真正釋放(重入鎖)。這模仿了synchronized的語義;如果線程進入由線程已經擁有的監控器保護的 synchronized 塊,就允許線程繼續進行,當線程退出第二個(或者後續)synchronized塊的時候,不釋放鎖,只有線程退出它進入的監控器保護的第一個synchronized塊時,才釋放鎖。
public class Child extends Father implements Runnable{ final static Child child = new Child();//為了保證鎖唯一
public static void main(String[] args) { for (int i = 0; i < 50; i++) { new Thread(child).start();
}
}
public synchronized void doSomething() {
System.out.println("1child.doSomething()");
doAnotherThing(); // 調用自己類中其他的synchronized方法
}
private synchronized void doAnotherThing() { super.doSomething(); // 調用父類的synchronized方法
System.out.println("3child.doAnotherThing()");
}
@Override
public void run() {
child.doSomething();
}
}class Father { public synchronized void doSomething() {
System.out.println("2father.doSomething()");
}
}
我們可以看到一個線程進入不同的synchronized方法,是不會釋放之前得到的鎖的。所以輸出還是順序輸出。所以synchronized也是重入鎖
輸出:
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
1child.doSomething()
2father.doSomething()
3child.doAnotherThing()
...
1.1.2.可中斷
與synchronized不同的是,ReentrantLock對中斷是有響應的。中斷相關知識查看[高並發Java 二] 多線程基礎
普通的lock.lock()是不能響應中斷的,lock.lockInterruptibly()能夠響應中斷。
我們模擬出一個死鎖現場,然後用中斷來處理死鎖
package test;import java.lang.management.ManagementFactory;import java.lang.management.ThreadInfo;import java.lang.management.ThreadMXBean;import java.util.concurrent.locks.ReentrantLock;public class Test implements Runnable{ public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock; public Test(int lock)
{ this.lock = lock;
} @Override
public void run()
{ try
{ if (lock == 1)
{
lock1.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock2.lockInterruptibly();
} else
{
lock2.lockInterruptibly(); try
{
Thread.sleep(500);
} catch (Exception e)
{ // TODO: handle exception
}
lock1.lockInterruptibly();
}
} catch (Exception e)
{ // TODO: handle exception
} finally
{ if (lock1.isHeldByCurrentThread())
{
lock1.unlock();
} if (lock2.isHeldByCurrentThread())
{
lock2.unlock();
}
System.out.println(Thread.currentThread().getId() + ":線程退出");
}
} public static void main(String[] args) throws InterruptedException {
Test t1 = new Test(1);
Test t2 = new Test(2);
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
Thread.sleep(1000); //DeadlockChecker.check();
} static class DeadlockChecker
{ private final static ThreadMXBean mbean = ManagementFactory
.getThreadMXBean(); final static Runnable deadlockChecker = new Runnable()
{ @Override
public void run()
{ // TODO Auto-generated method stub
while (true)
{ long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); if (deadlockedThreadIds != null)
{
ThreadInfo[] threadInfos = mbean.getThreadInfo(deadlockedThreadIds); for (Thread t : Thread.getAllStackTraces().keySet())
{ for (int i = 0; i < threadInfos.length; i++)
{ if(t.getId() == threadInfos[i].getThreadId())
{
t.interrupt();
}
}
}
} try
{
Thread.sleep(5000);
} catch (Exception e)
{ // TODO: handle exception
}
}
}
};
public static void check()
{
Thread t = new Thread(deadlockChecker);
t.setDaemon(true);
t.start();
}
}
}
上述代碼有可能會發生死鎖,線程1得到lock1,線程2得到lock2,然後彼此又想獲得對方的鎖。
我們用jstack查看運行上述代碼後的情況
下面舉個例子:
package test;import java.util.concurrent.CyclicBarrier;public class Test implements Runnable{ private String soldier; private final CyclicBarrier cyclic; public Test(String soldier, CyclicBarrier cyclic)
{ this.soldier = soldier; this.cyclic = cyclic;
} @Override
public void run()
{ try
{ //等待所有士兵到齊
cyclic.await();
dowork(); //等待所有士兵完成工作
cyclic.await();
} catch (Exception e)
{ // TODO Auto-generated catch block
e.printStackTrace();
}
} private void dowork()
{ // TODO Auto-generated method stub
try
{
Thread.sleep(3000);
} catch (Exception e)
{ // TODO: handle exception
}
System.out.println(soldier + ": done");
} public static class BarrierRun implements Runnable
{ boolean flag; int n; public BarrierRun(boolean flag, int n)
{ super(); this.flag = flag; this.n = n;
} @Override
public void run()
{ if (flag)
{
System.out.println(n + "個任務完成");
} else
{
System.out.println(n + "個集合完成");
flag = true;
}
}
} public static void main(String[] args)
{ final int n = 10;
Thread[] threads = new Thread[n]; boolean flag = false;
CyclicBarrier barrier = new CyclicBarrier(n, new BarrierRun(flag, n));
System.out.println("集合"); for (int i = 0; i < n; i++)
{
System.out.println(i + "報道");
threads[i] = new Thread(new Test("士兵" + i, barrier));
threads[i].start();
}
}
}
列印結果:
集合
士兵5: done士兵7: done士兵8: done士兵3: done士兵4: done士兵1: done士兵6: done士兵2: done士兵0: done士兵9: done10個任務完成
1.7 LockSupport
提供線程阻塞原語
和suspend類似
LockSupport.park();
LockSupport.unpark(t1);
與suspend相比不容易引起線程凍結
LockSupport的思想呢,和Semaphore有點相似,內部有一個許可,park的時候拿掉這個許可,unpark的時候申請這個許可。所以如果unpark在park之前,是不會發生線程凍結的。
下面的代碼是[高並發Java 二] 多線程基礎中suspend示例代碼,在使用suspend時會發生死鎖。
而使用LockSupport則不會發生死鎖。
另外
park()能夠響應中斷,但不拋出異常。中斷響應的結果是,park()函數的返回,可以從Thread.interrupted()得到中斷標志。
在JDK當中有大量地方使用到了park,當然LockSupport的實現也是使用unsafe.park()來實現的。
public static void park() { unsafe.park(false, 0L);
}
1.8 ReentrantLock 的實現
下面來介紹下ReentrantLock的實現,ReentrantLock的實現主要由3部分組成:
CAS狀態
等待隊列
park()
ReentrantLock的父類中會有一個state變數來表示同步的狀態
通過CAS操作來設置state來獲取鎖,如果設置成了1,則將鎖的持有者給當前線程
如果拿鎖不成功,則會做一個申請
首先,再去申請下試試看tryAcquire,因為此時可能另一個線程已經釋放了鎖。
如果還是沒有申請到鎖,就addWaiter,意思是把自己加到等待隊列中去
其間還會有多次嘗試去申請鎖,如果還是申請不到,就會被掛起
同理,如果在unlock操作中,就是釋放了鎖,然後unpark,這里就不具體講了。
2. 並發容器及典型源碼分析
2.1ConcurrentHashMap
我們知道HashMap不是一個線程安全的容器,最簡單的方式使HashMap變成線程安全就是使用Collections.synchronizedMap,它是對HashMap的一個包裝
同理對於List,Set也提供了相似方法。
但是這種方式只適合於並發量比較小的情況。
我們來看下synchronizedMap的實現
它會將HashMap包裝在裡面,然後將HashMap的每個操作都加上synchronized。
由於每個方法都是獲取同一把鎖(mutex),這就意味著,put和remove等操作是互斥的,大大減少了並發量。
下面來看下ConcurrentHashMap是如何實現的
在ConcurrentHashMap內部有一個Segment段,它將大的HashMap切分成若干個段(小的HashMap),然後讓數據在每一段上Hash,這樣多個線程在不同段上的Hash操作一定是線程安全的,所以只需要同步同一個段上的線程就可以了,這樣實現了鎖的分離,大大增加了並發量。
在使用ConcurrentHashMap.size時會比較麻煩,因為它要統計每個段的數據和,在這個時候,要把每一個段都加上鎖,然後再做數據統計。這個就是把鎖分離後的小小弊端,但是size方法應該是不會被高頻率調用的方法。
在實現上,不使用synchronized和lock.lock而是盡量使用trylock,同時在HashMap的實現上,也做了一點優化。這里就不提了。
2.2BlockingQueue
BlockingQueue不是一個高性能的容器。但是它是一個非常好的共享數據的容器。是典型的生產者和消費者的實現。
8. Java 的 DirectBuffer 是什麼東西
那我們想知道 DirectBuffer 到底是怎麼分配的?這些內存如何被回收?查看了 ByteBuffer.allocateDirect(int capacity) 方法的源代碼,只有一行:?View CodeJAVA return new DirectByteBuffer(capacity); 就是創建一個 DirectByteBuffer 對象。我們再看 DirectByteBuffer 類的代碼可以發現 DirectByteBuffer 的構造方法里調用一個本地(native)方法 allocateMemory 申請了一塊指定大小的內存:?View CodeJAVA try { address = unsafe.allocateMemory(cap); } catch (OutOfMemoryError x) { …… } allocateMemory 是一個本地方法,功能就是做一些參數檢查和位元組對齊,然後使用系統調用 malloc 申請一塊內存。從上面的分析也可以看出,創建一個 DirectByteBuffer 對象代價還是很大的,因為 malloc 系統調用代價就很大,需要 60 ~ 100 條 CPU 指令(一個 new Object() 只需要不足 10 條機器指令)。DirectByteBuffer 是如何回收這些內存的呢?DirectByteBuffer 類有一個內部的靜態類 Deallocator,這個類實現了 Runnable 介面並在 run() 方法內釋放了內存:?View CodeJAVA unsafe.freeMemory(address); 那這個 Deallocator 線程是哪裡調用了呢?這里就用到了 Java 的虛引用(PhantomReference),Java 虛引用允許對象被回收之前做一些清理工作。在 DirectByteBuffer 的構造方法中創建了一個 Cleaner:?View CodeJAVA cleaner = Cleaner.create(this , new Deallocator(address, cap) ); 而Cleaner 類繼承了 PhantomReference 類,並且在自己的 clean() 方法中啟動了清理線程,當 DirectByteBuffer 被 GC 之前 cleaner 對象會被放入一個引用隊列(ReferenceQueue),JVM 會啟動一個低優先順序線程掃描這個隊列,並且執行 Cleaner 的 clean 方法來做清理工作。OK,DirectByteBuffer 的實現大概搞清楚了,那我們是否該在自己的代碼中使用 DirectByteBuffer 呢?我個人認為可以適當的使用,使用直接內存確實避免了 GC 問題和內存拷貝的問題,但是我們不得不考慮兩個問題:1)操作系統可能會把 DirectByteBuffer 的內存交換到磁碟上,這樣勢必會影響性能,為了避免這個問題我們不得不對操作系統做相應的配置;2)DirectByteBuffer 申請內存失敗會直接拋出 OutOfMemoryError,對於這種情況,還是要想辦法處理。