먼저 컴파일하고, 그다음 실행하기
이제 JDK를 설치했으니 Java를 실행하는 것은 두 단계 사이클입니다. 인터프리터를 파일에 겨누면 바로 실행되는 스크립트 언어와 달리, Java는 먼저 사람이 읽을 수 있는 소스를 바이트코드로 컴파일한 다음, 별도의 단계에서 그 바이트코드를 JVM에서 실행합니다.
이 두 단계는 JDK와 함께 제공되는 두 개의 명령줄 도구에 대응됩니다.
javac- 컴파일러.Main.java를 읽어Main.class(바이트코드)를 작성합니다.java- 런처. JVM을 시작하고Main.class의 바이트코드를 실행합니다.
자체 완결된 예제라면 어떤 것이든 이 페이지에서 바로 실행할 수 있습니다. 아래 에디터가 두 단계를 모두 대신 처리해 줍니다. 하지만 자신의 컴퓨터에서 무슨 일이 일어나는지 이해해 둘 가치가 있습니다. 실제 프로젝트는 그곳에서 살아가니까요.
내 컴퓨터에서의 두 명령어
이 코드를 Main.java로 저장했다고 합시다.
public class Main {
public static void main(String[] args) {
System.out.println("Hello from the terminal");
}
}
파일이 들어 있는 폴더에서 터미널을 열고 컴파일러를 실행하세요.
javac Main.java
코드가 문제없이 컴파일되면 javac는 아무것도 출력하지 않고, 소스 옆에 Main.class라는 새 파일을 만듭니다. 그 .class 파일이 바이트코드입니다. 읽을 수 있는 텍스트가 아니며, 운영체제에 묶여 있지도 않습니다.
이제 java 명령으로 실행하세요.
java Main
Hello from the terminal
초보자가 가장 흔히 하는 실수가 바로 여기 있습니다. 프로그램을 실행할 때는 javac가 아니라 java를 실행하고, 확장자 없이 클래스 이름을 전달합니다. java Main이 맞으며, 절대 java Main.class나 java Main.java가 아닙니다.
파일 이름은 public 클래스와 일치해야 한다
Java는 초보자를 놀라게 하는 규칙을 강제합니다. public 클래스는 정확히 같은 이름에 .java를 붙인 파일 안에 있어야 합니다. public class Main은 Main.java 안에 있어야 합니다. 대소문자를 틀리면 javac는 컴파일을 거부합니다.
// This is in a file called Greeting.java
public class Greeting { // ERROR: class Greeting should be in Greeting.java... wait, it is
public static void main(String[] args) {
System.out.println("Hi");
}
}
사람들을 실제로 괴롭히는 불일치는 더 미묘합니다. 클래스는 Greeting인데 파일을 greeting.java(소문자)로 이름 짓거나, Main.java로 저장해 놓고 public class Hello라고 쓰는 경우입니다. 컴파일러는 다음과 같은 메시지를 보고합니다.
Main.java:1: error: class Hello is public, should be declared in a file named Hello.java
해결책은 언제나 같습니다. 파일 이름과 public 클래스 이름을 글자 하나하나까지 동일하게 만드는 것입니다.
지름길: 단일 파일을 바로 실행하기
JDK 11부터는 단일 소스 파일에 대해 명시적인 컴파일 단계를 건너뛸 수 있습니다. java 명령이 메모리에서 컴파일하고 한 번에 실행합니다. .class 파일은 디스크에 기록되지 않습니다.
java Main.java
이것은 빠른 실험과 작은 스크립트에 완벽합니다. 전달하는 것의 차이에 주목하세요.
java Main- 이미 컴파일된Main.class를 실행합니다.java Main.java- 소스 파일을 한 번에 컴파일하고 실행합니다.
단일 파일 모드는 프로그램 전체가 한 파일에 들어맞을 때만 작동합니다. 코드를 여러 파일의 여러 클래스로 나누는 순간, 다시 javac로 먼저 컴파일하는 방식으로 돌아갑니다. 기초를 배우는 동안에는 단일 파일 모드가 사이클을 짧게 유지해 줍니다.
프로그램에 인자 전달하기
main 안의 String[] args를 기억하시나요? 이것들은 명령줄 인자입니다. java 명령에서 클래스 이름 뒤에 입력하는 것은 무엇이든 그 배열에 들어갑니다.
자신의 컴퓨터에서는 이렇게 인자를 넘겨줍니다.
java Main hello world
First argument: hello
Total arguments: 2
위의 에디터는 터미널 인자를 받을 수 없어서 "인자 없음" 분기를 실행합니다. 하지만 같은 코드가 두 경우를 모두 처리합니다. 이것이 파일이나 사용자 입력에 도달하기 훨씬 전에, Java 프로그램이 실행 시점에 전달된 입력을 읽는 방식입니다.
컴파일러의 오류 메시지 읽기
javac가 코드를 거부하면 파일, 줄, 그리고 무엇이 잘못되었는지를 알려줍니다. 이 메시지를 읽는 법을 배우는 것이 막힌 상황에서 벗어나는 길의 절반입니다. 고전적인 예가 있습니다. 빠진 세미콜론입니다.
public class Main {
public static void main(String[] args) {
System.out.println("Oops") // no semicolon
}
}
Main.java:3: error: ';' expected
System.out.println("Oops")
^
1 error
캐럿(^)은 컴파일러가 무언가를 기대했던 위치를 가리킵니다. Main.java:3은 파일과 줄 번호입니다. 추측하고 싶은 충동을 누르세요. 그 줄을 읽고, 메시지가 지목한 한 가지를 고친 다음, 다시 컴파일하세요. 컴파일 오류는 아직 아무것도 실행되지 않았다는 뜻이고, 런타임 오류(생김새가 다릅니다)는 프로그램이 시작된 후에 실패했다는 뜻입니다.
동작 확인용 프로그램
이것을 에디터에서 실행하거나, Main.java로 저장해 자신의 컴퓨터에서 javac Main.java 다음 java Main을 실행하세요. 세 줄이 모두 보이면 여러분의 툴체인이 처음부터 끝까지 작동하는 것입니다.
여기에는 곧 제대로 만나게 될 세 가지가 등장합니다. int 변수, +를 이용한 문자열 연결, 그리고 .length를 가진 배열입니다. 지금은 프로그램이 컴파일되고 출력되는 것으로 충분합니다.
다음: Java 문법
프로그램을 몇 개 실행해 보았지만, 우리는 문장 부호는 대충 넘어갔습니다. 중괄호, 세미콜론, public static void main, 그리고 각 줄이 왜 그런 모습인지 말이죠. 다음 페이지에서는 구조가 더 이상 상용구처럼 느껴지지 않고 의미를 갖기 시작하도록 Java의 문법을 한 조각씩 분해합니다.
자주 묻는 질문
Java 프로그램은 어떻게 실행하나요?
코드를 public 클래스와 같은 이름의 파일(예: Main.java)에 저장하고, 그 폴더에서 터미널을 연 다음 두 개의 명령을 실행하세요. 바이트코드로 컴파일하는 javac Main.java, 그다음 실행하는 java Main입니다. JDK 11 이상에서는 단일 파일의 경우 첫 단계를 건너뛰고 java Main.java를 바로 실행할 수 있습니다.
javac와 java의 차이는 무엇인가요?
javac는 컴파일러입니다. .java 소스를 읽어 바이트코드로 가득 찬 .class 파일을 생성합니다. java는 런처입니다. JVM을 시작하고 .class 파일 안의 바이트코드를 실행합니다. javac로 한 번 컴파일한 다음, java로 원하는 만큼 여러 번 실행하면 됩니다.
왜 "could not find or load main class" 오류가 나나요?
거의 항상 java에 잘못된 이름을 전달했기 때문입니다. 파일 이름이 아니라 클래스 이름을 사용하세요. java Main이 맞으며, java Main.class나 java Main.java가 아닙니다. 또한 Main.class가 들어 있는 폴더에 있는지, 그리고 클래스 이름이 대소문자를 포함해 파일 이름과 정확히 일치하는지 확인하세요.