본문 바로가기

System·Math·Random 기본 도구

프로세스 환경, 수학 계산, 재현 가능한 의사 난수를 각각 작은 예제로 구분합니다.

표준 라이브러리는 자주 필요한 기능을 이미 구현해 제공합니다.

System은 현재 Java 프로세스의 입출력과 환경에, Math는 상태 없는 수치 계산에, Random은 의사 난수 순서에 접근합니다.

세 도구는 편리하지만 역할이 다르므로 한 객체처럼 묶어 생각하지 않습니다.

이전 문서에서 기본형과 래퍼를 배웠고, 이 문서가 끝나면 실행 환경의 값·결정적 계산·외부에서 주어지는 난수를 구분할 수 있습니다.

타입 자체를 조사하는 Class와 리플렉션은 ch28에서 처음부터 배웁니다.


System의 입출력과 속성

System.out은 일반 출력, System.err는 오류 출력, System.in은 표준 입력입니다.

System.getProperty는 현재 JVM의 시스템 속성을 읽습니다. 일부 속성은 실행 중에도 변경될 수 있으므로 모두 시작 시점에 고정된 값으로 보지는 않습니다.

src/SystemBasics.java — 정상 최소 예제
public class SystemBasics {
    public static void main(String[] args) {
        System.out.println("java=" + System.getProperty("java.version"));
        System.out.println("os=" + System.getProperty("os.name"));
        System.out.println("separator=" + System.lineSeparator().length());
        System.err.println("diagnostic=sample");
    }
}

Java 버전과 운영체제 이름은 컴퓨터마다 다릅니다.

프로그램의 정답으로 문자열 전체를 고정하지 말고 실제로 필요한 조건만 검사합니다.

업무 메서드 곳곳에서 전역 환경을 직접 읽으면 입력이 숨으므로, 시작점에서 읽어 필요한 객체에 값으로 전달하는 편이 좋습니다.


경과 시간 측정

System.nanoTime()은 두 시점 사이의 경과 시간을 재는 기준입니다.

달력 시각이나 파일에 저장할 현재 날짜로 해석하지 않습니다.

src/ElapsedTime.java
public class ElapsedTime {
    public static void main(String[] args) {
        long start = System.nanoTime();

        long total = 0;
        for (int number = 1; number <= 100_000; number++) {
            total += number;
        }

        long elapsed = System.nanoTime() - start;
        System.out.println("total=" + total);
        System.out.println("elapsed-nonnegative=" + (elapsed >= 0));
    }
}

경과 나노초의 실제 숫자는 실행마다 달라집니다.

짧은 코드 한 번의 시간으로 성능 결론을 내리면 JIT 준비, 운영체제 스케줄링, 다른 프로세스의 영향을 분리하지 못합니다.

정확한 벤치마크는 ch38에서 다룹니다.


Math의 결정적 계산

여기서 사용하는 Math.ceil, min, max, abs는 같은 입력에 같은 계산 결과를 냅니다. Math.random()처럼 난수 생성기를 사용하는 메서드까지 이 설명을 일반화하지는 않습니다.

src/MathBasics.java
public class MathBasics {
    public static void main(String[] args) {
        int postCount = 47;
        int pageSize = 25;

        int pages = (int) Math.ceil((double) postCount / pageSize);
        int bounded = Math.min(Math.max(postCount, 1), 600);
        int distance = Math.abs(postCount - 50);

        System.out.println("pages=" + pages);
        System.out.println("bounded=" + bounded);
        System.out.println("distance=" + distance);
    }
}
pages=2
bounded=47
distance=3

ceil은 올림, min·max는 작은 값과 큰 값을 선택하고 abs는 절댓값을 구합니다.

페이지 계산은 47 / 25 = 1.88을 먼저 얻고 ceil로 2를 선택합니다. (double)을 빼면 int 나눗셈이 먼저 1을 만들므로, 그 뒤 올림해도 1입니다. 소수 정보를 버리기 전에 실수로 변환해야 합니다. 이 예제의 페이지 크기는 양수입니다.

