ch5_배열

통합 문서입니다.


1. 메소드와 스코프

메소드와 스코프

학습 목표


1. 왜 메소드가 중요한가

메소드는 단순 문법이 아니라, 코드 구조를 결정하는 핵심 단위다.

  1. 중복 제거
  2. 책임 분리
  3. 테스트 용이성 향상
  4. 변경 파급 범위 축소

좋은 메소드의 기준:


2. 메소드 선언 구조

[접근제어자] [static 여부] 반환타입 메소드명(매개변수 목록) {
    // 본문
}

예:

public static int add(int a, int b) {
    return a + b;
}

구성 요소 의미:


3. 호출 스택과 실행 흐름

메소드를 호출하면 JVM은 새로운 스택 프레임을 만든다.

  1. 매개변수/지역변수 저장 공간 생성
  2. 메소드 본문 실행
  3. return 시 프레임 제거
  4. 호출 지점으로 복귀

메소드 호출과 스코프

실무 포인트:


4. 매개변수 전달: Java는 항상 Pass-by-Value

Java는 항상 값 복사로 인자를 전달한다.

4.1 primitive 전달

static void increase(int x) {
    x++;
}

int n = 10;
increase(n);
System.out.println(n); // 10

n의 값이 복사되어 x로 들어가므로 원본은 바뀌지 않는다.

4.2 reference 전달

static void rename(User u) {
    u.setName("Lee");
}

참조값 자체가 복사된다.
복사된 참조가 같은 객체를 가리키므로 객체 내부 상태는 바뀔 수 있다.

하지만 메소드 안에서 참조를 재대입해도 호출자 변수가 바뀌진 않는다.


5. 반환값 설계

5.1 void vs 값 반환

5.2 다중 결과가 필요할 때

  1. 작은 전용 클래스/레코드 반환
  2. 컬렉션 반환
  3. 출력 파라미터 패턴 지양(가독성/안정성 저하)

5.3 조기 반환(guard clause)

public int divide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("b must not be zero");
    }
    return a / b;
}

조건 예외를 빨리 반환하면 중첩을 줄일 수 있다.


6. 메소드 오버로딩(Overloading)

같은 이름의 메소드를 매개변수 시그니처(개수/타입/순서)로 구분해 여러 개 정의할 수 있다.

int sum(int a, int b) { ... }
int sum(int a, int b, int c) { ... }
double sum(double a, double b) { ... }

주의:


7. 스코프(scope)와 생명주기(lifetime)

스코프는 “어디서 보이는가”, 생명주기는 “언제까지 살아있는가”다.

7.1 지역 변수

int x;
// System.out.println(x); // 컴파일 오류(초기화 전 사용)

7.2 매개변수

7.3 필드(인스턴스 변수)

7.4 static 필드


8. 변수 가림(Shadowing)

지역 변수/매개변수가 필드와 같은 이름을 가지면 필드를 가릴 수 있다.

class User {
    private String name;

    void setName(String name) {
        this.name = name;
    }
}

this.name으로 필드를 명시하지 않으면 의도 혼동이 발생한다.


9. 재귀 메소드와 종료 조건

재귀는 자기 자신을 호출하는 방식이다.

static int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

반드시 종료 조건(base case)이 필요하다.
종료 조건이 없으면 StackOverflowError가 발생할 수 있다.


10. 실무에서 자주 하는 실수

  1. 메소드 하나에 비즈니스 로직, 검증, 출력을 모두 넣음
  2. 이름이 모호한 메소드(doProcess, handleData)
  3. 매개변수가 너무 많아 호출부 가독성이 떨어짐
  4. 메소드가 외부 상태를 과도하게 변경(부작용 증가)
  5. 스코프를 넓게 잡아 디버깅 난이도 상승

11. 메소드 설계 체크리스트

  1. 이름이 동작을 명확히 설명하는가?
  2. 입력/출력이 최소한으로 설계되었는가?
  3. 예외 조건을 초기에 배제했는가?
  4. 부작용(상태 변경)이 필요한가?
  5. 단위 테스트를 작성하기 쉬운가?

12. 정리


2. 배열

배열 (Array)

학습 목표


1. 배열의 본질

배열은 같은 타입의 데이터를 고정 길이로 저장하는 참조형 객체다.

int[] scores = new int[5];

핵심 특성:

배열 인덱스 구조


2. 선언, 생성, 초기화

2.1 선언과 생성 분리

int[] arr;
arr = new int[3];

2.2 선언+생성

int[] arr = new int[3];

2.3 리터럴 초기화

int[] arr = {10, 20, 30};

주의:


3. 배열 기본값

기본값 자동 초기화는 “배열 요소”에만 해당한다.
지역 변수 자체는 자동 초기화되지 않는다.


4. 순회 패턴

4.1 인덱스 순회

for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

장점:

4.2 향상된 for

for (int value : arr) {
    System.out.println(value);
}

장점:

제약:


5. 다차원 배열

Java의 2차원 배열은 “배열의 배열”이다.

