클래스를 논리적으로 묶는 단위이자 파일 시스템의 디렉토리 구조와 1:1로 대응된다.
src/
├── com/
│ └── myapp/
│ ├── model/
│ │ └── User.java → package com.myapp.model;
│ └── service/
│ └── UserService.java → package com.myapp.service;
선언은 파일의 첫 번째 줄에 위치해야 한다.
package com.myapp.model; // 이 파일이 속한 패키지 선언
다른 패키지에 있는 클래스를 현재 파일에서 쓸 수 있도록 참조하는 선언이다.
import는 클래스를 "복사"하거나 "포함"하는 게 아니라, 컴파일러에게 풀 경로(FQCN)를 알려주는 것이다.
FQCN (Fully Qualified Class Name):
com.myapp.model.User처럼 패키지 + 클래스명 전체
파일 구조
src/
├── com/myapp/model/User.java
└── com/myapp/Main.java
User.java
package com.myapp.model;
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void printInfo() {
System.out.println("이름: " + name + ", 나이: " + age);
}
}
Main.java
package com.myapp;
import com.myapp.model.User; // User 클래스 import
public class Main {
public static void main(String[] args) {
User user = new User("워그레이몬", 25);
user.printInfo();
}
}
이름: 워그레이몬, 나이: 25
import를 쓰지 않으면 클래스를 쓸 때마다 풀 경로를 명시해야 한다.
package com.myapp;
public class Main {
public static void main(String[] args) {
// import 없이 FQCN으로 직접 참조
com.myapp.model.User user = new com.myapp.model.User("브이몬", 30);
user.printInfo();
}
}
이름: 브이몬, 나이: 30
import는 이 FQCN을 반복해서 쓰지 않기 위한 단축 선언이다. 동작 결과는 동일하다.
*)같은 패키지의 클래스를 여러 개 쓸 때 *로 한 번에 선언할 수 있다.
import com.myapp.model.*을 선언해도 컴파일러는 패키지 전체를 무조건 읽어들이는 게 아니다.
소스 코드에서 실제로 사용된 클래스 이름을 먼저 확인하고, 그 클래스가 * 패키지 안에 있는지 탐색한다.
컴파일 결과인 .class 파일에는 실제 사용된 클래스의 FQCN만 기록된다.
런타임에서도 JVM 클래스 로더는 참조가 발생하는 시점에만 해당 클래스를 로딩한다.
파일 구조
src/
└── com/myapp/model/
├── User.java
├── Product.java
└── Order.java
User.java
package com.myapp.model;
public class User {
static {
System.out.println("[클래스 로딩] User 로딩됨");
}
public void hello() {
System.out.println("User 사용");
}
}
Product.java
package com.myapp.model;
public class Product {
static {
System.out.println("[클래스 로딩] Product 로딩됨");
}
public void hello() {
System.out.println("Product 사용");
}
}
Order.java
package com.myapp.model;
public class Order {
static {
System.out.println("[클래스 로딩] Order 로딩됨");
}
public void hello() {
System.out.println("Order 사용");
}
}
static { }블록은 해당 클래스가 JVM에 로딩되는 순간 딱 한 번 실행된다.
로딩 시점을 눈으로 확인하기 위해 사용했다.
Main.java
package com.myapp;
import com.myapp.model.*; // model 패키지 전체 선언
public class Main {
public static void main(String[] args) {
System.out.println("=== main 시작 ===");
User user = new User(); // User만 실제 사용
user.hello();
// Product, Order는 import 선언됐지만 사용 안 함
}
}
=== main 시작 ===
[클래스 로딩] User 로딩됨
User 사용
//
//Source code recreated from a .class file by IntelliJ IDEA
//(powered by FernFlower decompiler)
//
package myapp;
import myapp.model.User;
public class Main {
public static void main(String[] args) {
System.out.println("=== main 시작 ===");
User user = new User();
user.hello();
}
}
Product와 Order는 import com.myapp.model.*에 포함되어 있었지만
실제로 사용되지 않았으므로 로딩 자체가 발생하지 않는다.
import com.myapp.* → com.myapp 패키지의 클래스만 포함
com.myapp.model.User 는 포함되지 않음
*는 해당 패키지의 클래스만 가져온다. 하위 패키지는 포함되지 않는다.
java.util.Date와 java.sql.Date는 이름이 같다.
이런 경우 둘 다 import할 수 없고, 하나는 반드시 FQCN으로 써야 한다.
package com.myapp;
import java.util.Date; // 이것만 import
public class Main {
public static void main(String[] args) {
Date utilDate = new Date(); // java.util.Date → import된 것 사용
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); // FQCN 사용
System.out.println("util.Date : " + utilDate);
System.out.println("sql.Date : " + sqlDate);
}
}
util.Date : Wed Apr 08 21:00:00 KST 2026
sql.Date : 2026-04-08
클래스의 static 멤버(필드, 메서드)를 클래스명 없이 바로 사용할 수 있다.
package com.myapp;
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import static java.lang.Math.pow;
public class Main {
public static void main(String[] args) {
double radius = 5.0;
// Math. 없이 바로 사용 가능
double area = PI * pow(radius, 2);
double diagonal = sqrt(pow(3, 2) + pow(4, 2));
System.out.println("원의 넓이: " + area);
System.out.println("빗변의 길이: " + diagonal);
}
}
원의 넓이: 78.53981633974483
빗변의 길이: 5.0
패키지를 선언하지 않으면 default package에 속한다.
// package 선언 없음 → default package
public class HelloWorld {
public static void main(String[] args) {
System.out.println("default package입니다.");
}
}
default package입니다.
default package에 있는 클래스는 다른 패키지에서 import할 수 없다.
src/
└── com/myapp/
├── Calculator.java
└── Main.java
Calculator.java
package com.myapp;
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
Main.java
package com.myapp;
// Calculator는 같은 패키지이므로 import 불필요
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println("합계: " + calc.add(10, 20));
}
}
합계: 30
| 항목 | 내용 |
|---|---|
package 위치 | 파일의 첫 번째 줄 (주석 제외) |
import 위치 | package 선언 다음, class 선언 이전 |
import A.* | A 패키지의 클래스만 포함, 하위 패키지 미포함 |
| 와일드카드 성능 | 컴파일/런타임 모두 사용된 클래스만 로딩, 성능 차이 없음 |
| 와일드카드 vs 단일 import | 성능은 동일, 차이는 가독성과 명시성 |
| import 생략 가능한 경우 | java.lang 패키지, 같은 패키지 내 클래스 |
| 이름 충돌 시 | 하나만 import하고 나머지는 FQCN으로 직접 사용 |
static import | static 멤버를 클래스명 없이 사용할 때 |