C# e Java sono parenti stretti. Entrambi sono linguaggi a tipizzazione statica, orientati agli oggetti, con garbage collector e sintassi in stile C, entrambi compilano in bytecode per una macchina virtuale ed entrambi si usano per grandi sistemi backend. C# è uscito nel 2002 in parte come risposta di Microsoft a Java, e in due decenni i due si sono presi a vicenda diverse funzionalità (lambda, generics, record, pattern matching). Chi conosce uno dei due riesce a leggere l'altro quasi subito.
Le differenze che contano nel lavoro di tutti i giorni sono più piccole e più specifiche. Questa pagina le passa in rassegna con il codice: snippet C# che puoi eseguire, e Java in blocchi statici per il confronto.
In sintesi
| C# | Java | |
|---|---|---|
| Creato da | Microsoft (2002) | Sun Microsystems (1995), ora Oracle |
| Runtime | CLR (.NET), compila IL in codice nativo | JVM, compila bytecode in codice nativo |
| Piattaforme | Windows, Linux, macOS | Windows, Linux, macOS |
| Proprietà | Integrate: public int Age { get; set; } | Metodi getter e setter |
| Tipi valore definiti dall'utente | struct | Non ancora (Project Valhalla) |
| Generics | Reificati: i tipi esistono a runtime | Cancellati: i tipi vengono rimossi dopo la compilazione |
| Interrogare collezioni | LINQ | Streams API |
| Async | async / await | Virtual thread (Java 21), CompletableFuture |
| Checked exception | No | Sì |
| Overload degli operatori | Sì | No |
| Sicurezza rispetto a null | Tipi riferimento nullable (C# 8) | Optional<T>, annotazioni |
| Usi principali | Giochi con Unity, ASP.NET Core, desktop Windows, Azure | Backend enterprise, Android, big data |
Sintassi: quasi uguale
Classi, metodi, if, for, while, switch, try/catch si leggono in modo quasi identico. Le differenze visibili sono le convenzioni di denominazione e alcune parole chiave: i metodi C# sono in PascalCase (ToUpper, WriteLine), i metodi Java in camelCase (toUpperCase, println); C# scrive string e bool, Java String e boolean; l'ereditarietà è class Dog : Animal in C# e class Dog extends Animal in 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());
}
}
}
Output:
ANA
BEN
C# confronta inoltre le stringhe per valore con ==, il che elimina uno dei classici bug da principiante di Java: in Java, == tra due oggetti String confronta i riferimenti, quindi due stringhe uguali costruite a runtime (lette dall'input, concatenate) possono risultare diverse.
Proprietà invece di getter e setter
Le classi Java espongono lo stato tramite metodi getName() e setName(), di solito generati da un IDE o da Lombok. C# ha le proprietà: membri che si leggono come campi ma eseguono codice.
// 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;
}
}
Output:
Mug: 9.50
L'inizializzatore di oggetto new Product { Name = ..., Price = ... } imposta le proprietà subito dopo la costruzione. I record di Java (Java 16) coprono il caso dei dati immutabili; anche C# ha i record (C# 9), più le proprietà per tutto il resto.
Tipi valore: le struct
In Java ogni tipo definito dall'utente è un tipo riferimento memorizzato nell'heap; solo gli otto tipi primitivi (int, double, ...) sono valori. C# ti permette di definire i tuoi tipi valore con struct. Una struct viene copiata nell'assegnazione e può vivere direttamente dentro array e altri oggetti, senza un'allocazione separata nell'heap.
Output:
a = (1, 2)
b = (99, 2)
1000
Questo conta nei giochi e nel codice numerico, dove milioni di piccoli oggetti metterebbero sotto pressione il garbage collector. Vedi le struct.
Generics: reificati vs cancellati
Java cancella gli argomenti di tipo generici in fase di compilazione: a runtime una List<Integer> è solo una List, e List<int> non è ammessa (ottieni oggetti Integer con boxing). C# conserva gli argomenti di tipo a runtime, quindi List<int> memorizza int puri e il codice può chiedere cosa sia T.
Output:
3 items of type Int32
1 items of type String
List`1
typeof(T) e new T() funzionano perché il runtime conosce T. In Java sono entrambi errori di compilazione, e il workaround tipico è passare in giro un oggetto Class<T>.
LINQ vs stream
Entrambi i linguaggi hanno un'API fluente per filtrare e trasformare collezioni. Quella di Java è la Streams API (Java 8); quella di C# è LINQ (C# 3), che ha anche una sintassi di query e funziona sui database tramite Entity Framework.
// Java
List<String> expensive = products.stream()
.filter(p -> p.getPrice() > 20)
.sorted(Comparator.comparing(Product::getPrice))
.map(Product::getName)
.collect(Collectors.toList());
Output:
Lamp, Chair
Total: 126
Le operazioni corrispondono una a una (filter è Where, map è Select, collect(toList()) è ToList()), e le versioni C# non hanno bisogno di stream() né di un collector. Gli oggetti new { Name = ..., Price = ... } sono tipi anonimi, per i quali Java non ha un equivalente diretto.
Async e concorrenza
C# ha introdotto async/await nel 2012: un metodo async restituisce un Task, e await lo sospende senza bloccare un thread. JavaScript, Python e Rust hanno poi adottato le stesse parole chiave. Java ha preso un'altra strada: catene di CompletableFuture e, da Java 21, i virtual thread che rendono economico il normale codice bloccante.
// 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()));
}
Entrambi gli approcci scalano su molte richieste concorrenti. C# rende visibile l'asincronia nelle firme dei metodi; Java la nasconde nel runtime.
Eccezioni, operatori e differenze minori
- Checked exception. Java obbliga un metodo a dichiarare o catturare certe eccezioni (
throws IOException). C# non ha alcuna checked exception. - Overload degli operatori. C# permette ai tipi di definire
+,==,<e simili, ed è per questo che l'aritmetica condecimaleDateTimesi legge in modo naturale. Java no (BigDecimal.add). - Virtuale per impostazione predefinita. I metodi Java possono essere sovrascritti a meno che non siano marcati
final; i metodi C# devono essere marcativirtualper poter essere sovrascritti, e l'override deve direoverride. - Extension method. C# può aggiungere metodi a tipi esistenti (
"text".IsPalindrome()); Java no. - Tipi valore nullable.
int?in C# è un int che può essere null; Java usa l'Integercon boxing. - Tipi senza segno. C# ha
uint,ulong,ushorte unbytesenza segno; ilbytedi Java ha il segno e, a parte ilchara 16 bit, Java non ha tipi interi senza segno.
Ecosistema, lavoro e quale imparare
L'ecosistema di Java è più ampio nel mondo open source lato server: Spring, Kafka, Spark, Hadoop, Elasticsearch e la maggior parte dei progetti Apache sono in Java. Le app Android si scrivono in Kotlin o Java. C# ha lo stack più integrato: un unico fornitore mantiene linguaggio, runtime, framework web (ASP.NET Core), ORM (Entity Framework) e l'IDE principale (Visual Studio), e Unity lo mette al centro dello sviluppo di videogiochi.
Entrambi i linguaggi hanno ampi mercati del lavoro, e l'equilibrio tra i due dipende dalla regione e dal settore. C# è forte nelle aziende basate su Microsoft e Azure, nel software per finanza e sanità e negli studi di videogiochi.
Scegli C# se vuoi creare giochi con Unity, sviluppare app desktop per Windows o lavorare in un'azienda .NET. Scegli Java se punti ad Android, ai big data o a un'azienda il cui backend gira sulla JVM. Entrambi ti insegnano i concetti che ti servono per l'altro: classi, interfacce, generics, collezioni, eccezioni e un runtime con garbage collector.
Domande frequenti
C# è simile a Java?
Sì, molto. Entrambi sono linguaggi a tipizzazione statica, orientati agli oggetti, con garbage collector e sintassi in stile C, che compilano in bytecode per una macchina virtuale. Uno sviluppatore Java riesce a leggere C# fin dal primo giorno. Le differenze stanno nei dettagli: C# ha proprietà, tipi valore definiti dall'utente, generics reificati, LINQ e async/await, mentre Java ha le checked exception e un ecosistema open source lato server più ampio.
Meglio imparare prima C# o Java?
Scegli in base a cosa vuoi costruire. C# per i giochi con Unity, le app desktop per Windows e le aziende basate su Microsoft e Azure. Java per Android (insieme a Kotlin), i grandi backend enterprise e gli strumenti per big data come Kafka e Spark. I linguaggi sono abbastanza vicini che passare dall'uno all'altro in seguito richiede settimane, non mesi.
C# è più veloce di Java?
Sono nella stessa fascia di prestazioni: entrambi compilano con il JIT in codice nativo e usano garbage collector generazionali. C# ti dà più controllo a basso livello tramite struct, Span<T> e generics sui tipi valore senza boxing, il che aiuta nel codice che alloca molto. Nei tipici carichi web il framework e il database contano molto più del linguaggio.
C# ha le checked exception come Java?
No. In C# nessun tipo di eccezione deve essere dichiarato o catturato; la firma di un metodo non elenca cosa lancia. I progettisti hanno giudicato che le checked exception portassero a blocchi catch vuoti e firme fragili. Le eccezioni si documentano invece con commenti XML /// <exception>.
C'è più lavoro per C# o per Java?
Entrambi hanno ampi mercati del lavoro, e quale abbia più annunci dipende dal paese e dal settore, quindi controlla le offerte dove vuoi lavorare. C# è forte nelle aziende enterprise basate su Microsoft, nella finanza, nella sanità e negli studi di videogiochi, Java nelle grandi banche, nelle telecomunicazioni, nei backend di e-commerce e su Android.