C#과 Java는 가까운 친척입니다. 둘 다 C 계열 문법을 쓰는 정적 타입, 객체 지향, 가비지 컬렉션 언어이고, 둘 다 가상 머신용 바이트코드로 컴파일되며, 둘 다 대규모 백엔드 시스템에 쓰입니다. C#은 2002년에 어느 정도 Microsoft의 Java 대항마로 공개되었고, 20여 년 동안 두 언어는 서로의 기능(람다, 제네릭, 레코드, 패턴 매칭)을 빌려 왔습니다. 한쪽을 아는 개발자는 다른 쪽을 거의 바로 읽을 수 있습니다.
매일 체감하는 차이는 더 작고 구체적입니다. 이 페이지는 그 차이를 코드로 살펴봅니다. C# 코드는 실행해 볼 수 있고, 비교용 Java 코드는 정적 블록으로 보여 줍니다.
한눈에 보기
| C# | Java | |
|---|---|---|
| 개발 | Microsoft(2002) | Sun Microsystems(1995), 현재 Oracle |
| 런타임 | CLR(.NET), IL을 네이티브 코드로 컴파일 | JVM, 바이트코드를 네이티브 코드로 컴파일 |
| 플랫폼 | Windows, Linux, macOS | Windows, Linux, macOS |
| 속성 | 내장: public int Age { get; set; } | getter와 setter 메서드 |
| 사용자 정의 값 형식 | struct | 아직 없음(Project Valhalla) |
| 제네릭 | 구체화: 실행 시점에도 타입이 존재 | 소거: 컴파일 후 타입이 제거됨 |
| 컬렉션 쿼리 | LINQ | Streams API |
| 비동기 | async / await | 가상 스레드(Java 21), CompletableFuture |
| 검사 예외 | 없음 | 있음 |
| 연산자 오버로딩 | 있음 | 없음 |
| null 안전성 | nullable 참조 형식(C# 8) | Optional<T>, 어노테이션 |
| 주요 용도 | Unity 게임, ASP.NET Core, Windows 데스크톱, Azure | 기업 백엔드, Android, 빅데이터 |
문법: 대부분 같습니다
클래스, 메서드, if, for, while, switch, try/catch는 거의 똑같이 읽힙니다. 눈에 보이는 차이는 명명 규칙과 몇몇 키워드입니다. C# 메서드는 PascalCase(ToUpper, WriteLine), Java 메서드는 camelCase(toUpperCase, println)입니다. C#은 string과 bool, Java는 String과 boolean이라고 쓰며, 상속은 C#에서 class Dog : Animal, 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());
}
}
}
출력:
ANA
BEN
C#은 ==로 문자열을 값으로 비교하므로 Java의 대표적인 입문자 버그 하나가 사라집니다. Java에서는 두 String 객체에 ==를 쓰면 참조를 비교하므로, 실행 중에 만들어진(입력에서 읽거나 이어 붙인) 같은 내용의 두 문자열이 다르다고 나올 수 있습니다.
getter와 setter 대신 속성
Java 클래스는 보통 IDE나 Lombok이 생성한 getName()과 setName() 메서드로 상태를 노출합니다. C#에는 속성이 있습니다. 필드처럼 읽히지만 코드를 실행하는 멤버입니다.
// 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;
}
}
출력:
Mug: 9.50
개체 이니셜라이저 new Product { Name = ..., Price = ... }는 생성 직후 속성을 설정합니다. 불변 데이터는 Java 레코드(Java 16)가 담당하고, C#에도 레코드(C# 9)가 있으며 그 밖의 모든 경우에는 속성을 씁니다.
값 형식: 구조체
Java에서는 모든 사용자 정의 타입이 힙에 저장되는 참조 형식이고, 여덟 가지 기본형(int, double, ...)만 값입니다. C#에서는 struct로 직접 값 형식을 정의할 수 있습니다. 구조체는 대입할 때 복사되며, 별도의 힙 할당 없이 배열이나 다른 객체 안에 그대로 들어갈 수 있습니다.
출력:
a = (1, 2)
b = (99, 2)
1000
작은 객체 수백만 개가 가비지 컬렉터에 부담을 주는 게임과 수치 계산 코드에서 이 점이 중요합니다. 구조체를 참고하세요.
제네릭: 구체화와 소거
Java는 컴파일 시점에 제네릭 타입 인수를 지웁니다. 실행 시점에 List<Integer>는 그냥 List이고, List<int>는 허용되지 않습니다(박싱된 Integer 객체를 씁니다). C#은 실행 시점에도 타입 인수를 유지하므로 List<int>는 int를 그대로 저장하고, 코드에서 T가 무엇인지 물어볼 수 있습니다.
출력:
3 items of type Int32
1 items of type String
List`1
typeof(T)와 new T()가 동작하는 것은 런타임이 T를 알기 때문입니다. Java에서는 둘 다 컴파일 오류이며, 보통 Class<T> 객체를 넘겨서 우회합니다.
LINQ와 스트림
두 언어 모두 컬렉션을 필터링하고 변환하는 플루언트 API가 있습니다. Java는 Streams API(Java 8), C#은 LINQ(C# 3)이며, LINQ에는 쿼리 문법도 있고 Entity Framework를 통해 데이터베이스에도 동작합니다.
// Java
List<String> expensive = products.stream()
.filter(p -> p.getPrice() > 20)
.sorted(Comparator.comparing(Product::getPrice))
.map(Product::getName)
.collect(Collectors.toList());
출력:
Lamp, Chair
Total: 126
연산은 일대일로 대응합니다(filter는 Where, map은 Select, collect(toList())는 ToList()). C# 쪽은 stream()도 컬렉터도 필요 없습니다. new { Name = ..., Price = ... } 객체는 익명 형식이며, Java에는 직접 대응하는 것이 없습니다.
비동기와 동시성
C#은 2012년에 async/await를 도입했습니다. async 메서드는 Task를 반환하고, await는 스레드를 막지 않고 메서드를 일시 중단합니다. 나중에 JavaScript, Python, Rust가 같은 키워드를 채택했습니다. Java는 다른 길을 택했습니다. CompletableFuture 체인, 그리고 Java 21부터는 평범한 블로킹 코드를 저렴하게 만드는 가상 스레드입니다.
// 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#은 비동기성을 메서드 시그니처에 드러내고, Java는 런타임 안에 숨깁니다.
예외, 연산자, 그 밖의 작은 차이
- 검사 예외. Java는 특정 예외를 메서드가 선언하거나 잡도록 강제합니다(
throws IOException). C#에는 검사 예외가 전혀 없습니다. - 연산자 오버로딩. C#에서는 타입이
+,==,<같은 연산자를 정의할 수 있어서decimal과DateTime연산이 자연스럽게 읽힙니다. Java는 그렇지 않습니다(BigDecimal.add). - 기본값이 virtual인가. Java 메서드는
final로 표시하지 않는 한 재정의할 수 있습니다. C# 메서드는virtual로 표시해야 재정의할 수 있고, 재정의하는 쪽은override라고 써야 합니다. - 확장 메서드. C#은 기존 타입에 메서드를 추가할 수 있습니다(
"text".IsPalindrome()). Java는 할 수 없습니다. - nullable 값 형식. C#의
int?는 null이 될 수 있는 int입니다. Java는 박싱된Integer를 씁니다. - 부호 없는 형식. C#에는
uint,ulong,ushort와 부호 없는byte가 있습니다. Java의byte는 부호가 있고, 16비트char를 빼면 부호 없는 정수 형식이 없습니다.
생태계, 취업, 무엇을 배울까
오픈 소스 서버 세계에서는 Java의 생태계가 더 큽니다. Spring, Kafka, Spark, Hadoop, Elasticsearch와 대부분의 Apache 프로젝트가 Java입니다. Android 앱은 Kotlin이나 Java로 작성합니다. C#은 더 통합된 스택을 갖고 있습니다. 한 업체가 언어, 런타임, 웹 프레임워크(ASP.NET Core), ORM(Entity Framework), 주요 IDE(Visual Studio)를 관리하고, Unity 덕분에 게임 개발의 중심에 있습니다.
두 언어 모두 채용 시장이 크고, 둘 사이의 비중은 지역과 산업에 따라 다릅니다. C#은 Microsoft와 Azure 기반 회사, 금융과 헬스케어 소프트웨어, 게임 스튜디오에서 강합니다.
Unity로 게임을 만들거나, Windows 데스크톱 앱을 만들거나, .NET 회사에서 일하고 싶다면 C#을 고르세요. Android, 빅데이터, 또는 백엔드가 JVM 위에 있는 회사를 목표로 한다면 Java를 고르세요. 어느 쪽을 배워도 다른 쪽에 필요한 개념을 익히게 됩니다: 클래스, 인터페이스, 제네릭, 컬렉션, 예외, 가비지 컬렉션 런타임.
자주 묻는 질문
C#은 Java와 비슷한가요?
네, 아주 비슷합니다. 둘 다 C 계열 문법을 쓰는 정적 타입, 객체 지향, 가비지 컬렉션 언어이며 가상 머신용 바이트코드로 컴파일됩니다. Java 개발자는 첫날부터 C#을 읽을 수 있습니다. 차이는 세부 사항에 있습니다. C#에는 속성, 사용자 정의 값 형식, 구체화된 제네릭, LINQ, async/await가 있고, Java에는 검사 예외와 더 큰 오픈 소스 서버 생태계가 있습니다.
C#과 Java 중 무엇을 먼저 배워야 하나요?
만들고 싶은 것을 기준으로 고르세요. Unity 게임, Windows 데스크톱 앱, Microsoft와 Azure 기반 회사라면 C#입니다. Android(Kotlin과 함께), 대규모 기업 백엔드, Kafka와 Spark 같은 빅데이터 도구라면 Java입니다. 두 언어는 충분히 비슷해서 나중에 바꾸는 데 몇 달이 아니라 몇 주면 됩니다.
C#이 Java보다 빠른가요?
성능은 같은 급입니다. 둘 다 네이티브 코드로 JIT 컴파일하고 세대별 가비지 컬렉터를 씁니다. C#은 구조체, Span<T>, 박싱 없는 값 형식 제네릭으로 더 낮은 수준의 제어를 제공하므로 할당이 많은 코드에서 유리합니다. 일반적인 웹 작업에서는 언어보다 프레임워크와 데이터베이스가 훨씬 중요합니다.
C#에도 Java처럼 검사 예외가 있나요?
없습니다. C#에서는 어떤 예외 타입도 선언하거나 잡도록 강제되지 않으며, 메서드 시그니처에 던지는 예외가 나열되지 않습니다. 설계자들은 검사 예외가 빈 catch 블록과 깨지기 쉬운 시그니처를 낳는다고 판단했습니다. 대신 /// <exception> XML 주석으로 예외를 문서화합니다.
C#과 Java 중 일자리가 더 많은 쪽은 어디인가요?
둘 다 채용 시장이 크고, 어느 쪽 공고가 더 많은지는 나라와 산업에 따라 다르므로 일하고 싶은 곳의 공고를 확인하세요. C#은 Microsoft 기반 기업, 금융, 헬스케어, 게임 스튜디오에서 강하고, Java는 대형 은행, 통신, 전자상거래 백엔드, Android에서 강합니다.