int[][] board = new int[3][4];

가변 행(jagged array)도 가능:

int[][] jagged = {
    {1, 2},
    {3, 4, 5},
    {6}
};

각 행 길이가 다를 수 있으므로 내부 루프에서 arr[i].length를 사용해야 한다.


6. 배열 복사: 참조 복사 vs 실제 복사

6.1 참조 복사

int[] a = {1, 2, 3};
int[] b = a;
b[0] = 99;
System.out.println(a[0]); // 99

6.2 실제 복사

int[] c1 = a.clone();
int[] c2 = java.util.Arrays.copyOf(a, a.length);
int[] c3 = new int[a.length];
System.arraycopy(a, 0, c3, 0, a.length);

참조형 배열의 경우 요소 객체까지 깊게 복사되지 않을 수 있다.


7. 예외와 경계값 처리

7.1 ArrayIndexOutOfBoundsException

int[] arr = new int[3];
arr[3] = 1; // 범위 초과

배열 인덱스 유효 범위는 항상 0 <= i < length.

7.2 NullPointerException

int[] arr = null;
System.out.println(arr.length); // NPE

실무에서는 입력 배열 null 가능성을 먼저 검사한다.


8. Arrays 유틸리티 실전

자주 쓰는 API:

Arrays.toString(arr);
Arrays.sort(arr);
Arrays.binarySearch(arr, key);
Arrays.equals(a, b);
Arrays.fill(arr, 0);

binarySearch는 정렬된 배열 전제가 필요하다.


9. 알고리즘 관점 기초

배열 연산의 시간복잡도:

배열은 읽기와 인덱스 접근이 빠르며,
삽입/삭제가 많은 경우는 컬렉션(List) 검토가 필요하다.


10. 실무 패턴

  1. 입력 데이터 검증: null, length, 범위
  2. 불변 보장이 필요하면 방어적 복사
  3. 대량 반복 처리 시 인덱스 계산/분기 최소화
  4. 매직 넘버 대신 상수 사용 (MAX_SIZE)
  5. 테스트는 빈 배열, 1개, 경계값 배열 포함

11. 정리


3. 문제

문제

ch5 범위(메소드/스코프/배열) 문제입니다.


A. 메소드 기초

  1. 두 정수의 합/차/곱/몫을 각각 반환하는 메소드를 작성하시오.
  2. 문자열 길이가 8 이상이면 true를 반환하는 메소드를 작성하시오.
  3. 배열 평균을 계산하는 메소드를 작성하시오 (double 반환).
  4. 매개변수 검증(예: null, 길이 0) 후 계산하는 메소드로 개선하시오.

B. 스코프/호출 스택

  1. 지역 변수와 필드의 이름이 같은 경우 this를 사용해 구분하는 예제를 작성하시오.
  2. 블록 내부에서 선언한 변수를 블록 밖에서 사용하려 할 때 발생하는 컴파일 오류를 재현하시오.
  3. 재귀 팩토리얼 메소드를 작성하고 종료 조건이 없을 때 어떤 문제가 생기는지 설명하시오.
  4. 메소드 호출 순서(메인 -> A -> B)를 출력해 호출 스택 흐름을 확인하시오.

C. 배열 기초

  1. 정수 배열의 최댓값, 최솟값, 합계, 평균을 구하시오.
  2. 배열을 역순으로 출력하시오.
  3. 배열 요소 중 짝수 개수와 홀수 개수를 구하시오.
  4. 배열에서 특정 값의 첫 인덱스를 찾는 메소드를 작성하시오(없으면 -1).

D. 배열 응용

  1. 1~45 사이 중복 없는 로또 번호 6개를 생성하시오.
  2. 2차원 배열의 행별 합계와 전체 합계를 출력하시오.
  3. 가변 행(jagged) 2차원 배열을 만들고 모든 값을 순회하시오.
  4. 배열 정렬 후 이진 탐색으로 특정 값을 찾으시오.

E. 복사/참조 문제

  1. b = aa.clone() 결과 차이를 코드로 비교하시오.
  2. Arrays.copyOf, System.arraycopy를 각각 사용해 복사하시오.
  3. 참조형 배열(객체 배열)에서 얕은 복사가 만드는 문제를 예제로 설명하시오.
  4. 방어적 복사가 필요한 시나리오(생성자/게터)를 구현하시오.

F. 챌린지

  1. 학생 성적 배열을 받아 등급 분포(A/B/C/D/F)를 계산하는 메소드를 작성하시오.
  2. 회문(palindrome)인지 검사하는 문자열 메소드를 작성하시오(문자 배열 활용).
  3. 행렬 덧셈 메소드를 작성하시오(차원 검증 포함).

제출 체크리스트

  1. 메소드 이름이 역할을 정확히 설명하는가?
  2. null/길이 0/경계 인덱스 검증을 했는가?
  3. 참조 복사와 실제 복사를 구분했는가?
  4. 반복 로직에서 오프바이원 오류(<= length)가 없는가?