코딩테스트에서의 클래스와 메서드에 대해 간단히 정리했습니다.
1️⃣ 클래스와 객체
클래스 정의와 필드/생성자/메서드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 파일: Main.java (보통 public class Main, public 클래스는 하나만 가능)
class Point { // (public 생략 → 같은 파일에서만 사용 가능)
int x; // 필드(인스턴스 변수)
int y;
static int val = 10; // 클래스(static) 변수
// 기본 생성자 (생략 가능)
Point() {}
// 생성자 오버로딩
Point(int x, int y) {
this.x = x;
this.y = y;
}
// 인스턴스 메서드 (객체 상태 사용)
int manhattan() {
return Math.abs(x) + Math.abs(y);
}
// static 메서드 (객체 상태 없이 동작)
static int distanceSquared(Point a, Point b) {
int dx = a.x - b.x, dy = a.y - b.y;
return dx*dx + dy*dy;
}
}
객체 생성/사용
1
2
3
Point p = new Point(3, -4); // 생성
int m = p.manhattan(); // 인스턴스 메서드 호출
int d2 = Point.distanceSquared(p, new Point(0, 0)); // static 메서드는 클래스명.메서드
2️⃣ 함수(메서드)
메서드 정의
1
2
3
4
5
6
7
8
9
반환타입 메서드이름(매개변수들) {
// 로직
return 값; // void면 생략
}
static int gcd(int a, int b) { // 인스턴스 변수를 사용하지 않음 → static 적합
while (b != 0) { int t = a % b; a = b; b = t; }
return a;
}
메서드 오버로딩
1
2
static int sum(int a, int b) { return a + b; }
static long sum(long a, long b) { return a + b; } // 같은 이름, 파라미터 다름
가변인자
1
2
3
4
static int sumAll(int... nums) {
int s = 0; for (int v : nums) s += v;
return s;
}
3️⃣ static vs non-static
구분 | static (정적) | non-static (인스턴스) |
---|---|---|
소속 | 클래스에 소속 (전체 공유) | 객체(인스턴스)에 소속 (객체마다 별도) |
수명 | 클래스가 메모리에 올라갈 때 → 종료/클래스 언로드 시까지 | new 할 때마다 생성 → 인스턴스가 소멸할 때 (가비지 컬렉터가 회수) |
메모리 | 메소드(Static) 영역 | 힙 영역 |
접근 방식 | 클래스명.필드/메서드() | 참조변수.필드/메서드() (객체 필요) |
인스턴스 멤버 접근 | 불가 (this 없음, 인스턴트보다 static이 먼저 생성됨) | 가능 |
쓰임새 | 유틸성 함수, 전역 상수/공유 상태, 캐시 | 객체 상태 기반 동작, 다형성/캡슐화 |
오버라이딩 | 불가(정확히는 숨김/hiding) | 가능 (다형성) |
스레드 안전 | 공유하므로 주의 필요 | 객체별로 분리되어 상대적으로 안전 |
4️⃣ Main과 보조 클래스 배치
자바 규칙: 한 파일엔 public 최상위 클래스 1개만 허용 (파일명과 동일)
코테에선 보통 public class Main만 public이고, 그 외 클래스는 두 방법 모두 가능
(A) 최상위(Top-Level) 비공개 클래스로 Main 바깥에 배치
- 장점 : 깔끔, 일반 자바와 동일한 패턴
- 주의 : public 붙이면 컴파일 에러
1
2
3
4
5
6
7
8
public class Main {
public static void main(String[] args) {}
}
class Node { // public 없음
int v, w;
Node(int v, int w){ this.v=v; this.w=w; }
}
(B) Main 안에 중첩 클래스로 배치
- static class는 바깥 인스턴스 없이 독립적이라 사용 간단
- non-static inner class는 new Main().new Node()처럼 외부 인스턴스 필요 → 비추천
1
2
3
4
5
6
7
8
9
10
11
public class Main {
static class Node { // 권장
int v, w;
Node(int v, int w){ this.v=v; this.w=w; }
}
// non-static class Node { ... } // 비권장: 바깥 Main 인스턴스가 필요해져 번거로움
public static void main(String[] args) {
Node n = new Node(1, 3); // static 중첩 클래스는 바로 생성 가능
}
}
5️⃣ 코딩테스트에선?
- 상태를 안 쓰는 함수(예: 파싱, 수학, 그래프 유틸) → static 메서드가 간단/빠름
- 공유 상수/공통 배열(예: 방향 배열) → static final 상수로 선언
- 문제 모델링용 구조체/노드 → 보통 static 중첩 클래스로 정의
- 단, 전역 static 가변 상태는 테스트 케이스 반복 시 초기화 누락 버그 주의 (init() 함수)
지역 변수 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import java.io.*;
import java.util.*;
public class Main {
// ✅ 전역 상수/배열 (예: 방향 벡터)
static final int[] DX = {-1, 1, 0, 0};
static final int[] DY = {0, 0, -1, 1};
// ✅ 자료구조용 static class
static class Node implements Comparable<Node> {
int x, y, dist;
Node(int x, int y, int dist) {
this.x = x; this.y = y; this.dist = dist;
}
@Override
public int compareTo(Node o) {
return Integer.compare(this.dist, o.dist); // 우선순위 큐용
}
}
// ✅ 유틸 함수 예시
static int gcd(int a, int b) {
while (b != 0) { int t = a % b; a = b; b = t; }
return a;
}
static int lcm(int a, int b) {
return a / gcd(a, b) * b;
}
static boolean inRange(int x, int y, int n, int m) {
return (0 <= x && x < n && 0 <= y && y < m);
}
// ✅ 문제 풀이 함수 (호출 시 지역 변수 자동 초기화)
static void solve(BufferedReader br, StringBuilder sb) throws Exception {
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
// 2차원 배열 입력
int[][] grid = new int[n][m];
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < m; j++) {
grid[i][j] = Integer.parseInt(st.nextToken());
}
}
// BFS 예시
boolean[][] visited = new boolean[n][m];
Queue<int[]> q = new ArrayDeque<>();
q.offer(new int[]{0, 0});
visited[0][0] = true;
while (!q.isEmpty()) {
int[] cur = q.poll();
int x = cur[0], y = cur[1];
for (int d = 0; d < 4; d++) {
int nx = x + DX[d], ny = y + DY[d];
if (inRange(nx, ny, n, m) && !visited[nx][ny] && grid[nx][ny] == 0) {
visited[nx][ny] = true;
q.offer(new int[]{nx, ny});
}
}
}
sb.append("탐색 완료\n");
}
// ✅ main: 입출력 전담
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int T = 1;
// 여러 테스트케이스 지원 시: T = Integer.parseInt(br.readLine());
for (int t = 0; t < T; t++) {
solve(br, sb);
}
System.out.print(sb);
}
}
전역 변수 사용 (init 함수)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import java.io.*;
import java.util.*;
public class Main {
// 전역 상수
static final int[] DX = {-1, 1, 0, 0};
static final int[] DY = {0, 0, -1, 1};
// 전역 변수 (테스트케이스마다 초기화 필요할 때 사용)
static int n, m;
static int[][] grid;
static boolean[][] visited;
// ✅ init 함수
static void init(int n, int m) {
// 전역 변수 초기화
Main.n = n;
Main.m = m;
grid = new int[n][m];
visited = new boolean[n][m];
}
// ✅ 유틸 함수
static boolean inRange(int x, int y) {
return (0 <= x && x < n && 0 <= y && y < m);
}
// ✅ 문제 풀이 함수
static void solve(BufferedReader br, StringBuilder sb) throws Exception {
StringTokenizer st = new StringTokenizer(br.readLine());
n = Integer.parseInt(st.nextToken());
m = Integer.parseInt(st.nextToken());
// init으로 초기화
init(n, m);
// grid 입력
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < m; j++) {
grid[i][j] = Integer.parseInt(st.nextToken());
}
}
// BFS 예시
Queue<int[]> q = new ArrayDeque<>();
q.offer(new int[]{0, 0});
visited[0][0] = true;
while (!q.isEmpty()) {
int[] cur = q.poll();
int x = cur[0], y = cur[1];
for (int d = 0; d < 4; d++) {
int nx = x + DX[d], ny = y + DY[d];
if (inRange(nx, ny) && !visited[nx][ny] && grid[nx][ny] == 0) {
visited[nx][ny] = true;
q.offer(new int[]{nx, ny});
}
}
}
sb.append("탐색 완료\n");
}
// ✅ main 함수
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int T = 1;
// 여러 테스트 케이스
// T = Integer.parseInt(br.readLine());
for (int t = 0; t < T; t++) {
solve(br, sb);
}
System.out.print(sb);
}
}