Menu

C# vs Java: cú pháp, runtime, generic, LINQ, nên học cái nào

C# và Java trông giống nhau và giải quyết những bài toán tương tự. Khác biệt thật sự nằm ở property, kiểu giá trị, generic, LINQ và stream, async, ngoại lệ và hệ sinh thái xung quanh. So sánh song song kèm code.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

C# và Java là họ hàng gần. Cả hai đều là ngôn ngữ kiểu tĩnh, hướng đối tượng, có bộ thu gom rác (garbage collector) với cú pháp kiểu C, cả hai đều biên dịch ra bytecode cho máy ảo, và đều được dùng cho các hệ thống backend lớn. C# ra mắt năm 2002, phần nào là câu trả lời của Microsoft cho Java, và qua hai thập kỷ hai ngôn ngữ đã học hỏi tính năng của nhau (lambda, generic, record, pattern matching). Ai biết một ngôn ngữ thì đọc được ngôn ngữ kia gần như ngay lập tức.

Những khác biệt quan trọng hằng ngày thì nhỏ và cụ thể hơn. Trang này đi qua từng điểm bằng code: đoạn C# bạn có thể chạy, còn Java nằm trong khối tĩnh để so sánh.

Tóm tắt nhanh

C#Java
Tạo bởiMicrosoft (2002)Sun Microsystems (1995), nay là Oracle
RuntimeCLR (.NET), biên dịch IL ra mã máyJVM, biên dịch bytecode ra mã máy
Nền tảngWindows, Linux, macOSWindows, Linux, macOS
PropertyCó sẵn: public int Age { get; set; }Phương thức getter và setter
Kiểu giá trị tự định nghĩastructChưa có (Project Valhalla)
GenericReified: kiểu vẫn tồn tại lúc chạyErased: kiểu bị xóa sau khi biên dịch
Truy vấn collectionLINQStreams API
Bất đồng bộasync / awaitVirtual thread (Java 21), CompletableFuture
Checked exceptionKhôngCó
Nạp chồng toán tửCóKhông
An toàn nullNullable reference type (C# 8)Optional<T>, annotation
Ứng dụng chínhGame Unity, ASP.NET Core, desktop Windows, AzureBackend doanh nghiệp, Android, big data

Cú pháp: phần lớn giống nhau

Lớp, phương thức, if, for, while, switch, try/catch đọc gần như y hệt. Khác biệt dễ thấy là quy ước đặt tên và vài từ khóa: phương thức C# viết PascalCase (ToUpper, WriteLine), phương thức Java viết camelCase (toUpperCase, println); C# viết string và bool, Java viết String và boolean; kế thừa là class Dog : Animal trong C# và class Dog extends Animal trong Java.

// Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Ana");
        names.add("Ben");
        for (String n : names) {
            System.out.println(n.toUpperCase());
        }
    }
}

Đầu ra:

ANA
BEN

C# còn so sánh chuỗi theo giá trị bằng ==, xóa bỏ một lỗi kinh điển của người mới học Java: trong Java, == trên hai đối tượng String so sánh tham chiếu, nên hai chuỗi bằng nhau được tạo lúc chạy (đọc từ đầu vào, nối chuỗi) có thể bị coi là khác nhau.

Property thay cho getter và setter

Lớp Java để lộ trạng thái qua các phương thức getName() và setName(), thường do IDE hoặc Lombok sinh ra. C# có property (thuộc tính): thành viên đọc như field nhưng chạy code.

// Java
public class Product {
    private double price;
    public double getPrice() { return price; }
    public void setPrice(double price) {
        if (price < 0) throw new IllegalArgumentException("negative");
        this.price = price;
    }
}

Đầu ra:

Mug: 9.50

Trình khởi tạo đối tượng new Product { Name = ..., Price = ... } gán property ngay sau khi tạo đối tượng. Record của Java (Java 16) lo phần dữ liệu bất biến; C# cũng có record (C# 9), cùng với property cho mọi trường hợp khác.

