[CS] 자바 추가 내용

U·2025년 12월 5일

CS

목록 보기
16/23
post-thumbnail

이번주가 CS 스터디의 마지막 Java 주제로, 추가적으로 알면 좋을 내용들을 알아보았다.

📚 Call by Value vs Call by Reference

그럼 자바는 Call by Value일까, Call by Reference일까?

먼저 Call by Value와 Call by Reference에 대해서 알아보겠다.

Call by Value = 값에 의한 호출

메서드를 호출할 때, 인자로 전달되는 변수의 값 자체를 복사하여 전달하는 방식이다.

전달되는 변수가 int, boolean, double과 같은 기본 타입(Primitive Type)일 경우, 변수가 가진 실제 데이터 값이 복사되어 전달된다. 따라서 메서드 내에서 복사된 값(매개변수)을 변경하더라도, 원본 변수에는 아무런 영향이 없다.

  public class CallByValueExample {

      // 메서드: 전달받은 정수 값을 100으로 변경
      public static void changeValue(int number) {
          System.out.println("  [메서드 내부 시작] 매개변수 number: " + number); // 10
          number = 100; // 매개변수 값 변경 (원본이 아닌 복사된 값 변경)
          System.out.println("  [메서드 내부 종료] 변경된 number: " + number); // 100
      }

      public static void main(String[] args) {
          int original = 10;
          System.out.println("호출 전: original = " + original); // 10

          changeValue(original); // original의 값 '10'이 복사되어 전달됨

          System.out.println("호출 후: original = " + original); // 결과: 10
      }
  }

반면 변수가 객체와 배열 같은 참조 타입(Reference Type)일 경우, 변수가 가진 객체의 주소 값이 복사되어 전달된다. 즉, 원본 변수와 매개변수가 모두 동일한 메모리 주소를 가리키게 된다. 메서드 내에서 이 주소에 접근하여 객체의 내부 내용을 변경하면, 원본 객체의 내용도 변경된다. 하지만 매개변수에 새로운 객체를 할당하여 주소 값을 변경하더라도, 원본 변수는 여전히 기존 객체를 가리키고 있으므로 원본 변수 자체는 변경되지 않는다.

  class Data {
      int value;
      public Data(int v) {
          this.value = v;
      }
  }

  public class CallByValueReferenceExample {

      // 1. 객체의 '내용'을 변경하는 메서드 (원본 객체에 영향 O)
      public static void modifyObjectContent(Data data) {
          // data는 원본 객체의 '주소 복사본'을 가지고 있음
          data.value = 50; // 주소를 따라가 객체 내부 필드 값 변경
      }

      // 2. 객체 '자체'(주소)를 변경하는 메서드 (원본 변수에 영향 X)
      public static void changeObjectReference(Data data) {
          data = new Data(999); // 매개변수 data에 '새로운 객체의 주소' 할당
          // 이 변경은 복사된 주소 값만 변경하며, 원본 변수에는 영향을 미치지 않음
          System.out.println("  [메서드 내부] 새 객체 value: " + data.value); // 999
      }

      public static void main(String[] args) {
          Data myData = new Data(10);
          System.out.println("초기 값: myData.value = " + myData.value); // 10

          // Case 1: 객체 '내용' 변경 (Call by Value이지만, 복사된 주소로 객체 내용 변경)
          modifyObjectContent(myData);
          System.out.println("내용 변경 후: myData.value = " + myData.value); // 결과: 50 (객체 내용이 변경됨)

          // Case 2: 객체 '참조(주소)' 변경 시도 (Call by Value임을 증명)
          changeObjectReference(myData);
          System.out.println("참조 변경 시도 후: myData.value = " + myData.value); // 결과: 50 (원본 변수는 바뀌지 않음)
      }
  }

Call by Reference = 참조에 의한 호출

메서드를 호출할 때, 인자로 전달하는 변수의 메모리 주소(참조 값) 자체를 복사하는 것이 아니라, 원본 변수 자체를 메서드에 전달하는 방식이다.

메서드의 매개변수는 인자로 전달된 원본 변수의 별칭(Alias) 또는 참조가 된다. 즉, 매개변수와 원본 변수가 동일한 메모리 공간을 공유한다.

또한 메서드 내에서 매개변수를 변경하면, 이 변경이 곧 원본 변수의 값게 직접 반영된다.

큰 데이터 구조를 복사할 필요 없이 주소만 사용하여 접근하므로, 데이터 복사 비용을 절약할 수 있어 효율적이다.

C++ 같은 언어가 Call by Reference를 사용하며, swap 함수를 예시로 들어보겠다.

  • ab의 메모리 주소를 swap 함수로 전달
  • 함수 내부의 매개변수 xy는 전달받은 주소에 직접 접근
  • xy의 값을 바꾸는 것은 곧 원본 변수 ab의 메모리 공간에 있는 값 자체를 바꾸는 행위가 됨