abs에도 자료형 경계가 있습니다. Math.abs(int)의 공식 계약에서 Integer.MIN_VALUE의 결과는 같은 음수입니다. 대응하는 양수가 int 범위에 없기 때문입니다. 그 값까지 거리로 받아야 한다면 먼저 long으로 바꾸고 Math.abs((long) value)를 계산합니다.

금액처럼 정확한 십진 계산이 필요한 값에는 부동소수점 double보다 BigDecimal 같은 별도 표현을 검토합니다.


Random과 시드

의사 난수 생성기는 내부 상태에서 다음 값을 계산합니다.

생성할 때 같은 시드를 주면 같은 구현에서 같은 순서를 다시 얻을 수 있어 예제를 재현하기 쉽습니다.

src/SeededPicker.java
import java.util.Random;

public class SeededPicker {
    public static void main(String[] args) {
        String[] categories = {"notice", "question", "free"};
        Random random = new Random(42);

        int first = random.nextInt(categories.length);
        int second = random.nextInt(categories.length);

        System.out.println("first=" + categories[first]);
        System.out.println("second=" + categories[second]);
    }
}

nextInt(bound)는 0 이상 bound 미만을 반환하므로 배열 인덱스로 사용할 수 있습니다.

배열 길이가 0이면 유효한 범위가 없으므로 호출 전에 입력을 확인해야 합니다.

위 실행의 첫 인덱스는 2라 free, 다음 인덱스는 0이라 notice입니다. 두 호출은 같은 Random 객체의 상태를 차례로 진행시킵니다. Random의 재현 계약은 같은 시드뿐 아니라 같은 메서드 호출 순서도 요구합니다. 중간에 호출을 하나 추가하면 뒤에서 읽는 위치가 달라지며, 매번 new Random(42)를 만들면 매번 첫 위치부터 시작합니다.

고정 시드는 디버깅과 예제 재현에 적합하지만 비밀번호 재설정 토큰이나 인증 코드를 만들기에는 예측 가능합니다.

보안 목적에는 SecureRandom을 사용하고 운영 정책에 맞는 엔트로피와 저장 방식을 정합니다.

계산 입력과 난수의 호출 상태를 구별한다

같은 Random(42)의 두 nextInt 호출에서 index 2→index 0으로 상태가 진행합니다. 독립 Math 예제를 고정한 채 동일 난수 객체의 호출 이력을 단계로 읽으며 상태 변화를 확인할 수 있습니다.

계산 입력과 난수의 호출 상태를 구별한다

MathBasics의 명시적 수치 입력은 pages2 bounded47 distance3을 결정한다. SeededPicker의 Random42는 같은 객체에 nextInt3를 두 번 호출하여 free 다음 notice를 고른다.

계산은 입력값으로, 난수는 시드와 호출 순서로 결정됩니다

MathBasics와 SeededPicker는 별도 프로그램입니다. 같은 숫자 인수를 반복하는 계산과, 호출할 때 내부 상태가 진행하는 난수를 구별합니다.

계산은 입력값으로, 난수는 시드와 호출 순서로 결정됩니다 Math47/25 실수 몫1.88→ceil2. 별도 Random42의 같은 객체에서 nextInt3 첫2free/다음0notice. 영역 간 실행 연결 없음. Math · 입력으로 계산 47개 / 25개 실수 몫 1.88 ceil: pages=2 bounded47 / distance3 Random42 · 같은 객체 첫 nextInt(3) index2 → free 다음 nextInt(3) index0 → notice Math와 Random 예제는 독립 · 시드와 호출 이력을 함께 고정
계산은 입력값으로, 난수는 시드와 호출 순서로 결정됩니다 Math47/25 실수 몫1.88→ceil2. 별도 Random42의 같은 객체에서 nextInt3 첫2free/다음0notice. 영역 간 실행 연결 없음. Math · 수치 입력 47개 / 25개 실수 몫 1.88 ceil의 결과 pages=2 bounded47 / distance3 Random42 · 같은 객체 첫 nextInt(3) index2 → free 다음 nextInt(3) index0 → notice 호출마다 내부 상태가 진행 별도 프로그램 사이 연결 없음
명시적 계산 입력과 난수의 시드 및 호출 순서
실행 지점결과를 정하는 입력·상태출력
MathBasics게시글 47개, 페이지 크기 25. ceil, min/max, abs에 값을 전달합니다.
pages=2
bounded=47
distance=3
난수 첫 호출new Random(42)의 첫 nextInt(3)은 인덱스 2입니다.first=free
난수 둘째 호출같은 객체의 다음 nextInt(3)은 인덱스 0입니다. 시드를 다시 설정하지 않습니다.second=notice
MathBasics
결과를 정하는 입력·상태: 게시글 47개, 페이지 크기 25. ceil, min/max, abs에 값을 전달합니다.
출력:
pages=2
bounded=47
distance=3
난수 첫 호출
결과를 정하는 입력·상태: new Random(42)의 첫 nextInt(3)은 인덱스 2입니다.
출력: first=free
난수 둘째 호출
결과를 정하는 입력·상태: 같은 객체의 다음 nextInt(3)은 인덱스 0입니다. 시드를 다시 설정하지 않습니다.
출력: second=notice

새 Random(42)에 같은 메서드 호출 순서를 적용하면 같은 난수 순서를 재현합니다. 같은 객체를 계속 호출하는 것은 처음부터 재생하는 것과 다릅니다.

계산 비교는 여기서 사용한 Math 메서드에 한정합니다. Math.random()은 난수 생성기를 사용합니다. 일반 Random은 보안 토큰 용도가 아닙니다.

이 예의 조건

  • Math와 Random은 독립 예제이다.
  • 난수 객체의 시드와 호출 순서를 고정한다.
관계도의 보충 설명
원 도표의 전체 관계·라벨·조건
구분내용
2Math의 47/25 실수 몫 1.88→ceil 결과 2. 별도 Random(42)의 같은 객체에서 nextInt(3)의 첫 결과는 인덱스 2인 free, 다음 결과는 인덱스 0인 notice. 영역 간 실행 연결 없음.
447개
525개
8bounded47
9distance3
10Random(42) · 같은 객체
원 도표의 설명과 주석
구분내용
설명Math의 47/25 실수 몫 1.88→ceil 결과 2. 별도 Random(42)의 같은 객체에서 nextInt(3)의 첫 결과는 인덱스 2인 free, 다음 결과는 인덱스 0인 notice. 영역 간 실행 연결 없음.
전체 읽기 정보계산 입력과 난수의 호출 상태를 구별한다 계산 입력과 난수의 호출 상태를 구별한다 MathBasics의 명시적 수치 입력은 pages 2, bounded 47, distance 3을 결정한다. SeededPicker의 Random(42)는 같은 객체에 nextInt(3)를 두 번 호출하여 free 다음 notice를 고른다. 계산은 입력값으로, 난수는 시드와 호출 순서로 결정됩니다 MathBasics 와 SeededPicker 는 별도 프로그램입니다. 같은 숫자 인수를 반복하는 계산과, 호출할 때 내부 상태가 진행하는 난수를 구별합니다. 명시적 계산 입력과 난수의 시드 및 호출 순서 실행 지점 결과를 정하는 입력·상태 출력 MathBasics 게시글 47 개, 페이지 크기 25 . ceil , min/max , abs 에 값을 전달합니다. pages=2 bounded=47 distance=3 난수 첫 호출 new Random(42) 의 첫 nextInt(3) 은 인덱스 2 입니다. first=free 난수 둘째 호출 같은 객체의 다음 nextInt(3) 은 인덱스 0 입니다. 시드를 다시 설정하지 않습니다. second=notice MathBasics 결과를 정하는 입력·상태: 게시글 47 개, 페이지 크기 25 . ceil , min/max , abs 에 값을 전달합니다. 출력: pages=2 bounded=47 distance=3 난수 첫 호출 결과를 정하는 입력·상태: new Random(42) 의 첫 nextInt(3) 은 인덱스 2 입니다. 출력: first=free 난수 둘째 호출 결과를 정하는 입력·상태: 같은 객체의 다음 nextInt(3) 은 인덱스 0 입니다. 시드를 다시 설정하지 않습니다. 출력: second=notice 새 Random(42) 에 같은 메서드 호출 순서를 적용하면 같은 난수 순서를 재현합니다. 같은 객체를 계속 호출하는 것은 처음부터 재생하는 것과 다릅니다. 계산 비교는 여기서 사용한 Math 메서드에 한정합니다. Math.random() 은 난수 생성기를 사용합니다. 일반 Random 은 보안 토큰 용도가 아닙니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
초기 조건

Random(42)를 만들고 별도 Math 계산 조건을 둡니다.

  • 시드: 42
  • Math: 47/25=1.88, ceil=2
  • 기타 계산: bounded 47, distance 3
  • 계산 입력과 난수 호출 이력은 서로 다른 결정 조건이다.
첫 난수 호출

같은 객체의 첫 nextInt(3)을 사용합니다.

  • index: 2
  • 선택: free
  • 첫 호출이 난수 객체의 이력을 진행시킨다.
다음 난수 호출

같은 객체를 다시 호출합니다.

  • index: 0
  • 선택: notice
  • 시드가 같아도 매번 첫 결과를 다시 반환하는 것이 아니다.

계산 입력과 난수의 호출 상태를 구별한다

같은 Random(42)의 두 nextInt 호출에서 index 2→index 0으로 상태가 진행합니다. 독립 Math 예제를 고정한 채 동일 난수 객체의 호출 이력을 단계로 읽으며 상태 변화를 확인할 수 있습니다.

계산 입력과 난수의 호출 상태를 구별한다

MathBasics의 명시적 수치 입력은 pages2 bounded47 distance3을 결정한다. SeededPicker의 Random42는 같은 객체에 nextInt3를 두 번 호출하여 free 다음 notice를 고른다.

계산은 입력값으로, 난수는 시드와 호출 순서로 결정됩니다

MathBasics와 SeededPicker는 별도 프로그램입니다. 같은 숫자 인수를 반복하는 계산과, 호출할 때 내부 상태가 진행하는 난수를 구별합니다.

계산은 입력값으로, 난수는 시드와 호출 순서로 결정됩니다 Math47/25 실수 몫1.88→ceil2. 별도 Random42의 같은 객체에서 nextInt3 첫2free/다음0notice. 영역 간 실행 연결 없음. Math · 입력으로 계산 47개 / 25개 실수 몫 1.88 ceil: pages=2 bounded47 / distance3 Random42 · 같은 객체 첫 nextInt(3) index2 → free 다음 nextInt(3) index0 → notice Math와 Random 예제는 독립 · 시드와 호출 이력을 함께 고정
계산은 입력값으로, 난수는 시드와 호출 순서로 결정됩니다 Math47/25 실수 몫1.88→ceil2. 별도 Random42의 같은 객체에서 nextInt3 첫2free/다음0notice. 영역 간 실행 연결 없음. Math · 수치 입력 47개 / 25개 실수 몫 1.88 ceil의 결과 pages=2 bounded47 / distance3 Random42 · 같은 객체 첫 nextInt(3) index2 → free 다음 nextInt(3) index0 → notice 호출마다 내부 상태가 진행 별도 프로그램 사이 연결 없음
명시적 계산 입력과 난수의 시드 및 호출 순서
실행 지점결과를 정하는 입력·상태출력
MathBasics게시글 47개, 페이지 크기 25. ceil, min/max, abs에 값을 전달합니다.
pages=2
bounded=47
distance=3
난수 첫 호출new Random(42)의 첫 nextInt(3)은 인덱스 2입니다.first=free
난수 둘째 호출같은 객체의 다음 nextInt(3)은 인덱스 0입니다. 시드를 다시 설정하지 않습니다.second=notice
MathBasics
결과를 정하는 입력·상태: 게시글 47개, 페이지 크기 25. ceil, min/max, abs에 값을 전달합니다.
출력:
pages=2
bounded=47
distance=3
난수 첫 호출
결과를 정하는 입력·상태: new Random(42)의 첫 nextInt(3)은 인덱스 2입니다.
출력: first=free
난수 둘째 호출
결과를 정하는 입력·상태: 같은 객체의 다음 nextInt(3)은 인덱스 0입니다. 시드를 다시 설정하지 않습니다.
출력: second=notice

새 Random(42)에 같은 메서드 호출 순서를 적용하면 같은 난수 순서를 재현합니다. 같은 객체를 계속 호출하는 것은 처음부터 재생하는 것과 다릅니다.

계산 비교는 여기서 사용한 Math 메서드에 한정합니다. Math.random()은 난수 생성기를 사용합니다. 일반 Random은 보안 토큰 용도가 아닙니다.

이 예의 조건

  • Math와 Random은 독립 예제이다.
  • 난수 객체의 시드와 호출 순서를 고정한다.

관계도의 보충 설명

원 도표의 전체 관계·라벨·조건
구분내용
2Math의 47/25 실수 몫 1.88→ceil 결과 2. 별도 Random(42)의 같은 객체에서 nextInt(3)의 첫 결과는 인덱스 2인 free, 다음 결과는 인덱스 0인 notice. 영역 간 실행 연결 없음.
447개
525개
8bounded47
9distance3
10Random(42) · 같은 객체
원 도표의 설명과 주석
구분내용
설명Math의 47/25 실수 몫 1.88→ceil 결과 2. 별도 Random(42)의 같은 객체에서 nextInt(3)의 첫 결과는 인덱스 2인 free, 다음 결과는 인덱스 0인 notice. 영역 간 실행 연결 없음.
전체 읽기 정보계산 입력과 난수의 호출 상태를 구별한다 계산 입력과 난수의 호출 상태를 구별한다 MathBasics의 명시적 수치 입력은 pages 2, bounded 47, distance 3을 결정한다. SeededPicker의 Random(42)는 같은 객체에 nextInt(3)를 두 번 호출하여 free 다음 notice를 고른다. 계산은 입력값으로, 난수는 시드와 호출 순서로 결정됩니다 MathBasics 와 SeededPicker 는 별도 프로그램입니다. 같은 숫자 인수를 반복하는 계산과, 호출할 때 내부 상태가 진행하는 난수를 구별합니다. 명시적 계산 입력과 난수의 시드 및 호출 순서 실행 지점 결과를 정하는 입력·상태 출력 MathBasics 게시글 47 개, 페이지 크기 25 . ceil , min/max , abs 에 값을 전달합니다. pages=2 bounded=47 distance=3 난수 첫 호출 new Random(42) 의 첫 nextInt(3) 은 인덱스 2 입니다. first=free 난수 둘째 호출 같은 객체의 다음 nextInt(3) 은 인덱스 0 입니다. 시드를 다시 설정하지 않습니다. second=notice MathBasics 결과를 정하는 입력·상태: 게시글 47 개, 페이지 크기 25 . ceil , min/max , abs 에 값을 전달합니다. 출력: pages=2 bounded=47 distance=3 난수 첫 호출 결과를 정하는 입력·상태: new Random(42) 의 첫 nextInt(3) 은 인덱스 2 입니다. 출력: first=free 난수 둘째 호출 결과를 정하는 입력·상태: 같은 객체의 다음 nextInt(3) 은 인덱스 0 입니다. 시드를 다시 설정하지 않습니다. 출력: second=notice 새 Random(42) 에 같은 메서드 호출 순서를 적용하면 같은 난수 순서를 재현합니다. 같은 객체를 계속 호출하는 것은 처음부터 재생하는 것과 다릅니다. 계산 비교는 여기서 사용한 Math 메서드에 한정합니다. Math.random() 은 난수 생성기를 사용합니다. 일반 Random 은 보안 토큰 용도가 아닙니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
초기 조건

Random(42)를 만들고 별도 Math 계산 조건을 둡니다.

  • 시드: 42
  • Math: 47/25=1.88, ceil=2
  • 기타 계산: bounded 47, distance 3
  • 계산 입력과 난수 호출 이력은 서로 다른 결정 조건이다.
첫 난수 호출

같은 객체의 첫 nextInt(3)을 사용합니다.

  • index: 2
  • 선택: free
  • 첫 호출이 난수 객체의 이력을 진행시킨다.
다음 난수 호출

같은 객체를 다시 호출합니다.

  • index: 0
  • 선택: notice
  • 시드가 같아도 매번 첫 결과를 다시 반환하는 것이 아니다.

배열 복사

System.arraycopy는 한 배열의 연속 구간을 다른 배열로 복사합니다.

시작 위치와 길이의 단위는 원소 개수입니다.

src/ArrayCopyBasics.java
import java.util.Arrays;

public class ArrayCopyBasics {
    public static void main(String[] args) {
        int[] source = {20, 30, 40, 50};
        int[] target = new int[2];

        System.arraycopy(source, 1, target, 0, 2);
        System.out.println(Arrays.toString(target));
    }
}
[30, 40]

이 호출은 source[1]의 30을 target[0]에, source[2]의 40을 target[1]에 복사합니다. 마지막 인수 2는 마지막 인덱스가 아니라 복사할 원소 수입니다. source의 네 값과 target의 길이 2는 그대로이며, target의 두 값만 바뀝니다.

잘못된 시작 위치나 너무 긴 길이는 배열 범위 예외를 만듭니다.

단순 반복문이 더 읽기 쉬운 작은 변환이라면 반복문을 사용하고, 연속 구간 복사라는 의도가 분명하면 arraycopy 또는 Arrays.copyOfRange를 선택합니다.

arraycopy는 원본의 두 값을 대상 슬롯에 복사한다

arraycopy의 원본 구간과 대상 구간, 호출 뒤 대상 값 변경·원본 보존을 추적합니다. 범위 선택과 호출 결과를 단계로 구별하되 개별 원소 복사 순서를 구현 보장처럼 만들면 안 됩니다.

arraycopy는 원본의 두 값을 대상 슬롯에 복사한다

System.arraycopy(source,1,target,0,2)는 source1의30을 target0에, source2의40을 target1에 복사한다. 원본20,30,40,50은 유지되고 대상만0,0에서30,40이 된다.

arraycopy는 두 값을 복사하고 원본 배열은 유지합니다

System.arraycopy(source, 1, target, 0, 2)에서 시작 인덱스는 원본 1, 대상 0, 길이는 2개입니다.

arraycopy는 두 값을 복사하고 원본 배열은 유지합니다 source20/30/40/50을 유지하며 source1의30→target0,source2의40→target1로 int값 복사. 점선은 값 복사이며 이동이나 참조 공유가 아니다. source target [0] 20 [1] 30 [2] 40 [3] 50 [0] 30 [1] 40 source[1] → target[0] source[2] → target[1] 점선: int값 복사 source의 값은 유지
원본과 대상의 두 인덱스 대응 원본 source의 1번30과 2번40이 대상 target의 0번과 1번으로 각각 복사된다. 원본의 네 값20,30,40,50은 남고 대상은30,40이 된다. 연결선은 값 복사이며 객체 이동이나 참조 공유가 아니다. 값 복사 값 복사 source · 복사 뒤에도 원본 유지 [0] 20 [1] 30 [2] 40 [3] 50 target [0] 30 [1] 40
원본 배열 source
[20, 30, 40, 50]. 복사 후에도 이 네 값이 유지됩니다.
첫 번째 대응
source[1]의 30을 target[0]에 복사합니다.
두 번째 대응
source[2]의 40을 target[1]에 복사합니다.
대상 배열 target
new int[2]의 [0, 0]이 [30, 40]으로 바뀝니다.

원문의 출력은 [30, 40]입니다. 값은 복사되며 원본에서 지워지지 않습니다. 대상 배열의 길이 2도 바뀌지 않습니다.

이 예의 조건

  • arraycopy 내부의 개별 원소 복사 순서는 구현 보장으로 표현하지 않는다.
관계도의 보충 설명
원 도표의 전체 관계·라벨·조건
구분내용
2source의 20/30/40/50을 유지하며 source[1]의 30→target[0], source[2]의 40→target[1]로 int 값 복사. 점선은 값 복사이며 이동이나 참조 공유가 아니다.
19점선: int 값 복사
원 도표의 설명과 주석
구분내용
설명source의 20/30/40/50을 유지하며 source[1]의 30→target[0], source[2]의 40→target[1]로 int 값 복사. 점선은 값 복사이며 이동이나 참조 공유가 아니다.
전체 읽기 정보arraycopy는 원본의 두 값을 대상 슬롯에 복사한다 arraycopy는 원본의 두 값을 대상 슬롯에 복사한다 System.arraycopy(source,1,target,0,2)는 source[1]의 30을 target[0]에, source[2]의 40을 target[1]에 복사한다. 원본 20,30,40,50은 유지되고 대상만 0,0에서 30,40이 된다. arraycopy는 두 값을 복사하고 원본 배열은 유지합니다 System.arraycopy(source, 1, target, 0, 2) 에서 시작 인덱스는 원본 1 , 대상 0 , 길이는 2 개입니다. 원본 배열 source [20, 30, 40, 50] . 복사 후에도 이 네 값이 유지됩니다. 첫 번째 대응 source[1] 의 30 을 target[0] 에 복사합니다. 두 번째 대응 source[2] 의 40 을 target[1] 에 복사합니다. 대상 배열 target new int[2] 의 [0, 0] 이 [30, 40] 으로 바뀝니다. 원문의 출력은 [30, 40] 입니다. 값은 복사되며 원본에서 지워지지 않습니다. 대상 배열의 길이 2 도 바뀌지 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
구간 지정

source의 인덱스 1부터 두 값을 대상으로 고릅니다.

  • source: [20,30,40,50]
  • 복사 구간: source[1,3) → target[0,2)
  • 원본과 대상의 대응 인덱스를 호출 전에 정한다.
벌크 복사

arraycopy 호출이 두 int 값을 대상 구간에 반영합니다.

  • 대응: 30→target[0], 40→target[1]
  • 의미: 값 복사
  • 원본 요소를 이동하거나 배열 참조를 공유하는 연산이 아니다.
호출 후

target은 30/40이고 source는 그대로입니다.

  • target: [30,40]
  • source: [20,30,40,50] 유지
  • 원본 보존과 대상 변경이 동시에 성립한다.

arraycopy는 원본의 두 값을 대상 슬롯에 복사한다

arraycopy의 원본 구간과 대상 구간, 호출 뒤 대상 값 변경·원본 보존을 추적합니다. 범위 선택과 호출 결과를 단계로 구별하되 개별 원소 복사 순서를 구현 보장처럼 만들면 안 됩니다.

arraycopy는 원본의 두 값을 대상 슬롯에 복사한다

System.arraycopy(source,1,target,0,2)는 source1의30을 target0에, source2의40을 target1에 복사한다. 원본20,30,40,50은 유지되고 대상만0,0에서30,40이 된다.

arraycopy는 두 값을 복사하고 원본 배열은 유지합니다

System.arraycopy(source, 1, target, 0, 2)에서 시작 인덱스는 원본 1, 대상 0, 길이는 2개입니다.

arraycopy는 두 값을 복사하고 원본 배열은 유지합니다 source20/30/40/50을 유지하며 source1의30→target0,source2의40→target1로 int값 복사. 점선은 값 복사이며 이동이나 참조 공유가 아니다. source target [0] 20 [1] 30 [2] 40 [3] 50 [0] 30 [1] 40 source[1] → target[0] source[2] → target[1] 점선: int값 복사 source의 값은 유지
원본과 대상의 두 인덱스 대응 원본 source의 1번30과 2번40이 대상 target의 0번과 1번으로 각각 복사된다. 원본의 네 값20,30,40,50은 남고 대상은30,40이 된다. 연결선은 값 복사이며 객체 이동이나 참조 공유가 아니다. 값 복사 값 복사 source · 복사 뒤에도 원본 유지 [0] 20 [1] 30 [2] 40 [3] 50 target [0] 30 [1] 40
원본 배열 source
[20, 30, 40, 50]. 복사 후에도 이 네 값이 유지됩니다.
첫 번째 대응
source[1]의 30을 target[0]에 복사합니다.
두 번째 대응
source[2]의 40을 target[1]에 복사합니다.
대상 배열 target
new int[2]의 [0, 0]이 [30, 40]으로 바뀝니다.

원문의 출력은 [30, 40]입니다. 값은 복사되며 원본에서 지워지지 않습니다. 대상 배열의 길이 2도 바뀌지 않습니다.

이 예의 조건

  • arraycopy 내부의 개별 원소 복사 순서는 구현 보장으로 표현하지 않는다.

관계도의 보충 설명

원 도표의 전체 관계·라벨·조건
구분내용
2source의 20/30/40/50을 유지하며 source[1]의 30→target[0], source[2]의 40→target[1]로 int 값 복사. 점선은 값 복사이며 이동이나 참조 공유가 아니다.
19점선: int 값 복사
원 도표의 설명과 주석
구분내용
설명source의 20/30/40/50을 유지하며 source[1]의 30→target[0], source[2]의 40→target[1]로 int 값 복사. 점선은 값 복사이며 이동이나 참조 공유가 아니다.
전체 읽기 정보arraycopy는 원본의 두 값을 대상 슬롯에 복사한다 arraycopy는 원본의 두 값을 대상 슬롯에 복사한다 System.arraycopy(source,1,target,0,2)는 source[1]의 30을 target[0]에, source[2]의 40을 target[1]에 복사한다. 원본 20,30,40,50은 유지되고 대상만 0,0에서 30,40이 된다. arraycopy는 두 값을 복사하고 원본 배열은 유지합니다 System.arraycopy(source, 1, target, 0, 2) 에서 시작 인덱스는 원본 1 , 대상 0 , 길이는 2 개입니다. 원본 배열 source [20, 30, 40, 50] . 복사 후에도 이 네 값이 유지됩니다. 첫 번째 대응 source[1] 의 30 을 target[0] 에 복사합니다. 두 번째 대응 source[2] 의 40 을 target[1] 에 복사합니다. 대상 배열 target new int[2] 의 [0, 0] 이 [30, 40] 으로 바뀝니다. 원문의 출력은 [30, 40] 입니다. 값은 복사되며 원본에서 지워지지 않습니다. 대상 배열의 길이 2 도 바뀌지 않습니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
구간 지정

source의 인덱스 1부터 두 값을 대상으로 고릅니다.

  • source: [20,30,40,50]
  • 복사 구간: source[1,3) → target[0,2)
  • 원본과 대상의 대응 인덱스를 호출 전에 정한다.
벌크 복사

arraycopy 호출이 두 int 값을 대상 구간에 반영합니다.

  • 대응: 30→target[0], 40→target[1]
  • 의미: 값 복사
  • 원본 요소를 이동하거나 배열 참조를 공유하는 연산이 아니다.
호출 후

target은 30/40이고 source는 그대로입니다.

  • target: [30,40]
  • source: [20,30,40,50] 유지
  • 원본 보존과 대상 변경이 동시에 성립한다.

도구 선택 기준

질문도구
프로세스의 출력·속성이 필요한가System
같은 입력의 수치 결과가 필요한가Math
난수 순서가 필요한가Random
예측하기 어려운 보안 난수가 필요한가SecureRandom
타입의 필드·메서드를 실행 중 조사하는가ch28의 Class·리플렉션

연습 문제

1부터 100 사이의 게시글 수를 무작위로 하나 고르고, 페이지당 25개씩 보여 줄 때 몇 페이지가 필요한지 올림해 출력하세요.

Random(7)을 사용해 반복 실행 결과를 재현하고, 결과가 항상 1~100 범위인지 Math.min·Math.max 없이 조건문으로 확인하세요.

난수 생성은 한 번만 호출합니다. 그 결과로 페이지 수를 계산한 뒤, 게시글 수가 1·25·26·100일 때 페이지 수가 각각 얼마인지도 따로 확인하세요.

범위와 올림을 나누어 푸는 예시

nextInt(100)은 0..99이므로 1을 더하면 1..100입니다. 범위 밖 값을 억지로 min/max로 맞추면 생성식의 실수를 숨길 수 있으므로 조건으로 검사합니다.

src/RandomPagesExercise.java
import java.util.Random;

public final class RandomPagesExercise {
    public static void main(String[] args) {
        int postCount = new Random(7).nextInt(100) + 1;
        if (postCount < 1 || postCount > 100) {
            throw new IllegalStateException("postCount");
        }
        int pages = (int) Math.ceil((double) postCount / 25);
        System.out.println("posts=" + postCount);
        System.out.println("pages=" + pages);
    }
}
posts=37
pages=2

별도 경계 입력 1·25·26·100의 페이지 수는 1·1·2·4입니다. 첫 페이지가 꽉 찬 25와 다음 페이지가 필요한 26을 비교하면 올림이 필요한 이유를 확인할 수 있습니다.