C SharpとJavaの比較
C#のメソッドはデフォルトで非仮想(非virtual)であり、仮想メソッドにする必要がある場合は明示的にvirtualと宣言しなければならない。Javaのメソッドは常に仮想 (C#でvirtual指定された状態) であり、非仮想にすることはできないが、final修飾子を指定することでオーバーライドを禁止することはできる。
Javaでは、メソッドを非仮想的にする方法はない。これは、派生クラスが同名の無関係なメソッドを再定義することが不可能であることを意味する。これは基底クラスが別のプログラマによって書かれており、バージョン更新の際に、派生クラスに既に存在していたものと同じシグネチャのメソッドが追加されてしまった場合に問題になる。Javaでは、この場合どちらのプログラマの意図とも異なり、派生クラスのメソッドは暗黙的に基底クラスのオーバーライドになってしまう(基底クラスでfinal宣言されていた場合はコンパイルエラーになってしまう)。
Javaではジェネリクスは型消去 (type erasure) によって実装されている。これによってジェネリック型についての情報は実行時には失われ、リフレクションを通してのみ取得できるようになる。.NET 2.0では、ジェネリック型についての情報は完全に保存される。Javaはプリミティブ型に対するジェネリック型は定義できないが、C#では参照型・値型(プリミティブ型を含む)いずれに対してもジェネリック型を定義できる。Javaはその代わりにボックス化した型を使用することができる(List<int>の代わりにList<Integer>など)が、全ての値をヒープに確保し直す必要があるため、パフォーマンスコストが高い。JavaとC#はいずれも、参照型に特殊化されたジェネリック型は、型によらず共通のコードが実行される。しかし、C#において値型に特殊化された場合、CLRは型に最適化されたコードを動的に生成する。.NETにおいては、ジェネリック型に対する型安全性はコンパイル時にチェックされ、CLRにロードされる時に強制される。Javaにおいてはコンパイル時に部分的にチェックされるのみであり、Java VMは実行時にジェネリック型に関する情報を持たないため、キャスト操作を行う必要がある。