C# und Java sind eng verwandt. Beide sind statisch typisierte, objektorientierte Sprachen mit Garbage Collection und C-artiger Syntax, beide kompilieren in Bytecode für eine virtuelle Maschine, und beide werden für große Backend-Systeme eingesetzt. C# erschien 2002 zum Teil als Microsofts Antwort auf Java, und in über zwei Jahrzehnten haben die beiden sich gegenseitig Features abgeschaut (Lambdas, Generics, Records, Pattern Matching). Wer die eine Sprache kennt, kann die andere fast sofort lesen.
Die Unterschiede, die im Alltag zählen, sind kleiner und konkreter. Diese Seite geht sie mit Code durch: C#-Snippets, die du ausführen kannst, und Java in statischen Blöcken zum Vergleich.
Auf einen Blick
| C# | Java | |
|---|---|---|
| Entwickelt von | Microsoft (2002) | Sun Microsystems (1995), heute Oracle |
| Runtime | CLR (.NET), kompiliert IL in nativen Code | JVM, kompiliert Bytecode in nativen Code |
| Plattformen | Windows, Linux, macOS | Windows, Linux, macOS |
| Properties | Eingebaut: public int Age { get; set; } | Getter- und Setter-Methoden |
| Benutzerdefinierte Werttypen | struct | Noch nicht (Project Valhalla) |
| Generics | Reifiziert: Typen existieren zur Laufzeit | Erased: Typen werden nach dem Kompilieren entfernt |
| Collections abfragen | LINQ | Streams API |
| Async | async / await | Virtuelle Threads (Java 21), CompletableFuture |
| Checked Exceptions | Nein | Ja |
| Operatorüberladung | Ja | Nein |
| Null-Sicherheit | Nullable-Referenztypen (C# 8) | Optional<T>, Annotationen |
| Haupteinsatz | Unity-Spiele, ASP.NET Core, Windows-Desktop, Azure | Enterprise-Backends, Android, Big Data |
Syntax: größtenteils gleich
Klassen, Methoden, if, for, while, switch, try/catch lesen sich fast identisch. Die sichtbaren Unterschiede sind Namenskonventionen und ein paar Schlüsselwörter: C#-Methoden sind PascalCase (ToUpper, WriteLine), Java-Methoden camelCase (toUpperCase, println); C# schreibt string und bool, Java String und boolean; Vererbung heißt in C# class Dog : Animal und in Java class Dog extends Animal.
// 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());
}
}
}
Ausgabe:
ANA
BEN
C# vergleicht Strings mit == außerdem nach Wert, was einen der klassischen Anfängerfehler aus Java beseitigt: In Java vergleicht == bei zwei String-Objekten die Referenzen, sodass zwei gleiche, zur Laufzeit erzeugte Strings (aus einer Eingabe gelesen, verkettet) als ungleich gelten können.
Properties statt Getter und Setter
Java-Klassen legen ihren Zustand über Methoden wie getName() und setName() offen, die meist von einer IDE oder Lombok erzeugt werden. C# hat Properties: Member, die sich wie Felder lesen, aber Code ausführen.
// 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;
}
}
Ausgabe:
Mug: 9.50
Der Objektinitialisierer new Product { Name = ..., Price = ... } setzt Properties direkt nach der Konstruktion. Java-Records (Java 16) decken den Fall unveränderlicher Daten ab; C# hat ebenfalls Records (C# 9) und für alles andere Properties.
Werttypen: Structs
In Java ist jeder benutzerdefinierte Typ ein Referenztyp auf dem Heap; nur die acht primitiven Typen (int, double, ...) sind Werte. In C# definierst du mit struct eigene Werttypen. Ein Struct wird bei der Zuweisung kopiert und kann direkt in Arrays und anderen Objekten liegen, ohne eigene Heap-Allokation.
Ausgabe:
a = (1, 2)
b = (99, 2)
1000
Das zählt in Spielen und numerischem Code, wo Millionen kleiner Objekte den Garbage Collector belasten würden. Siehe Structs.
Generics: reifiziert vs. erased
Java entfernt generische Typargumente beim Kompilieren: Zur Laufzeit ist eine List<Integer> nur eine List, und List<int> ist nicht erlaubt (du bekommst geboxte Integer-Objekte). C# behält die Typargumente zur Laufzeit, also speichert List<int> rohe Ints, und Code kann abfragen, was T ist.
Ausgabe:
3 items of type Int32
1 items of type String
List`1
typeof(T) und new T() funktionieren, weil die Runtime T kennt. In Java sind beide Kompilierfehler, und der übliche Workaround ist, ein Class<T>-Objekt herumzureichen.
LINQ vs. Streams
Beide Sprachen haben eine Fluent API zum Filtern und Transformieren von Collections. In Java ist das die Streams API (Java 8), in C# LINQ (C# 3), das zusätzlich eine Abfragesyntax hat und über Entity Framework mit Datenbanken funktioniert.
// Java
List<String> expensive = products.stream()
.filter(p -> p.getPrice() > 20)
.sorted(Comparator.comparing(Product::getPrice))
.map(Product::getName)
.collect(Collectors.toList());
Ausgabe:
Lamp, Chair
Total: 126
Die Operationen entsprechen sich eins zu eins (filter ist Where, map ist Select, collect(toList()) ist ToList()), und die C#-Versionen brauchen weder stream() noch einen Collector. Die Objekte new { Name = ..., Price = ... } sind anonyme Typen, für die Java kein direktes Gegenstück hat.
Async und Nebenläufigkeit
C# führte async/await 2012 ein: Eine async-Methode gibt einen Task zurück, und await unterbricht sie, ohne einen Thread zu blockieren. JavaScript, Python und Rust übernahmen später dieselben Schlüsselwörter. Java ging einen anderen Weg: Ketten mit CompletableFuture und seit Java 21 virtuelle Threads, die normalen blockierenden Code günstig machen.
// 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()));
}
Beide Ansätze skalieren auf viele gleichzeitige Anfragen. C# macht die Asynchronität in Methodensignaturen sichtbar; Java versteckt sie in der Runtime.
Exceptions, Operatoren und kleinere Unterschiede
- Checked Exceptions. Java zwingt eine Methode, bestimmte Exceptions zu deklarieren oder abzufangen (
throws IOException). C# hat überhaupt keine Checked Exceptions. - Operatorüberladung. In C# können Typen
+,==,<und Co. definieren, deshalb liest sich Arithmetik mitdecimalundDateTimenatürlich. Java kann das nicht (BigDecimal.add). - Standardmäßig virtuell. Java-Methoden können überschrieben werden, solange sie nicht
finalsind; C#-Methoden müssen alsvirtualmarkiert sein, um überschreibbar zu sein, und die Überschreibung mussoverrideangeben. - Erweiterungsmethoden. C# kann bestehenden Typen Methoden hinzufügen (
"text".IsPalindrome()); Java nicht. - Nullable-Werttypen.
int?ist in C# ein Int, das null sein kann; Java nutzt das geboxteInteger. - Vorzeichenlose Typen. C# hat
uint,ulong,ushortund ein vorzeichenlosesbyte; in Java istbytevorzeichenbehaftet, und abgesehen vom 16-Bit-chargibt es keine vorzeichenlosen Ganzzahltypen.
Ökosystem, Jobs und was du lernen solltest
Das Ökosystem von Java ist in der Open-Source-Serverwelt größer: Spring, Kafka, Spark, Hadoop, Elasticsearch und die meisten Apache-Projekte sind Java. Android-Apps werden in Kotlin oder Java geschrieben. C# hat den stärker integrierten Stack: Ein Hersteller pflegt Sprache, Runtime, Webframework (ASP.NET Core), ORM (Entity Framework) und die wichtigste IDE (Visual Studio), und Unity stellt die Sprache ins Zentrum der Spieleentwicklung.
Beide Sprachen haben große Arbeitsmärkte, und das Verhältnis hängt von Region und Branche ab. C# ist stark in Firmen, die auf Microsoft und Azure setzen, in Finanz- und Gesundheitssoftware und in Spielestudios.
Nimm C#, wenn du Spiele mit Unity bauen, Windows-Desktop-Apps entwickeln oder in einer .NET-Firma arbeiten willst. Nimm Java, wenn du Android, Big Data oder eine Firma mit Backend auf der JVM anpeilst. Jede der beiden vermittelt die Konzepte, die du für die andere brauchst: Klassen, Interfaces, Generics, Collections, Exceptions und eine Runtime mit Garbage Collection.
Häufig gestellte Fragen
Ist C# ähnlich wie Java?
Ja, sehr. Beide sind statisch typisierte, objektorientierte Sprachen mit Garbage Collection und C-artiger Syntax, die in Bytecode für eine virtuelle Maschine kompiliert werden. Ein Java-Entwickler kann C# vom ersten Tag an lesen. Die Unterschiede liegen im Detail: C# hat Properties, benutzerdefinierte Werttypen, reifizierte Generics, LINQ und async/await, Java hat Checked Exceptions und ein größeres Open-Source-Ökosystem auf dem Server.
Soll ich zuerst C# oder Java lernen?
Entscheide danach, was du bauen willst. C# für Unity-Spiele, Windows-Desktop-Apps und Firmen, die auf Microsoft und Azure setzen. Java für Android (neben Kotlin), große Enterprise-Backends und Big-Data-Tools wie Kafka und Spark. Die Sprachen sind sich so nah, dass ein späterer Wechsel Wochen dauert, nicht Monate.
Ist C# schneller als Java?
Sie spielen in derselben Leistungsklasse: Beide kompilieren per JIT in nativen Code und nutzen generationelle Garbage Collectors. C# gibt dir mehr Kontrolle auf niedriger Ebene durch Structs, Span<T> und Generics über Werttypen ohne Boxing, was in Code mit vielen Allokationen hilft. In typischen Web-Workloads sind Framework und Datenbank viel wichtiger als die Sprache.
Hat C# Checked Exceptions wie Java?
Nein. In C# muss kein Exception-Typ deklariert oder abgefangen werden; eine Methodensignatur listet nicht auf, was sie wirft. Die Entwickler der Sprache kamen zu dem Schluss, dass Checked Exceptions zu leeren catch-Blöcken und fragilen Signaturen führen. Stattdessen dokumentierst du Exceptions mit XML-Kommentaren /// <exception>.
Wofür gibt es mehr Jobs, C# oder Java?
Beide haben große Arbeitsmärkte, und welche Sprache mehr Stellenanzeigen hat, hängt von Land und Branche ab. Schau also in die Anzeigen dort, wo du arbeiten willst. C# ist stark in Enterprise-Umgebungen mit Microsoft-Stack, in Finanzen, Gesundheitswesen und Spielestudios, Java in großen Banken, Telekommunikation, E-Commerce-Backends und Android.