Kiểu giá trị: struct

Trong Java, mọi kiểu do người dùng định nghĩa đều là kiểu tham chiếu nằm trên heap; chỉ tám kiểu nguyên thủy (int, double, ...) là giá trị. C# cho bạn tự định nghĩa kiểu giá trị bằng struct. Struct được sao chép khi gán và có thể nằm trực tiếp trong mảng hay đối tượng khác, không cần cấp phát riêng trên heap.

Đầu ra:

a = (1, 2)
b = (99, 2)
1000

Điều này quan trọng trong game và code tính toán số học, nơi hàng triệu đối tượng nhỏ sẽ gây áp lực lên bộ thu gom rác. Xem struct.

Generic: reified và erased

Java xóa đối số kiểu generic lúc biên dịch: lúc chạy, một List<Integer> chỉ là List, và List<int> không được phép (bạn nhận các đối tượng Integer đã boxing). C# giữ đối số kiểu lúc chạy, nên List<int> lưu số nguyên thô và code có thể hỏi T là gì.

Đầu ra:

3 items of type Int32
1 items of type String
List`1

typeof(T) và new T() hoạt động vì runtime biết T. Trong Java cả hai đều là lỗi biên dịch, và cách xử lý thường thấy là truyền một đối tượng Class<T> đi khắp nơi.

LINQ và stream

Cả hai ngôn ngữ đều có API dạng chuỗi gọi để lọc và biến đổi collection. Của Java là Streams API (Java 8); của C# là LINQ (C# 3), có thêm cú pháp truy vấn và làm việc với cơ sở dữ liệu qua Entity Framework.

// Java
List<String> expensive = products.stream()
    .filter(p -> p.getPrice() > 20)
    .sorted(Comparator.comparing(Product::getPrice))
    .map(Product::getName)
    .collect(Collectors.toList());

Đầu ra:

Lamp, Chair
Total: 126

Các thao tác tương ứng một đối một (filter là Where, map là Select, collect(toList()) là ToList()), và bản C# không cần stream() hay collector. Các đối tượng new { Name = ..., Price = ... } là kiểu ẩn danh (anonymous type), thứ Java không có tương đương trực tiếp.

Bất đồng bộ và đồng thời

C# giới thiệu async/await năm 2012: phương thức async trả về một Task, và await tạm dừng nó mà không chặn luồng. JavaScript, Python và Rust sau này dùng cùng các từ khóa đó. Java đi hướng khác: chuỗi CompletableFuture, và từ Java 21 là virtual thread giúp code chặn thông thường trở nên rẻ.

// C#
async Task<string> LoadAsync(HttpClient http, string url)
{
    string body = await http.GetStringAsync(url);
    return body.Substring(0, 100);
}
// Java 21 with virtual threads: plain blocking code on a cheap thread
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> client.send(request, BodyHandlers.ofString()));
}

Cả hai cách đều mở rộng được cho rất nhiều request đồng thời. C# thể hiện tính bất đồng bộ ngay trong chữ ký phương thức; Java giấu nó trong runtime.

Ngoại lệ, toán tử và những khác biệt nhỏ hơn

  • Checked exception. Java bắt một phương thức phải khai báo hoặc bắt một số ngoại lệ nhất định (throws IOException). C# hoàn toàn không có checked exception.
  • Nạp chồng toán tử. C# cho các kiểu định nghĩa +, ==, < và các toán tử tương tự, nhờ vậy phép tính với decimal và DateTime đọc rất tự nhiên. Java thì không (BigDecimal.add).
  • Virtual mặc định. Phương thức Java có thể bị ghi đè trừ khi đánh dấu final; phương thức C# phải đánh dấu virtual mới ghi đè được, và phương thức ghi đè phải ghi override.
  • Extension method. C# có thể thêm phương thức vào kiểu có sẵn ("text".IsPalindrome()); Java thì không.
  • Kiểu giá trị nullable. int? trong C# là một int có thể là null; Java dùng kiểu boxing Integer.
  • Kiểu không dấu. C# có uint, ulong, ushort và byte không dấu; byte của Java có dấu, và ngoài char 16 bit, Java không có kiểu số nguyên không dấu nào.

Hệ sinh thái, việc làm và nên học ngôn ngữ nào

Hệ sinh thái của Java lớn hơn trong thế giới máy chủ mã nguồn mở: Spring, Kafka, Spark, Hadoop, Elasticsearch và phần lớn dự án Apache đều là Java. Ứng dụng Android được viết bằng Kotlin hoặc Java. C# có bộ công cụ liền mạch hơn: một nhà cung cấp duy trì ngôn ngữ, runtime, web framework (ASP.NET Core), ORM (Entity Framework) và IDE chính (Visual Studio), còn Unity đặt C# vào trung tâm của việc làm game.

Cả hai ngôn ngữ đều có thị trường việc làm lớn, và cán cân giữa chúng tùy vào khu vực và ngành. C# mạnh ở các công ty xây dựng trên Microsoft và Azure, ở phần mềm tài chính và y tế, và ở studio game.

Chọn C# nếu bạn muốn làm game bằng Unity, làm ứng dụng desktop Windows hoặc làm việc ở công ty .NET. Chọn Java nếu bạn nhắm tới Android, big data hoặc công ty có backend chạy trên JVM. Học ngôn ngữ nào cũng dạy bạn những khái niệm cần cho ngôn ngữ kia: lớp, interface, generic, collection, ngoại lệ và runtime có bộ thu gom rác.

Câu hỏi thường gặp

C# có giống Java không?

Có, rất giống. Cả hai đều là ngôn ngữ kiểu tĩnh, hướng đối tượng, có bộ thu gom rác, cú pháp kiểu C và biên dịch ra bytecode cho máy ảo. Lập trình viên Java đọc được C# ngay ngày đầu. Khác biệt nằm ở chi tiết: C# có property, kiểu giá trị do người dùng định nghĩa, generic reified, LINQ và async/await, còn Java có checked exception và hệ sinh thái máy chủ mã nguồn mở lớn hơn.

Nên học C# hay Java trước?

Chọn theo thứ bạn muốn làm. C# cho game Unity, ứng dụng desktop Windows và các công ty dùng nền tảng Microsoft và Azure. Java cho Android (cùng Kotlin), backend doanh nghiệp lớn và các công cụ big data như Kafka và Spark. Hai ngôn ngữ đủ gần nhau để việc chuyển đổi sau này chỉ mất vài tuần, không phải vài tháng.

C# có nhanh hơn Java không?

Hai ngôn ngữ cùng một đẳng cấp hiệu năng: cả hai đều biên dịch JIT ra mã máy và dùng bộ thu gom rác theo thế hệ. C# cho bạn nhiều quyền kiểm soát cấp thấp hơn qua struct, Span<T> và generic trên kiểu giá trị mà không cần boxing, có ích với code cấp phát nhiều. Với ứng dụng web thông thường, framework và cơ sở dữ liệu quan trọng hơn nhiều so với ngôn ngữ.

C# có checked exception như Java không?

Không. Trong C#, không có kiểu ngoại lệ nào bắt buộc phải khai báo hay bắt; chữ ký phương thức không liệt kê những gì nó ném ra. Những người thiết kế C# cho rằng checked exception dẫn đến các khối catch rỗng và chữ ký dễ vỡ. Thay vào đó, bạn ghi chú ngoại lệ bằng chú thích XML /// <exception>.

C# hay Java có nhiều việc làm hơn?

Cả hai đều có thị trường việc làm lớn, và ngôn ngữ nào có nhiều tin tuyển dụng hơn tùy vào quốc gia và ngành, nên hãy xem tin tuyển dụng ở nơi bạn muốn làm việc. C# mạnh ở các doanh nghiệp dùng Microsoft, tài chính, y tế và studio game; Java mạnh ở ngân hàng lớn, viễn thông, backend thương mại điện tử và Android.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu