相信部分小伙伴都听说过C#和Java,虽然大多数人学习的技术栈都是Java的,当然也包括作者本人,现在因为工作需要学习开始学习C#,发现二者有很多相似的地方,完全可以快速上手,既然如此那我就写一篇文章来带大家快速入门C#


一、整体观感:同一个世界,不同的口音

首先消除陌生感。你会在 C# 里看到这些熟悉的面孔:

  • 强类型、面向对象、类、接口、继承、多态 —— Java 有的它全有。

  • 单继承 + 多接口实现 —— 一模一样。

  • namespace 取代 package —— 用来组织代码,用法几乎相同。

  • using 取代 import —— 导入命名空间。

  • 垃圾回收 —— 内存管理不用你操心,同样有堆、栈、托管资源。

语法层面,{} 代码块、; 分号结尾、if/elseforwhileswitch 这些控制流都完全一致。你可以把 C# 源代码直接当成看着稍有不同的 Java 来读,基本不会有理解障碍。

好,接下来我们直击关键:和 Java 不一样但你可以瞬间理解的那些地方


二、类型系统:多了几颗糖,但本质一样

基本类型与包装类型

在 Java 里,int 是基本类型,Integer 是包装类。C# 统一了这一切——所有类型都继承自 System.Object,包括 intbool 等。

csharp

int age = 18;
string text = age.ToString(); // 基本类型也能直接调用方法,无需装箱
C# Java 备注
int int 直接用,32 位整数
long long 64 位
float float 32 位浮点
double double 64 位浮点
bool boolean 注意名字变了
char char Uni code 16 位
string String 小写开头,但都是引用类型
decimal BigDecimal(近似) 128 位高精度小数,财务专用

自动属性(Auto-Properties)

Java 你经常要手写 getXxx / setXxx,或者用 Lombok。C# 直接内建了这个能力:

csharp

public class Person
{
    public string Name { get; set; }   // 自动实现 getter / setter
    public int Age { get; private set; } // 私有 set,只读属性
}

这就是属性(Property),使用时却像字段一样自然:

csharp

var p = new Person();
p.Name = "Alice";
Console.WriteLine(p.Name);

不需要写 getName 方法了,直接在类内部使用。如果你需要在 get/set 里加逻辑,也能展开成完整属性——和 Java 写 getter/setter 类似,但用一个结构搞定。

var 关键字 —— 类似 Java 10+ 的 var

C# 从一开始就有 var,用于隐式类型推断:

csharp

var list = new List<string>();  // 编译器推导出 list 是 List<string>

字符串处理

C# 的 string 和 Java 的 String 几乎一样,不可变。但 C# 多了个逐字字符串(Verbatim string),用 @ 前缀,不用疯狂转义:

csharp

string path = @"C:\Users\name\documents"; // 反斜杠不用转义
string json = @"
{
    ""name"": ""Alice""
}";
// 另外 C# 11 还有原始字符串字面量 """ ... """,更方便

三、面向对象:一个体系,两个习惯

类与继承

