첫 프로그램 — main 을 찾는 이유
Article
지난 강의에서 Hello.java 를 컴파일하고 실행했다. 이번에는 그 파일을 조금 망가뜨려 보자. main 이라는 줄을 통째로 지운다.
public class Hello {
System.out.println("안녕");
}
컴파일부터 실패한다.
- Hello.java:2: error: <identifier> expected
- System.out.println("안녕");
- ^
그럼 이번엔 문법은 맞게 두고, 이름만 main 에서 start 로 바꿔보자.
public class Hello {
public static void start(String[] args) {
System.out.println("안녕");
}
}
컴파일은 통과한다. Hello.class 도 잘 생긴다. 그런데 실행하면 이렇다.
- 오류: Hello 클래스에서 기본 메소드를 찾을 수 없습니다. 다음 형식으로 기본 메소드를 정의하십시오.
- public static void main(String[] args)
- 또는 JavaFX 애플리케이션 클래스는 javafx.application.Application을(를) 확장해야 합니다.
JVM 은 main 이라는 이름을 찾는다. 못 찾으면 아무것도 하지 않고 그냥 나간다. 친절하게 원하는 생김새까지 그대로 적어준다. 마지막 줄은 JavaFX 라는 옛 기술 얘기라 지금은 무시해도 된다.
이번 강의는 저 한 줄을 뜯어본다. 그리고 솔직히 말해두면, 다섯 단어 중 두 개는 여기서 답하지 못한다.
JVM 은 어디서부터 읽어야 할지 모른다
.class 파일 안에는 여러 개의 코드 덩어리가 들어 있을 수 있다. JVM 이 그 파일을 열었을 때, 어느 덩어리부터 실행해야 할까?
정답은 "알 수 없다"이다. 그래서 약속을 정해뒀다.
main이라는 이름의, 이러이러하게 생긴 덩어리부터 실행한다.
이 약속의 정확한 생김새가 바로 그 한 줄이다.
public static void main(String[] args)
이걸 진입점이라고 부른다. 프로그램이 시작되는 지점이다.
- JVM 이 뜬다
- Hello.class 를 읽는다바이트코드
- main 을 찾는다이름도 생김새도 맞아야 한다
- 첫 줄부터 실행
다섯 단어
한 단어씩 본다. 지금 다 이해하지 못해도 괜찮다. 이 표는 이 시리즈의 지도이기도 하다.
| 단어 | 지금 알아둘 것 | 제대로 배우는 곳 |
|---|---|---|
public | 바깥에서 불러도 된다는 표시 | 12강 |
static | 객체를 만들지 않고도 부를 수 있다 | 13강 |
void | 일을 끝내고 돌려주는 값이 없다 | 10강 |
main | JVM 과 약속한 이름. 바꾸면 못 찾는다 | 여기 |
String[] args | 실행할 때 같이 넘긴 글자들 | 9강 |
public 과 static 은 지금 설명해봤자 와닿지 않는다. 둘 다 객체라는 개념을 알아야 이해되는 말인데, 객체는 11강에 가서야 나온다.
그래서 이렇게 하자. 지금은 "JVM 이 알아볼 수 있게 정해진 대로 적는 주문" 이라고 여기고 넘어간다. 13강에서 static 을 배우고 나면, 저 줄을 다시 보게 될 거다. 그때 "아, 그래서 main 이 static 이었구나" 하는 순간이 온다.
args 는 진짜로 쓸 수 있다
String[] args 를 장식품처럼 넘기기 쉬운데, 실제로 값이 들어온다. 실행할 때 뒤에 글자를 붙이면 된다.
public class Hello {
public static void main(String[] args) {
System.out.println("받은 개수: " + args.length);
System.out.println("첫 번째: " + args[0]);
}
}
java Hello 사과 바나나
- 받은 개수: 2
- 첫 번째: 사과
args 는 글자 여러 개를 담는 상자다. 이런 상자를 배열이라고 부르고, 9강에서 제대로 다룬다.
아무것도 안 붙이고 그냥 java Hello 를 치면 어떻게 될까? args.length 는 0 이 되고, args[0] 을 꺼내려는 순간 프로그램이 죽는다. 없는 걸 꺼내려 했기 때문이다. 이 죽음의 정체는 18강에서 밝힌다.
System.out.println 도 뜯어보면
우리가 매번 치는 이 긴 주문에도 구조가 있다.
- System
- out
- println("안녕")
- out
점(.)은 "안으로 들어간다" 는 뜻이다. System 안의 out 을 꺼내고, 그 out 에게 println 을 시킨다.
println 은 print line 의 준말이다. 줄바꿈 없이 찍고 싶으면 print 를 쓴다.
System.out.print("안");
System.out.print("녕");
System.out.println("!");
System.out.println("다음 줄");
- 안녕!
- 다음 줄
System 을 객체로 만든 적이 없는데 System.out 을 바로 꺼내 쓴 게 이상하지 않은가? 그게 가능한 이유가 static 이다. 13강까지 미뤄둔 그 단어다.
파일 이름은 마음대로 못 정한다
public 이 붙은 클래스는 파일 이름과 클래스 이름이 같아야 한다. 이건 컴파일러가 강제한다.
Hello.java 안에 public class Greeting 이라고 쓰면 이렇게 혼난다.
- Hello.java:1: error: class Greeting is public, should be declared in a file named Greeting.java
친절하게 파일 이름까지 알려준다. 자바 컴파일러는 인색하지만 정확하다.
그리고 클래스 이름은 관례상 대문자로 시작한다. Hello, Greeting, UserService. 단어가 여러 개면 각 단어의 첫 글자를 대문자로 붙여 쓴다.
두 종류의 에러를 구분한다
지금까지 본 에러는 사실 두 부류였다. 이 구분을 지금 잡아두면 앞으로 편하다.
컴파일 에러
javac 가 낸다 · .class 가 안 생긴다
error:로 시작하고 줄 번호를 알려준다- 클래스 이름과 파일 이름이 다를 때
- 세미콜론을 빠뜨렸을 때
- 프로그램이 실행조차 되지 않는다
실행 에러
JVM 이 낸다 · 돌다가 죽는다
오류:또는Exception으로 시작한다main을 못 찾을 때- 없는 값을 꺼내려 할 때
- 여기까지는 잘 돌았다는 뜻이기도 하다
에러 메시지를 보면 어느 쪽인지 먼저 가늠하는 습관을 들이자. 컴파일 에러는 내가 문법을 틀린 것이고, 실행 에러는 문법은 맞았는데 상황이 틀어진 것이다. 고치는 방법이 완전히 다르다.
주석 — 컴파일러가 무시하는 글자
코드 사이에 사람이 읽을 말을 끼워 넣을 수 있다. javac 는 이걸 통째로 건너뛴다.
public class Hello {
// 한 줄 주석. 이 줄 끝까지 무시된다.
/*
여러 줄 주석.
닫을 때까지 전부 무시된다.
*/
public static void main(String[] args) {
System.out.println("안녕"); // 코드 뒤에 붙여도 된다
}
}
주석은 바이트코드에 남지 않는다. 1강에서 본 .class 파일에는 흔적조차 없다.
정리하면
JVM 은 main 이라는 약속된 이름의 진입점을 찾아 거기서부터 실행한다.
- 01
진입점은
main이다이름을 바꾸면 컴파일은 되지만 실행이 안 된다. 키워드가 아니라 약속이다.
- 02
다섯 단어 중 셋은 나중에 답한다
public 은 12강, static 은 13강, void 는 10강. 지금은 정해진 대로 적는다.
public static void main(String[] args) - 03
args에는 실행할 때 붙인 글자가 들어온다배열이라는 상자다. 9강에서 다룬다.
java Hello 사과 바나나 - 04
public클래스 이름 = 파일 이름다르면 컴파일러가 파일 이름까지 알려주며 거절한다.
- 05
에러는 두 종류다
컴파일 에러는 javac 가, 실행 에러는 JVM 이 낸다.
Part 1 이 끝났다. 두 강의를 요약하면 이렇다. 우리가 쓴 텍스트를 javac 가 바이트코드로 옮기고, JVM 이 그 바이트코드에서 main 을 찾아 실행한다. 자바를 배우는 동안 이 그림은 바뀌지 않는다.
이제 껍데기를 벗어나 안으로 들어간다. 다음 강의부터는 main 의 중괄호 안쪽만 본다.
첫 순서는 변수다. 자바에서 값에 이름을 붙이려면 이름 앞에 한 가지를 더 적어야 한다. 그게 무엇이고, 컴파일러가 왜 그 귀찮은 걸 시키는지 본다.