1. 소개
프로그래밍을 막 시작할 때는 코드를 줄줄이 쓰기만 해도 다 잘 동작할 것처럼 느껴질 수 있습니다. 하지만 프로그램이 조금만 복잡해져도 머릿속이 금세 혼란스러워집니다. 무엇이 어디에 있고, 왜 그렇게 작성됐는지, 한 줄을 바꿔야 할 때 그 줄이 도대체 어디에 있는지 헷갈리기 쉽죠.
그래서 Java를 비롯한 대부분의 현대 언어는 언제든 프로그램이 무엇을 하는지 파악하고 필요한 부분을 빠르게 찾을 수 있도록, 코드를 ‘정리’하는 방법을 제공합니다.
회사를 떠올려 보세요. 인사팀, 회계팀, IT 부서, 물류팀 등등이 있죠. 각 부서에는 고유한 책임, 직원, 그리고 업무가 있습니다.
Java 프로그램에서도 이러한 ‘부서’ 하나가 바로 클래스입니다. 어떤 클래스는 사용자 작업을 담당하고, 다른 클래스는 계산을, 또 다른 클래스는 파일 작업을 맡습니다.
다음은 간단한 프로젝트의 전형적인 구조입니다:
- Main.java — 본사: 모든 것이 시작되는 “진입점”이 있습니다.
- User.java — 사용자의 데이터를 저장하고 처리하는 부서.
- Calculator.java — 계산 부서.
- FileHelper.java — 파일을 담당하는 전문가들.
아주 작은 프로그램에도 보통 최소 하나의 클래스가 있습니다. 큰 프로젝트는 수백 개의 클래스로 이루어진 하나의 “마천루”와 같습니다!
2. 클래스 — 도구 상자
Java는 객체 지향 언어이며, 그 안의 모든 것은 객체입니다. 객체의 유형을 클래스라고 부릅니다.
클래스는 우리가 필요한 것들(변수, 메서드)을 넣어 두는 상자나 캐비닛과 같습니다. “망치”라고 적힌 상자를 떠올려 보세요. 그 안에는 망치와 관련된 도구만 들어 있습니다.
class Example
{
// 우리의 도구: 변수와 메서드가 여기에 있습니다
}
클래스에는 보통 변수와 함수가 들어 있습니다. Java에서는 클래스의 변수를 필드, 클래스의 함수를 메서드라고 부르는 것이 일반적이지만, 의미는 같습니다.
우리의 첫 번째 코드를 기억하나요?
System.out.println("모두 안녕하세요!");
이 코드에는 클래스와 메서드가 모두 등장합니다:
- System — 시스템 작업을 위한 메서드를 담고 있는 클래스입니다.
- out — PrintStream 클래스의 객체인 필드입니다.
- println — 텍스트를 화면에 출력하는 PrintStream 클래스의 메서드입니다.
프로그래밍은 본질적으로 자신만의 클래스와 메서드를 작성하고, 다른 클래스의 메서드를 호출하는 일의 연속입니다.
3. main() 메서드의 축약 표기
사실 최소한의 Java 프로그램은 다음과 같습니다:
class Program
{
public static void main(String[] args)
{
// 여기에 코드를 작성하세요
}
}
이는 최소 하나의 클래스를 포함하며, 그 클래스에는 다음과 같은 헤더(시그니처)를 가진 main 메서드가 있어야 합니다:
public static void main(String[] args)
main 메서드의 시그니처
하지만 Java 21에서는 main() 메서드를 축약해서 작성할 수 있게 되었습니다. 이후 Java 컴파일러가 컴파일 시 이를 올바른 main() 메서드로 변환해 줍니다.
따라서 다음과 같은 코드를 작성할 때마다:
void main()
{
Scanner console = new Scanner(System.in);
int age = console.nextInt();
System.out.println("나이: " + age);
}
Java 컴파일러는 이를 다음 코드로 변환합니다:
class Program
{
public static void main(String[] args)
{
Scanner console = new Scanner(System.in);
int age = console.nextInt();
System.out.println("나이: " + age);
}
}
4. 전형적인 Java 클래스
전형적인 Java 클래스는 대략 다음과 같습니다:
public class MathUtils
{
// 정적 변수(클래스 전역)
static final double PI = 3.14159;
static int callCounter = 0;
// 정적 메서드: 원의 면적 계산
public static double circleArea(double radius)
{
callCounter++;
return PI * radius * radius;
}
// 정적 메서드: 두 수 중 최대값 찾기
public static int max(int a, int b)
{
callCounter++;
return (a > b) ? a : b;
}
// 정적 메서드: 메서드가 호출된 횟수 반환
public static int getCallCount()
{
return callCounter;
}
// main() 메서드에서 필드와 메서드를 사용
public static void main(String[] args)
{
System.out.println("원의 면적: " + circleArea(5));
System.out.println("최대값: " + max(10, 42));
System.out.println("메서드가 호출된 횟수: " + getCallCount() + "회");
}
}
여기에서 볼 수 있는 것들:
- 클래스에는 많은 메서드가 있을 수 있습니다.
- 클래스에는 자체 변수(필드)가 있을 수 있습니다.
- 클래스의 메서드들은 서로를 호출할 수 있습니다.
- 클래스의 메서드들은 공용 변수에 접근할 수 있습니다.
- 각 메서드 앞에는 public static 같은 동일한 “마법의 단어”가 있습니다.
- 각 메서드 앞에는 int, double, void와 같은 반환 타입이 있습니다.
아래에서 이러한 모든 세부 사항을 살펴보겠습니다.
5. 클래스의 필드(변수) — 클래스의 기억 공간
클래스 내부에는 흔히 필드(fields)라고 부르는 변수를 저장할 수 있습니다. 이는 데이터를 담아 두는 메모리 칸과 같습니다.
public class MathUtils
{
// 정적 변수(클래스 전역)
static final double PI = 3.14159;
static int callCounter = 0;
}
필드는 서로 다른 메서드 호출 사이에서도 클래스로 하여금 무언가를 기억하게 해 줍니다. 예를 들어, 버튼을 몇 번 눌렀는지, 혹은 메서드가 몇 번 호출되었는지(callCounter)를 셀 수 있습니다. 필드의 개수에는 제한이 없습니다.
6. 메서드 — 클래스가 수행할 수 있는 동작
메서드는 클래스의 ‘능력’입니다. 클래스를 직원에 비유하면, 메서드는 그 직원이 할 수 있는 기술입니다.
예를 들어, 메서드는 다음을 할 수 있습니다:
- 화면에 무언가를 출력한다,
- 두 수의 합을 계산한다,
- 입력 데이터를 받아 결과를 반환한다.
메서드의 구성:
- 반환 타입(예: void — 아무것도 반환하지 않을 때, 또는 int — 숫자를 반환할 때).
- 메서드 이름(예: circleArea).
- 괄호 안의 매개변수(없을 수도 있음).
- 중괄호 { ... } — 메서드 본문.
public class MathUtils
{
// 정적 메서드: 원의 면적 계산
public static double circleArea(double radius)
{
callCounter++;
return PI * radius * radius;
}
// 정적 메서드: 두 수 중 최대값 찾기
public static int max(int a, int b)
{
callCounter++;
return (a > b) ? a : b;
}
}
메서드와 관련된 자세한 내용은 곧 이어지는 강의에서 다룹니다.
7. 접근 제한자
Java에서는 어떤 클래스의 어떤 부분을 다른 클래스에서 접근할 수 있는지, 그리고 내부에서만 사용하게 할 것인지를 지정할 수 있습니다. 이를 위해 접근 제한자를 사용합니다.
- public — 모두에게 보임. 이런 메서드와 필드는 프로그램 어디서든 사용할 수 있습니다.
- private — 해당 클래스 내부에서만 보임. 외부에 공개하지 않을 내부 구현 세부 사항입니다. 마치 존재하지 않는 것처럼 취급됩니다.
public class MathUtils
{
// 정적 변수(클래스 전역)
private static final double PI = 3.14159;
private static int callCounter = 0;
// 정적 메서드: 원의 면적 계산
public static double circleArea(double radius)
{
callCounter++;
return PI * radius * radius;
}
}
황금률: 필드는 보통 private로 두고, 외부에는 필요한 메서드만 public으로 공개합니다. 가시성(접근 수준)에 대한 자세한 내용은 이어지는 강의에서 다룹니다.
8. static 메서드와 변수
보통 Java에서 클래스는 객체를 생성하기 위한 설계도로 사용됩니다. 클래스를 작성한 뒤 키워드 new로 그 클래스의 객체를 만들고, 그 다음에야 객체의 메서드를 호출합니다.
하지만 두 번째 방식도 있습니다. 클래스 자체를 전역 변수와 메서드의 컨테이너로 사용하는 것입니다. 이를 위해 키워드 static을 사용합니다. 이는 메서드와 필드가 클래스의 인스턴스가 아니라 클래스 자체에 귀속된다는 뜻입니다. 따라서 객체를 생성하지 않고도 이를 호출할 수 있습니다.
사실 여러분은 이런 메서드를 늘 사용하고 있습니다:
Integer.parseInt(); // Integer 클래스의 정적 메서드 parseInt()
String.valueOf(); // String 클래스의 정적 메서드 valueOf()
Double.isInfinite(); // Double 클래스의 정적 메서드 isInfinite()
Arrays.copy(); // Arrays 클래스의 정적 메서드 copy()
메서드 main()은 항상 정적으로 선언됩니다. 여러분의 Java 프로그램은 이 메서드에서 시작되며, 어떤 객체도 생성되기 전에 실행되기 때문입니다.
9. IDEA 팁
main() 메서드의 전체 시그니처는 다음과 같습니다:
public static void main(String[] args)
IntelliJ IDEA에서 매번 이를 직접 입력하지 않도록 단축키가 준비되어 있습니다. psvm을 입력하고 Tab 키를 누르면, IntelliJ IDEA가 다음 코드를 자동으로 작성해 줍니다:
public static void main(String[] args)
{
}
외우기도 쉽습니다 — psvm — public static void main의 머릿글자입니다.
GO TO FULL VERSION