定义类、构造方法(C# 叫“构造函数”)、继承的写法略有不同:

csharp

// Java: public class Dog extends Animal { ... }
public class Dog : Animal
{
    // 构造函数
    public Dog(string name) : base(name) // 调用父类构造函数
    {
        // 初始化
    }
}
  • 继承用 : 而不是 extends

  • 调用父类构造用 : base(...)

  • 基类关键字:Java 里是 super,C# 里是 base

访问修饰符——多了几个选项

Java 的 default(包内可见)在 C# 里对应 internal,表示同一程序集内可访问。对比:

C# Java 说明
public public 完全公开
private private 仅类内部
protected protected 自身及子类
internal (无,default 近似) 同一程序集(Assembly)内可见
protected internal 程序集内  子类可访问
private protected 程序集内  子类可访问(C# 7.2+)

虚方法、重写与隐藏

Java 中方法默认是虚的(可被重写),而 C# 中方法默认是非虚的。这可能是第一个真正的思维转换:

  • 若想允许子类重写,父类方法必须标记 virtual

  • 子类重写时,必须用 override 关键字。

  • 如果子类想定义一个同名但无关的方法,用 new 关键字来隐藏。

csharp

public class Parent
{
    public virtual void Speak() => Console.WriteLine("Parent");
}

public class Child : Parent
{
    public override void Speak() => Console.WriteLine("Child"); // 重写
}

最佳实践:在 Java 里你习惯依赖 @Override 注解来防止失误,在 C# 里强制用 override 关键字。忘记写 override 会变成隐藏,编译器会给出警告。

抽象类与接口

abstract class 完全一致。接口用 interface 定义,实现时也使用 : 跟在类后面,多个接口用逗号分隔:

csharp

public interface IMovable
{
    void Move();
}

public class Car : Vehicle, IMovable, IDisposable
{
    public void Move() { ... }
    public void Dispose() { ... }
}

C# 接口可以包含默认实现(C# 8.0+),和 Java 的接口默认方法类似。

Object 类的方法

Java 有 toString()equals()hashCode()。C# 对应:

  • ToString() —— 一样

  • Equals(object obj) —— 一样

  • GetHashCode() —— 一样

  • 另外还有 GetType() 获取运行时类型

注意命名大写开头,所有方法都是 PascalCase。


四、集合与泛型:换了名字的老朋友

你几乎能在 C# 里找到所有 Java 集合的影子,只是名字改了:

C# 常用集合 Java 对应 类型
List<T> ArrayList<T> 或 List<T> 动态数组
Dictionary<K,V> HashMap<K,V> 哈希表
HashSet<T> HashSet<T> 集合
Queue<T> Queue<T> / LinkedList<T> 队列
Stack<T> Stack<T>
SortedList<K,V> TreeMap<K,V> 排序字典
SortedSet<T> TreeSet<T> 排序集合

遍历同样流畅,C# 有 foreach,和 Java 的增强 for 一样:

csharp

foreach (var item in list)
{
    Console.WriteLine(item);
}

LINQ 是杀手锏——对应 Java 的 Stream API,但要强大且简洁得多:

csharp

var names = people
    .Where(p => p.Age > 18)
    .OrderBy(p => p.Name)
    .Select(p => p.Name)
    .ToList();

=> 是 Lambda 表达式,写法比 Java 的 -> 更常用。LINQ 还有查询表达式语法(类似 SQL),让代码更加可读。


五、异常处理:几乎照搬

try-catch-finally 结构一模一样,连语义都相同。差异只是文件小写变为大写的首字母:

csharp

try
{
    int.Parse("not a number");
}
catch (FormatException ex) // Java: NumberFormatException
{
    Console.WriteLine(ex.Message);
}
finally
{
    // 清理
}

C# 还有 using 语句用于自动释放资源(类似 Java 的 try-with-resources):

csharp

using (var reader = new StreamReader("file.txt"))
{
    string content = reader.ReadToEnd();
} // reader.Dispose() 自动调用

实现 IDisposable 接口的类都可以这样用。


六、I/O 与文件操作:换个写法,思路不变

Java 里你习惯用 FileInputStream / BufferedReader 等。C# 的 System.IO 命名空间下有极其相似的类:

  • File.ReadAllText("path") ↔ Files.readString(Path.of("path")) (Java 11+)

  • File.WriteAllText("path", content) 一步写入

  • StreamReader / StreamWriter ↔ BufferedReader / BufferedWriter

可以说 C# 的文件操作 API 更加简洁,静态方法 File.xxx 开箱即用。


七、并发与异步:从线程到 Task

在 Java 里你用 ThreadRunnableExecutorService,后来有了 CompletableFuture。C# 相应有:

  • System.Threading.Thread —— 和 java.lang.Thread 一样

  • ThreadPool —— 线程池

  • Task / Task<T> —— 强烈推荐使用的异步抽象,相当于 CompletableFuture 的加强版

但 C# 的真正杀招是 async / await,它让你用同步思维写异步代码:

csharp

public async Task<string> FetchDataAsync()
{
    var client = new HttpClient();
    var result = await client.GetStringAsync("https://api.example.com");
    return result;
}

Java 目前也有 CompletableFuture 配合 thenApply 等,但 async/await 的易读性无与伦比,这是你转入 C# 后一定要第一时间掌握的特性。


八、委托与事件:Java 可以用接口模拟,C# 内建

Java 里常用回调接口(如 RunnableActionListener)或函数式接口(ConsumerFunction 等)来实现行为传递。C# 直接用委托(delegate)——类型安全的函数指针:

csharp

public delegate void MyDelegate(string message);

// 可以像使用接口回调一样
MyDelegate del = Console.WriteLine;
del("Hello");

更常用的是内置泛型委托 ActionFuncPredicate,这和 Java 的函数式接口完全对应:

C# Java 类似
Action Runnable (无参无返) / Consumer (有参无返)
Action<T> Consumer<T>
Func<TResult> Supplier<T>
Func<T, TResult> Function<T, R>
Predicate<T> Predicate<T>

事件(event)是基于委托的发布-订阅机制,替代了 Java 里手写的观察者模式。你定义一个事件,外部可以订阅,非常简洁安全:

csharp

public class Button
{
    public event EventHandler Clicked;
    protected virtual void OnClicked() => Clicked?.Invoke(this, EventArgs.Empty);
}
// 使用
button.Clicked += (sender, args) => Console.WriteLine("Clicked!");

九、命名规范与习惯:PascalCase 的主场

Java 中方法、变量常使用 camelCase,类名 PascalCase。C# 里一切公开成员都倾向于 PascalCase:

  • 方法名:GetUserCalculate (PascalCase)

  • 属性名:NameAge

  • 接口名:必须 I 开头,如 IDisposable

  • 私有字段:常用 _camelCase 前缀,如 _firstName

  • 局部变量:camelCase

这与 Java 的惯例不同,但很好适应,因为 IDE 会自动帮你格式化。

枚举值也是 PascalCase,而不是全大写。


十、你需要立刻记住的“小不同”

  • 没有 checked exception:C# 所有异常都是运行时异常,不需要在方法签名上声明 throws。你信守的“要么捕获要么声明”规则,在这里解放了。

  • 没有匿名内部类,直接有 lambda 和匿名方法:不用再写冗长的 new Runnable() { ... }

  • == 对字符串比较内容:不像 Java 中 == 比较引用,C# 中 string 的 == 已经重载为比较值,等同于 equals

  • 枚举是强类型的命名整数,但更强大,可以定义位标志等。

  • 结构体(struct):是值类型,不像 Java 全是类。用于小型数据的轻量对象,无需堆分配(在高性能场景下很关键)。


总结性思维:把你的 Java 直觉 “翻译” 过来

如果你在 C# 中要实现某个 Java 中常见的功能,只要问自己:

  1. 这个类在 C# 里通常叫什么?(List、Dictionary、File……)

  2. 名字的首字母需要大写吗?(PascalCase)

  3. 是不是有内建关键字取代了之前的模式?(属性取代 getter/setter,事件取代观察者)

  4. 有没有更现代的表达方式?(LINQ 代替 for 循环拼接集合,async/await 代替线程管理)

C# 在很多地方就是“进化后的 Java”,它从 Java 乃至整个 C/C++ 生态中吸收了大量优点,并在这个基础上加上了语法糖和现代特性。你已有的面向对象思维、设计模式、架构原则,通通可以移植,只是换一套惯用语。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