첫 Java 프로그램 만들고 실행하기
Hello.java를 저장하고 javac로 컴파일한 뒤 java로 실행해 보고, 소스를 고쳤을 때 왜 다시 컴파일해야 하는지 확인합니다.
앞 글에서 JDK를 설치하고 java-board 실습 폴더를 만들었습니다. 이제 그 폴더에 첫 프로그램을 저장하고 실행해 봅니다. 문법은 아주 조금만 다루고, 소스를 작성하고 컴파일한 뒤 실행하는 한 바퀴를 직접 성공시키는 데 집중합니다. 이 흐름을 한 번 몸에 익혀 두면 앞으로 나오는 모든 예제를 같은 방법으로 실행할 수 있습니다.
소스 파일을 저장할 편집기 고르기
Java 소스는 일반 텍스트 파일입니다. 그래서 글자만 저장하는 편집기라면 무엇이든 쓸 수 있습니다. Windows의 메모장, macOS의 텍스트 편집기, Visual Studio Code, IntelliJ IDEA 모두 가능합니다. 처음이라면 Visual Studio Code처럼 코드를 쓰기 위한 편집기를 권합니다. 무료이고 세 운영체제에서 모두 쓸 수 있으며, 기본 설정 그대로 UTF-8 일반 텍스트로 저장해 주기 때문입니다.
반대로 워드나 한글 같은 문서 편집기는 쓰면 안 됩니다. 이런 프로그램은 글꼴과 문단 모양 같은 서식 정보를 파일에 함께 저장하고, 곧은 따옴표 "를 둥글게 굽은 따옴표 “ ”로 바꾸기도 합니다. javac는 파일의 글자를 하나도 빼지 않고 Java 문법으로 읽으므로, 이런 정보가 섞이면 컴파일하지 못합니다. 같은 이유로 메신저나 문서에 붙여 넣었던 코드를 다시 복사해 올 때도 따옴표가 바뀌지 않았는지 확인해야 합니다.
Hello.java 저장하기
편집기에서 새 파일을 만들고 다음 다섯 줄을 그대로 입력합니다. 대문자와 소문자, 괄호의 종류, 마지막의 세미콜론까지 똑같아야 합니다.
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, Java!");
}
}이 파일을 java-board 폴더 안의 src 폴더에 Hello.java라는 이름으로 저장합니다. 파일 이름의 대소문자도 정확히 맞춰야 하는데, 첫 줄의 public class Hello에 적은 이름이 Hello이기 때문입니다. javac는 public이 붙은 클래스를 그 클래스와 이름이 같은 .java 파일에서만 받아들이므로, 예를 들어 첫 줄을 public class hello로 바꾸면 컴파일 단계에서 오류가 납니다. Windows와 macOS는 기본 설정에서 파일 이름의 대소문자를 구분하지 않아서, 파일을 hello.java로 저장해도 아래 명령이 그대로 통과하는 경우가 있습니다. 하지만 대소문자를 구분하는 Linux에서는 같은 명령이 파일을 찾지 못하므로 처음부터 정확히 맞춰 둡니다. 이름 규칙을 어겼을 때의 오류는 처음 만나는 Java 오류에서 직접 재현해 봅니다.
메모장을 쓴다면 저장할 때 두 가지를 확인합니다. 먼저 저장 창의 '파일 형식'을 '모든 파일'로 바꿉니다. '텍스트 문서'로 둔 채 저장하면 이름 뒤에 .txt가 붙어 Hello.java.txt가 될 수 있습니다. 다음으로 인코딩은 'UTF-8'을 고르고, BOM이 있는 UTF-8은 고르지 않습니다. BOM은 파일 맨 앞에 붙는 보이지 않는 표식인데, javac는 이것을 잘못된 글자로 보고 illegal character: '\ufeff' 오류를 낼 수 있습니다.
Windows의 파일 탐색기는 기본 설정에서 확장자를 숨기므로, 파일 이름이 Hello.java.txt여도 Hello.java처럼 보일 수 있습니다. Windows 10은 [보기] 탭에서, Windows 11은 [보기] 메뉴의 [표시]에서 '파일 확장명'을 켜 두면 실제 이름을 확인할 수 있습니다.
macOS의 텍스트 편집기는 새 문서를 서식 있는 문서로 만들기 때문에, 입력하기 전에 [포맷] 메뉴에서 '일반 텍스트로 만들기'를 먼저 선택해야 합니다. 또 따옴표를 굽은 따옴표로 바꾸는 스마트 인용 부호(Smart Quotes) 기능이 켜져 있을 수 있으니 [편집] 메뉴의 [대치] 항목에서 꺼 둡니다. 저장할 때 확장자를 묻는 창이 나오면 .java를 쓰겠다고 고르세요. 이런 설정이 번거롭다면 처음부터 Visual Studio Code를 쓰는 편이 낫습니다.
코드를 바깥에서 안쪽으로 읽기
처음 보는 단어가 많지만 지금 모두 외울 필요는 없습니다. 이 다섯 줄은 상자 안에 상자가 들어 있는 구조라서, 바깥에서 안쪽으로 읽으면 전체 모양이 잡힙니다. 가장 바깥은 Hello.java 파일이고, 그 안에 Hello 클래스가 있고, 클래스 안에 main 메서드가 있으며, main 안에 실제로 실행될 출력 문장이 있습니다.
NESTED · SOURCE CONTAINMENT
Hello.java를 바깥에서 안쪽으로 읽는 세 범위
코드의 단어를 따로 외우기보다 포함 관계를 따라간다. 파일 안에 공개 클래스가 있고, 그 클래스 안의 main에서 출력 문장이 실행된다.
- 파일 ·
Hello.java파일은 공개 클래스 선언을 담고, 공개 클래스 이름
Hello와 파일명이 정확히 일치합니다. - 클래스 ·
public class Hello첫 중괄호부터 마지막 중괄호까지가
Hello클래스에 속하는 가장 바깥 코드 범위입니다. - 진입점 ·
mainJVM은 클래스 범위 안의
public static void main(String[] args)를 찾아 실행을 시작합니다. - 실행 문장 ·
printlnmain안의System.out.println(...);이 문자열 한 줄을 표준 출력에 쓰고, 세미콜론이 문장을 끝냅니다.
포함 관계는 파일 → 클래스 → 메서드 → 문장 순서이며, 각 중괄호 쌍이 어느 범위에 속하는지 먼저 확인한다.
첫 줄의 public class Hello는 Hello라는 이름의 클래스(class)를 만든다는 뜻입니다. 클래스는 Java 코드를 담는 가장 바깥 단위이며, Java의 메서드와 문장은 모두 어떤 클래스에 속합니다. 첫 줄 끝의 여는 중괄호 {부터 마지막 줄의 닫는 중괄호 }까지가 Hello 클래스에 속하는 범위입니다. 앞에 붙은 public은 이 클래스를 어디서든 쓸 수 있다는 표시이고, 앞에서 본 파일 이름 규칙이 바로 이 public 때문에 생깁니다.
둘째 줄의 public static void main(String[] args)는 main이라는 메서드(method)를 만듭니다. 메서드는 이름을 붙인 명령 묶음이고, 둘째 줄 끝의 {부터 넷째 줄의 }까지가 main의 범위입니다. main은 특별한 이름이라서, 이 클래스를 실행하면 JVM이 main을 찾아 그 안의 문장부터 실행합니다. void는 이 메서드가 일을 마친 뒤 돌려주는 값이 없다는 뜻입니다. static은 6장에서, String[] args는 이 장의 마지막 글에서 다루므로 지금은 시작 위치를 알리는 표준 형식이라고 생각하면 됩니다.
셋째 줄이 실제로 일을 하는 문장입니다. System.out은 표준 출력, 즉 보통은 프로그램을 실행한 터미널 화면을 가리킵니다. println은 print line의 줄임말로, 괄호 안의 값을 출력한 뒤 줄을 바꿉니다. 큰따옴표로 감싼 "Hello, Java!"는 문자열(string), 즉 글자를 나열한 값이고, 따옴표 자체는 출력되지 않습니다. 줄 끝의 세미콜론 ;은 문장 하나가 끝났다는 표시입니다.
들여쓰기는 사람이 범위를 쉽게 알아보도록 넣는 것입니다. javac는 들여쓰기나 줄 바꿈이 아니라 중괄호의 짝으로 범위를 판단하므로, 여는 중괄호와 닫는 중괄호의 개수가 맞지 않으면 컴파일하지 못합니다. 반면 대소문자는 정확히 구분합니다. System을 system으로 쓰면 javac는 그런 이름을 찾지 못해 error: package system does not exist 같은 오류를 냅니다. Hello, hello, HELLO도 Java에서는 모두 다른 이름입니다.
javac로 컴파일하기
터미널을 열고 java-board 폴더로 이동합니다. 새로 연 터미널은 사용자 폴더에서 시작하므로 cd java-board를 먼저 입력하고, Get-Location이나 pwd로 현재 폴더가 java-board인지 확인합니다. 그다음 다음 명령을 실행합니다. 이 명령은 세 운영체제에서 똑같습니다.
javac -d out src/Hello.java명령은 세 부분으로 나뉩니다. javac는 컴파일러를 실행하고, src/Hello.java는 컴파일할 소스 파일을 가리킵니다. 가운데의 -d out은 만들어진 .class 파일을 out 폴더에 넣으라는 옵션입니다. 이 옵션을 빼면 .class 파일이 소스 파일 옆, 즉 src 폴더에 생겨서 원본과 결과물이 섞입니다.
컴파일에 성공하면 아무 메시지도 나오지 않고 다음 입력을 기다리는 줄이 나타납니다. 처음에는 아무 일도 없었던 것처럼 보이지만, 명령 도구들은 대개 문제가 있을 때만 메시지를 출력합니다. 정말 파일이 만들어졌는지 out 폴더를 확인해 보겠습니다. 첫 명령은 Windows PowerShell용이고, 둘째는 macOS·Linux용입니다.
Get-ChildItem out -Namels outHello.classHello.class는 javac가 Hello.java를 번역해 만든 파일입니다. 안에는 JVM이 실행할 명령이 바이트코드(bytecode)라는 형식으로 들어 있습니다. 사람이 읽는 글이 아니라서 편집기로 열면 대부분 알아볼 수 없는 글자로 보이며, 이 파일은 직접 고치지 않습니다. 고칠 것이 있으면 언제나 src의 .java를 고치고 다시 컴파일합니다.
컴파일이 실패하면 javac는 어느 파일의 몇 번째 줄에서 무엇이 문제인지 알려 줍니다. 오류를 읽는 방법은 처음 만나는 Java 오류에서 자세히 다루고, 여기서는 첫 프로그램을 저장할 때 자주 생기는 실수만 모아 보겠습니다.
| 오류 메시지 | 흔한 원인 | 고치는 방법 |
|---|---|---|
error: file not found: src/Hello.java | 터미널이 java-board가 아닌 폴더에 있음, 파일이 Hello.java.txt로 저장됨 | 현재 폴더와 src 안의 실제 파일 이름을 확인 |
error: illegal character: '\u201c' | 따옴표가 굽은 따옴표 “ ”로 바뀜 | 편집기에서 곧은 따옴표 "로 다시 입력 |
error: unmappable character (0xBF) for encoding UTF-8 | 한글이 든 파일을 UTF-8이 아닌 인코딩(메모장의 ANSI 등)으로 저장함 | 파일을 UTF-8로 다시 저장 |
error: class hello is public, should be declared in a file named hello.java | 클래스 이름과 파일 이름의 대소문자가 다름 | 두 이름을 Hello로 맞춤 |
세 번째 메시지의 0xBF는 문제가 된 바이트 값이라서 파일 내용에 따라 다른 값이 나옵니다. JDK 18부터 javac는 따로 지정하지 않으면 운영체제와 관계없이 소스 파일을 UTF-8로 읽기 때문에, 한글이 든 소스는 UTF-8로 저장해야 합니다.
java로 실행하기
이제 만든 클래스를 실행합니다. 이 명령도 세 운영체제에서 같습니다.
java -cp out HelloHello, Java!-cp out은 실행할 클래스를 out 폴더에서 찾으라는 옵션입니다. 마지막의 Hello는 파일 경로가 아니라 실행할 클래스의 이름이므로 .class를 붙이지 않습니다. java는 JVM을 시작하고 out 폴더에서 Hello.class를 찾아 싣습니다. 그러면 JVM이 Hello 클래스의 main을 실행합니다. 그 결과 main 안의 println이 터미널에 Hello, Java! 한 줄을 출력했습니다. main의 마지막 문장까지 실행하면 프로그램이 끝나고, 터미널은 다시 다음 명령을 기다립니다.
이 출력이 보이면 작성, 컴파일, 실행 세 단계를 모두 성공한 것입니다. 지금까지의 흐름을 파일 기준으로 그리면 다음과 같습니다.
src/Hello.java --javac--> out/Hello.class --java--> 화면 출력-cp가 정확히 무엇인지, .class를 붙이거나 -cp를 빼면 어떻게 되는지는 다음 글에서 실제 오류와 함께 살펴봅니다.
소스를 고친 뒤 다시 컴파일해야 하는 이유
출력 문구를 바꿔 보겠습니다. Hello.java의 셋째 줄을 다음처럼 고치고 저장합니다.
System.out.println("회원가입 시작");저장한 뒤 컴파일하지 않고 실행 명령만 다시 입력해 봅니다. 소스 파일은 바뀌었지만 javac는 다시 실행하지 않았다는 점을 기억하고, 화면에 새 문구와 예전 문구 중 어느 쪽이 나오는지 보세요.
java -cp out HelloHello, Java!소스를 고쳤는데도 예전 문구가 나옵니다. java는 .java 파일을 읽지 않고 out 폴더의 Hello.class만 실행하는데, 그 .class는 문구를 고치기 전에 컴파일한 것이기 때문입니다. 소스를 저장한다고 .class가 저절로 바뀌지는 않습니다. 이제 컴파일한 뒤 다시 실행합니다.
javac -d out src/Hello.java
java -cp out Hello회원가입 시작이번에는 바뀐 문구가 나옵니다. javac가 고친 소스로 Hello.class를 다시 만들었기 때문입니다. 지금까지 두 파일의 상태가 어떻게 바뀌었는지 정리하면 다음과 같습니다.
| 순서 | 한 일 | src/Hello.java의 문구 | out/Hello.class의 문구 | 출력 |
|---|---|---|---|---|
| 1 | 앞 절에서 컴파일하고 실행 | Hello, Java! | Hello, Java! | Hello, Java! |
| 2 | 문구를 고쳐 저장 | 회원가입 시작 | Hello, Java! | (실행하지 않음) |
| 3 | java만 실행 | 회원가입 시작 | Hello, Java! | Hello, Java! |
| 4 | javac 후 java 실행 | 회원가입 시작 | 회원가입 시작 | 회원가입 시작 |
3번 줄에서 보듯이 출력은 소스가 아니라 .class에 담긴 문구를 따라갑니다. 컴파일이 실패했을 때도 마찬가지입니다. 문구를 다시 고치면서 세미콜론을 빠뜨리면 javac는 오류를 내고 .class를 새로 만들지 않습니다. 이때 java를 실행하면 오류 없이 마지막으로 컴파일에 성공한 문구가 나오므로, 실행이 됐다고 해서 지금의 소스가 반영됐다고 단정하면 안 됩니다.
java는 최신 소스가 아니라 마지막 class를 실행한다
소스 저장 뒤 class가 즉시 바뀌지 않고 javac 성공 때만 갱신되는 상태 전환입니다. 본문의 이전 출력 재현과 직접 연결돼 저장·컴파일·실행 장면이 유용합니다.
FLOWCHART · ARTIFACT FRESHNESS
java는 최신 소스가 아니라 마지막 class를 실행한다
소스를 저장하는 일과 class를 갱신하는 일은 별개다. java는 out에서 발견한 마지막 class를 실행하므로 재컴파일을 생략하거나 실패하면 이전 출력이 남을 수 있다.
- 소스 수정과 저장
src/Hello.java를 저장하면 소스만 바뀌며out/Hello.class는 자동으로 갱신되지 않습니다. - javac 성공 여부
javac -d out src/Hello.java가 성공해야 class가 현재 소스를 반영합니다. 명령을 생략하거나 실패하면 기존 class가 남고, 최초라면 class가 아직 없습니다. - classpath에서 class 탐색
java -cp out Hello에서out은 classpath root이고Hello는 확장자가 없는 binary class name입니다. - JVM 진입
javalauncher가 JVM을 시작하고, JVM은 발견한 class의main을 호출해 표준 출력을 만듭니다. - 오래된 출력 해석
오류 없이 이전 문구가 보이면 마지막 성공 컴파일 이후 소스만 바뀌었는지 확인합니다.
java는 최신.java를 직접 읽지 않습니다.
컴파일 실패 뒤에도 이전 .class가 남을 수 있으므로, 실행 성공만으로 최신 소스가 반영됐다고 단정하지 않습니다. 최초 컴파일 전에는 .class가 없어 클래스 탐색에서 실패하고, 그림의 main 호출·출력까지 진행하지 않습니다.
이 예의 조건
- javac 성공/미실행·실패는 서로 다른 경로이다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 전체 읽기 정보 | java는 최신 소스가 아니라 마지막 class를 실행한다 Hello 소스와 class의 신선도 및 실행 흐름 FLOWCHART · ARTIFACT FRESHNESS java는 최신 소스가 아니라 마지막 class를 실행한다 소스를 저장하는 일과 class를 갱신하는 일은 별개다. java는 out에서 발견한 마지막 class를 실행하므로 재컴파일을 생략하거나 실패하면 이전 출력이 남을 수 있다. 소스 수정과 저장 src/Hello.java 를 저장하면 소스만 바뀌며 out/Hello.class 는 자동으로 갱신되지 않습니다. javac 성공 여부 javac -d out src/Hello.java 가 성공해야 class가 현재 소스를 반영합니다. 명령을 생략하거나 실패하면 기존 class가 남고, 최초라면 class가 아직 없습니다. classpath에서 class 탐색 java -cp out Hello 에서 out 은 classpath root이고 Hello 는 확장자가 없는 binary class name입니다. JVM 진입 java launcher가 JVM을 시작하고, JVM은 발견한 class의 main 을 호출해 표준 출력을 만듭니다. 오래된 출력 해석 오류 없이 이전 문구가 보이면 마지막 성공 컴파일 이후 소스만 바뀌었는지 확인합니다. java 는 최신 .java 를 직접 읽지 않습니다. 컴파일 실패 뒤에도 이전 .class 가 남을 수 있으므로, 실행 성공만으로 최신 소스가 반영됐다고 단정하지 않습니다. 최초 컴파일 전에는 .class 가 없어 클래스 탐색에서 실패하고, 그림의 main 호출·출력까지 진행하지 않습니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 소스 저장 Hello.java의 새 내용을 보관합니다. |
|
|
| 컴파일 판정 javac 성공이면 out의 class가 갱신됩니다. |
|
|
| 산출물 실행 java -cp out Hello는 찾은 class의 main을 호출합니다. |
|
|
java는 최신 소스가 아니라 마지막 class를 실행한다
소스 저장 뒤 class가 즉시 바뀌지 않고 javac 성공 때만 갱신되는 상태 전환입니다. 본문의 이전 출력 재현과 직접 연결돼 저장·컴파일·실행 장면이 유용합니다.
FLOWCHART · ARTIFACT FRESHNESS
java는 최신 소스가 아니라 마지막 class를 실행한다
소스를 저장하는 일과 class를 갱신하는 일은 별개다. java는 out에서 발견한 마지막 class를 실행하므로 재컴파일을 생략하거나 실패하면 이전 출력이 남을 수 있다.
- 소스 수정과 저장
src/Hello.java를 저장하면 소스만 바뀌며out/Hello.class는 자동으로 갱신되지 않습니다. - javac 성공 여부
javac -d out src/Hello.java가 성공해야 class가 현재 소스를 반영합니다. 명령을 생략하거나 실패하면 기존 class가 남고, 최초라면 class가 아직 없습니다. - classpath에서 class 탐색
java -cp out Hello에서out은 classpath root이고Hello는 확장자가 없는 binary class name입니다. - JVM 진입
javalauncher가 JVM을 시작하고, JVM은 발견한 class의main을 호출해 표준 출력을 만듭니다. - 오래된 출력 해석
오류 없이 이전 문구가 보이면 마지막 성공 컴파일 이후 소스만 바뀌었는지 확인합니다.
java는 최신.java를 직접 읽지 않습니다.
컴파일 실패 뒤에도 이전 .class가 남을 수 있으므로, 실행 성공만으로 최신 소스가 반영됐다고 단정하지 않습니다. 최초 컴파일 전에는 .class가 없어 클래스 탐색에서 실패하고, 그림의 main 호출·출력까지 진행하지 않습니다.
이 예의 조건
- javac 성공/미실행·실패는 서로 다른 경로이다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 전체 읽기 정보 | java는 최신 소스가 아니라 마지막 class를 실행한다 Hello 소스와 class의 신선도 및 실행 흐름 FLOWCHART · ARTIFACT FRESHNESS java는 최신 소스가 아니라 마지막 class를 실행한다 소스를 저장하는 일과 class를 갱신하는 일은 별개다. java는 out에서 발견한 마지막 class를 실행하므로 재컴파일을 생략하거나 실패하면 이전 출력이 남을 수 있다. 소스 수정과 저장 src/Hello.java 를 저장하면 소스만 바뀌며 out/Hello.class 는 자동으로 갱신되지 않습니다. javac 성공 여부 javac -d out src/Hello.java 가 성공해야 class가 현재 소스를 반영합니다. 명령을 생략하거나 실패하면 기존 class가 남고, 최초라면 class가 아직 없습니다. classpath에서 class 탐색 java -cp out Hello 에서 out 은 classpath root이고 Hello 는 확장자가 없는 binary class name입니다. JVM 진입 java launcher가 JVM을 시작하고, JVM은 발견한 class의 main 을 호출해 표준 출력을 만듭니다. 오래된 출력 해석 오류 없이 이전 문구가 보이면 마지막 성공 컴파일 이후 소스만 바뀌었는지 확인합니다. java 는 최신 .java 를 직접 읽지 않습니다. 컴파일 실패 뒤에도 이전 .class 가 남을 수 있으므로, 실행 성공만으로 최신 소스가 반영됐다고 단정하지 않습니다. 최초 컴파일 전에는 .class 가 없어 클래스 탐색에서 실패하고, 그림의 main 호출·출력까지 진행하지 않습니다. |
| 관찰·상황 | 값·보충 정보 | 변화·이유 |
|---|---|---|
| 소스 저장 Hello.java의 새 내용을 보관합니다. |
|
|
| 컴파일 판정 javac 성공이면 out의 class가 갱신됩니다. |
|
|
| 산출물 실행 java -cp out Hello는 찾은 class의 main을 호출합니다. |
|
|
컴파일에는 성공했는데 한글이 ?나 알아볼 수 없는 글자로 출력된다면, 소스가 아니라 터미널의 문자 인코딩 설정이 맞지 않을 가능성이 큽니다. 문자 인코딩은 23장 문자 인코딩과 스트림에서 자세히 다룹니다. IntelliJ IDEA 같은 통합 개발 환경(IDE)은 실행 버튼을 누르면 저장과 컴파일을 알아서 한 뒤 실행해 줍니다. 편리하지만 단계가 눈에 보이지 않으므로, 이 장에서는 각 단계를 구분할 수 있도록 터미널 명령을 직접 입력합니다.
더 깊이: javac를 건너뛰는 실행 방법
JDK 11부터는 java src/Hello.java처럼 .java 파일을 java 명령에 바로 넘길 수도 있습니다. 이를 소스 파일 모드(JEP 330)라고 합니다. java가 소스를 메모리 안에서 컴파일한 뒤 곧바로 실행하므로 out 폴더에 .class 파일이 남지 않습니다. 매번 컴파일을 다시 하니 앞에서 본 '예전 .class가 실행되는' 문제는 생기지 않습니다. 하지만 컴파일과 실행이 한 명령 안에 숨어 버리기 때문에, 이 장에서는 두 단계를 나눠서 실행합니다.
JDK 25에서는 간결한 소스 파일(compact source file)과 인스턴스 main 메서드가 정식 기능이 되었습니다(JEP 512). 그래서 클래스 선언 없이 void main() 메서드만 쓰고 그 안에서 IO.println("Hello, Java!");처럼 출력하는 짧은 프로그램도 컴파일하고 실행할 수 있습니다. 이때도 컴파일러가 클래스를 암묵적으로 선언해 메서드를 그 안에 넣으므로, 코드가 클래스에 속한다는 규칙은 그대로입니다. 그런데도 이 교재가 public class와 public static void main(String[] args)를 쓰는 데는 두 가지 이유가 있습니다. 하나는 클래스와 메서드의 구조를 처음부터 그대로 보여 주기 위해서이고, 다른 하나는 지금 쓰이는 대부분의 Java 코드와 라이브러리가 이 형태로 작성돼 있기 때문입니다. 짧아진 main 선언은 이 장의 마지막 글에서 main의 나머지 단어를 정리할 때 다시 살펴봅니다.
정리
- Java 소스는 일반 텍스트 파일입니다. UTF-8로 저장하고,
public클래스의 이름과 파일 이름을 대소문자까지 맞춥니다. javac -d out src/Hello.java는 소스를 컴파일해out/Hello.class를 만들며, 성공하면 아무것도 출력하지 않습니다.java -cp out Hello는out폴더에서Hello클래스를 찾아main부터 실행합니다. 마지막에는 파일 이름이 아니라 클래스 이름을 씁니다.java는 소스가 아니라 마지막으로 컴파일에 성공한.class를 실행합니다. 소스를 고쳤다면 반드시 다시 컴파일합니다.
연습 문제
-
src/NewMember.java를 만들어 다음 두 줄을 출력하세요. 파일 이름과public클래스 이름은NewMember로 맞추고, 아래 명령으로 컴파일하고 실행합니다.출력 예 [email protected] role=memberjavac -d out src/NewMember.java java -cp out NewMember -
1번에서 만든 프로그램의
println두 개를 모두print로 바꾸고 다시 컴파일해 실행하면 출력이 어떻게 달라질지 예상한 뒤 확인하세요. -
Hello.java를 저장하고javac -d out src/Hello.java를 실행했더니error: file not found: src/Hello.java가 나왔습니다. 원인으로 의심할 수 있는 것 두 가지와 각각을 확인하는 방법을 적으세요. -
Hello.java의 문구를 고치다가 세미콜론을 빠뜨린 채 저장하고 컴파일했더니 오류가 났습니다. 그런데 오류를 무시하고java -cp out Hello를 실행하자 오류 없이 무언가가 출력됐습니다. 무엇이 출력됐고 왜 그런지 설명하세요.
힌트
Hello.java를 복사한 뒤 클래스 이름과 출력 문장을 바꾸면 됩니다. 출력 문장이 두 줄이면println도 두 번 씁니다.println의 ln이 무엇을 하는지 떠올려 보세요.javac는 현재 폴더에서 출발해src/Hello.java를 찾습니다.- 컴파일에 실패하면
out/Hello.class는 어떻게 될까요?
해설
-
클래스 이름이
NewMember이므로 파일 이름도NewMember.java여야 하고,main안에 출력 문장 두 개를 차례로 씁니다. 문장은 위에서부터 순서대로 실행되므로email줄이 먼저 나옵니다. 컴파일하면out에NewMember.class가 생기고, 실행하면 출력 예와 같은 두 줄이 나옵니다.src/NewMember.java public class NewMember { public static void main(String[] args) { System.out.println("[email protected]"); System.out.println("role=member"); } } -
print는 출력한 뒤 줄을 바꾸지 않으므로 두 문구가 한 줄에 붙어[email protected]=member가 출력됩니다. 마지막에도 줄을 바꾸지 않기 때문에, 터미널에 따라 다음 명령을 입력하는 줄이 출력 바로 뒤에 붙어 나타나기도 합니다. 줄을 바꾸는 일은println의 ln 부분이 맡고 있다는 뜻입니다. 소스만 고치고 컴파일을 잊으면 예전처럼 두 줄이 나오니, 반드시javac부터 다시 실행해야 합니다. -
첫째, 터미널이
java-board가 아닌 폴더에 있을 수 있습니다.Get-Location이나pwd로 현재 폴더를 확인하고, 다르다면java-board로 이동합니다. 둘째, 파일이Hello.java.txt처럼 다른 이름으로 저장됐을 수 있습니다.Get-ChildItem src -Name이나ls src로 실제 이름을 확인하고, 이름이 다르면 편집기에서 '모든 파일' 형식으로 다시 저장합니다. -
마지막으로 컴파일에 성공했을 때의 문구가 출력됩니다. 컴파일이 실패하면
javac는.class를 새로 만들지 않으므로out/Hello.class는 이전 상태 그대로 남고,java는 그 파일을 실행합니다. 그래서 출력만 보고는 지금의 소스가 반영됐는지 알 수 없습니다. 세미콜론을 고치고 컴파일이 오류 없이 끝나는 것을 확인한 다음 실행해야 합니다.
다음 글
다음 글에서는 방금 성공한 Hello 프로그램을 따라가며 javac, java, JVM이 각각 무엇을 받아 무엇을 만드는지 정리하고, -cp가 클래스를 찾는 방식과 버전이 맞아야 하는 이유를 살펴봅니다.