Java SE
고친 사람 github-actions[bot]
Java SE 는 자바로 짠 프로그램이 어느 컴퓨터에서나 같게 돌도록 미리 맞춰 둔 약속입니다. 자바 한 벌에 무엇이 들어가야 하는지를 문서로 정해 둡니다. 자바를 깔아 쓰는 사람이 내려받는 것은 이 약속대로 만들어 놓은 꾸러미입니다.
쉽고 빠른 이해
Java SE 는 자바 한 벌이 무엇으로 이루어지는지를 적어 둔 규격입니다. 어느 꾸러미를 깔든 List 나 String 같은 이름이 같은 모양으로 그대로 들어 있는 것이 이 규격 덕입니다.
규격이 없으면 만드는 곳마다 문법과 라이브러리가 갈립니다. 그러면 내가 짠 코드가 내 컴퓨터에 깔린 자바에서만 돌고, 다른 데로 옮길 때마다 고쳐야 합니다.
어떻게 도나:
- 문서 세 벌이 언어 문법과 실행기 동작과 기본 라이브러리 목록을 정합니다
- 여러 회사와 재단이 그 문서를 보고 각자 꾸러미를 만들어 내놓습니다
- 그 꾸러미는 정해진 시험을 통과해야 자바와 호환된다고 내세울 수 있습니다
대가는 한번 들어간 것을 빼기 어렵다는 점입니다. 옛 코드가 계속 돌아야 하므로 잘못 지은 이름도 오래 남고, 새것은 대개 그 옆에 하나 더 생기는 꼴로 들어옵니다.
상세
Java SE 는 깔아서 쓰는 프로그램이 아니라 문서 묶음입니다. 자바를 깔 때 내려받는 꾸러미는 그 문서를 따라 만든 물건입니다. 이 절은 그 문서가 무엇을 정하는지 보고, 문서와 꾸러미를 가르는 선을 긋습니다.
자바 표준판이라는 이름
Java SE 는 Java Platform, Standard Edition 을 줄인 이름입니다. 우리말로 옮기면 자바 플랫폼 표준판입니다. 「표준」이 붙은 까닭은 자바 플랫폼에 판이 여럿 있어서입니다. 그중 데스크톱과 서버에서 쓰는 판이 이것입니다.
이 이름이 가리키는 것은 프로그램이 아니라 문서 묶음입니다. 콘센트 규격과 벽에 박힌 콘센트의 사이입니다. 규격은 읽는 것입니다. 손에 잡히는 쪽은 그것을 따라 만든 꾸러미입니다.
규격을 따로 세워 둔 까닭은 자바를 만들어 내놓는 곳이 하나가 아니기 때문입니다. 오라클 · 이클립스 재단 · 아마존처럼 여러 곳이 각자 꾸러미를 만들어 배포합니다. 그래도 문법과 라이브러리가 갈리지 않게 붙잡아 주는 것이 이 문서 묶음입니다.
규격을 이루는 문서 세 벌
Java SE 규격은 다루는 것이 서로 다른 문서 세 벌로 이루어집니다.
첫째는 자바 언어 명세입니다. 무엇이 올바른 자바 소스인지, 그 소스가 어떤 동작을 뜻하는지를 정합니다. for 문이 어떻게 생겼는지, 정수끼리 나눌 때 소수점 아래를 어떻게 버리는지가 여기서 정해집니다.
둘째는 JVM 명세입니다. JVM 은 Java Virtual Machine 을 줄인 이름으로, 자바 프로그램을 대신 돌려 주는 가상의 기계입니다. 이 문서는 그 기계가 받는 것과 알아듣는 말을 정합니다.
받는 것은 소스를 컴파일해서 나온 클래스 파일입니다. 그 바이트가 어떤 모양이어야 하는지가 이 문서에 적혀 있습니다. 알아듣는 말은 바이트코드입니다. JVM 이 실행할 수 있는 명령 목록을 그렇게 부릅니다.
셋째는 기본 라이브러리 명세입니다. 표준 라이브러리라고도 부릅니다. 자바를 깔면 따라오는 이름들의 목록입니다.
각 이름이 무엇을 약속하는지를 이 문서가 정합니다. java.util.List 처럼 아무것도 내려받지 않고 바로 부를 수 있는 것이 전부 여기 적혀 있습니다.
문서가 셋으로 갈린 까닭은 자바가 소스를 곧바로 돌리지 않고 중간에 한 번 끊기 때문입니다. 소스를 클래스 파일로 옮기는 앞 구간과 그 파일을 받아 돌리는 뒤 구간이 서로 다른 문서에 적혀 있습니다. 라이브러리 명세는 양쪽이 같이 봅니다.
flowchart TD
subgraph 앞구간["앞 구간 · 소스를 클래스 파일로"]
S["소스"] --> C["클래스 파일"]
end
subgraph 뒤구간["뒤 구간 · 클래스 파일을 돌린다"]
R["JVM 이 명령을 하나씩 실행"]
end
C --> R
L["자바 언어 명세"] --> 앞구간
V["JVM 명세"] --> 뒤구간
A["기본 라이브러리 명세"] --> 앞구간
A --> 뒤구간
규격과 꾸러미를 가르는 선
자바를 처음 만나면 비슷하게 생긴 이름 넷이 한꺼번에 나옵니다. 깔아 쓰는 쪽에 JDK(Java Development Kit, 자바 개발 키트)·JRE(Java Runtime Environment, 자바 실행 환경)·JVM 셋이 더 있기 때문입니다.
| 이름 | 무엇인가 | 깔 수 있나 |
|---|---|---|
| Java SE | 지켜야 할 내용을 적어 둔 규격 | 문서라서 못 깐다 |
| JDK | 규격대로 만들어 내놓은 꾸러미. 컴파일러와 실행기가 다 들어 있다 | 깐다 |
| JRE | JDK 에서 개발 도구를 뺀, 돌리기만 하는 꾸러미 | 깐다 |
| JVM | 꾸러미 안에서 프로그램을 실제로 돌리는 실행기 | JDK 에 들어 있다 |
넷은 나란히 선 것이 아니라 하나가 다른 하나를 품고 있습니다.
flowchart TD
SPEC["Java SE · 규격 · 문서"]
subgraph JDK["JDK · 꾸러미"]
TOOLS["javac 등 개발 도구"]
subgraph JRE["JRE · 돌리기만 하는 꾸러미"]
JVMBOX["JVM · 실행기"]
LIB["기본 라이브러리"]
end
end
SPEC -->|"따라 만든다"| JDK
JDK 를 깔면 JRE 가 딸려 옵니다. 그 안에 JVM 과 기본 라이브러리가 들어 있습니다.
표의 첫 줄만 성격이 다릅니다. 「Java SE 를 깔았다」는 말은 대개 OpenJDK 계열 배포판 하나를 깔았다는 뜻입니다. 여기서 배포판은 앞에서 말한 꾸러미와 같은 말입니다.
규격 자체는 내려받아 읽는 문서입니다. 손에 잡히는 것은 언제나 그 꾸러미입니다.
같은 코드가 여러 기계에서 도는 까닭
자바는 한 번 컴파일해 두면 운영체제를 안 가리고 돈다고 내세웁니다. 이 성질은 두 가지가 같이 받쳐 줍니다.
하나는 컴파일 결과입니다. javac 가 내놓는 클래스 파일은 특정 기계의 기계어가 아니라 어느 기계에도 안 매인 명령으로 채워집니다. 그 파일을 이 기계에서 돌게 만드는 일은 JVM 이 맡습니다.
flowchart TD
S["소스 코드"] --> C["클래스 파일 · 어느 기계에도 안 매인 명령"]
C --> W["윈도의 JVM"]
C --> L["리눅스의 JVM"]
C --> M["맥의 JVM"]
컴파일은 한 번만 합니다. 갈라지는 대목은 그 뒤입니다. 운영체제마다 다른 것은 클래스 파일이 아니라 그것을 받아 돌리는 JVM 쪽입니다.
다른 하나는 이름입니다. 코드가 부르는 이름은 어느 꾸러미에서나 같습니다. 기본 라이브러리 명세가 그 이름과 동작을 미리 정해 두기 때문입니다.
List<String> names = List.of("a", "b"); // java.util.List
System.out.println(names.size()); // 2
이 두 줄은 어느 꾸러미에서 돌려도 같은 값을 냅니다. List 라는 이름이 어디에 있고 size() 가 무엇을 돌려주는지가 규격에 적혀 있어서입니다.
약속을 지켰는지는 호환성 시험으로 가립니다. 꾸러미를 만든 쪽은 정해진 시험 묶음을 받아 돌려 봅니다. 통과해야 자바와 호환된다고 내세울 수 있습니다. 이 시험 묶음을 TCK(Technology Compatibility Kit, 기술 호환성 시험 도구)라고 부릅니다.
flowchart TD
SPEC["Java SE 규격 · 문서"] --> IMPL["여러 곳이 각자 만든 꾸러미"]
IMPL --> TCK["TCK · 정해진 시험 묶음"]
TCK -->|"통과"| OK["자바와 호환된다고 내세운다"]
TCK -->|"못 통과"| NO["자바라는 이름을 못 쓴다"]
새 버전에서도 옛 코드가 도는 까닭
Java SE 는 버전을 올리면서도 옛것을 버리지 않는 쪽을 골라 왔습니다. 오래전에 컴파일해 둔 클래스 파일을 새 실행기가 대개 그대로 읽습니다. 예전 버전을 보고 짠 소스도 새 컴파일러가 대개 받아 줍니다. 이런 성질을 하위 호환이라고 부릅니다.
그래서 새 버전에 들어오는 것은 거의 더하기입니다. 쓸 수 있는 이름을 늘리고 문법을 새로 얹되, 이미 있던 것의 뜻은 바꾸지 않습니다.
빼는 일은 아주 느리게 일어납니다. 지우고 싶은 이름에는 먼저 이제 쓰지 말라는 표시를 답니다. 그 상태로 오래 남겨 둡니다. 쓰던 코드가 다 옮겨 갔다고 볼 만할 때에야 뺍니다.
stateDiagram-v2
state "쓰는 이름" as A
state "이제 쓰지 말라는 표시가 붙은 이름" as B
state "빠진 이름" as C
[*] --> A
A --> B: 고친 이름이 옆에 새로 생겼다
B --> C: 쓰던 코드가 다 옮겨 갔다
이것이 Java SE 가 치르는 대가입니다. 한번 규격에 들어간 이름은 잘못 지었어도 오래 남습니다. 고친 것은 그 옆에 새 이름으로 하나 더 생깁니다. 비슷한 일을 하는 이름이 라이브러리에 여럿 보이는 까닭이 이것입니다.
Java SE 밖으로 밀려나 있는 기능
백엔드에서 자바로 하는 일이 전부 이 규격 안에 있지는 않습니다. 밖에 있는 것을 알아 두면 필요한 것을 어디서 찾을지가 분명해집니다.
Jakarta EE(Enterprise Edition, 기업용 판)는 Java SE 위에 얹혀 도는 규격입니다. 웹 요청을 받고 트랜잭션을 걸고 메시지를 주고받는 서버 기능을 여기서 맡습니다. Java SE 꾸러미 위에서 도는 것이라 Java SE 를 대신하지 않습니다.
Java ME(Micro Edition, 소형 기기용 판)는 옆에 선 다른 판입니다. 손바닥만 한 기기에 넣으려고 잘라 낸 것이라 Java SE 와 위아래 관계가 아닙니다.
| 규격이 안 담는 것 | 어디가 받나 |
|---|---|
| 웹 요청 처리·트랜잭션 관리 같은 서버 기능 | Jakarta EE |
| 소형 기기에 맞춰 잘라 낸 실행 환경 | Java ME |
| 라이브러리 내려받기와 빌드 | Maven·Gradle 같은 빌드 도구 |
마지막 줄이 실무에서 제일 자주 걸립니다. 자바를 깔아도 남이 만든 라이브러리를 받아 오는 수단은 딸려 오지 않습니다. 그 일은 빌드 도구가 따로 맡습니다.
Android 도 이 선 밖에 있습니다. 자바 문법으로 짜기는 하지만 실행기와 라이브러리가 달라서 Java SE 를 구현한 것이 아닙니다. 그래서 규격 문서에 있는 이름이 안드로이드에는 없는 일이 생깁니다.
flowchart TD
EE["Jakarta EE · 서버 기능"]
subgraph SE["Java SE · 선 안"]
LANG["언어 문법"]
JVMIN["JVM"]
LIBIN["기본 라이브러리"]
end
ME["Java ME · 옆에 선 다른 판"]
subgraph OUT["선 밖"]
AND["Android"]
BUILD["Maven · Gradle 같은 빌드 도구"]
end
EE -->|"위에 얹혀 돈다"| SE
SE --- ME
SE --- OUT
관련 항목
Java SE 규격을 이루는 문서
자바 언어 명세 · JVM 명세 · 표준 라이브러리 · 클래스 파일 · 바이트코드
Java SE 를 구현해 내려받게 만든 배포판
JDK · JRE · OpenJDK · Eclipse Temurin · Amazon Corretto
Java SE 규격이 정한 실행 장치
JVM · 가상 머신 · 클래스 로더 · 클래스패스 · 가비지 컬렉션
Java SE 코드를 컴파일하고 들여다보는 명령
javac · javap · java 명령 · jar 명령 · jlink
Java SE 밖에서 서버 기능을 맡는 규격과 틀
Jakarta EE · 서블릿 · Spring · 애플리케이션 서버 · Java ME
Java SE 를 딛고 도는 다른 언어
Kotlin · Scala · Groovy · Clojure · kotlinc
Java SE 가 버전을 올리며 지키는 약속
하위 호환 · 호환성 시험 · 디프리케이션 · 상호운용성
Java SE 프로그램을 묶어 내보내는 산출물
JAR · WAR · 아티팩트 저장소 · Maven · Gradle
Java SE 가 속하는 상위 분류
다른 이름: 자바 SE · 자바 표준판 · Java Platform Standard Edition