구분Call by Reference 시 동작
호출 전int a = 10;, int b = 20;
함수 호출swap(&a, &b) (a와 b의 주소를 전달)
함수 내부매개변수 xy가 원본 a, b를 참조
함수 실행xy의 값을 교환 -> 원본 ab의 값이 변경됨
호출 후a는 20, b는 10이 됨

그래서 자바는 어떤걸 쓰는데?🤔

자바는 오직 Call by Value 방식만을 사용한다.

자바의 모든 인자 전달은 값의 복사를 통해 이루어진다. '값'이 기본 타입인지 참조 타입인지에 따라 동작 방식에 차이가 있어 Call by Reference와 헷갈릴 수 있다.

Call by Reference와 결정적으로 다른 점은, 자바에서는 매개변수에 새로운 객체(새로운 주소)를 할당해도 원본 변수에는 영향을 주지 않는다는 것이다.

  • 자바의 Call by Value (참조 타입의 경우) : 객체의 주소 값 복사본이 전달된다. 매개변수에 새로운 주소를 할당하면, 복사본만 변경되고 원본 주소는 그대로 유지된다.
  • Call by Reference : 객체의 주소 값 자체가 아닌 변수 자체가 전달된다. 매개변수에 새로운 주소를 할당하면, 원본 변수 자체가 그 새로운 주소를 가리키게 되어야 한다. 자바에서는 이렇게 동작하지 않는다.

📚 System.currentTimeMillis() vs System.nanoTime()

두 메서드는 모두 시간을 측정하는 데 사용되지만, 측정 기준과 용도, 정밀도 면에서 큰 차이가 있다.

특징System.currentTimeMillis()System.nanoTime()
단위밀리초(milliseconds, ms)나노초(nanoseconds, ns)
기준 시점표준 시계
- UTC 1970년 1월 1일 00:00:00부터 경과한 시간
임의의 시점(JVM 시작 등)
- 절대적인 날짜/시간과는 관련 없는 임의의 기준 시점
용도절대적인 현재 시간이 필요할 때
예: 로그 기록, 파일 타임스탬프, UUID 생성, 실제 날짜/시간 표시
시간 간격 측정이 필요할 때
예: 코드 성능 측정, 벤치마킹, 경과 시간 계산
영향시스템 시계(OS 시간) 변경(시간 수정, 서머타임)에 영향을 받음
값이 갑자기 점프하거나 뒤로 돌아갈 수 있음
시스템 시계 변경에 영향을 받지 않음
단조 증가(Monotonic)가 보장되어 성능 측정에 적합
정밀도밀리초 단위의 정확도밀리초보다 훨씬 높은 정밀도

System.currentTimeMillis()

  • 현재 시간을 알려주는 메서드
  • 1970년 1월 1일 00:00:00 협정 세계시(UTC)부터 현재까지 경과한 시간을 밀리초 단위의 long으로 반환
  • Wall-Clock Time(벽 세계 시간) 불리며, 실제 달력상의 날짜 및 시간과 일치
  • 운영체제의 시간 설정을 따르기 때문에, 만약 사용자가 컴퓨터의 시간을 변경하거나, 네트워크 시간 동기화(NTP)로 인해 시스템 시간이 조정되면, 이 메서드가 반환하는 값도 갑자기 변동될 수 있음
  • 로그에 타임스탬프를 남기거나, 데이터베이스에 레코드가 생성된 시간을 기록할 때 사용

System.nanoTime()

  • 경과 시간 측정을 위해 설계됨
  • JVM이 시작된 시점이나 다른 임의의 내부적인 기준 시점부터 현재까지 경과한 나노초를 long으로 반환
  • 단조 증가(Monotonic)가 보장되는 시간으로, 시간이 절대 뒤로 돌아가지 않으며 시스템 시계 변경에도 영향을 받지 않음
  • 단위는 나노초이지만, 실제 측정 가능한 정밀도는 OS와 하드웨어에 따라 다를 수 있음
  • 일반적으로 currentTimeMilis()보다 훨씬 더 정교한 시간 간격 측정에 사용됨
  • 특정 코드 블록의 실행 성능을 측정(벤치마킹)할 때 사용

1️⃣ 시작 시간 기록 : long startTime = System.nanoTime()
2️⃣ 작업 실행
3️⃣ 종료 시간 기록 : long endTime = System.nanoTime()
4️⃣ 경과 시간 계산 : long elapsedTime = endTime - startTime;

profile
백엔드 개발자 연습생

0개의 댓글