C# i Java to bliscy krewni. Oba to statycznie typowane, obiektowe języki z odśmiecaczem pamięci i składnią w stylu C, oba kompilują się do kodu bajtowego dla maszyny wirtualnej i oba są używane w dużych systemach backendowych. C# powstał w 2002 roku częściowo jako odpowiedź Microsoftu na Javę, a przez dwie dekady oba języki zapożyczały od siebie funkcje (lambdy, generyki, rekordy, dopasowywanie wzorców). Programista, który zna jeden z nich, niemal od razu przeczyta drugi.
Różnice, które mają znaczenie na co dzień, są mniejsze i bardziej konkretne. Ta strona omawia je z kodem: fragmenty w C# możesz uruchomić, a Java jest w statycznych blokach dla porównania.
W skrócie
| C# | Java | |
|---|---|---|
| Twórca | Microsoft (2002) | Sun Microsystems (1995), obecnie Oracle |
| Środowisko uruchomieniowe | CLR (.NET), kompiluje IL do kodu natywnego | JVM, kompiluje kod bajtowy do kodu natywnego |
| Platformy | Windows, Linux, macOS | Windows, Linux, macOS |
| Właściwości | Wbudowane: public int Age { get; set; } | Metody getterów i setterów |
| Typy wartościowe użytkownika | struct | Jeszcze nie (Project Valhalla) |
| Generyki | Reifikowane: typy istnieją w czasie działania | Wymazywane: typy usuwane po kompilacji |
| Zapytania na kolekcjach | LINQ | Streams API |
| Asynchroniczność | async / await | Wątki wirtualne (Java 21), CompletableFuture |
| Wyjątki kontrolowane | Nie | Tak |
| Przeciążanie operatorów | Tak | Nie |
| Bezpieczeństwo null | Referencyjne typy nullable (C# 8) | Optional<T>, adnotacje |
| Główne zastosowania | Gry w Unity, ASP.NET Core, desktop na Windows, Azure | Backendy korporacyjne, Android, big data |
Składnia: w większości taka sama
Klasy, metody, if, for, while, switch, try/catch czyta się niemal identycznie. Widoczne różnice to konwencje nazewnictwa i kilka słów kluczowych: metody w C# są w PascalCase (ToUpper, WriteLine), w Javie w camelCase (toUpperCase, println); C# pisze string i bool, Java String i boolean; dziedziczenie to class Dog : Animal w C# i class Dog extends Animal w Javie.
// 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());
}
}
}
Wynik:
ANA
BEN
C# porównuje też napisy po wartości przez ==, co usuwa jeden z klasycznych błędów początkujących w Javie: tam == na dwóch obiektach String porównuje referencje, więc dwa równe napisy zbudowane w czasie działania (wczytane z wejścia, sklejone) mogą okazać się nierówne.
Właściwości zamiast getterów i setterów
Klasy w Javie udostępniają stan przez metody getName() i setName(), zwykle generowane przez IDE lub Lombok. C# ma właściwości: składowe, które czyta się jak pola, ale które wykonują kod.
// 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;
}
}
Wynik:
Mug: 9.50
Inicjalizator obiektu new Product { Name = ..., Price = ... } ustawia właściwości zaraz po utworzeniu obiektu. Rekordy w Javie (Java 16) pokrywają przypadek niemodyfikowalnych danych; C# też ma rekordy (C# 9), a do tego właściwości do całej reszty.
Typy wartościowe: struktury
W Javie każdy typ zdefiniowany przez użytkownika jest typem referencyjnym przechowywanym na stercie; wartościami jest tylko osiem typów prostych (int, double, ...). C# pozwala definiować własne typy wartościowe przez struct. Struktura jest kopiowana przy przypisaniu i może być przechowywana bezpośrednio w tablicach i innych obiektach, bez osobnej alokacji na stercie.
Wynik:
a = (1, 2)
b = (99, 2)
1000
Ma to znaczenie w grach i kodzie numerycznym, gdzie miliony małych obiektów obciążałyby odśmiecacz pamięci. Zobacz struktury.
Generyki: reifikowane a wymazywane
Java wymazuje argumenty typów generycznych w czasie kompilacji: w czasie działania List<Integer> to po prostu List, a List<int> jest niedozwolone (dostajesz opakowane obiekty Integer). C# zachowuje argumenty typów w czasie działania, więc List<int> przechowuje surowe liczby int, a kod może zapytać, czym jest T.
Wynik:
3 items of type Int32
1 items of type String
List`1
typeof(T) i new T() działają, bo środowisko uruchomieniowe zna T. W Javie oba to błędy kompilacji, a typowe obejście to przekazywanie obiektu Class<T>.
LINQ a strumienie
Oba języki mają płynne API do filtrowania i przekształcania kolekcji. W Javie to Streams API (Java 8); w C# to LINQ (C# 3), który ma też składnię zapytań i działa na bazach danych przez Entity Framework.
// Java
List<String> expensive = products.stream()
.filter(p -> p.getPrice() > 20)
.sorted(Comparator.comparing(Product::getPrice))
.map(Product::getName)
.collect(Collectors.toList());
Wynik:
Lamp, Chair
Total: 126
Operacje odpowiadają sobie jeden do jednego (filter to Where, map to Select, collect(toList()) to ToList()), a wersje w C# nie potrzebują stream() ani kolektora. Obiekty new { Name = ..., Price = ... } to typy anonimowe, które nie mają bezpośredniego odpowiednika w Javie.
Asynchroniczność i współbieżność
C# wprowadził async/await w 2012 roku: metoda async zwraca Task, a await wstrzymuje ją bez blokowania wątku. JavaScript, Python i Rust przejęły później te same słowa kluczowe. Java poszła inną drogą: łańcuchy CompletableFuture, a od Javy 21 wątki wirtualne, dzięki którym zwykły blokujący kod jest tani.
// 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()));
}
Oba podejścia skalują się do wielu równoczesnych żądań. C# pokazuje asynchroniczność w sygnaturach metod; Java ukrywa ją w środowisku uruchomieniowym.
Wyjątki, operatory i mniejsze różnice
- Wyjątki kontrolowane. Java wymusza, żeby metoda deklarowała lub łapała pewne wyjątki (
throws IOException). C# w ogóle nie ma wyjątków kontrolowanych. - Przeciążanie operatorów. C# pozwala typom definiować
+,==,<i podobne, dlatego arytmetyka nadecimaliDateTimeczyta się naturalnie. Java na to nie pozwala (BigDecimal.add). - Domyślna wirtualność. Metody w Javie można nadpisywać, chyba że są oznaczone
final; metody w C# muszą być oznaczonevirtual, żeby dało się je nadpisać, a nadpisanie musi miećoverride. - Metody rozszerzające. C# może dodawać metody do istniejących typów (
"text".IsPalindrome()); Java nie. - Typy wartościowe nullable.
int?w C# to int, który może być null; Java używa opakowanegoInteger. - Typy bez znaku. C# ma
uint,ulong,ushortibytebez znaku;bytew Javie jest ze znakiem, a poza 16-bitowymcharJava nie ma typów całkowitych bez znaku.
Ekosystem, praca i czego się uczyć
Ekosystem Javy jest większy w świecie serwerów open source: Spring, Kafka, Spark, Hadoop, Elasticsearch i większość projektów Apache to Java. Aplikacje na Androida pisze się w Kotlinie lub Javie. C# ma bardziej zintegrowany stos: jeden dostawca utrzymuje język, środowisko uruchomieniowe, framework webowy (ASP.NET Core), ORM (Entity Framework) i główne IDE (Visual Studio), a Unity stawia go w centrum tworzenia gier.
Oba języki mają duże rynki pracy, a proporcje między nimi zależą od regionu i branży. C# jest mocny w firmach opartych na Microsoft i Azure, w oprogramowaniu dla finansów i ochrony zdrowia oraz w studiach gier.
Wybierz C#, jeśli chcesz tworzyć gry w Unity, budować aplikacje desktopowe na Windows albo pracować w firmie .NET. Wybierz Javę, jeśli celujesz w Androida, big data albo firmę, której backend działa na JVM. Każdy z nich uczy pojęć potrzebnych w drugim: klas, interfejsów, generyków, kolekcji, wyjątków i środowiska uruchomieniowego z odśmiecaczem pamięci.
Najczęściej zadawane pytania
Czy C# jest podobny do Javy?
Tak, bardzo. Oba to statycznie typowane, obiektowe języki z odśmiecaczem pamięci i składnią w stylu C, kompilowane do kodu bajtowego dla maszyny wirtualnej. Programista Javy przeczyta kod w C# od pierwszego dnia. Różnice tkwią w szczegółach: C# ma właściwości, typy wartościowe definiowane przez użytkownika, generyki reifikowane, LINQ i async/await, a Java ma wyjątki kontrolowane (checked) i większy ekosystem open source po stronie serwera.
Czego uczyć się najpierw, C# czy Javy?
Wybierz według tego, co chcesz budować. C# do gier w Unity, aplikacji desktopowych na Windows i w firmach opartych na Microsoft i Azure. Javę do Androida (obok Kotlina), dużych backendów korporacyjnych i narzędzi big data, takich jak Kafka i Spark. Języki są na tyle bliskie, że późniejsza zmiana zajmuje tygodnie, a nie miesiące.
Czy C# jest szybszy niż Java?
Są w tej samej klasie wydajności: oba kompilują JIT do kodu natywnego i używają pokoleniowych odśmiecaczy pamięci. C# daje więcej kontroli niskopoziomowej przez struktury, Span<T> i generyki na typach wartościowych bez opakowywania (boxing), co pomaga w kodzie z dużą liczbą alokacji. W typowych zastosowaniach webowych framework i baza danych mają znacznie większe znaczenie niż język.
Czy C# ma wyjątki kontrolowane jak Java?
Nie. W C# żadnego typu wyjątku nie trzeba deklarować ani łapać; sygnatura metody nie wymienia tego, co metoda rzuca. Projektanci uznali, że wyjątki kontrolowane prowadzą do pustych bloków catch i kruchych sygnatur. Zamiast tego wyjątki dokumentuje się komentarzami XML /// <exception>.
Gdzie jest więcej pracy, w C# czy w Javie?
Oba języki mają duże rynki pracy, a to, który ma więcej ofert, zależy od kraju i branży, więc sprawdź ogłoszenia tam, gdzie chcesz pracować. C# jest mocny w firmach opartych na technologiach Microsoft, w finansach, ochronie zdrowia i studiach gier, a Java w dużych bankach, telekomunikacji, backendach e-commerce i na Androidzie.