페이지 불러오는 중
이 시리즈의 첫 장입니다. 클래스 하나, 메서드 하나를 짜서 실행하기까지 소스 파일이 어떤 순서로 구성되는지, javac와 java가 각각 무엇을 하는지, 그리고 파일명·문장·주석 같은 겉보기 규칙이 왜 그렇게 정해졌는지를 다룹니다.
.java)은 ① 패키지 선언 → ② import 선언 → ③ 타입 선언(class/interface/enum/record) 순서로 작성합니다. 이 순서는 문법상 강제입니다.public 타입은 최대 하나만 둘 수 있고, 그 타입의 이름은 파일명과 정확히 일치해야 합니다(대소문자 포함).public static void main(String[] args) 메서드입니다. 이름·제어자·매개변수 형태가 약속돼 있어 JVM이 이 시그니처를 찾아 호출합니다.javac가 .java → .class(바이트코드)로 컴파일하고, java가 그 .class를 JVM에 올려 실행합니다.;)으로 끝나고, 블록은 중괄호({})로 묶으며, 줄바꿈·들여쓰기는 문법적 의미가 없습니다(파이썬과 반대).이 장은 세 계층 중 주로 ①을 다루고, 컴파일·실행을 설명할 때만 ②③을 언급합니다.
.class 바이트코드가 JVM에서 해석·JIT 컴파일되는 하이브리드 실행 구조 자체는 Java는 컴파일 언어인가 인터프리터 언어인가 - 하이브리드 실행 구조 분석에서 다룹니다.
Hello.java:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, Java");
}
}public class Hello — Hello라는 이름의 클래스를 선언합니다. public이므로 파일명은 반드시 Hello.java입니다.public static void main(String[] args) — 실행 진입점. 3절에서 각 키워드의 의미를 봅니다.System.out.println(...) — 표준 출력 스트림(System.out)에 문자열과 줄바꿈을 씁니다.완전한 형태의 소스 파일은 다음 세 부분으로 이뤄지며, 이 순서를 지켜야 컴파일됩니다.
// ① 패키지 선언 — 있다면 반드시 첫 줄(주석·공백 제외)
package com.example.app;
// ② import 선언 — 패키지 선언 다음, 타입 선언 앞
import java.util.List;
import java.util.ArrayList;
import static java.lang.Math.max; // static import: 정적 멤버를 이름만으로 사용
// ③ 타입 선언 — class / interface / enum / record / @interface
public class App {
// 필드, 메서드, 중첩 타입 ...
}package)com.example.project)을 씁니다. 8절에서 디렉터리 구조와의 관계를 봅니다.import 없이도 완전한 이름(fully-qualified name) java.util.List를 매번 써서 쓸 수는 있습니다.java.lang 패키지(String, System, Integer, Math 등)는 자동 import되므로 선언이 필요 없습니다.import java.util.*;는 해당 패키지의 타입들을 한꺼번에 가져오는 on-demand import입니다. 런타임 성능에는 영향이 없지만, 어느 패키지에서 온 타입인지 모호해질 수 있어 명시적 import를 선호하는 편입니다.import static ...는 정적 멤버(정적 메서드·상수)를 클래스명 없이 쓰게 합니다. Math.max(a, b) → max(a, b). 남용하면 출처가 흐려집니다.class, interface, enum, record, 애너테이션 타입(@interface) 중 하나 이상.public은 최대 하나이며, 나머지는 접근 제어자 없는(package-private) 타입이어야 합니다.public 최상위 타입이 있으면 파일명 = 그 타입명 + .java. public class Hello는 Hello.java에만 있을 수 있습니다. 어기면 컴파일 에러입니다.error: class Hello is public, should be declared in a file named Hello.javapublic 타입이 없다면 파일명은 자유지만, 관례상 "그 파일의 대표 타입명"을 씁니다.OrderService), 패키지는 소문자, 상수는 UPPER_SNAKE_CASE입니다. 문법이 강제하진 않지만 표준 라이브러리와 도구가 이 관례를 전제합니다.main 메서드 — 시그니처의 의미public static void main(String[] args) { ... }String... args(가변 인자)로 써도 됩니다. 배열과 동등하게 취급됩니다.args는 프로그램 이름을 포함하지 않습니다. C의 argv[0]와 다릅니다.args가 관례일 뿐 무엇이든 됩니다. 타입과 형태(String[])만 맞으면 됩니다.main, 매개변수 없는 main, 클래스 선언 없는 소스 파일을 허용하는 간이 진입점이 프리뷰(JEP 445)로 들어왔습니다. 정식 문법이 아니므로 이 시리즈는 표준 형태만 씁니다.$ javac Hello.java # Hello.java -> Hello.class (바이트코드)
$ java Hello # Hello.class를 JVM에 로딩하고 main() 호출 (.class 확장자는 붙이지 않음)
Hello, Javajavac는 파일당 .class를 하나씩 만듭니다. 중첩 타입은 Outer$Inner.class처럼 별도 파일이 됩니다.java의 인자는 파일 경로가 아니라 클래스의 완전한 이름입니다. 패키지가 있으면 java com.example.app.App이고, 이때 현재 디렉터리 기준으로 com/example/app/App.class가 있어야 합니다.cp(classpath) 옵션으로 .class와 라이브러리(.jar)를 찾을 위치를 지정합니다.$ java Hello.java # 컴파일 결과를 디스크에 남기지 않고 메모리에서 바로 실행.java를 직접 넘기면 내부적으로 컴파일 후 실행합니다.-class-path로 해결 가능).;으로 끝납니다. 줄바꿈이 아니라 세미콜론이 경계입니다.int a = 1; int b = 2; // 한 줄에 두 문장 — 합법
int c =
a + b; // 여러 줄에 걸친 한 문장 — 합법{ ... }로 묶은 문장 묶음이며, 하나의 문장처럼 취급됩니다. if, for, 메서드 본문 등에 씁니다.; 하나)도 문법적으로 유효하지만 대개 실수입니다. while (cond);처럼 의도치 않은 무한 루프를 만들 수 있습니다.// 한 줄 주석
/*
여러 줄 주석. 중첩되지 않음.
*/
/**
* Javadoc 주석. 타입·메서드·필드 선언 바로 위에 두면
* javadoc 도구가 API 문서(HTML)로 추출한다.
*
* @param name 인사할 대상
* @return 완성된 인사말
*/
public String greet(String name) { ... }//, /* */는 컴파일 시 제거됩니다./** */(Javadoc)는 선언 바로 앞에 있을 때만 의미가 있으며, @param·@return·@throws·{@link ...} 같은 태그를 씁니다. IDE의 호버 도움말도 이 주석을 읽습니다..)은 디렉터리 계층에 대응합니다. package com.example.app;인 소스는 소스 루트 기준 com/example/app/ 아래에 있어야 하고, 컴파일된 .class도 같은 구조로 놓입니다.src/
└─ com/
└─ example/
└─ app/
├─ App.java // package com.example.app;
└─ OrderService.javapublic이 아닌 멤버는 같은 패키지에서만 보임)의 두 역할을 합니다. 접근 제어는 06장에서 자세히 다룹니다.src/main/java를 소스 루트로 약속하고 이 구조를 강제합니다
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.