C# 與 Java 語言機制與語法的異同

本文是 從 C# 到 Java 系列的文章。此係列文章是為了方便從 C# 轉到 Java 平臺的工程師快速學習新的語言和平臺。整個系列的目錄請點擊此處瀏覽

這篇文章從基礎的語言機制、語法層面來分析 C# 和 Java 的不同。部分與虛擬機(CLR | JVM)有關的不同會放在相關的另一篇裡,詳見總目錄。

命名規範

Java 中的方法和變量,無論是否為public,都要以小寫字母開頭,其餘與 C# 大體相當。

那些簡單的同義詞或近義詞

下面的詞彙,在 JavaC# 中是 同義近義 的:

  • C# 中獲取一個對象的類型使用 typeof 關鍵詞,Java 中使用 .class,如 String.class;C# 也可以使用對象實例的 GetType 方法,Java 中對應的是 getClass 方法。參考鏈接
  • 代表一個對象的類型的類型,C# 為 Type 類型,Java 則為 Class 類型。參考鏈接
  • C# 中的 const (編譯時常量)和 readonly(運行時常量)在 Java 中都是 final,同時還代表了一個類不能被繼承(等同於 C#sealed)。
  • Java 中的 native 關鍵字(聲明使用 native 實現的代碼)類似於 C# 中的 extern(當聲明非託管的外部方法,如動態鏈接庫時使用)。
  • stringString:在 Java 只有後者,在 C# 中前後兩者是同義詞。
  • Java 中的 synchronized 等同於 C# 語句塊中的 lock,以及 方法體上的特性MethodImplOptions.Synchronized
  • Java 中的 transient (短暫)等同於 C# 的 NonSerializableAttribute,代表在序列化中跳過。
  • Java 的 ... 操作符等同於 C# 的 params。例如:String... args,則 args 為一個所有參數組成的數組。

值類型與裝箱

Java 的值類型(Value Types)僅限於原生類型,如 intboolen 等。Java 不允許自定義值類型,即 Java 不存在 C# 中的 struct

值類型 的作用可以從以下幾個方面來考慮:

  • 性能:C# 中的 struct 類型有助於減少 GC、減少內存使用、提高性能,但 Java 強大的虛擬機 JVM 通過優化部分緩解了這個問題。
  • 語義:原生類型無法為 null,且通過傳值方式賦值。

但是,由於 C# 中的值類型通過傳值類型賦值造成了意外錯誤的可能(例如用類似類的方式在杯傳遞後的值類型中修改成員,試圖影響到原有的值),使用值類型要警惕。

值類型在 Java 中不繼承於 Object,但 C# 中卻繼承,這是通過自動裝箱實現的。Java 的值類型可以通過裝箱變為類實例,比如:

  • int => Integer
  • float => Float
  • byte => Byte

Java 具體的數據類型方面的內容,可以參考本系列文章中的另一篇:數據結構(文章末尾有目錄導航)。

泛型

Java 支持泛型,但其支持的方式和 .NET 差別比較大。Java 的泛型使用 類型擦除 實現。所以,你雖然可以設置某個方法或類有一個類型參數 T,但是你在其實現內部卻無法獲知與該類型的任何信息,解決方法就是使用一個補償的 class 類型(類似於 C# 中的 Type 類型)參數手工傳遞數據類型。

有關 Java 泛型的類型擦除,請參考鏈接

委託,事件

C# 中的委託(delegate)用於指向固定某個方法原型(一系列參數、某種返回值)的指針(姑且這麼叫吧)。Java 中沒有 delegate 的概念。

Java 中一般通過接口來實現類似委託的效果。

例如,C# 中假如有下面的定義:

 // 定義委託
public delegate void onClickListener(View v);

// 定義委託的實現
public void listener(View v) {
  // foo
}

// 使用委託
onClickListener = listener;
onClickListener(this); 

在 Java 中可以這樣實現。先定義一個接口:

public interface OnClickListener {
    void onClick(View v);
}

然後,使用這個接口來存儲一個匿名類,其中帶有具體的實現,例如:

OnClickListener listener = new OnClickListener() {
    @Override
        void onClick(View v) {
            // 在此書寫代碼
        }
};

// 調用接口
listener.onClick(someView);

除了使用匿名類,也可以讓某個命名類繼承此接口:

public class TestClass implements OnClickListener {
        void OnClick(View v) {
            // 在此書寫代碼
        }
        
        void init() {
            OnClickListener listener = this;
                
                // 調用接口
                listener.onClick(someView);
        }
}

C# 支持事件(event),其內部是通過委託來實現的。Java 中沒有事件的概念。

一般來說,用於事件或委託的情況,Java 會使用接口來實現。

例如,C# 中的事件:

public event EventHandler SomeEvent; // 事件的發出者

// 事件監聽
someInstance.SomeEvent += (sender, args) => { /* xxx */ };   

大約等價於 Java 中的方法:

// 先定義接口
public interface SomeEventListener {
    void onEvent(int arg);
}

// 搞一個東西用來存儲接口的具體實現對象
private ArrayList<SomeEventListener> someEventListeners = new ArrayList<>();

// 在要產生事件的類中,添加一對方法用於監聽或取消監聽
public void addSomeEventListener(SomeEventListener listener) {
    someEventListeners.add(listener);
}

public void removeSomeEventListener(SomeEventListener listener) {
    someEventListeners.remove(listener);
}

// 觸發事件
for (int i = 0; i < someEventListeners,size(); ++i) {
    someEventListeners.get(i).onEvent(5);
}

// 事件監聽者,使用 Java8 中才支持的 Lambd 表達式
someInstance.addSomeEventListener(() -> {  /* xxx */ });

// 事件監聽者,使用 Java7 以前版本的 匿名類
someInstance.addSomeEventListener(new SomeEventListener() {
    @Override
        void onEvent(int arg) {
            // 代碼邏輯
        }
});

也可以參考這篇文章

本文是 從 C# 到 Java 系列的文章。此係列文章是為了方便從 C# 轉到 Java 平臺的工程師快速學習新的語言和平臺。整個系列的目錄請點擊此處瀏覽

当前页阅读量为: