01. 실행 환경과 기본 문법
코드가 어떤 단계를 거쳐 실행되는지 확인하고, 값을 다루는 문법을 정리합니다.
JDK와 JVM
세 가지 이름이 섞여 쓰입니다.
| 이름 | 역할 |
|---|---|
| JVM | 바이트코드를 읽어 실행하는 가상 머신 |
| JRE | JVM과 표준 라이브러리 — 실행에 필요한 최소 |
| JDK | JRE에 컴파일러와 개발 도구를 더한 것 |
개발할 때는 JDK를 설치합니다. 소스는 운영체제가 아니라 JVM을 대상으로 컴파일되고, 각 플랫폼의 JVM이 그것을 실행합니다.
java -version
javac -version버전은 LTS를 기준으로 고르는 것이 보통입니다. 17과 21이 널리 쓰입니다.
컴파일과 실행
javac Main.java # Main.class 생성
java Main # 클래스 이름으로 실행한 파일짜리는 바로 실행할 수도 있습니다.
java Main.java진입점은 정해진 형태의 메서드입니다.
public class Main {
public static void main(String[] args) {
System.out.println("실행");
}
}공개 클래스의 이름은 파일 이름과 같아야 합니다. Main.java 안의 public class는 Main이어야 합니다.
기본 타입과 참조 타입
기본 타입은 값을 직접 담고, 참조 타입은 객체가 있는 위치를 담습니다.
int count = 1; // 기본 타입
String title = "공부"; // 참조 타입
int[] numbers = {1, 2, 3};기본 타입은 여덟 가지입니다.
byte short int long
float double
char boolean정수 기본은 int, 실수 기본은 double입니다. 범위를 넘기면 조용히 뒤집힙니다.
int max = Integer.MAX_VALUE; // 2147483647
max + 1; // -2147483648
long big = 2147483648L; // L을 붙여야 long 리터럴입니다실수 계산의 오차도 다른 언어와 같습니다. 금액에는 BigDecimal을 씁니다.
0.1 + 0.2; // 0.30000000000000004
new BigDecimal("0.1").add(new BigDecimal("0.2")); // 0.3문자열 비교는 ==가 아니라 equals입니다. ==는 같은 객체인지를 봅니다.
String a = new String("공부");
String b = new String("공부");
a == b; // false
a.equals(b); // true기본 타입에는 대응하는 래퍼 클래스가 있습니다. 컬렉션에는 래퍼만 넣을 수 있습니다.
int → Integer
long → Long
boolean → Boolean
List<Integer> list = new ArrayList<>();
list.add(1); // 자동으로 Integer로 박싱됩니다래퍼는 null이 될 수 있습니다. 언박싱되는 순간 터집니다.
Integer value = null;
int unboxed = value; // NullPointerExceptionvar와 지역 변수 타입 추론
메서드 안의 지역 변수는 타입을 생략할 수 있습니다. 오른쪽에서 추론합니다.
var todos = new ArrayList<String>();
var count = 1; // int
var entry = Map.entry("id", 1); // 긴 타입을 적지 않아도 됩니다동적 타입이 되는 것이 아닙니다. 타입은 컴파일 시점에 정해지고 바뀌지 않습니다.
var count = 1;
count = "공부"; // 컴파일 오류쓸 수 없는 자리가 있습니다.
var value; // 오류: 초기값이 없습니다
var nothing = null; // 오류: 추론할 수 없습니다
class A {
var field = 1; // 오류: 필드에는 쓸 수 없습니다
}오른쪽만 보고 타입을 알 수 있을 때 씁니다. var result = process();처럼 무엇이 나오는지 알 수 없으면 적어 주는 편이 낫습니다.
switch 표현식과 텍스트 블록
예전 switch는 break를 빠뜨리면 다음 분기로 흘러갔습니다. 화살표를 쓰면 그 문제가 사라지고 값을 돌려줄 수 있습니다.
String label = switch (status) {
case "todo" -> "할 일";
case "done" -> "완료";
default -> "알 수 없음";
};여러 값을 묶거나 블록을 쓸 수 있습니다. 블록에서는 yield로 값을 냅니다.
int priority = switch (status) {
case "urgent", "today" -> 1;
case "done" -> {
log("완료 항목");
yield 3;
}
default -> 2;
};표현식 형태는 모든 경우를 다뤄야 컴파일됩니다. enum에 값을 추가하면 빠뜨린 분기를 바로 알려 줍니다.
여러 줄 문자열은 따옴표 세 개로 씁니다.
String query = """
SELECT id, title
FROM todos
WHERE done = false
""";가장 왼쪽 줄을 기준으로 공통 들여쓰기가 제거됩니다. 이스케이프를 쓰지 않아도 되어 JSON이나 SQL을 적을 때 편합니다.
조건문과 반복문
조건에는 boolean만 올 수 있습니다. if (count)처럼 숫자를 넣으면 컴파일되지 않습니다.
if (count > 0) {
...
} else if (count == 0) {
...
} else {
...
}반복은 목적에 따라 고릅니다.
for (int i = 0; i < list.size(); i++) { } // 인덱스가 필요할 때
for (String item : list) { } // 값만 순회
list.forEach(item -> { }); // 람다instanceof로 확인한 뒤 형변환하던 코드는 한 줄로 줄일 수 있습니다.
// 예전
if (value instanceof String) {
String text = (String) value;
text.length();
}
// 패턴 변수
if (value instanceof String text) {
text.length();
}