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 平台的工程师快速学习新的语言和平台。整个系列的目录请点击此处浏览

题外话:我帮你整理了包括 AI 写作、绘画、视频(自媒体制作)零门槛 AI 课程 + 国内可直接顺畅使用的软件。想让自己快速用上 AI 工具来降本增效,辅助工作和生活?限时报名

当前页阅读量为: